2016-01-04 15:00 GMT+01:00 Purna Chandra Mandal <purna.man...@microchip.com>: > Signed-off-by: Purna Chandra Mandal <purna.man...@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
please rename to 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.man...@microchip.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + * > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <clk.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 > + > +/* 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.man...@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 > > -- - Daniel _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot