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