uboot/drivers/clk/meson/g12a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2018 - Beniamino Galvani <b.galvani@gmail.com>
   4 * (C) Copyright 2018 - BayLibre, SAS
   5 * Author: Neil Armstrong <narmstrong@baylibre.com>
   6 */
   7
   8#include <common.h>
   9#include <asm/arch/clock-g12a.h>
  10#include <asm/io.h>
  11#include <clk-uclass.h>
  12#include <dm.h>
  13#include <regmap.h>
  14#include <syscon.h>
  15#include <div64.h>
  16#include <dt-bindings/clock/g12a-clkc.h>
  17#include "clk_meson.h"
  18
  19#define XTAL_RATE 24000000
  20
  21struct meson_clk {
  22        struct regmap *map;
  23};
  24
  25static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
  26                                      ulong rate, ulong current_rate);
  27static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id);
  28
  29#define NUM_CLKS 178
  30
  31static struct meson_gate gates[NUM_CLKS] = {
  32        /* Everything Else (EE) domain gates */
  33        MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8),
  34        MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9),
  35        MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13),
  36        MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 14),
  37        MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25),
  38        MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26),
  39        MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3),
  40        MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16),
  41        MESON_GATE(CLKID_USB, HHI_GCLK_MPEG1, 25),
  42        MESON_GATE(CLKID_USB1_DDR_BRIDGE, HHI_GCLK_MPEG2, 8),
  43
  44        /* Peripheral Gates */
  45        MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23),
  46        MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7),
  47};
  48
  49static int meson_set_gate(struct clk *clk, bool on)
  50{
  51        struct meson_clk *priv = dev_get_priv(clk->dev);
  52        struct meson_gate *gate;
  53
  54        if (clk->id >= ARRAY_SIZE(gates))
  55                return -ENOENT;
  56
  57        gate = &gates[clk->id];
  58
  59        if (gate->reg == 0)
  60                return 0;
  61
  62        regmap_update_bits(priv->map, gate->reg,
  63                           BIT(gate->bit), on ? BIT(gate->bit) : 0);
  64
  65        return 0;
  66}
  67
  68static int meson_clk_enable(struct clk *clk)
  69{
  70        return meson_set_gate(clk, true);
  71}
  72
  73static int meson_clk_disable(struct clk *clk)
  74{
  75        return meson_set_gate(clk, false);
  76}
  77
  78static unsigned long meson_clk81_get_rate(struct clk *clk)
  79{
  80        struct meson_clk *priv = dev_get_priv(clk->dev);
  81        unsigned long parent_rate;
  82        uint reg;
  83        int parents[] = {
  84                -1,
  85                -1,
  86                CLKID_FCLK_DIV7,
  87                CLKID_MPLL1,
  88                CLKID_MPLL2,
  89                CLKID_FCLK_DIV4,
  90                CLKID_FCLK_DIV3,
  91                CLKID_FCLK_DIV5
  92        };
  93
  94        /* mux */
  95        regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
  96        reg = (reg >> 12) & 7;
  97
  98        switch (reg) {
  99        case 0:
 100                parent_rate = XTAL_RATE;
 101                break;
 102        case 1:
 103                return -ENOENT;
 104        default:
 105                parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]);
 106        }
 107
 108        /* divider */
 109        regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
 110        reg = reg & ((1 << 7) - 1);
 111
 112        return parent_rate / reg;
 113}
 114
 115static long mpll_rate_from_params(unsigned long parent_rate,
 116                                  unsigned long sdm,
 117                                  unsigned long n2)
 118{
 119        unsigned long divisor = (SDM_DEN * n2) + sdm;
 120
 121        if (n2 < N2_MIN)
 122                return -EINVAL;
 123
 124        return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
 125}
 126
 127static struct parm meson_mpll0_parm[2] = {
 128        {HHI_MPLL_CNTL1, 0, 14}, /* psdm */
 129        {HHI_MPLL_CNTL1, 20, 9}, /* pn2 */
 130};
 131
 132static struct parm meson_mpll1_parm[2] = {
 133        {HHI_MPLL_CNTL3, 0, 14}, /* psdm */
 134        {HHI_MPLL_CNTL3, 20, 9}, /* pn2 */
 135};
 136
 137static struct parm meson_mpll2_parm[2] = {
 138        {HHI_MPLL_CNTL5, 0, 14}, /* psdm */
 139        {HHI_MPLL_CNTL5, 20, 9}, /* pn2 */
 140};
 141
 142/*
 143 * MultiPhase Locked Loops are outputs from a PLL with additional frequency
 144 * scaling capabilities. MPLL rates are calculated as:
 145 *
 146 * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
 147 */
 148static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id)
 149{
 150        struct meson_clk *priv = dev_get_priv(clk->dev);
 151        struct parm *psdm, *pn2;
 152        unsigned long sdm, n2;
 153        unsigned long parent_rate;
 154        uint reg;
 155
 156        switch (id) {
 157        case CLKID_MPLL0:
 158                psdm = &meson_mpll0_parm[0];
 159                pn2 = &meson_mpll0_parm[1];
 160                break;
 161        case CLKID_MPLL1:
 162                psdm = &meson_mpll1_parm[0];
 163                pn2 = &meson_mpll1_parm[1];
 164                break;
 165        case CLKID_MPLL2:
 166                psdm = &meson_mpll2_parm[0];
 167                pn2 = &meson_mpll2_parm[1];
 168                break;
 169        default:
 170                return -ENOENT;
 171        }
 172
 173        parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL);
 174        if (IS_ERR_VALUE(parent_rate))
 175                return parent_rate;
 176
 177        regmap_read(priv->map, psdm->reg_off, &reg);
 178        sdm = PARM_GET(psdm->width, psdm->shift, reg);
 179
 180        regmap_read(priv->map, pn2->reg_off, &reg);
 181        n2 = PARM_GET(pn2->width, pn2->shift, reg);
 182
 183        return mpll_rate_from_params(parent_rate, sdm, n2);
 184}
 185
 186static struct parm meson_fixed_pll_parm[3] = {
 187        {HHI_FIX_PLL_CNTL0, 0, 8}, /* pm */
 188        {HHI_FIX_PLL_CNTL0, 10, 5}, /* pn */
 189        {HHI_FIX_PLL_CNTL0, 16, 2}, /* pod */
 190};
 191
 192static struct parm meson_sys_pll_parm[3] = {
 193        {HHI_SYS_PLL_CNTL0, 0, 8}, /* pm */
 194        {HHI_SYS_PLL_CNTL0, 10, 5}, /* pn */
 195        {HHI_SYS_PLL_CNTL0, 16, 2}, /* pod */
 196};
 197
 198static ulong meson_pll_get_rate(struct clk *clk, unsigned long id)
 199{
 200        struct meson_clk *priv = dev_get_priv(clk->dev);
 201        struct parm *pm, *pn, *pod;
 202        unsigned long parent_rate_mhz = XTAL_RATE / 1000000;
 203        u16 n, m, od;
 204        uint reg;
 205
 206        /*
 207         * FIXME: Between the unit conversion and the missing frac, we know
 208         * rate will be slightly off ...
 209        */
 210
 211        switch (id) {
 212        case CLKID_FIXED_PLL:
 213                pm = &meson_fixed_pll_parm[0];
 214                pn = &meson_fixed_pll_parm[1];
 215                pod = &meson_fixed_pll_parm[2];
 216                break;
 217        case CLKID_SYS_PLL:
 218                pm = &meson_sys_pll_parm[0];
 219                pn = &meson_sys_pll_parm[1];
 220                pod = &meson_sys_pll_parm[2];
 221                break;
 222        default:
 223                return -ENOENT;
 224        }
 225
 226        regmap_read(priv->map, pn->reg_off, &reg);
 227        n = PARM_GET(pn->width, pn->shift, reg);
 228
 229        regmap_read(priv->map, pm->reg_off, &reg);
 230        m = PARM_GET(pm->width, pm->shift, reg);
 231
 232        regmap_read(priv->map, pod->reg_off, &reg);
 233        od = PARM_GET(pod->width, pod->shift, reg);
 234
 235        return ((parent_rate_mhz * m / n) >> od) * 1000000;
 236}
 237
 238static struct parm meson_pcie_pll_parm[3] = {
 239        {HHI_PCIE_PLL_CNTL0, 0, 8}, /* pm */
 240        {HHI_PCIE_PLL_CNTL0, 10, 5}, /* pn */
 241        {HHI_PCIE_PLL_CNTL0, 16, 5}, /* pod */
 242};
 243
 244static ulong meson_pcie_pll_get_rate(struct clk *clk)
 245{
 246        struct meson_clk *priv = dev_get_priv(clk->dev);
 247        struct parm *pm, *pn, *pod;
 248        unsigned long parent_rate_mhz = XTAL_RATE / 1000000;
 249        u16 n, m, od;
 250        uint reg;
 251
 252        pm = &meson_pcie_pll_parm[0];
 253        pn = &meson_pcie_pll_parm[1];
 254        pod = &meson_pcie_pll_parm[2];
 255
 256        regmap_read(priv->map, pn->reg_off, &reg);
 257        n = PARM_GET(pn->width, pn->shift, reg);
 258
 259        regmap_read(priv->map, pm->reg_off, &reg);
 260        m = PARM_GET(pm->width, pm->shift, reg);
 261
 262        regmap_read(priv->map, pod->reg_off, &reg);
 263        od = PARM_GET(pod->width, pod->shift, reg);
 264
 265        return ((parent_rate_mhz * m / n) / 2 / od / 2) * 1000000;
 266}
 267
 268static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id)
 269{
 270        ulong rate;
 271
 272        switch (id) {
 273        case CLKID_FIXED_PLL:
 274        case CLKID_SYS_PLL:
 275                rate = meson_pll_get_rate(clk, id);
 276                break;
 277        case CLKID_FCLK_DIV2:
 278                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2;
 279                break;
 280        case CLKID_FCLK_DIV3:
 281                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3;
 282                break;
 283        case CLKID_FCLK_DIV4:
 284                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4;
 285                break;
 286        case CLKID_FCLK_DIV5:
 287                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5;
 288                break;
 289        case CLKID_FCLK_DIV7:
 290                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7;
 291                break;
 292        case CLKID_MPLL0:
 293        case CLKID_MPLL1:
 294        case CLKID_MPLL2:
 295                rate = meson_mpll_get_rate(clk, id);
 296                break;
 297        case CLKID_CLK81:
 298                rate = meson_clk81_get_rate(clk);
 299                break;
 300        case CLKID_PCIE_PLL:
 301                rate = meson_pcie_pll_get_rate(clk);
 302                break;
 303        default:
 304                if (gates[id].reg != 0) {
 305                        /* a clock gate */
 306                        rate = meson_clk81_get_rate(clk);
 307                        break;
 308                }
 309                return -ENOENT;
 310        }
 311
 312        debug("clock %lu has rate %lu\n", id, rate);
 313        return rate;
 314}
 315
 316static ulong meson_clk_get_rate(struct clk *clk)
 317{
 318        return meson_clk_get_rate_by_id(clk, clk->id);
 319}
 320
 321static ulong meson_pcie_pll_set_rate(struct clk *clk, ulong rate)
 322{
 323        struct meson_clk *priv = dev_get_priv(clk->dev);
 324
 325        regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x20090496);
 326        regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x30090496);
 327        regmap_write(priv->map, HHI_PCIE_PLL_CNTL1, 0x00000000);
 328        regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001100);
 329        regmap_write(priv->map, HHI_PCIE_PLL_CNTL3, 0x10058e00);
 330        regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x000100c0);
 331        regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000048);
 332        regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000068);
 333        udelay(20);
 334        regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x008100c0);
 335        udelay(10);
 336        regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x34090496);
 337        regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x14090496);
 338        udelay(10);
 339        regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001000);
 340        regmap_update_bits(priv->map, HHI_PCIE_PLL_CNTL0,
 341                                0x1f << 16, 9 << 16);
 342
 343        return 100000000;
 344}
 345
 346static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id,
 347                                      ulong rate, ulong current_rate)
 348{
 349        if (current_rate == rate)
 350                return 0;
 351
 352        switch (id) {
 353        /* Fixed clocks */
 354        case CLKID_PCIE_PLL:
 355                return meson_pcie_pll_set_rate(clk, rate);
 356
 357        default:
 358                return -ENOENT;
 359        }
 360
 361        return -EINVAL;
 362}
 363
 364
 365static ulong meson_clk_set_rate(struct clk *clk, ulong rate)
 366{
 367        ulong current_rate = meson_clk_get_rate_by_id(clk, clk->id);
 368        int ret;
 369
 370        if (IS_ERR_VALUE(current_rate))
 371                return current_rate;
 372
 373        debug("%s: setting rate of %ld from %ld to %ld\n",
 374              __func__, clk->id, current_rate, rate);
 375
 376        ret = meson_clk_set_rate_by_id(clk, clk->id, rate, current_rate);
 377        if (IS_ERR_VALUE(ret))
 378                return ret;
 379
 380        debug("clock %lu has new rate %lu\n", clk->id,
 381              meson_clk_get_rate_by_id(clk, clk->id));
 382
 383        return 0;
 384}
 385
 386static int meson_clk_probe(struct udevice *dev)
 387{
 388        struct meson_clk *priv = dev_get_priv(dev);
 389
 390        priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node);
 391        if (IS_ERR(priv->map))
 392                return PTR_ERR(priv->map);
 393
 394        debug("meson-clk-g12a: probed\n");
 395
 396        return 0;
 397}
 398
 399static struct clk_ops meson_clk_ops = {
 400        .disable        = meson_clk_disable,
 401        .enable         = meson_clk_enable,
 402        .get_rate       = meson_clk_get_rate,
 403        .set_rate       = meson_clk_set_rate,
 404};
 405
 406static const struct udevice_id meson_clk_ids[] = {
 407        { .compatible = "amlogic,g12a-clkc" },
 408        { .compatible = "amlogic,g12b-clkc" },
 409        { }
 410};
 411
 412U_BOOT_DRIVER(meson_clk_g12a) = {
 413        .name           = "meson_clk_g12a",
 414        .id             = UCLASS_CLK,
 415        .of_match       = meson_clk_ids,
 416        .priv_auto_alloc_size = sizeof(struct meson_clk),
 417        .ops            = &meson_clk_ops,
 418        .probe          = meson_clk_probe,
 419};
 420