Message ID | 1431988559-23338-5-git-send-email-manabian@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Joachim, Quoting Joachim Eastwood (2015-05-18 15:35:57) <snip> > +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, > + int base_clk_id, > + const char *parent) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { > + if (clk_branches[i].base_id != base_clk_id) > + continue; > + > + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, > + parent); > + > + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) > + parent = clk_branches[i].name; > + } > +} > + > +static void __init lpc18xx_ccu_init(struct device_node *np) > +{ > + struct lpc18xx_branch_clk_data *clk_data; > + int num_base_ids, *base_ids; > + void __iomem *reg_base; > + const char *parent; > + int base_clk_id; > + int i; > + > + reg_base = of_iomap(np, 0); > + if (!reg_base) { > + pr_warn("%s: failed to map address range\n", __func__); > + return; > + } > + > + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); > + if (!clk_data) > + return; > + > + num_base_ids = of_clk_get_parent_count(np); > + > + base_ids = kcalloc(num_base_ids, sizeof(int), GFP_KERNEL); > + if (!base_ids) { > + kfree(clk_data); > + return; > + } > + > + clk_data->base_ids = base_ids; > + clk_data->num_base_ids = num_base_ids; > + > + for (i = 0; i < num_base_ids; i++) { > + struct clk *clk = of_clk_get(np, i); > + if (IS_ERR(clk)) { > + pr_warn("%s: failed to get clock at idx %d\n", > + __func__, i); > + continue; > + } > + > + parent = __clk_get_name(clk); > + base_clk_id = of_clk_get_index(np, i); > + > + clk_data->base_ids[i] = base_clk_id; > + lpc18xx_ccu_register_branch_clks(reg_base, base_clk_id, > + parent); Thanks for sending V3. This driver is getting close! So the main thing I don't understand is why you do not encode the CGU clock parent string names into this CCU driver. If I understand your approach correctly, you do the following in lpc18xx_ccu_init: 1) count the number of parent clocks (ostensibly CGU clocks) 2) iterate through all of those CGU clocks, extracting a base_id value (loop #1) 3) using this base_id as a key you walk through an array in the CCU driver trying to find a matching base_id value (loop #2) 4) after finding the corresponding CCU clocks you get the parent clock with of_clk_get 5) using of_clk_get you fetch its name with __clk_get_name 6) you pass this parent name into a fairly typical looking registration function Assuming I got all of that right, I hope we can simplify it considerably. You already have an array of CCU clock information in this driver, clk_branches[]. Why not encode the parent string name here? This would involve adding a "parent_name" member to struct lpc18xx_clk_branch. Doing the above, your O(n^2)-ish registration function becomes O(n): 1) iterate through the array of the CCU clocks (clk_branchs[]) 2) register them 3) profit I'm starting to think any reference to base_id is sign that things are wrong in your driver. I am unconvinced that you need to "share" this base_id across CGU and CCU drivers in the way you do. If I'm wrong please help me to understand. As a wild thought, if you do not want to encode parent string names into this driver, have you tried to use the clock-names property in the CCU blob? You do not need clock-output-names in the CGU blob either. But this is just an idea. It is far for straightforward for you t encode the parent names in your clk_branches[] array. Thanks, Mike
Quoting Michael Turquette (2015-05-27 20:44:24) > Hi Joachim, > > Quoting Joachim Eastwood (2015-05-18 15:35:57) > <snip> > > +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, > > + int base_clk_id, > > + const char *parent) > > +{ > > + int i; > > + > > + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { > > + if (clk_branches[i].base_id != base_clk_id) > > + continue; > > + > > + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, > > + parent); > > + > > + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) > > + parent = clk_branches[i].name; > > + } > > +} > > + > > +static void __init lpc18xx_ccu_init(struct device_node *np) > > +{ > > + struct lpc18xx_branch_clk_data *clk_data; > > + int num_base_ids, *base_ids; > > + void __iomem *reg_base; > > + const char *parent; > > + int base_clk_id; > > + int i; > > + > > + reg_base = of_iomap(np, 0); > > + if (!reg_base) { > > + pr_warn("%s: failed to map address range\n", __func__); > > + return; > > + } > > + > > + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); > > + if (!clk_data) > > + return; > > + > > + num_base_ids = of_clk_get_parent_count(np); > > + > > + base_ids = kcalloc(num_base_ids, sizeof(int), GFP_KERNEL); > > + if (!base_ids) { > > + kfree(clk_data); > > + return; > > + } > > + > > + clk_data->base_ids = base_ids; > > + clk_data->num_base_ids = num_base_ids; > > + > > + for (i = 0; i < num_base_ids; i++) { > > + struct clk *clk = of_clk_get(np, i); > > + if (IS_ERR(clk)) { > > + pr_warn("%s: failed to get clock at idx %d\n", > > + __func__, i); > > + continue; > > + } > > + > > + parent = __clk_get_name(clk); > > + base_clk_id = of_clk_get_index(np, i); > > + > > + clk_data->base_ids[i] = base_clk_id; > > + lpc18xx_ccu_register_branch_clks(reg_base, base_clk_id, > > + parent); > > Thanks for sending V3. This driver is getting close! > > So the main thing I don't understand is why you do not encode the CGU > clock parent string names into this CCU driver. If I understand your > approach correctly, you do the following in lpc18xx_ccu_init: Ugh, I just saw that you mentioned this approach in your cover letter. No need for you to answer all of my questions then. Please make the change to encode the parent string names directly. V4 should be good to go after that. Thanks, Mike > > 1) count the number of parent clocks (ostensibly CGU clocks) > 2) iterate through all of those CGU clocks, extracting a base_id value > (loop #1) > 3) using this base_id as a key you walk through an array in the CCU > driver trying to find a matching base_id value > (loop #2) > 4) after finding the corresponding CCU clocks you get the parent clock > with of_clk_get > 5) using of_clk_get you fetch its name with __clk_get_name > 6) you pass this parent name into a fairly typical looking registration > function > > Assuming I got all of that right, I hope we can simplify it > considerably. > > You already have an array of CCU clock information in this driver, > clk_branches[]. Why not encode the parent string name here? This would > involve adding a "parent_name" member to struct lpc18xx_clk_branch. > > Doing the above, your O(n^2)-ish registration function becomes O(n): > > 1) iterate through the array of the CCU clocks (clk_branchs[]) > 2) register them > 3) profit > > I'm starting to think any reference to base_id is sign that things are > wrong in your driver. I am unconvinced that you need to "share" this > base_id across CGU and CCU drivers in the way you do. If I'm wrong > please help me to understand. > > As a wild thought, if you do not want to encode parent string names into > this driver, have you tried to use the clock-names property in the CCU > blob? You do not need clock-output-names in the CGU blob either. But > this is just an idea. It is far for straightforward for you t encode the > parent names in your clk_branches[] array. > > Thanks, > Mike
Hi Michael, On 28 May 2015 at 05:44, Michael Turquette <mturquette@linaro.org> wrote: > Thanks for sending V3. This driver is getting close! Thanks for your patience. > So the main thing I don't understand is why you do not encode the CGU > clock parent string names into this CCU driver. If I understand your > approach correctly, you do the following in lpc18xx_ccu_init: > > 1) count the number of parent clocks (ostensibly CGU clocks) > 2) iterate through all of those CGU clocks, extracting a base_id value > (loop #1) > 3) using this base_id as a key you walk through an array in the CCU > driver trying to find a matching base_id value > (loop #2) > 4) after finding the corresponding CCU clocks you get the parent clock > with of_clk_get > 5) using of_clk_get you fetch its name with __clk_get_name > 6) you pass this parent name into a fairly typical looking registration > function > > Assuming I got all of that right, I hope we can simplify it > considerably. > > You already have an array of CCU clock information in this driver, > clk_branches[]. Why not encode the parent string name here? This would > involve adding a "parent_name" member to struct lpc18xx_clk_branch. > > Doing the above, your O(n^2)-ish registration function becomes O(n): > > 1) iterate through the array of the CCU clocks (clk_branchs[]) > 2) register them > 3) profit Since there are two instances of the CCU IP block and the clk_branchs[] table contain branch clocks from both of them the driver needs to know on which CCU instance the clocks belong. So just registering all clocks in clk_branchs[] we would end up with all the clocks on both CCU nodes and some clock ids would conflict. An alternative approach could be to split the clk_branchs[]. Each one with branch clocks for the respective CCU. But the driver would still need to know which of the tables to register. This could be done either by using two DT compac strings; like "nxp,lpc1850-ccu1" and "...-ccu2" or by having a 'id' propery in DT. I don't find these two approaches very appealing. So the reason for some of the complexity in the driver is to keep it generic and work on multiple CCU instances by looking at the connected clocks. I hope this make it more understandable. Sorry for failing to describe the hardware properly. Either way I'll send out a v4 with parent clocks in the clk_branchs[] and which use a clock-names property to match the clocks between the CCU. Let me know what you think when you get a chance to look at it. > I'm starting to think any reference to base_id is sign that things are > wrong in your driver. I am unconvinced that you need to "share" this > base_id across CGU and CCU drivers in the way you do. If I'm wrong > please help me to understand. I don't mind putting the parent clock names in the clk_branches[] but the driver still needs to distinguish between the CCUs in the system and register the correct clocks on them. If you have a another good way of doing so I am all ears. I have put up the full DT in the link if you would like to take a look. http://slexy.org/raw/s21h9lCxQa You may also find the data sheet at the link below. There is a figure of the CGU and both CCUs at page 161, Figure 34. http://www.nxp.com/documents/user_manual/UM10503.pdf regards, Joachim Eastwood
diff --git a/drivers/clk/nxp/Makefile b/drivers/clk/nxp/Makefile index aca9d3e4f7fe..7f608b0ad7b4 100644 --- a/drivers/clk/nxp/Makefile +++ b/drivers/clk/nxp/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c new file mode 100644 index 000000000000..75dc61948fca --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -0,0 +1,305 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU) + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <dt-bindings/clock/lpc18xx-cgu.h> +#include <dt-bindings/clock/lpc18xx-ccu.h> + +/* Bit defines for CCU branch configuration register */ +#define LPC18XX_CCU_RUN BIT(0) +#define LPC18XX_CCU_AUTO BIT(1) +#define LPC18XX_CCU_DIV BIT(5) +#define LPC18XX_CCU_DIVSTAT BIT(27) + +/* CCU branch feature bits */ +#define CCU_BRANCH_IS_BUS BIT(0) +#define CCU_BRANCH_HAVE_DIV2 BIT(1) + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +struct lpc18xx_branch_clk_data { + int *base_ids; + int num_base_ids; +}; + +struct lpc18xx_clk_branch { + int base_id; + const char *name; + u16 offset; + u16 flags; + struct clk *clk; + struct clk_gate gate; +}; + +static struct lpc18xx_clk_branch clk_branches[] = { + {BASE_APB3_CLK, "apb3_bus", CLK_APB3_BUS, CCU_BRANCH_IS_BUS}, + {BASE_APB3_CLK, "apb3_i2c1", CLK_APB3_I2C1, 0}, + {BASE_APB3_CLK, "apb3_dac", CLK_APB3_DAC, 0}, + {BASE_APB3_CLK, "apb3_adc0", CLK_APB3_ADC0, 0}, + {BASE_APB3_CLK, "apb3_adc1", CLK_APB3_ADC1, 0}, + {BASE_APB3_CLK, "apb3_can0", CLK_APB3_CAN0, 0}, + + {BASE_APB1_CLK, "apb1_bus", CLK_APB1_BUS, CCU_BRANCH_IS_BUS}, + {BASE_APB1_CLK, "apb1_motorcon_pwm", CLK_APB1_MOTOCON_PWM, 0}, + {BASE_APB1_CLK, "apb1_i2c0", CLK_APB1_I2C0, 0}, + {BASE_APB1_CLK, "apb1_i2s", CLK_APB1_I2S, 0}, + {BASE_APB1_CLK, "apb1_can1", CLK_APB1_CAN1, 0}, + + {BASE_SPIFI_CLK, "spifi", CLK_SPIFI, 0}, + + {BASE_CPU_CLK, "cpu_bus", CLK_CPU_BUS, CCU_BRANCH_IS_BUS}, + {BASE_CPU_CLK, "cpu_spifi", CLK_CPU_SPIFI, 0}, + {BASE_CPU_CLK, "cpu_gpio", CLK_CPU_GPIO, 0}, + {BASE_CPU_CLK, "cpu_lcd", CLK_CPU_LCD, 0}, + {BASE_CPU_CLK, "cpu_ethernet", CLK_CPU_ETHERNET, 0}, + {BASE_CPU_CLK, "cpu_usb0", CLK_CPU_USB0, 0}, + {BASE_CPU_CLK, "cpu_emc", CLK_CPU_EMC, 0}, + {BASE_CPU_CLK, "cpu_sdio", CLK_CPU_SDIO, 0}, + {BASE_CPU_CLK, "cpu_dma", CLK_CPU_DMA, 0}, + {BASE_CPU_CLK, "cpu_core", CLK_CPU_CORE, 0}, + {BASE_CPU_CLK, "cpu_sct", CLK_CPU_SCT, 0}, + {BASE_CPU_CLK, "cpu_usb1", CLK_CPU_USB1, 0}, + {BASE_CPU_CLK, "cpu_emcdiv", CLK_CPU_EMCDIV, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_flasha", CLK_CPU_FLASHA, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_flashb", CLK_CPU_FLASHB, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_m0app", CLK_CPU_M0APP, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_adchs", CLK_CPU_ADCHS, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_eeprom", CLK_CPU_EEPROM, CCU_BRANCH_HAVE_DIV2}, + {BASE_CPU_CLK, "cpu_wwdt", CLK_CPU_WWDT, 0}, + {BASE_CPU_CLK, "cpu_uart0", CLK_CPU_UART0, 0}, + {BASE_CPU_CLK, "cpu_uart1", CLK_CPU_UART1, 0}, + {BASE_CPU_CLK, "cpu_ssp0", CLK_CPU_SSP0, 0}, + {BASE_CPU_CLK, "cpu_timer0", CLK_CPU_TIMER0, 0}, + {BASE_CPU_CLK, "cpu_timer1", CLK_CPU_TIMER1, 0}, + {BASE_CPU_CLK, "cpu_scu", CLK_CPU_SCU, 0}, + {BASE_CPU_CLK, "cpu_creg", CLK_CPU_CREG, 0}, + {BASE_CPU_CLK, "cpu_ritimer", CLK_CPU_RITIMER, 0}, + {BASE_CPU_CLK, "cpu_uart2", CLK_CPU_UART2, 0}, + {BASE_CPU_CLK, "cpu_uart3", CLK_CPU_UART3, 0}, + {BASE_CPU_CLK, "cpu_timer2", CLK_CPU_TIMER2, 0}, + {BASE_CPU_CLK, "cpu_timer3", CLK_CPU_TIMER3, 0}, + {BASE_CPU_CLK, "cpu_ssp1", CLK_CPU_SSP1, 0}, + {BASE_CPU_CLK, "cpu_qei", CLK_CPU_QEI, 0}, + + {BASE_PERIPH_CLK, "periph_bus", CLK_PERIPH_BUS, CCU_BRANCH_IS_BUS}, + {BASE_PERIPH_CLK, "periph_core", CLK_PERIPH_CORE, 0}, + {BASE_PERIPH_CLK, "periph_sgpio", CLK_PERIPH_SGPIO, 0}, + + {BASE_USB0_CLK, "usb0", CLK_USB0, 0}, + {BASE_USB1_CLK, "usb1", CLK_USB1, 0}, + {BASE_SPI_CLK, "spi", CLK_SPI, 0}, + {BASE_ADCHS_CLK, "adchs", CLK_ADCHS, 0}, + + {BASE_AUDIO_CLK, "audio", CLK_AUDIO, 0}, + {BASE_UART3_CLK, "apb2_uart3", CLK_APB2_UART3, 0}, + {BASE_UART2_CLK, "apb2_uart2", CLK_APB2_UART2, 0}, + {BASE_UART1_CLK, "apb0_uart1", CLK_APB0_UART1, 0}, + {BASE_UART0_CLK, "apb0_uart0", CLK_APB0_UART0, 0}, + {BASE_SSP1_CLK, "apb2_ssp1", CLK_APB2_SSP1, 0}, + {BASE_SSP0_CLK, "apb0_ssp0", CLK_APB0_SSP0, 0}, + {BASE_SDIO_CLK, "sdio", CLK_SDIO, 0}, +}; + +static struct clk *lpc18xx_ccu_branch_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct lpc18xx_branch_clk_data *clk_data = data; + unsigned int offset = clkspec->args[0]; + int i, j; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (clk_branches[i].offset != offset) + continue; + + for (j = 0; j < clk_data->num_base_ids; j++) { + if (clk_data->base_ids[j] == clk_branches[i].base_id) + return clk_branches[i].clk; + } + } + + pr_err("%s: invalid clock offset %d\n", __func__, offset); + + return ERR_PTR(-EINVAL); +} + +static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + u32 val; + + /* + * Divider field is write only, so divider stat field must + * be read so divider field can be set accordingly. + */ + val = clk_readl(gate->reg); + if (val & LPC18XX_CCU_DIVSTAT) + val |= LPC18XX_CCU_DIV; + + if (enable) { + val |= LPC18XX_CCU_RUN; + } else { + /* + * To safely disable a branch clock a squence of two separate + * writes must be used. First write should set the AUTO bit + * and the next write should clear the RUN bit. + */ + val |= LPC18XX_CCU_AUTO; + clk_writel(val, gate->reg); + + val &= ~LPC18XX_CCU_RUN; + } + + clk_writel(val, gate->reg); + + return 0; +} + +static int lpc18xx_ccu_gate_enable(struct clk_hw *hw) +{ + return lpc18xx_ccu_gate_endisable(hw, true); +} + +static void lpc18xx_ccu_gate_disable(struct clk_hw *hw) +{ + lpc18xx_ccu_gate_endisable(hw, false); +} + +static int lpc18xx_ccu_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + + return clk_readl(gate->reg) & LPC18XX_CCU_RUN; +} + +static const struct clk_ops lpc18xx_ccu_gate_ops = { + .enable = lpc18xx_ccu_gate_enable, + .disable = lpc18xx_ccu_gate_disable, + .is_enabled = lpc18xx_ccu_gate_is_enabled, +}; + +static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *branch, + void __iomem *reg_base, + const char *parent) +{ + const struct clk_ops *div_ops = NULL; + struct clk_divider *div = NULL; + struct clk_hw *div_hw = NULL; + + if (branch->flags & CCU_BRANCH_HAVE_DIV2) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return; + + div->reg = branch->offset + reg_base; + div->flags = CLK_DIVIDER_READ_ONLY; + div->shift = 27; + div->width = 1; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + branch->gate.reg = branch->offset + reg_base; + branch->gate.bit_idx = 0; + + branch->clk = clk_register_composite(NULL, branch->name, &parent, 1, + NULL, NULL, + div_hw, div_ops, + &branch->gate.hw, &lpc18xx_ccu_gate_ops, 0); + if (IS_ERR(branch->clk)) { + kfree(div); + pr_warn("%s: failed to register %s\n", __func__, branch->name); + return; + } + + /* Grab essential branch clocks for CPU and SDRAM */ + switch (branch->offset) { + case CLK_CPU_EMC: + case CLK_CPU_CORE: + case CLK_CPU_CREG: + case CLK_CPU_EMCDIV: + clk_prepare_enable(branch->clk); + } +} + +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, + int base_clk_id, + const char *parent) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (clk_branches[i].base_id != base_clk_id) + continue; + + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, + parent); + + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) + parent = clk_branches[i].name; + } +} + +static void __init lpc18xx_ccu_init(struct device_node *np) +{ + struct lpc18xx_branch_clk_data *clk_data; + int num_base_ids, *base_ids; + void __iomem *reg_base; + const char *parent; + int base_clk_id; + int i; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + num_base_ids = of_clk_get_parent_count(np); + + base_ids = kcalloc(num_base_ids, sizeof(int), GFP_KERNEL); + if (!base_ids) { + kfree(clk_data); + return; + } + + clk_data->base_ids = base_ids; + clk_data->num_base_ids = num_base_ids; + + for (i = 0; i < num_base_ids; i++) { + struct clk *clk = of_clk_get(np, i); + if (IS_ERR(clk)) { + pr_warn("%s: failed to get clock at idx %d\n", + __func__, i); + continue; + } + + parent = __clk_get_name(clk); + base_clk_id = of_clk_get_index(np, i); + + clk_data->base_ids[i] = base_clk_id; + lpc18xx_ccu_register_branch_clks(reg_base, base_clk_id, + parent); + } + + of_clk_add_provider(np, lpc18xx_ccu_branch_clk_get, clk_data); +} +CLK_OF_DECLARE(lpc18xx_ccu, "nxp,lpc1850-ccu", lpc18xx_ccu_init); diff --git a/include/dt-bindings/clock/lpc18xx-ccu.h b/include/dt-bindings/clock/lpc18xx-ccu.h new file mode 100644 index 000000000000..bbfe00b6ab7d --- /dev/null +++ b/include/dt-bindings/clock/lpc18xx-ccu.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +/* Clock Control Unit 1 (CCU1) clock offsets */ +#define CLK_APB3_BUS 0x100 +#define CLK_APB3_I2C1 0x108 +#define CLK_APB3_DAC 0x110 +#define CLK_APB3_ADC0 0x118 +#define CLK_APB3_ADC1 0x120 +#define CLK_APB3_CAN0 0x128 +#define CLK_APB1_BUS 0x200 +#define CLK_APB1_MOTOCON_PWM 0x208 +#define CLK_APB1_I2C0 0x210 +#define CLK_APB1_I2S 0x218 +#define CLK_APB1_CAN1 0x220 +#define CLK_SPIFI 0x300 +#define CLK_CPU_BUS 0x400 +#define CLK_CPU_SPIFI 0x408 +#define CLK_CPU_GPIO 0x410 +#define CLK_CPU_LCD 0x418 +#define CLK_CPU_ETHERNET 0x420 +#define CLK_CPU_USB0 0x428 +#define CLK_CPU_EMC 0x430 +#define CLK_CPU_SDIO 0x438 +#define CLK_CPU_DMA 0x440 +#define CLK_CPU_CORE 0x448 +#define CLK_CPU_SCT 0x468 +#define CLK_CPU_USB1 0x470 +#define CLK_CPU_EMCDIV 0x478 +#define CLK_CPU_FLASHA 0x480 +#define CLK_CPU_FLASHB 0x488 +#define CLK_CPU_M0APP 0x490 +#define CLK_CPU_ADCHS 0x498 +#define CLK_CPU_EEPROM 0x4a0 +#define CLK_CPU_WWDT 0x500 +#define CLK_CPU_UART0 0x508 +#define CLK_CPU_UART1 0x510 +#define CLK_CPU_SSP0 0x518 +#define CLK_CPU_TIMER0 0x520 +#define CLK_CPU_TIMER1 0x528 +#define CLK_CPU_SCU 0x530 +#define CLK_CPU_CREG 0x538 +#define CLK_CPU_RITIMER 0x600 +#define CLK_CPU_UART2 0x608 +#define CLK_CPU_UART3 0x610 +#define CLK_CPU_TIMER2 0x618 +#define CLK_CPU_TIMER3 0x620 +#define CLK_CPU_SSP1 0x628 +#define CLK_CPU_QEI 0x630 +#define CLK_PERIPH_BUS 0x700 +#define CLK_PERIPH_CORE 0x710 +#define CLK_PERIPH_SGPIO 0x718 +#define CLK_USB0 0x800 +#define CLK_USB1 0x900 +#define CLK_SPI 0xA00 +#define CLK_ADCHS 0xB00 + +/* Clock Control Unit 2 (CCU2) clock offsets */ +#define CLK_AUDIO 0x100 +#define CLK_APB2_UART3 0x200 +#define CLK_APB2_UART2 0x300 +#define CLK_APB0_UART1 0x400 +#define CLK_APB0_UART0 0x500 +#define CLK_APB2_SSP1 0x600 +#define CLK_APB0_SSP0 0x700 +#define CLK_SDIO 0x800
Add driver for NXP LPC18xx/43xx Clock Control Unit (CCU). The CCU provides fine grained gating of most clocks present in the SoC. Signed-off-by: Joachim Eastwood <manabian@gmail.com> --- drivers/clk/nxp/Makefile | 1 + drivers/clk/nxp/clk-lpc18xx-ccu.c | 305 ++++++++++++++++++++++++++++++++ include/dt-bindings/clock/lpc18xx-ccu.h | 74 ++++++++ 3 files changed, 380 insertions(+) create mode 100644 drivers/clk/nxp/clk-lpc18xx-ccu.c create mode 100644 include/dt-bindings/clock/lpc18xx-ccu.h