123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- */
- #include <common.h>
- #include <clk-uclass.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_get_rate(struct clk *clk)
- {
- struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
- ulong rate;
- switch (clk->id) {
- case PB1CLK ... PB7CLK:
- rate = pic32_get_pbclk(priv, clk->id);
- break;
- case REF1CLK ... REF5CLK:
- rate = pic32_get_refclk(priv, clk->id);
- 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_rate(struct clk *clk, ulong rate)
- {
- struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
- ulong pll_hz;
- switch (clk->id) {
- case REF1CLK ... REF5CLK:
- pll_hz = pic32_get_pll_rate(priv);
- pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
- break;
- default:
- break;
- }
- return rate;
- }
- static struct clk_ops pic32_pic32_clk_ops = {
- .set_rate = pic32_set_rate,
- .get_rate = pic32_get_rate,
- };
- static int pic32_clk_probe(struct udevice *dev)
- {
- struct pic32_clk_priv *priv = dev_get_priv(dev);
- fdt_addr_t addr;
- fdt_size_t size;
- addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
- if (addr == FDT_ADDR_T_NONE)
- return -EINVAL;
- priv->iobase = ioremap(addr, size);
- if (!priv->iobase)
- return -EINVAL;
- priv->syscfg_base = pic32_get_syscfg_base();
- /* initialize clocks */
- pic32_clk_init(dev);
- return 0;
- }
- static const struct udevice_id pic32_clk_ids[] = {
- { .compatible = "microchip,pic32mzda-clk"},
- {}
- };
- U_BOOT_DRIVER(pic32_clk) = {
- .name = "pic32_clk",
- .id = UCLASS_CLK,
- .of_match = pic32_clk_ids,
- .flags = DM_FLAG_PRE_RELOC,
- .ops = &pic32_pic32_clk_ops,
- .probe = pic32_clk_probe,
- .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
- };
|