uboot/drivers/clk/clk_pic32.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
   4 *
   5 */
   6
   7#include <common.h>
   8#include <clk-uclass.h>
   9#include <dm.h>
  10#include <div64.h>
  11#include <time.h>
  12#include <wait_bit.h>
  13#include <dm/lists.h>
  14#include <asm/io.h>
  15#include <mach/pic32.h>
  16#include <dt-bindings/clock/microchip,clock.h>
  17
  18DECLARE_GLOBAL_DATA_PTR;
  19
  20/* Primary oscillator */
  21#define SYS_POSC_CLK_HZ 24000000
  22
  23/* FRC clk rate */
  24#define SYS_FRC_CLK_HZ  8000000
  25
  26/* Clock Registers */
  27#define OSCCON          0x0000
  28#define OSCTUNE         0x0010
  29#define SPLLCON         0x0020
  30#define REFO1CON        0x0080
  31#define REFO1TRIM       0x0090
  32#define PB1DIV          0x0140
  33
  34/* SPLL */
  35#define ICLK_MASK       0x00000080
  36#define PLLIDIV_MASK    0x00000007
  37#define PLLODIV_MASK    0x00000007
  38#define CUROSC_MASK     0x00000007
  39#define PLLMUL_MASK     0x0000007F
  40#define FRCDIV_MASK     0x00000007
  41
  42/* PBCLK */
  43#define PBDIV_MASK      0x00000007
  44
  45/* SYSCLK MUX */
  46#define SCLK_SRC_FRC1   0
  47#define SCLK_SRC_SPLL   1
  48#define SCLK_SRC_POSC   2
  49#define SCLK_SRC_FRC2   7
  50
  51/* Reference Oscillator Control Reg fields */
  52#define REFO_SEL_MASK   0x0f
  53#define REFO_SEL_SHIFT  0
  54#define REFO_ACTIVE     BIT(8)
  55#define REFO_DIVSW_EN   BIT(9)
  56#define REFO_OE         BIT(12)
  57#define REFO_ON         BIT(15)
  58#define REFO_DIV_SHIFT  16
  59#define REFO_DIV_MASK   0x7fff
  60
  61/* Reference Oscillator Trim Register Fields */
  62#define REFO_TRIM_REG   0x10
  63#define REFO_TRIM_MASK  0x1ff
  64#define REFO_TRIM_SHIFT 23
  65#define REFO_TRIM_MAX   511
  66
  67#define ROCLK_SRC_SCLK          0x0
  68#define ROCLK_SRC_SPLL          0x7
  69#define ROCLK_SRC_ROCLKI        0x8
  70
  71/* Memory PLL */
  72#define MPLL_IDIV               0x3f
  73#define MPLL_MULT               0xff
  74#define MPLL_ODIV1              0x7
  75#define MPLL_ODIV2              0x7
  76#define MPLL_VREG_RDY           BIT(23)
  77#define MPLL_RDY                BIT(31)
  78#define MPLL_IDIV_SHIFT         0
  79#define MPLL_MULT_SHIFT         8
  80#define MPLL_ODIV1_SHIFT        24
  81#define MPLL_ODIV2_SHIFT        27
  82#define MPLL_IDIV_INIT          0x03
  83#define MPLL_MULT_INIT          0x32
  84#define MPLL_ODIV1_INIT         0x02
  85#define MPLL_ODIV2_INIT         0x01
  86
  87struct pic32_clk_priv {
  88        void __iomem *iobase;
  89        void __iomem *syscfg_base;
  90};
  91
  92static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
  93{
  94        u32 iclk, idiv, odiv, mult;
  95        ulong plliclk, v;
  96
  97        v = readl(priv->iobase + SPLLCON);
  98        iclk = (v & ICLK_MASK);
  99        idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
 100        odiv = ((v >> 24) & PLLODIV_MASK);
 101        mult = ((v >> 16) & PLLMUL_MASK) + 1;
 102
 103        plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
 104
 105        if (odiv < 2)
 106                odiv = 2;
 107        else if (odiv < 5)
 108                odiv = (1 << odiv);
 109        else
 110                odiv = 32;
 111
 112        return ((plliclk / idiv) * mult) / odiv;
 113}
 114
 115static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
 116{
 117        ulong v;
 118        ulong hz;
 119        ulong div, frcdiv;
 120        ulong curr_osc;
 121
 122        /* get clk source */
 123        v = readl(priv->iobase + OSCCON);
 124        curr_osc = (v >> 12) & CUROSC_MASK;
 125        switch (curr_osc) {
 126        case SCLK_SRC_FRC1:
 127        case SCLK_SRC_FRC2:
 128                frcdiv = ((v >> 24) & FRCDIV_MASK);
 129                div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
 130                hz = SYS_FRC_CLK_HZ / div;
 131                break;
 132
 133        case SCLK_SRC_SPLL:
 134                hz = pic32_get_pll_rate(priv);
 135                break;
 136
 137        case SCLK_SRC_POSC:
 138                hz = SYS_POSC_CLK_HZ;
 139                break;
 140
 141        default:
 142                hz = 0;
 143                printf("clk: unknown sclk_src.\n");
 144                break;
 145        }
 146
 147        return hz;
 148}
 149
 150static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
 151{
 152        void __iomem *reg;
 153        ulong div, clk_freq;
 154
 155        WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
 156
 157        clk_freq = pic32_get_sysclk(priv);
 158
 159        reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
 160        div = (readl(reg) & PBDIV_MASK) + 1;
 161
 162        return clk_freq / div;
 163}
 164
 165static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
 166{
 167        return pic32_get_pbclk(priv, PB7CLK);
 168}
 169
 170static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
 171                              int parent_rate, int rate, int parent_id)
 172{
 173        void __iomem *reg;
 174        u32 div, trim, v;
 175        u64 frac;
 176
 177        WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
 178
 179        /* calculate dividers,
 180         *   rate = parent_rate / [2 * (div + (trim / 512))]
 181         */
 182        if (parent_rate <= rate) {
 183                div = 0;
 184                trim = 0;
 185        } else {
 186                div = parent_rate / (rate << 1);
 187                frac = parent_rate;
 188                frac <<= 8;
 189                do_div(frac, rate);
 190                frac -= (u64)(div << 9);
 191                trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
 192        }
 193
 194        reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
 195
 196        /* disable clk */
 197        writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
 198
 199        /* wait till previous src change is active */
 200        wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
 201                          false, CONFIG_SYS_HZ, false);
 202
 203        /* parent_id */
 204        v = readl(reg);
 205        v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
 206        v |= (parent_id << REFO_SEL_SHIFT);
 207
 208        /* apply rodiv */
 209        v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
 210        v |= (div << REFO_DIV_SHIFT);
 211        writel(v, reg);
 212
 213        /* apply trim */
 214        v = readl(reg + REFO_TRIM_REG);
 215        v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
 216        v |= (trim << REFO_TRIM_SHIFT);
 217        writel(v, reg + REFO_TRIM_REG);
 218
 219        /* enable clk */
 220        writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
 221
 222        /* switch divider */
 223        writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
 224
 225        /* wait for divider switching to complete */
 226        return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
 227                                 CONFIG_SYS_HZ, false);
 228}
 229
 230static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
 231{
 232        u32 rodiv, rotrim, rosel, v, parent_rate;
 233        void __iomem *reg;
 234        u64 rate64;
 235
 236        WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
 237
 238        reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
 239        v = readl(reg);
 240        /* get rosel */
 241        rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
 242        /* get div */
 243        rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
 244
 245        /* get trim */
 246        v = readl(reg + REFO_TRIM_REG);
 247        rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
 248
 249        if (!rodiv)
 250                return 0;
 251
 252        /* get parent rate */
 253        switch (rosel) {
 254        case ROCLK_SRC_SCLK:
 255                parent_rate = pic32_get_cpuclk(priv);
 256                break;
 257        case ROCLK_SRC_SPLL:
 258                parent_rate = pic32_get_pll_rate(priv);
 259                break;
 260        default:
 261                parent_rate = 0;
 262                break;
 263        }
 264
 265        /* Calculation
 266         * rate = parent_rate / [2 * (div + (trim / 512))]
 267         */
 268        if (rotrim) {
 269                rodiv <<= 9;
 270                rodiv += rotrim;
 271                rate64 = parent_rate;
 272                rate64 <<= 8;
 273                do_div(rate64, rodiv);
 274                v = (u32)rate64;
 275        } else {
 276                v = parent_rate / (rodiv << 1);
 277        }
 278        return v;
 279}
 280
 281static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
 282{
 283        u32 v, idiv, mul;
 284        u32 odiv1, odiv2;
 285        u64 rate;
 286
 287        v = readl(priv->syscfg_base + CFGMPLL);
 288        idiv = v & MPLL_IDIV;
 289        mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
 290        odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
 291        odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
 292
 293        rate = (SYS_POSC_CLK_HZ / idiv) * mul;
 294        do_div(rate, odiv1);
 295        do_div(rate, odiv2);
 296
 297        return (ulong)rate;
 298}
 299
 300static int pic32_mpll_init(struct pic32_clk_priv *priv)
 301{
 302        u32 v, mask;
 303
 304        /* initialize */
 305        v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
 306            (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
 307            (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
 308            (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
 309
 310        writel(v, priv->syscfg_base + CFGMPLL);
 311
 312        /* Wait for ready */
 313        mask = MPLL_RDY | MPLL_VREG_RDY;
 314        return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
 315                                 true, get_tbclk(), false);
 316}
 317
 318static void pic32_clk_init(struct udevice *dev)
 319{
 320        const void *blob = gd->fdt_blob;
 321        struct pic32_clk_priv *priv;
 322        ulong rate, pll_hz;
 323        char propname[50];
 324        int i;
 325
 326        priv = dev_get_priv(dev);
 327        pll_hz = pic32_get_pll_rate(priv);
 328
 329        /* Initialize REFOs as not initialized and enabled on reset. */
 330        for (i = REF1CLK; i <= REF5CLK; i++) {
 331                snprintf(propname, sizeof(propname),
 332                         "microchip,refo%d-frequency", i - REF1CLK + 1);
 333                rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
 334                if (rate)
 335                        pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
 336        }
 337
 338        /* Memory PLL */
 339        pic32_mpll_init(priv);
 340}
 341
 342static ulong pic32_get_rate(struct clk *clk)
 343{
 344        struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 345        ulong rate;
 346
 347        switch (clk->id) {
 348        case PB1CLK ... PB7CLK:
 349                rate = pic32_get_pbclk(priv, clk->id);
 350                break;
 351        case REF1CLK ... REF5CLK:
 352                rate = pic32_get_refclk(priv, clk->id);
 353                break;
 354        case PLLCLK:
 355                rate = pic32_get_pll_rate(priv);
 356                break;
 357        case MPLL:
 358                rate = pic32_get_mpll_rate(priv);
 359                break;
 360        default:
 361                rate = 0;
 362                break;
 363        }
 364
 365        return rate;
 366}
 367
 368static ulong pic32_set_rate(struct clk *clk, ulong rate)
 369{
 370        struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 371        ulong pll_hz;
 372
 373        switch (clk->id) {
 374        case REF1CLK ... REF5CLK:
 375                pll_hz = pic32_get_pll_rate(priv);
 376                pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
 377                break;
 378        default:
 379                break;
 380        }
 381
 382        return rate;
 383}
 384
 385static struct clk_ops pic32_pic32_clk_ops = {
 386        .set_rate = pic32_set_rate,
 387        .get_rate = pic32_get_rate,
 388};
 389
 390static int pic32_clk_probe(struct udevice *dev)
 391{
 392        struct pic32_clk_priv *priv = dev_get_priv(dev);
 393        fdt_addr_t addr;
 394        fdt_size_t size;
 395
 396        addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
 397                                    &size);
 398        if (addr == FDT_ADDR_T_NONE)
 399                return -EINVAL;
 400
 401        priv->iobase = ioremap(addr, size);
 402        if (!priv->iobase)
 403                return -EINVAL;
 404
 405        priv->syscfg_base = pic32_get_syscfg_base();
 406
 407        /* initialize clocks */
 408        pic32_clk_init(dev);
 409
 410        return 0;
 411}
 412
 413static const struct udevice_id pic32_clk_ids[] = {
 414        { .compatible = "microchip,pic32mzda-clk"},
 415        {}
 416};
 417
 418U_BOOT_DRIVER(pic32_clk) = {
 419        .name           = "pic32_clk",
 420        .id             = UCLASS_CLK,
 421        .of_match       = pic32_clk_ids,
 422        .ops            = &pic32_pic32_clk_ops,
 423        .probe          = pic32_clk_probe,
 424        .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
 425};
 426