Message ID | 1452593909-16184-4-git-send-email-purna.mandal@microchip.com |
---|---|
State | Superseded |
Delegated to: | Daniel Schwierzeck |
Headers | show |
Am Dienstag, den 12.01.2016, 15:48 +0530 schrieb Purna Chandra Mandal: > 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@microchip.com> > Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> nits below > > --- > > 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@1f801200 { > + compatible = "microchip,pic32mzda_clk"; > + reg = <0x1f801200 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..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@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; you can drop this check. ioremap() always returns a mapped address > + > + 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@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 */
On Tue, Jan 12, 2016 at 03:48:18PM +0530, Purna Chandra Mandal wrote: > PIC32 clock module consists of multiple oscillators, PLLs, mutiplexers > and dividers capable of supplying clock to various controllers > on or off-chip. [snip] > include/dt-bindings/clock/microchip,clock.h | 29 ++ Has this been submitted for the kernel and reviewed there as well already? Thanks!
On 01/13/2016 07:08 PM, Daniel Schwierzeck wrote: > Am Dienstag, den 12.01.2016, 15:48 +0530 schrieb Purna Chandra Mandal: >> 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@microchip.com> >> Reviewed-by: Simon Glass <sjg@chromium.org> > Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> > > nits below > >> --- >> >> 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@1f801200 { >> + compatible = "microchip,pic32mzda_clk"; >> + reg = <0x1f801200 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..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@microchip.com> >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + * >> + */ >> + >> [...] >> + >> +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; > you can drop this check. ioremap() always returns a mapped address ack. Will drop. >> + >> + 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@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 */
On 01/13/2016 08:25 PM, Tom Rini wrote: > On Tue, Jan 12, 2016 at 03:48:18PM +0530, Purna Chandra Mandal wrote: > >> PIC32 clock module consists of multiple oscillators, PLLs, mutiplexers >> and dividers capable of supplying clock to various controllers >> on or off-chip. > [snip] >> include/dt-bindings/clock/microchip,clock.h | 29 ++ > Has this been submitted for the kernel and reviewed there as well > already? Thanks! > Clock driver in kernel is under review. [1] clock driver: https://lkml.org/lkml/2016/1/7/764 [0] clock binding: https://lkml.org/lkml/2016/1/7/762 Please note clock driver in Linux kernel is implemented in more elaborate/descriptive way - all the sub-modules are individually defined in device-tree (having "#clock-cells = <0>") and their phandles are directly referred in clock clients so there was no need of having dt-binding header.
On Thu, Jan 14, 2016 at 11:34:27AM +0530, Purna Chandra Mandal wrote: > On 01/13/2016 08:25 PM, Tom Rini wrote: > > > On Tue, Jan 12, 2016 at 03:48:18PM +0530, Purna Chandra Mandal wrote: > > > >> PIC32 clock module consists of multiple oscillators, PLLs, mutiplexers > >> and dividers capable of supplying clock to various controllers > >> on or off-chip. > > [snip] > >> include/dt-bindings/clock/microchip,clock.h | 29 ++ > > Has this been submitted for the kernel and reviewed there as well > > already? Thanks! > > > Clock driver in kernel is under review. > [1] clock driver: https://lkml.org/lkml/2016/1/7/764 > [0] clock binding: https://lkml.org/lkml/2016/1/7/762 > > Please note clock driver in Linux kernel is implemented in more elaborate/descriptive way - all the sub-modules are individually defined in device-tree (having "#clock-cells = <0>") and their phandles are directly > referred in clock clients so there was no need of having dt-binding header. Note that we really must have the same DT functional in both cases. So it's fine to have extra nodes that U-Boot doesn't use.
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@1f801200 { + compatible = "microchip,pic32mzda_clk"; + reg = <0x1f801200 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..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@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@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 */