uboot/drivers/clk/clk_meson_axg.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-axg.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/axg-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_get_rate_by_id(struct clk *clk, unsigned long id);
  26
  27static struct meson_gate gates[] = {
  28        /* Everything Else (EE) domain gates */
  29        MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8),
  30        MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9),
  31        MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13),
  32        MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 15),
  33        MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25),
  34        MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26),
  35        MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3),
  36        MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16),
  37
  38        /* Always On (AO) domain gates */
  39        MESON_GATE(CLKID_AO_I2C, HHI_GCLK_AO, 4),
  40
  41        /* PLL Gates */
  42        /* CLKID_FCLK_DIV2 is critical for the SCPI Processor */
  43        MESON_GATE(CLKID_MPLL2, HHI_MPLL_CNTL9, 14),
  44        /* CLKID_CLK81 is critical for the system */
  45
  46        /* Peripheral Gates */
  47        MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23),
  48        MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7),
  49};
  50
  51static int meson_set_gate(struct clk *clk, bool on)
  52{
  53        struct meson_clk *priv = dev_get_priv(clk->dev);
  54        struct meson_gate *gate;
  55
  56        if (clk->id >= ARRAY_SIZE(gates))
  57                return -ENOENT;
  58
  59        gate = &gates[clk->id];
  60
  61        if (gate->reg == 0)
  62                return 0;
  63
  64        regmap_update_bits(priv->map, gate->reg,
  65                           BIT(gate->bit), on ? BIT(gate->bit) : 0);
  66
  67        return 0;
  68}
  69
  70static int meson_clk_enable(struct clk *clk)
  71{
  72        return meson_set_gate(clk, true);
  73}
  74
  75static int meson_clk_disable(struct clk *clk)
  76{
  77        return meson_set_gate(clk, false);
  78}
  79
  80static unsigned long meson_clk81_get_rate(struct clk *clk)
  81{
  82        struct meson_clk *priv = dev_get_priv(clk->dev);
  83        unsigned long parent_rate;
  84        uint reg;
  85        int parents[] = {
  86                -1,
  87                -1,
  88                CLKID_FCLK_DIV7,
  89                CLKID_MPLL1,
  90                CLKID_MPLL2,
  91                CLKID_FCLK_DIV4,
  92                CLKID_FCLK_DIV3,
  93                CLKID_FCLK_DIV5
  94        };
  95
  96        /* mux */
  97        regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
  98        reg = (reg >> 12) & 7;
  99
 100        switch (reg) {
 101        case 0:
 102                parent_rate = XTAL_RATE;
 103                break;
 104        case 1:
 105                return -ENOENT;
 106        default:
 107                parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]);
 108        }
 109
 110        /* divider */
 111        regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
 112        reg = reg & ((1 << 7) - 1);
 113
 114        return parent_rate / reg;
 115}
 116
 117static long mpll_rate_from_params(unsigned long parent_rate,
 118                                  unsigned long sdm,
 119                                  unsigned long n2)
 120{
 121        unsigned long divisor = (SDM_DEN * n2) + sdm;
 122
 123        if (n2 < N2_MIN)
 124                return -EINVAL;
 125
 126        return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
 127}
 128
 129static struct parm meson_mpll0_parm[3] = {
 130        {HHI_MPLL_CNTL7, 0, 14}, /* psdm */
 131        {HHI_MPLL_CNTL7, 16, 9}, /* pn2 */
 132};
 133
 134static struct parm meson_mpll1_parm[3] = {
 135        {HHI_MPLL_CNTL8, 0, 14}, /* psdm */
 136        {HHI_MPLL_CNTL8, 16, 9}, /* pn2 */
 137};
 138
 139static struct parm meson_mpll2_parm[3] = {
 140        {HHI_MPLL_CNTL9, 0, 14}, /* psdm */
 141        {HHI_MPLL_CNTL9, 16, 9}, /* pn2 */
 142};
 143
 144/*
 145 * MultiPhase Locked Loops are outputs from a PLL with additional frequency
 146 * scaling capabilities. MPLL rates are calculated as:
 147 *
 148 * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
 149 */
 150static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id)
 151{
 152        struct meson_clk *priv = dev_get_priv(clk->dev);
 153        struct parm *psdm, *pn2;
 154        unsigned long sdm, n2;
 155        unsigned long parent_rate;
 156        uint reg;
 157
 158        switch (id) {
 159        case CLKID_MPLL0:
 160                psdm = &meson_mpll0_parm[0];
 161                pn2 = &meson_mpll0_parm[1];
 162                break;
 163        case CLKID_MPLL1:
 164                psdm = &meson_mpll1_parm[0];
 165                pn2 = &meson_mpll1_parm[1];
 166                break;
 167        case CLKID_MPLL2:
 168                psdm = &meson_mpll2_parm[0];
 169                pn2 = &meson_mpll2_parm[1];
 170                break;
 171        default:
 172                return -ENOENT;
 173        }
 174
 175        parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL);
 176        if (IS_ERR_VALUE(parent_rate))
 177                return parent_rate;
 178
 179        regmap_read(priv->map, psdm->reg_off, &reg);
 180        sdm = PARM_GET(psdm->width, psdm->shift, reg);
 181
 182        regmap_read(priv->map, pn2->reg_off, &reg);
 183        n2 = PARM_GET(pn2->width, pn2->shift, reg);
 184
 185        return mpll_rate_from_params(parent_rate, sdm, n2);
 186}
 187
 188static struct parm meson_fixed_pll_parm[3] = {
 189        {HHI_MPLL_CNTL, 0, 9}, /* pm */
 190        {HHI_MPLL_CNTL, 9, 5}, /* pn */
 191        {HHI_MPLL_CNTL, 16, 2}, /* pod */
 192};
 193
 194static struct parm meson_sys_pll_parm[3] = {
 195        {HHI_SYS_PLL_CNTL, 0, 9}, /* pm */
 196        {HHI_SYS_PLL_CNTL, 9, 5}, /* pn */
 197        {HHI_SYS_PLL_CNTL, 16, 2}, /* pod */
 198};
 199
 200static ulong meson_pll_get_rate(struct clk *clk, unsigned long id)
 201{
 202        struct meson_clk *priv = dev_get_priv(clk->dev);
 203        struct parm *pm, *pn, *pod;
 204        unsigned long parent_rate_mhz = XTAL_RATE / 1000000;
 205        u16 n, m, od;
 206        uint reg;
 207
 208        switch (id) {
 209        case CLKID_FIXED_PLL:
 210                pm = &meson_fixed_pll_parm[0];
 211                pn = &meson_fixed_pll_parm[1];
 212                pod = &meson_fixed_pll_parm[2];
 213                break;
 214        case CLKID_SYS_PLL:
 215                pm = &meson_sys_pll_parm[0];
 216                pn = &meson_sys_pll_parm[1];
 217                pod = &meson_sys_pll_parm[2];
 218                break;
 219        default:
 220                return -ENOENT;
 221        }
 222
 223        regmap_read(priv->map, pn->reg_off, &reg);
 224        n = PARM_GET(pn->width, pn->shift, reg);
 225
 226        regmap_read(priv->map, pm->reg_off, &reg);
 227        m = PARM_GET(pm->width, pm->shift, reg);
 228
 229        regmap_read(priv->map, pod->reg_off, &reg);
 230        od = PARM_GET(pod->width, pod->shift, reg);
 231
 232        return ((parent_rate_mhz * m / n) >> od) * 1000000;
 233}
 234
 235static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id)
 236{
 237        ulong rate;
 238
 239        switch (id) {
 240        case CLKID_FIXED_PLL:
 241        case CLKID_SYS_PLL:
 242                rate = meson_pll_get_rate(clk, id);
 243                break;
 244        case CLKID_FCLK_DIV2:
 245                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2;
 246                break;
 247        case CLKID_FCLK_DIV3:
 248                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3;
 249                break;
 250        case CLKID_FCLK_DIV4:
 251                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4;
 252                break;
 253        case CLKID_FCLK_DIV5:
 254                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5;
 255                break;
 256        case CLKID_FCLK_DIV7:
 257                rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7;
 258                break;
 259        case CLKID_MPLL0:
 260        case CLKID_MPLL1:
 261        case CLKID_MPLL2:
 262                rate = meson_mpll_get_rate(clk, id);
 263                break;
 264        case CLKID_CLK81:
 265                rate = meson_clk81_get_rate(clk);
 266                break;
 267        default:
 268                if (gates[id].reg != 0) {
 269                        /* a clock gate */
 270                        rate = meson_clk81_get_rate(clk);
 271                        break;
 272                }
 273                return -ENOENT;
 274        }
 275
 276        debug("clock %lu has rate %lu\n", id, rate);
 277        return rate;
 278}
 279
 280static ulong meson_clk_get_rate(struct clk *clk)
 281{
 282        return meson_clk_get_rate_by_id(clk, clk->id);
 283}
 284
 285static int meson_clk_probe(struct udevice *dev)
 286{
 287        struct meson_clk *priv = dev_get_priv(dev);
 288
 289        priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node);
 290        if (IS_ERR(priv->map))
 291                return PTR_ERR(priv->map);
 292
 293        debug("meson-clk-axg: probed\n");
 294
 295        return 0;
 296}
 297
 298static struct clk_ops meson_clk_ops = {
 299        .disable        = meson_clk_disable,
 300        .enable         = meson_clk_enable,
 301        .get_rate       = meson_clk_get_rate,
 302};
 303
 304static const struct udevice_id meson_clk_ids[] = {
 305        { .compatible = "amlogic,axg-clkc" },
 306        { }
 307};
 308
 309U_BOOT_DRIVER(meson_clk_axg) = {
 310        .name           = "meson_clk_axg",
 311        .id             = UCLASS_CLK,
 312        .of_match       = meson_clk_ids,
 313        .priv_auto_alloc_size = sizeof(struct meson_clk),
 314        .ops            = &meson_clk_ops,
 315        .probe          = meson_clk_probe,
 316};
 317