[U-Boot] [PATCH v3 03/14] drivers: clk: Add clock driver for Microchip PIC32 Microcontroller.

Purna Chandra Mandal purna.mandal at microchip.com
Tue Jan 12 11:18:18 CET 2016


PIC32 clock module consists of multiple oscillators, PLLs, mutiplexers
and dividers capable of supplying clock to various controllers
on or off-chip.

Signed-off-by: Purna Chandra Mandal <purna.mandal at microchip.com>
Reviewed-by: Simon Glass <sjg at chromium.org>

---

Changes in v3:
- rename clk-pic32.c to clk_pic32.c
- update clock binding documentation
- replace pic32_ioremap() with ioremap()
- separate MPLL initialization constants

Changes in v2:
- add mpll get clock rate

 .../clock/microchip,pic32-clock.txt                |  33 ++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk_pic32.c                            | 433 +++++++++++++++++++++
 include/dt-bindings/clock/microchip,clock.h        |  29 ++
 4 files changed, 496 insertions(+)
 create mode 100644 doc/device-tree-bindings/clock/microchip,pic32-clock.txt
 create mode 100644 drivers/clk/clk_pic32.c
 create mode 100644 include/dt-bindings/clock/microchip,clock.h

diff --git a/doc/device-tree-bindings/clock/microchip,pic32-clock.txt b/doc/device-tree-bindings/clock/microchip,pic32-clock.txt
new file mode 100644
index 0000000..02e5ce4
--- /dev/null
+++ b/doc/device-tree-bindings/clock/microchip,pic32-clock.txt
@@ -0,0 +1,33 @@
+* Microchip PIC32 Clock and Oscillator
+
+Microchip PIC32 clock tree consists of few oscillators, PLLs,
+multiplexers and few divider modules capable of supplying clocks
+to various controllers within SoC and also to off-chip.
+
+PIC32 clock controller output is defined by indices as defined
+in [0]
+
+[0] include/dt-bindings/clock/microchip,clock.h
+
+Required Properties:
+- compatible: should be "microchip,pic32mzda_clk"
+- reg: physical base address of the controller and length of memory mapped
+       region.
+- #clock-cells: should be 1.
+
+Example: Clock controller node:
+
+	clock: clk at 1f801200 {
+		compatible = "microchip,pic32mzda_clk";
+		reg = <0x1f801200 0x1000>;
+	};
+
+Example: UART controller node that consumes the clock generated by the clock
+controller:
+
+	uart1: serial at 1f822000 {
+		compatible = "microchip,pic32mzda-uart";
+		reg = <0xbf822000 0x50>;
+		interrupts = <112 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clock PB2CLK>;
+	};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4a6a4a8..adda769 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_CLK) += clk-uclass.o
 obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
 obj-$(CONFIG_SANDBOX) += clk_sandbox.o
+obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c
new file mode 100644
index 0000000..bb0a1cf
--- /dev/null
+++ b/drivers/clk/clk_pic32.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal at microchip.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <div64.h>
+#include <wait_bit.h>
+#include <dm/lists.h>
+#include <asm/io.h>
+#include <mach/pic32.h>
+#include <dt-bindings/clock/microchip,clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Primary oscillator */
+#define SYS_POSC_CLK_HZ	24000000
+
+/* FRC clk rate */
+#define SYS_FRC_CLK_HZ	8000000
+
+/* Clock Registers */
+#define OSCCON		0x0000
+#define OSCTUNE		0x0010
+#define SPLLCON		0x0020
+#define REFO1CON	0x0080
+#define REFO1TRIM	0x0090
+#define PB1DIV		0x0140
+
+/* SPLL */
+#define ICLK_MASK	0x00000080
+#define PLLIDIV_MASK	0x00000007
+#define PLLODIV_MASK	0x00000007
+#define CUROSC_MASK	0x00000007
+#define PLLMUL_MASK	0x0000007F
+#define FRCDIV_MASK	0x00000007
+
+/* PBCLK */
+#define PBDIV_MASK	0x00000007
+
+/* SYSCLK MUX */
+#define SCLK_SRC_FRC1	0
+#define SCLK_SRC_SPLL	1
+#define SCLK_SRC_POSC	2
+#define SCLK_SRC_FRC2	7
+
+/* Reference Oscillator Control Reg fields */
+#define REFO_SEL_MASK	0x0f
+#define REFO_SEL_SHIFT	0
+#define REFO_ACTIVE	BIT(8)
+#define REFO_DIVSW_EN	BIT(9)
+#define REFO_OE		BIT(12)
+#define REFO_ON		BIT(15)
+#define REFO_DIV_SHIFT	16
+#define REFO_DIV_MASK	0x7fff
+
+/* Reference Oscillator Trim Register Fields */
+#define REFO_TRIM_REG	0x10
+#define REFO_TRIM_MASK	0x1ff
+#define REFO_TRIM_SHIFT	23
+#define REFO_TRIM_MAX	511
+
+#define ROCLK_SRC_SCLK		0x0
+#define ROCLK_SRC_SPLL		0x7
+#define ROCLK_SRC_ROCLKI	0x8
+
+/* Memory PLL */
+#define MPLL_IDIV		0x3f
+#define MPLL_MULT		0xff
+#define MPLL_ODIV1		0x7
+#define MPLL_ODIV2		0x7
+#define MPLL_VREG_RDY		BIT(23)
+#define MPLL_RDY		BIT(31)
+#define MPLL_IDIV_SHIFT		0
+#define MPLL_MULT_SHIFT		8
+#define MPLL_ODIV1_SHIFT	24
+#define MPLL_ODIV2_SHIFT	27
+#define MPLL_IDIV_INIT		0x03
+#define MPLL_MULT_INIT		0x32
+#define MPLL_ODIV1_INIT		0x02
+#define MPLL_ODIV2_INIT		0x01
+
+struct pic32_clk_priv {
+	void __iomem *iobase;
+	void __iomem *syscfg_base;
+};
+
+static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
+{
+	u32 iclk, idiv, odiv, mult;
+	ulong plliclk, v;
+
+	v = readl(priv->iobase + SPLLCON);
+	iclk = (v & ICLK_MASK);
+	idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
+	odiv = ((v >> 24) & PLLODIV_MASK);
+	mult = ((v >> 16) & PLLMUL_MASK) + 1;
+
+	plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
+
+	if (odiv < 2)
+		odiv = 2;
+	else if (odiv < 5)
+		odiv = (1 << odiv);
+	else
+		odiv = 32;
+
+	return ((plliclk / idiv) * mult) / odiv;
+}
+
+static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
+{
+	ulong v;
+	ulong hz;
+	ulong div, frcdiv;
+	ulong curr_osc;
+
+	/* get clk source */
+	v = readl(priv->iobase + OSCCON);
+	curr_osc = (v >> 12) & CUROSC_MASK;
+	switch (curr_osc) {
+	case SCLK_SRC_FRC1:
+	case SCLK_SRC_FRC2:
+		frcdiv = ((v >> 24) & FRCDIV_MASK);
+		div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
+		hz = SYS_FRC_CLK_HZ / div;
+		break;
+
+	case SCLK_SRC_SPLL:
+		hz = pic32_get_pll_rate(priv);
+		break;
+
+	case SCLK_SRC_POSC:
+		hz = SYS_POSC_CLK_HZ;
+		break;
+
+	default:
+		hz = 0;
+		printf("clk: unknown sclk_src.\n");
+		break;
+	}
+
+	return hz;
+}
+
+static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
+{
+	void __iomem *reg;
+	ulong div, clk_freq;
+
+	WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
+
+	clk_freq = pic32_get_sysclk(priv);
+
+	reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
+	div = (readl(reg) & PBDIV_MASK) + 1;
+
+	return clk_freq / div;
+}
+
+static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
+{
+	return pic32_get_pbclk(priv, PB7CLK);
+}
+
+static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
+			      int parent_rate, int rate, int parent_id)
+{
+	void __iomem *reg;
+	u32 div, trim, v;
+	u64 frac;
+
+	WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
+
+	/* calculate dividers,
+	 *   rate = parent_rate / [2 * (div + (trim / 512))]
+	 */
+	if (parent_rate <= rate) {
+		div = 0;
+		trim = 0;
+	} else {
+		div = parent_rate / (rate << 1);
+		frac = parent_rate;
+		frac <<= 8;
+		do_div(frac, rate);
+		frac -= (u64)(div << 9);
+		trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
+	}
+
+	reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
+
+	/* disable clk */
+	writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
+
+	/* wait till previous src change is active */
+	wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE,
+		     false, CONFIG_SYS_HZ, false);
+
+	/* parent_id */
+	v = readl(reg);
+	v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
+	v |= (parent_id << REFO_SEL_SHIFT);
+
+	/* apply rodiv */
+	v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
+	v |= (div << REFO_DIV_SHIFT);
+	writel(v, reg);
+
+	/* apply trim */
+	v = readl(reg + REFO_TRIM_REG);
+	v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
+	v |= (trim << REFO_TRIM_SHIFT);
+	writel(v, reg + REFO_TRIM_REG);
+
+	/* enable clk */
+	writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
+
+	/* switch divider */
+	writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
+
+	/* wait for divider switching to complete */
+	return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false,
+			    CONFIG_SYS_HZ, false);
+}
+
+static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
+{
+	u32 rodiv, rotrim, rosel, v, parent_rate;
+	void __iomem *reg;
+	u64 rate64;
+
+	WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
+
+	reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
+	v = readl(reg);
+	/* get rosel */
+	rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
+	/* get div */
+	rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
+
+	/* get trim */
+	v = readl(reg + REFO_TRIM_REG);
+	rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
+
+	if (!rodiv)
+		return 0;
+
+	/* get parent rate */
+	switch (rosel) {
+	case ROCLK_SRC_SCLK:
+		parent_rate = pic32_get_cpuclk(priv);
+		break;
+	case ROCLK_SRC_SPLL:
+		parent_rate = pic32_get_pll_rate(priv);
+		break;
+	default:
+		parent_rate = 0;
+		break;
+	}
+
+	/* Calculation
+	 * rate = parent_rate / [2 * (div + (trim / 512))]
+	 */
+	if (rotrim) {
+		rodiv <<= 9;
+		rodiv += rotrim;
+		rate64 = parent_rate;
+		rate64 <<= 8;
+		do_div(rate64, rodiv);
+		v = (u32)rate64;
+	} else {
+		v = parent_rate / (rodiv << 1);
+	}
+	return v;
+}
+
+static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
+{
+	u32 v, idiv, mul;
+	u32 odiv1, odiv2;
+	u64 rate;
+
+	v = readl(priv->syscfg_base + CFGMPLL);
+	idiv = v & MPLL_IDIV;
+	mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
+	odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
+	odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
+
+	rate = (SYS_POSC_CLK_HZ / idiv) * mul;
+	do_div(rate, odiv1);
+	do_div(rate, odiv2);
+
+	return (ulong)rate;
+}
+
+static int pic32_mpll_init(struct pic32_clk_priv *priv)
+{
+	u32 v, mask;
+
+	/* initialize */
+	v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
+	    (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
+	    (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
+	    (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
+
+	writel(v, priv->syscfg_base + CFGMPLL);
+
+	/* Wait for ready */
+	mask = MPLL_RDY | MPLL_VREG_RDY;
+	return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask,
+			    true, get_tbclk(), false);
+}
+
+static void pic32_clk_init(struct udevice *dev)
+{
+	const void *blob = gd->fdt_blob;
+	struct pic32_clk_priv *priv;
+	ulong rate, pll_hz;
+	char propname[50];
+	int i;
+
+	priv = dev_get_priv(dev);
+	pll_hz = pic32_get_pll_rate(priv);
+
+	/* Initialize REFOs as not initialized and enabled on reset. */
+	for (i = REF1CLK; i <= REF5CLK; i++) {
+		snprintf(propname, sizeof(propname),
+			 "microchip,refo%d-frequency", i - REF1CLK + 1);
+		rate = fdtdec_get_int(blob, dev->of_offset, propname, 0);
+		if (rate)
+			pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
+	}
+
+	/* Memory PLL */
+	pic32_mpll_init(priv);
+}
+
+static ulong pic32_clk_get_rate(struct udevice *dev)
+{
+	struct pic32_clk_priv *priv = dev_get_priv(dev);
+
+	return pic32_get_cpuclk(priv);
+}
+
+static ulong pic32_get_periph_rate(struct udevice *dev, int periph)
+{
+	struct pic32_clk_priv *priv = dev_get_priv(dev);
+	ulong rate;
+
+	switch (periph) {
+	case PB1CLK ... PB7CLK:
+		rate = pic32_get_pbclk(priv, periph);
+		break;
+	case REF1CLK ... REF5CLK:
+		rate = pic32_get_refclk(priv, periph);
+		break;
+	case PLLCLK:
+		rate = pic32_get_pll_rate(priv);
+		break;
+	case MPLL:
+		rate = pic32_get_mpll_rate(priv);
+		break;
+	default:
+		rate = 0;
+		break;
+	}
+
+	return rate;
+}
+
+static ulong pic32_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+{
+	struct pic32_clk_priv *priv = dev_get_priv(dev);
+	ulong pll_hz;
+
+	switch (periph) {
+	case REF1CLK ... REF5CLK:
+		pll_hz = pic32_get_pll_rate(priv);
+		pic32_set_refclk(priv, periph, pll_hz, rate, ROCLK_SRC_SPLL);
+		break;
+	default:
+		break;
+	}
+
+	return rate;
+}
+
+static struct clk_ops pic32_pic32_clk_ops = {
+	.get_rate = pic32_clk_get_rate,
+	.set_periph_rate = pic32_set_periph_rate,
+	.get_periph_rate = pic32_get_periph_rate,
+};
+
+static int pic32_clk_probe(struct udevice *dev)
+{
+	struct pic32_clk_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->iobase = ioremap(addr, size);
+	if (!priv->iobase)
+		return -EINVAL;
+
+	priv->syscfg_base = pic32_get_syscfg_base();
+
+	/* initialize clocks */
+	pic32_clk_init(dev);
+
+	return 0;
+}
+
+static const struct udevice_id pic32_clk_ids[] = {
+	{ .compatible = "microchip,pic32mzda_clk"},
+	{}
+};
+
+U_BOOT_DRIVER(pic32_clk) = {
+	.name		= "pic32_clk",
+	.id		= UCLASS_CLK,
+	.of_match	= pic32_clk_ids,
+	.flags		= DM_FLAG_PRE_RELOC,
+	.ops		= &pic32_pic32_clk_ops,
+	.probe		= pic32_clk_probe,
+	.priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
+};
diff --git a/include/dt-bindings/clock/microchip,clock.h b/include/dt-bindings/clock/microchip,clock.h
new file mode 100644
index 0000000..93c222d
--- /dev/null
+++ b/include/dt-bindings/clock/microchip,clock.h
@@ -0,0 +1,29 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal at microchip.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#ifndef __CLK_MICROCHIP_PIC32
+#define __CLK_MICROCHIP_PIC32
+
+/* clock output indices */
+#define BASECLK	0
+#define PLLCLK	1
+#define MPLL	2
+#define SYSCLK	3
+#define PB1CLK	4
+#define PB2CLK	5
+#define PB3CLK	6
+#define PB4CLK	7
+#define PB5CLK	8
+#define PB6CLK	9
+#define PB7CLK	10
+#define REF1CLK	11
+#define REF2CLK	12
+#define REF3CLK	13
+#define REF4CLK	14
+#define REF5CLK	15
+
+#endif	/* __CLK_MICROCHIP_PIC32 */
-- 
1.8.3.1



More information about the U-Boot mailing list