
Am Montag, den 04.01.2016, 19:30 +0530 schrieb Purna Chandra Mandal:
Signed-off-by: Purna Chandra Mandal purna.mandal@microchip.com
Changes in v2:
- add get clock rate for mpll clock
.../clock/microchip,pic32-clock.txt | 28 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-pic32.c | 427 +++++++++++++++++++++ include/dt-bindings/clock/microchip,clock.h | 29 ++ 4 files changed, 485 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..d02b9d7 --- /dev/null +++ b/doc/device-tree-bindings/clock/microchip,pic32-clock.txt @@ -0,0 +1,28 @@ +* Microchip PIC32 Clock and Oscillator
+The PIC32 clock controller generates and supplies clock to various +controllers within the SoC.
+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@1f801200 {
compatible = "microchip,pic32mzda_clk";
reg = <0xbf801200 0x1000>;
- };
+Example: UART controller node that consumes the clock generated by the clock
- controller:
- uart1: serial@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..3c84e08 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..70aac05 --- /dev/null +++ b/drivers/clk/clk-pic32.c @@ -0,0 +1,427 @@ +/*
- Copyright (C) 2015 Purna Chandra Mandal <
purna.mandal@microchip.com>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <clk.h> +#include <div64.h> +#include <wait_bit.h>
I can't find wait_bit.h in mainline. Is there an unmerged patch which adds this file and you depend on? Could you point me to it? Thanks.
+#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
+/* PLL */ +#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 0x03 +#define MPLL_MULT 0x32 +#define MPLL_ODIV1 0x02 +#define MPLL_ODIV2 0x01 +#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
+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 */
- wait_for_bit(__func__, reg, REFO_DIVSW_EN, false,
CONFIG_SYS_HZ, false);
- return 0;
+}
+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 & 0x3f;
- mul = (v >> MPLL_MULT_SHIFT) & 0xff;
- odiv1 = (v >> MPLL_ODIV1_SHIFT) & 0x7;
- odiv2 = (v >> MPLL_ODIV2_SHIFT) & 0x7;
- rate = (SYS_POSC_CLK_HZ / idiv) * mul;
- do_div(rate, odiv1);
- do_div(rate, odiv2);
- return (ulong)rate;
+}
+static void pic32_mpll_init(struct pic32_clk_priv *priv) +{
- u32 v, mask;
- /* initialize */
- v = (MPLL_IDIV << MPLL_IDIV_SHIFT) |
(MPLL_MULT << MPLL_MULT_SHIFT) |
(MPLL_ODIV1 << MPLL_ODIV1_SHIFT) |
(MPLL_ODIV2 << MPLL_ODIV2_SHIFT);
- writel(v, priv->syscfg_base + CFGMPLL);
- /* Wait for ready */
- mask = MPLL_RDY | MPLL_VREG_RDY;
- 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;
- addr = dev_get_addr(dev);
- if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
- priv->iobase = pic32_ioremap(addr);
- priv->syscfg_base = pic32_ioremap(PIC32_CFG_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@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 */