[PATCH 2/6] clk: add lpc18xx cgu clk driver

Joachim Eastwood manabian at gmail.com
Thu Apr 2 13:31:44 PDT 2015


Add driver for NXP LPC18xx/43xx Clock Generation Unit (CGU). The CGU
contains several clock generators and output stages that route the
clocks either directly to peripherals or to a Clock Control Unit
(CCU).

Signed-off-by: Joachim Eastwood <manabian at gmail.com>
---
 drivers/clk/Makefile                    |   1 +
 drivers/clk/clk-lpc18xx-cgu.c           | 570 ++++++++++++++++++++++++++++++++
 include/dt-bindings/clock/lpc18xx-cgu.h |  41 +++
 3 files changed, 612 insertions(+)
 create mode 100644 drivers/clk/clk-lpc18xx-cgu.c
 create mode 100644 include/dt-bindings/clock/lpc18xx-cgu.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d478ceb69c5f..222a4e39bb81 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706)	+= clk-cdce706.o
 obj-$(CONFIG_ARCH_CLPS711X)		+= clk-clps711x.o
 obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
+obj-$(CONFIG_ARCH_LPC18XX)		+= clk-lpc18xx-cgu.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX_GEN)	+= clk-max-gen.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
diff --git a/drivers/clk/clk-lpc18xx-cgu.c b/drivers/clk/clk-lpc18xx-cgu.c
new file mode 100644
index 000000000000..9a941419b613
--- /dev/null
+++ b/drivers/clk/clk-lpc18xx-cgu.c
@@ -0,0 +1,570 @@
+/*
+ * Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU)
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian at 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/delay.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <dt-bindings/clock/lpc18xx-cgu.h>
+
+/* Clock Generation Unit (CGU) registers */
+#define LPC18XX_CGU_XTAL_OSC_CTRL	0x018
+#define LPC18XX_CGU_PLL0USB_STAT	0x01c
+#define LPC18XX_CGU_PLL0USB_CTRL	0x020
+#define LPC18XX_CGU_PLL0USB_MDIV	0x024
+#define LPC18XX_CGU_PLL0USB_NP_DIV	0x028
+#define LPC18XX_CGU_PLL0AUDIO_STAT	0x02c
+#define LPC18XX_CGU_PLL0AUDIO_CTRL	0x030
+#define LPC18XX_CGU_PLL0AUDIO_MDIV	0x034
+#define LPC18XX_CGU_PLL0AUDIO_NP_DIV	0x038
+#define LPC18XX_CGU_PLL0AUDIO_FRAC	0x03c
+#define LPC18XX_CGU_PLL1_STAT		0x040
+#define LPC18XX_CGU_PLL1_CTRL		0x044
+#define  LPC18XX_PLL1_CTRL_FBSEL	BIT(6)
+#define  LPC18XX_PLL1_CTRL_DIRECT	BIT(7)
+#define LPC18XX_CGU_IDIV_CTRL(n)	(0x048 + (n) * sizeof(u32))
+#define LPC18XX_CGU_BASE_CLK(id)	(0x05c + (id) * sizeof(u32))
+
+/* PLL0 bits common to both audio and USB0 PLL */
+#define LPC18XX_PLL0_STAT_LOCK		BIT(0)
+#define LPC18XX_PLL0_CTRL_PD		BIT(0)
+#define LPC18XX_PLL0_CTRL_BYPASS	BIT(1)
+#define LPC18XX_PLL0_CTRL_DIRECTI	BIT(2)
+#define LPC18XX_PLL0_CTRL_DIRECTO	BIT(3)
+#define LPC18XX_PLL0_CTRL_CLKEN		BIT(4)
+#define LPC18XX_PLL0_MDIV_MDEC_MASK	0x1ffff
+#define LPC18XX_PLL0_MDIV_SELP_SHIFT	17
+#define LPC18XX_PLL0_MDIV_SELI_SHIFT	22
+#define LPC18XX_PLL0_MSEL_MAX		BIT(15)
+
+/* Register value that gives PLL0 post/pre dividers equal to 1 */
+#define LPC18XX_PLL0_NP_DIVS_1		0x00302062
+
+#define LPC18XX_CGU_PLLS_NUM		3
+#define LPC18XX_CGU_DIVIDERS_NUM	5
+
+enum {
+	CLK_SRC_OSC32,
+	CLK_SRC_IRC,
+	CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK,
+	CLK_SRC_GP_CLKIN,
+	CLK_SRC_RESERVED1,
+	CLK_SRC_OSC,
+	CLK_SRC_PLL0USB,
+	CLK_SRC_PLL0AUDIO,
+	CLK_SRC_PLL1,
+	CLK_SRC_RESERVED2,
+	CLK_SRC_RESERVED3,
+	CLK_SRC_IDIVA,
+	CLK_SRC_IDIVB,
+	CLK_SRC_IDIVC,
+	CLK_SRC_IDIVD,
+	CLK_SRC_IDIVE,
+	CLK_SRC_MAX
+};
+
+static const char *clk_src_names[CLK_SRC_MAX] = {
+	[CLK_SRC_OSC32]		= "osc32",
+	[CLK_SRC_IRC]		= "irc",
+	[CLK_SRC_ENET_RX_CLK]	= "enet_rx_clk",
+	[CLK_SRC_ENET_TX_CLK]	= "enet_tx_clk",
+	[CLK_SRC_GP_CLKIN]	= "gp_clkin",
+	[CLK_SRC_RESERVED1]	= "",
+	[CLK_SRC_OSC]		= "osc",
+	[CLK_SRC_PLL0USB]	= "pll0usb",
+	[CLK_SRC_PLL0AUDIO]	= "pll0audio",
+	[CLK_SRC_PLL1]		= "pll1",
+	[CLK_SRC_RESERVED2]	= "",
+	[CLK_SRC_RESERVED3]	= "",
+	[CLK_SRC_IDIVA]		= "idiva",
+	[CLK_SRC_IDIVB]		= "idivb",
+	[CLK_SRC_IDIVC]		= "idivc",
+	[CLK_SRC_IDIVD]		= "idivd",
+	[CLK_SRC_IDIVE]		= "idive",
+};
+
+static const char *base_clk_names[BASE_CLK_MAX];
+
+static u32 pll0_srcs_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL1, CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC,
+	CLK_SRC_IDIVD, CLK_SRC_IDIVE,
+};
+
+static u32 pll1_srcs_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_IDIVA,
+	CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE,
+};
+
+static u32 idiva_src_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1
+};
+
+static u32 idivbcde_src_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA,
+};
+
+static u32 base_all_srcs_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1,
+	CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC,
+	CLK_SRC_IDIVD, CLK_SRC_IDIVE,
+};
+
+static u32 base_common_srcs_ids[] = {
+	CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
+	CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
+	CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA,
+	CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE,
+};
+
+#define CLK_MUX_TABLE(t) .table = t, .table_size = ARRAY_SIZE(t)
+
+static struct clk_gate clk_idiv_gates[LPC18XX_CGU_DIVIDERS_NUM];
+static struct clk_mux  clk_idiv_muxes[LPC18XX_CGU_DIVIDERS_NUM] = {
+	{CLK_MUX_TABLE(idiva_src_ids)},
+	{CLK_MUX_TABLE(idivbcde_src_ids)},
+	{CLK_MUX_TABLE(idivbcde_src_ids)},
+	{CLK_MUX_TABLE(idivbcde_src_ids)},
+	{CLK_MUX_TABLE(idivbcde_src_ids)},
+};
+
+static struct clk_divider clk_idiv_divs[LPC18XX_CGU_DIVIDERS_NUM] = {
+	{.shift = 2, .width = 2},
+	{.shift = 2, .width = 4},
+	{.shift = 2, .width = 4},
+	{.shift = 2, .width = 4},
+	{.shift = 2, .width = 8},
+};
+
+static struct clk_gate clk_base_gates[BASE_CLK_MAX];
+static struct clk_mux  clk_base_muxes[BASE_CLK_MAX] = {
+	[BASE_SAFE_CLK]		= { /* Source can only be IRC */ },
+	[BASE_USB0_CLK]		= { /* Source can only be USB0PLL */ },
+	[BASE_PERIPH_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_USB1_CLK]		= {CLK_MUX_TABLE(base_all_srcs_ids)},
+	[BASE_CPU_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_SPIFI_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_SPI_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_PHY_RX_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_PHY_TX_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_APB1_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_APB3_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_LCD_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_ADCHS_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_SDIO_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_SSP0_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_SSP1_CLK]		= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_UART0_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_UART1_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_UART2_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_UART3_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_OUT_CLK]		= {CLK_MUX_TABLE(base_all_srcs_ids)},
+	[BASE_RES1_CLK]		= {},
+	[BASE_RES2_CLK]		= {},
+	[BASE_RES3_CLK]		= {},
+	[BASE_RES4_CLK]		= {},
+	[BASE_AUDIO_CLK]	= {CLK_MUX_TABLE(base_common_srcs_ids)},
+	[BASE_CGU_OUT0_CLK]	= {CLK_MUX_TABLE(base_all_srcs_ids)},
+	[BASE_CGU_OUT1_CLK]	= {CLK_MUX_TABLE(base_all_srcs_ids)},
+};
+
+struct clk_lpc_pll {
+	struct		clk_hw hw;
+	void __iomem	*reg;
+	spinlock_t	*lock;
+	u8		flags;
+};
+
+#define to_lpc_pll(hw) container_of(hw, struct clk_lpc_pll, hw)
+static struct clk_lpc_pll clk_lpc_plls[LPC18XX_CGU_PLLS_NUM];
+
+static struct clk_gate clk_pll_gates[LPC18XX_CGU_PLLS_NUM];
+static struct clk_mux  clk_pll_muxes[LPC18XX_CGU_PLLS_NUM] = {
+	{CLK_MUX_TABLE(pll0_srcs_ids), .mask = 0x1f, .shift = 24},
+	{CLK_MUX_TABLE(pll0_srcs_ids), .mask = 0x1f, .shift = 24},
+	{CLK_MUX_TABLE(pll1_srcs_ids), .mask = 0x1f, .shift = 24},
+};
+
+static unsigned long clk_lpc_pll1_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct clk_lpc_pll *pll = to_lpc_pll(hw);
+	u16 msel, nsel, psel;
+	bool direct, fbsel;
+	u32 stat, ctrl;
+
+	stat = clk_readl(pll->reg + LPC18XX_CGU_PLL1_STAT);
+	ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL1_CTRL);
+
+	direct = (ctrl & LPC18XX_PLL1_CTRL_DIRECT) ? true : false;
+	fbsel = (ctrl & LPC18XX_PLL1_CTRL_FBSEL) ? true : false;
+
+	msel = ((ctrl >> 16) & 0xff) + 1;
+	nsel = ((ctrl >> 12) & 0x3) + 1;
+
+	if (direct || fbsel)
+		return msel * (parent_rate / nsel);
+
+	psel = (ctrl >>  8) & 0x3;
+	psel = 1 << psel;
+
+	return (msel / (2 * psel)) * (parent_rate / nsel);
+}
+
+const struct clk_ops clk_lpc_pll1_ops = {
+	.recalc_rate = clk_lpc_pll1_recalc_rate,
+};
+
+/*
+ * PLL0USB uses a special register value encoding. The compute functions below
+ * are taken or derived from the LPC1850 user manual (section 12.6.3.3).
+ */
+
+/* Compute PLL0 multiplier from decoded version */
+static u32 lpc18xx_pll0_mdec2msel(u32 x)
+{
+	int i;
+
+	switch (x) {
+	case 0x18003: return 1;
+	case 0x10003: return 2;
+	default:
+		for (i = LPC18XX_PLL0_MSEL_MAX + 1; x != 0x4000 && i > 0; i--)
+			x = ((x ^ x >> 14) & 1) | (x << 1 & 0x7fff);
+		return i;
+	}
+}
+/* Compute PLL0 decoded multiplier from binary version */
+static u32 lpc18xx_pll0_msel2mdec(u32 msel)
+{
+	u32 i, x = 0x4000;
+
+	switch (msel) {
+	case 0: return 0;
+	case 1: return 0x18003;
+	case 2: return 0x10003;
+	default:
+		for (i = msel; i <= LPC18XX_PLL0_MSEL_MAX; i++)
+			x = ((x ^ x >> 1) & 1) << 14 | (x >> 1 & 0xffff);
+		return x;
+	}
+}
+
+/* Compute PLL0 bandwidth SELI reg from multiplier */
+static u32 lpc18xx_pll0_msel2seli(u32 msel)
+{
+	u32 tmp;
+
+	if (msel > 16384) return 1;
+	if (msel >  8192) return 2;
+	if (msel >  2048) return 4;
+	if (msel >=  501) return 8;
+	if (msel >=   60) {
+		tmp = 1024 / (msel + 9);
+		return ((1024 == (tmp * (msel + 9))) == 0) ? tmp * 4 : (tmp + 1) * 4;
+	}
+
+	return (msel & 0x3c) + 4;
+}
+
+/* Compute PLL0 bandwidth SELP reg from multiplier */
+static u32 lpc18xx_pll0_msel2selp(u32 msel)
+{
+	if (msel < 60)
+		return (msel >> 1) + 1;
+
+	return 31;
+}
+
+static unsigned long clk_lpc_pll0_usb_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	struct clk_lpc_pll *pll = to_lpc_pll(hw);
+	u32 ctrl, mdiv, msel, npdiv;
+
+	ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
+	mdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_MDIV);
+	npdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV);
+
+	if (ctrl & LPC18XX_PLL0_CTRL_BYPASS)
+		return parent_rate;
+
+	if (npdiv != LPC18XX_PLL0_NP_DIVS_1) {
+		pr_warn("%s: pre/post dividers not supported\n", __func__);
+		return 0;
+	}
+
+	msel = lpc18xx_pll0_mdec2msel(mdiv & LPC18XX_PLL0_MDIV_MDEC_MASK);
+	if (msel)
+		return 2 * msel * parent_rate;
+
+	pr_warn("%s: unable to calculate rate\n", __func__);
+
+	return 0;
+}
+
+static long clk_lpc_pll0_usb_round_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long *prate)
+{
+	unsigned long m;
+
+	if (*prate < rate) {
+		pr_warn("%s: pll dividers not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	m = DIV_ROUND_UP_ULL(*prate, rate * 2);
+	if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
+		pr_warn("%s: unable to support rate %lu\n", __func__, rate);
+		return -EINVAL;
+	}
+
+	return 2 * *prate * m;
+}
+
+static int clk_lpc_pll0_usb_set_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct clk_lpc_pll *pll = to_lpc_pll(hw);
+	u32 ctrl, stat, m;
+	int retry = 3;
+
+	if (parent_rate < rate) {
+		pr_warn("%s: pll dividers not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	m = DIV_ROUND_UP_ULL(parent_rate, rate * 2);
+	if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
+		pr_warn("%s: unable to support rate %lu\n", __func__, rate);
+		return -EINVAL;
+	}
+
+	m  = lpc18xx_pll0_msel2mdec(m);
+	m |= lpc18xx_pll0_msel2selp(m) << LPC18XX_PLL0_MDIV_SELP_SHIFT;
+	m |= lpc18xx_pll0_msel2seli(m) << LPC18XX_PLL0_MDIV_SELI_SHIFT;
+
+	/* Power down PLL, disable clk output and dividers */
+	ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
+	ctrl |= LPC18XX_PLL0_CTRL_PD;
+	ctrl &= ~(LPC18XX_PLL0_CTRL_BYPASS | LPC18XX_PLL0_CTRL_DIRECTI |
+		  LPC18XX_PLL0_CTRL_DIRECTO | LPC18XX_PLL0_CTRL_CLKEN);
+	clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
+
+	/* Configure new PLL settings */
+	clk_writel(m, pll->reg + LPC18XX_CGU_PLL0USB_MDIV);
+	clk_writel(LPC18XX_PLL0_NP_DIVS_1, pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV);
+
+	/* Power up PLL and wait for lock */
+	ctrl &= ~LPC18XX_PLL0_CTRL_PD;
+	clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
+	do {
+		udelay(10);
+		stat = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_STAT);
+		if (stat & LPC18XX_PLL0_STAT_LOCK) {
+			ctrl |= LPC18XX_PLL0_CTRL_CLKEN;
+			clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
+
+			return 0;
+		}
+	} while (retry--);
+
+	pr_warn("%s: unable to lock pll\n", __func__);
+
+	return -EINVAL;
+}
+
+static const struct clk_ops clk_lpc_pll0_usb_ops = {
+	.recalc_rate	= clk_lpc_pll0_usb_recalc_rate,
+	.round_rate	= clk_lpc_pll0_usb_round_rate,
+	.set_rate	= clk_lpc_pll0_usb_set_rate,
+};
+
+static void __init lpc18xx_fill_parent_names(const char **parent, u32 *id,
+					     int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		parent[i] = clk_src_names[id[i]];
+}
+
+static void __init lpc18xx_cgu_register_clk_sources(void __iomem *base,
+						    struct device_node *np)
+{
+	const char *parent_name[CLK_SRC_MAX];
+	struct clk *clk;
+	int i;
+
+	/* Register the internal 12 MHz RC oscillator (IRC) */
+	clk = clk_register_fixed_rate(NULL, clk_src_names[CLK_SRC_IRC],
+				      NULL, CLK_IS_ROOT, 12000000);
+
+	/* Register crystal oscillator controlller */
+	parent_name[0] = of_clk_get_parent_name(np, 0);
+	clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parent_name[0],
+				0, base + LPC18XX_CGU_XTAL_OSC_CTRL,
+				0, CLK_GATE_SET_TO_DISABLE, NULL);
+
+	/* Register PLL0 for USB */
+	lpc18xx_fill_parent_names(parent_name, clk_pll_muxes[0].table,
+				  clk_pll_muxes[0].table_size);
+	clk_pll_muxes[0].reg = LPC18XX_CGU_PLL0USB_CTRL + base;
+	clk_pll_gates[0].reg = LPC18XX_CGU_PLL0USB_CTRL + base;
+	clk_pll_gates[0].flags = CLK_GATE_SET_TO_DISABLE;
+	clk_lpc_plls[0].reg = base;
+	clk = clk_register_composite(NULL, clk_src_names[CLK_SRC_PLL0USB],
+				     parent_name, clk_pll_muxes[0].table_size,
+				     &clk_pll_muxes[0].hw, &clk_mux_ops,
+				     &clk_lpc_plls[0].hw,  &clk_lpc_pll0_usb_ops,
+				     &clk_pll_gates[0].hw, &clk_gate_ops, 0);
+
+	/* Register PLL0 for audio */
+	lpc18xx_fill_parent_names(parent_name, clk_pll_muxes[1].table,
+				  clk_pll_muxes[1].table_size);
+	clk_pll_muxes[1].reg = LPC18XX_CGU_PLL0AUDIO_CTRL + base;
+	clk_pll_gates[1].reg = LPC18XX_CGU_PLL0AUDIO_CTRL + base;
+	clk_pll_gates[1].flags = CLK_GATE_SET_TO_DISABLE;
+	clk_lpc_plls[1].reg = base;
+	clk = clk_register_composite(NULL, clk_src_names[CLK_SRC_PLL0AUDIO],
+				     parent_name, clk_pll_muxes[1].table_size,
+				     &clk_pll_muxes[1].hw, &clk_mux_ops,
+				     NULL,  NULL,
+				     &clk_pll_gates[1].hw, &clk_gate_ops, 0);
+
+	/* Register main PLL1 */
+	lpc18xx_fill_parent_names(parent_name, clk_pll_muxes[2].table,
+				  clk_pll_muxes[2].table_size);
+	clk_pll_muxes[2].reg = LPC18XX_CGU_PLL1_CTRL + base;
+	clk_pll_gates[2].reg = LPC18XX_CGU_PLL1_CTRL + base;
+	clk_pll_gates[2].flags = CLK_GATE_SET_TO_DISABLE;
+	clk_lpc_plls[2].reg = base;
+	clk = clk_register_composite(NULL, clk_src_names[CLK_SRC_PLL1],
+				     parent_name, clk_pll_muxes[2].table_size,
+				     &clk_pll_muxes[2].hw, &clk_mux_ops,
+				     &clk_lpc_plls[2].hw,  &clk_lpc_pll1_ops,
+				     &clk_pll_gates[2].hw, &clk_gate_ops, 0);
+
+	/* Register dividers A-E */
+	for (i = 0; i < LPC18XX_CGU_DIVIDERS_NUM; i++) {
+		lpc18xx_fill_parent_names(parent_name, clk_idiv_muxes[i].table,
+					  clk_idiv_muxes[i].table_size);
+
+		clk_idiv_divs[i].reg = LPC18XX_CGU_IDIV_CTRL(i) + base;
+		clk_idiv_gates[i].reg = LPC18XX_CGU_IDIV_CTRL(i) + base;
+		clk_idiv_gates[i].flags = CLK_GATE_SET_TO_DISABLE;
+		clk_idiv_muxes[i].reg = LPC18XX_CGU_IDIV_CTRL(i) + base;
+		clk_idiv_muxes[i].mask = 0x1f;
+		clk_idiv_muxes[i].shift = 24;
+
+		clk = clk_register_composite(NULL, clk_src_names[CLK_SRC_IDIVA + i],
+					     parent_name, clk_idiv_muxes[i].table_size,
+					     &clk_idiv_muxes[i].hw, &clk_mux_ops,
+					     &clk_idiv_divs[i].hw,  &clk_divider_ops,
+					     &clk_idiv_gates[i].hw, &clk_gate_ops, 0);
+	}
+}
+
+static struct clk *clk_base[BASE_CLK_MAX];
+static struct clk_onecell_data clk_base_data = {
+	.clks = clk_base,
+	.clk_num = BASE_CLK_MAX,
+};
+
+static void __init lpc18xx_cgu_register_base_clks(void __iomem *base)
+{
+	const char *parent_name[CLK_SRC_MAX];
+	int i;
+
+	/* Register base safe clk as a fixed clk since parent is always IRC */
+	clk_base[BASE_SAFE_CLK] =
+		clk_register_fixed_rate(NULL, base_clk_names[BASE_SAFE_CLK],
+					clk_src_names[CLK_SRC_IRC], 0, 12000000);
+
+	/* Register base USB0 clk as a gate since parent is always PLL0 USB */
+	clk_base[BASE_USB0_CLK] =
+		clk_register_gate(NULL, base_clk_names[BASE_USB0_CLK],
+				  clk_src_names[CLK_SRC_PLL0USB], 0,
+				  LPC18XX_CGU_BASE_CLK(BASE_USB0_CLK) + base,
+				  0, CLK_GATE_SET_TO_DISABLE, NULL);
+
+	/* Register all other base clks with gate and mux */
+	for (i = BASE_PERIPH_CLK; i < BASE_CLK_MAX; i++) {
+
+		if (base_clk_names[i] == NULL) {
+			clk_base[i] = ERR_PTR(-ENOENT);
+			continue;
+		}
+
+		lpc18xx_fill_parent_names(parent_name, clk_base_muxes[i].table,
+					  clk_base_muxes[i].table_size);
+
+		clk_base_gates[i].reg = LPC18XX_CGU_BASE_CLK(i) + base;
+		clk_base_gates[i].flags = CLK_GATE_SET_TO_DISABLE;
+		clk_base_muxes[i].reg = LPC18XX_CGU_BASE_CLK(i) + base;
+		clk_base_muxes[i].mask = 0x1f;
+		clk_base_muxes[i].shift = 24;
+
+		clk_base[i] =
+			clk_register_composite(NULL, base_clk_names[i],
+					       parent_name, clk_base_muxes[i].table_size,
+					       &clk_base_muxes[i].hw, &clk_mux_ops,
+					       NULL,  NULL,
+					       &clk_base_gates[i].hw, &clk_gate_ops, 0);
+	}
+}
+
+static void __init lpc18xx_cgu_init(struct device_node *np)
+{
+	void __iomem *base;
+	const char *name;
+	int ret, i;
+	u32 idx;
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_warn("%s: failed to map address range\n", __func__);
+		return;
+	}
+
+	/* Get base clk names and ids from DT */
+	for (i = 0; i < BASE_CLK_MAX; i++) {
+		ret = of_property_read_u32_index(np, "clock-indices", i, &idx);
+		if (ret)
+			break;
+
+		ret = of_property_read_string_index(np, "clock-output-names",
+						    i, &name);
+		if (ret)
+			break;
+
+		if (idx < BASE_CLK_MAX)
+			base_clk_names[idx] = name;
+	}
+
+	lpc18xx_cgu_register_clk_sources(base, np);
+	lpc18xx_cgu_register_base_clks(base);
+
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_base_data);
+}
+CLK_OF_DECLARE(lpc18xx_cgu, "nxp,lpc1850-cgu", lpc18xx_cgu_init);
diff --git a/include/dt-bindings/clock/lpc18xx-cgu.h b/include/dt-bindings/clock/lpc18xx-cgu.h
new file mode 100644
index 000000000000..6e57c6d2ca66
--- /dev/null
+++ b/include/dt-bindings/clock/lpc18xx-cgu.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Joachim Eastwood <manabian at 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
+ *
+ */
+
+/* LPC18xx/43xx base clock ids */
+#define BASE_SAFE_CLK		0
+#define BASE_USB0_CLK		1
+#define BASE_PERIPH_CLK		2
+#define BASE_USB1_CLK		3
+#define BASE_CPU_CLK		4
+#define BASE_SPIFI_CLK		5
+#define BASE_SPI_CLK		6
+#define BASE_PHY_RX_CLK		7
+#define BASE_PHY_TX_CLK		8
+#define BASE_APB1_CLK		9
+#define BASE_APB3_CLK		10
+#define BASE_LCD_CLK		11
+#define BASE_ADCHS_CLK		12
+#define BASE_SDIO_CLK		13
+#define BASE_SSP0_CLK		14
+#define BASE_SSP1_CLK		15
+#define BASE_UART0_CLK		16
+#define BASE_UART1_CLK		17
+#define BASE_UART2_CLK		18
+#define BASE_UART3_CLK		19
+#define BASE_OUT_CLK		20
+#define BASE_RES1_CLK		21
+#define BASE_RES2_CLK		22
+#define BASE_RES3_CLK		23
+#define BASE_RES4_CLK		24
+#define BASE_AUDIO_CLK		25
+#define BASE_CGU_OUT0_CLK	26
+#define BASE_CGU_OUT1_CLK	27
+#define BASE_CLK_MAX		(BASE_CGU_OUT1_CLK + 1)
-- 
1.8.0




More information about the linux-arm-kernel mailing list