diff mbox

[U-Boot,v2,02/13] drivers: clk: Add clock driver for Microchip PIC32 Microcontroller.

Message ID 1451916042.27601.118.camel@microchip.com
State Superseded
Delegated to: Daniel Schwierzeck
Headers show

Commit Message

Purna Chandra Mandal Jan. 4, 2016, 2 p.m. UTC
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

Comments

Daniel Schwierzeck Jan. 5, 2016, 7:31 p.m. UTC | #1
2016-01-04 15:00 GMT+01:00 Purna Chandra Mandal <purna.mandal@microchip.com>:
> 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

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.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>
> +#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 */
> --
> 1.8.3.1
>
>
Purna Chandra Mandal Jan. 6, 2016, 6:56 a.m. UTC | #2
On 01/06/2016 01:01 AM, Daniel Schwierzeck wrote:

> 2016-01-04 15:00 GMT+01:00 Purna Chandra Mandal <purna.mandal@microchip.com>:
>> 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
> please rename to clk_pic32.o

ack.

>> 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+
>> + *
>> + */
>> +

[...]
Daniel Schwierzeck Jan. 11, 2016, 4:16 p.m. UTC | #3
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 */
Simon Glass Jan. 11, 2016, 4:57 p.m. UTC | #4
Hi,

On 4 January 2016 at 07:00, Purna Chandra Mandal
<purna.mandal@microchip.com> wrote:
> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
>

Commit message please.

> ---
>
> 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
>

Reviewed-by: Simon Glass <sjg@chromium.org>

nits below

> 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

As the other review mentions, should use underscore in filenames
unless it is a uclass.

> 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>

nit: clk.h should go above dm.h, below common.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;

Regards,
Simon
Purna Chandra Mandal Jan. 12, 2016, 4:55 a.m. UTC | #5
On 01/11/2016 09:46 PM, Daniel Schwierzeck wrote:

> 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.

Yes, this is not on mainline. Based on review (from Marek Vesut)
to use wait_for_bit() for unbounded loops I have pulled the
under-review patch from [1].

[1] http://patchwork.ozlabs.org/patch/561185/

>> +#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 */
Purna Chandra Mandal Jan. 12, 2016, 4:59 a.m. UTC | #6
On 01/11/2016 10:27 PM, Simon Glass wrote:
> Hi,
>
> On 4 January 2016 at 07:00, Purna Chandra Mandal
> <purna.mandal@microchip.com> wrote:
>> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
>>
> Commit message please.

Ack. Will add.

>> ---
>>
>> 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
>>
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> nits below
>
>> 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
> As the other review mentions, should use underscore in filenames
> unless it is a uclass.

Agreed.

>> 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>
> nit: clk.h should go above dm.h, below common.h

ack. If you could share *specific* reason behind this order (like dependencies)?

>> +#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;
> Regards,
> Simon
diff mbox

Patch

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>
+#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 */