diff mbox

[v3,4/6] clk: add lpc18xx ccu clk driver

Message ID 1431988559-23338-5-git-send-email-manabian@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Joachim Eastwood May 18, 2015, 10:35 p.m. UTC
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

Comments

Mike Turquette May 28, 2015, 3:44 a.m. UTC | #1
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
Mike Turquette May 28, 2015, 3:51 a.m. UTC | #2
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
Joachim Eastwood May 28, 2015, 5:13 p.m. UTC | #3
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 mbox

Patch

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