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.man...@microchip.com>
> Reviewed-by: Simon Glass <s...@chromium.org>

Reviewed-by: Daniel Schwierzeck <daniel.schwierz...@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.man...@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.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 */
-- 
- Daniel

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to