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