uboot/drivers/clk/mpc83xx_clk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2017
   4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
   5 */
   6
   7#include <common.h>
   8#include <clk-uclass.h>
   9#include <clock_legacy.h>
  10#include <command.h>
  11#include <dm.h>
  12#include <log.h>
  13#include <vsprintf.h>
  14#include <asm/global_data.h>
  15#include <dm/lists.h>
  16#include <dt-bindings/clk/mpc83xx-clk.h>
  17#include <asm/arch/soc.h>
  18#include <linux/bitops.h>
  19
  20#include "mpc83xx_clk.h"
  21
  22DECLARE_GLOBAL_DATA_PTR;
  23
  24/**
  25 * struct mpc83xx_clk_priv - Private data structure for the MPC83xx clock
  26 *                           driver
  27 * @speed: Array containing the speed values of all system clocks (initialized
  28 *         once, then only read back)
  29 */
  30struct mpc83xx_clk_priv {
  31        u32 speed[MPC83XX_CLK_COUNT];
  32};
  33
  34/**
  35 * is_clk_valid() - Check if clock ID is valid for given clock device
  36 * @clk: The clock device for which to check a clock ID
  37 * @id:  The clock ID to check
  38 *
  39 * Return: true if clock ID is valid for clock device, false if not
  40 */
  41static inline bool is_clk_valid(struct udevice *clk, int id)
  42{
  43        ulong type = dev_get_driver_data(clk);
  44
  45        switch (id) {
  46        case MPC83XX_CLK_MEM:
  47                return true;
  48        case MPC83XX_CLK_MEM_SEC:
  49                return type == SOC_MPC8360;
  50        case MPC83XX_CLK_ENC:
  51                return (type == SOC_MPC8308) || (type == SOC_MPC8309);
  52        case MPC83XX_CLK_I2C1:
  53                return true;
  54        case MPC83XX_CLK_TDM:
  55                return type == SOC_MPC8315;
  56        case MPC83XX_CLK_SDHC:
  57                return mpc83xx_has_sdhc(type);
  58        case MPC83XX_CLK_TSEC1:
  59        case MPC83XX_CLK_TSEC2:
  60                return mpc83xx_has_tsec(type);
  61        case MPC83XX_CLK_USBDR:
  62                return type == SOC_MPC8360;
  63        case MPC83XX_CLK_USBMPH:
  64                return type == SOC_MPC8349;
  65        case MPC83XX_CLK_PCIEXP1:
  66                return mpc83xx_has_pcie1(type);
  67        case MPC83XX_CLK_PCIEXP2:
  68                return mpc83xx_has_pcie2(type);
  69        case MPC83XX_CLK_SATA:
  70                return mpc83xx_has_sata(type);
  71        case MPC83XX_CLK_DMAC:
  72                return (type == SOC_MPC8308) || (type == SOC_MPC8309);
  73        case MPC83XX_CLK_PCI:
  74                /*
  75                 * FIXME: implement proper support for this.
  76                 */
  77                return 0 && mpc83xx_has_pci(type);
  78        case MPC83XX_CLK_CSB:
  79                return true;
  80        case MPC83XX_CLK_I2C2:
  81                return mpc83xx_has_second_i2c(type);
  82        case MPC83XX_CLK_QE:
  83        case MPC83XX_CLK_BRG:
  84                return mpc83xx_has_quicc_engine(type) && (type != SOC_MPC8309);
  85        case MPC83XX_CLK_LCLK:
  86        case MPC83XX_CLK_LBIU:
  87        case MPC83XX_CLK_CORE:
  88                return true;
  89        }
  90
  91        return false;
  92}
  93
  94/**
  95 * init_single_clk() - Initialize a clock with a given ID
  96 * @dev: The clock device for which to initialize the clock
  97 * @clk: The clock ID
  98 *
  99 * The clock speed is read from the hardware's registers, and stored in the
 100 * private data structure of the driver. From there it is only retrieved, and
 101 * not set.
 102 *
 103 * Return: 0 if OK, -ve on error
 104 */
 105static int init_single_clk(struct udevice *dev, int clk)
 106{
 107        struct mpc83xx_clk_priv *priv = dev_get_priv(dev);
 108        immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
 109        ulong type = dev_get_driver_data(dev);
 110        struct clk_mode mode;
 111        ulong mask;
 112        u32 csb_clk = get_csb_clk(im);
 113        int ret;
 114
 115        ret = retrieve_mode(clk, type, &mode);
 116        if (ret) {
 117                debug("%s: Could not retrieve mode for clk %d (ret = %d)\n",
 118                      dev->name, clk, ret);
 119                return ret;
 120        }
 121
 122        if (mode.type == TYPE_INVALID) {
 123                debug("%s: clock %d invalid\n", dev->name, clk);
 124                return -EINVAL;
 125        }
 126
 127        if (mode.type == TYPE_SCCR_STANDARD) {
 128                mask = GENMASK(31 - mode.low, 31 - mode.high);
 129
 130                switch (sccr_field(im, mask)) {
 131                case 0:
 132                        priv->speed[clk] = 0;
 133                        break;
 134                case 1:
 135                        priv->speed[clk] = csb_clk;
 136                        break;
 137                case 2:
 138                        priv->speed[clk] = csb_clk / 2;
 139                        break;
 140                case 3:
 141                        priv->speed[clk] = csb_clk / 3;
 142                        break;
 143                default:
 144                        priv->speed[clk] = 0;
 145                }
 146
 147                return 0;
 148        }
 149
 150        if (mode.type == TYPE_SPMR_DIRECT_MULTIPLY) {
 151                mask = GENMASK(31 - mode.low, 31 - mode.high);
 152
 153                priv->speed[clk] = csb_clk * (1 + sccr_field(im, mask));
 154                return 0;
 155        }
 156
 157        if (clk == MPC83XX_CLK_CSB || clk == MPC83XX_CLK_I2C2) {
 158                priv->speed[clk] = csb_clk; /* i2c-2 clk is equal to csb clk */
 159                return 0;
 160        }
 161
 162        if (clk == MPC83XX_CLK_QE || clk == MPC83XX_CLK_BRG) {
 163                u32 pci_sync_in = get_pci_sync_in(im);
 164                u32 qepmf = spmr_field(im, SPMR_CEPMF);
 165                u32 qepdf = spmr_field(im, SPMR_CEPDF);
 166                u32 qe_clk = (pci_sync_in * qepmf) / (1 + qepdf);
 167
 168                if (clk == MPC83XX_CLK_QE)
 169                        priv->speed[clk] = qe_clk;
 170                else
 171                        priv->speed[clk] = qe_clk / 2;
 172
 173                return 0;
 174        }
 175
 176        if (clk == MPC83XX_CLK_LCLK || clk == MPC83XX_CLK_LBIU) {
 177                u32 lbiu_clk = csb_clk *
 178                        (1 + spmr_field(im, SPMR_LBIUCM));
 179                u32 clkdiv = lcrr_field(im, LCRR_CLKDIV);
 180
 181                if (clk == MPC83XX_CLK_LBIU)
 182                        priv->speed[clk] = lbiu_clk;
 183
 184                switch (clkdiv) {
 185                case 2:
 186                case 4:
 187                case 8:
 188                        priv->speed[clk] = lbiu_clk / clkdiv;
 189                        break;
 190                default:
 191                        /* unknown lcrr */
 192                        priv->speed[clk] = 0;
 193                }
 194
 195                return 0;
 196        }
 197
 198        if (clk == MPC83XX_CLK_CORE) {
 199                u8 corepll = spmr_field(im, SPMR_COREPLL);
 200                u32 corecnf_tab_index = ((corepll & 0x1F) << 2) |
 201                                        ((corepll & 0x60) >> 5);
 202
 203                if (corecnf_tab_index > (ARRAY_SIZE(corecnf_tab))) {
 204                        debug("%s: Core configuration index %02x too high; possible wrong value",
 205                              dev->name, corecnf_tab_index);
 206                        return -EINVAL;
 207                }
 208
 209                switch (corecnf_tab[corecnf_tab_index].core_csb_ratio) {
 210                case RAT_BYP:
 211                case RAT_1_TO_1:
 212                        priv->speed[clk] = csb_clk;
 213                        break;
 214                case RAT_1_5_TO_1:
 215                        priv->speed[clk] = (3 * csb_clk) / 2;
 216                        break;
 217                case RAT_2_TO_1:
 218                        priv->speed[clk] = 2 * csb_clk;
 219                        break;
 220                case RAT_2_5_TO_1:
 221                        priv->speed[clk] = (5 * csb_clk) / 2;
 222                        break;
 223                case RAT_3_TO_1:
 224                        priv->speed[clk] = 3 * csb_clk;
 225                        break;
 226                default:
 227                        /* unknown core to csb ratio */
 228                        priv->speed[clk] = 0;
 229                }
 230
 231                return 0;
 232        }
 233
 234        /* Unknown clk value -> error */
 235        debug("%s: clock %d invalid\n", dev->name, clk);
 236        return -EINVAL;
 237}
 238
 239/**
 240 * init_all_clks() - Initialize all clocks of a clock device
 241 * @dev: The clock device whose clocks should be initialized
 242 *
 243 * Return: 0 if OK, -ve on error
 244 */
 245static inline int init_all_clks(struct udevice *dev)
 246{
 247        int i;
 248
 249        for (i = 0; i < MPC83XX_CLK_COUNT; i++) {
 250                int ret;
 251
 252                if (!is_clk_valid(dev, i))
 253                        continue;
 254
 255                ret = init_single_clk(dev, i);
 256                if (ret) {
 257                        debug("%s: Failed to initialize %s clock\n",
 258                              dev->name, names[i]);
 259                        return ret;
 260                }
 261        }
 262
 263        return 0;
 264}
 265
 266static int mpc83xx_clk_request(struct clk *clock)
 267{
 268        /* Reject requests of clocks that are not available */
 269        if (is_clk_valid(clock->dev, clock->id))
 270                return 0;
 271        else
 272                return -ENODEV;
 273}
 274
 275static ulong mpc83xx_clk_get_rate(struct clk *clk)
 276{
 277        struct mpc83xx_clk_priv *priv = dev_get_priv(clk->dev);
 278
 279        if (clk->id >= MPC83XX_CLK_COUNT) {
 280                debug("%s: clock index %lu invalid\n", __func__, clk->id);
 281                return 0;
 282        }
 283
 284        return priv->speed[clk->id];
 285}
 286
 287static int mpc83xx_clk_enable(struct clk *clk)
 288{
 289        /* MPC83xx clocks are always enabled */
 290        return 0;
 291}
 292
 293int get_clocks(void)
 294{
 295        /* Empty implementation to keep the prototype in common.h happy */
 296        return 0;
 297}
 298
 299int get_serial_clock(void)
 300{
 301        struct mpc83xx_clk_priv *priv;
 302        struct udevice *clk;
 303        int ret;
 304
 305        ret = uclass_first_device_err(UCLASS_CLK, &clk);
 306        if (ret) {
 307                debug("%s: Could not get clock device\n", __func__);
 308                return ret;
 309        }
 310
 311        priv = dev_get_priv(clk);
 312
 313        return priv->speed[MPC83XX_CLK_CSB];
 314}
 315
 316const struct clk_ops mpc83xx_clk_ops = {
 317        .request = mpc83xx_clk_request,
 318        .get_rate = mpc83xx_clk_get_rate,
 319        .enable = mpc83xx_clk_enable,
 320};
 321
 322static const struct udevice_id mpc83xx_clk_match[] = {
 323        { .compatible = "fsl,mpc8308-clk", .data = SOC_MPC8308 },
 324        { .compatible = "fsl,mpc8309-clk", .data = SOC_MPC8309 },
 325        { .compatible = "fsl,mpc8313-clk", .data = SOC_MPC8313 },
 326        { .compatible = "fsl,mpc8315-clk", .data = SOC_MPC8315 },
 327        { .compatible = "fsl,mpc832x-clk", .data = SOC_MPC832X },
 328        { .compatible = "fsl,mpc8349-clk", .data = SOC_MPC8349 },
 329        { .compatible = "fsl,mpc8360-clk", .data = SOC_MPC8360 },
 330        { .compatible = "fsl,mpc8379-clk", .data = SOC_MPC8379 },
 331        { /* sentinel */ }
 332};
 333
 334static int mpc83xx_clk_probe(struct udevice *dev)
 335{
 336        struct mpc83xx_clk_priv *priv = dev_get_priv(dev);
 337        ulong type;
 338        int ret;
 339
 340        ret = init_all_clks(dev);
 341        if (ret) {
 342                debug("%s: Could not initialize all clocks (ret = %d)\n",
 343                      dev->name, ret);
 344                return ret;
 345        }
 346
 347        type = dev_get_driver_data(dev);
 348
 349        if (mpc83xx_has_sdhc(type))
 350                gd->arch.sdhc_clk = priv->speed[MPC83XX_CLK_SDHC];
 351
 352        gd->arch.core_clk = priv->speed[MPC83XX_CLK_CORE];
 353        gd->arch.i2c1_clk = priv->speed[MPC83XX_CLK_I2C1];
 354        if (mpc83xx_has_second_i2c(type))
 355                gd->arch.i2c2_clk = priv->speed[MPC83XX_CLK_I2C2];
 356
 357        gd->mem_clk = priv->speed[MPC83XX_CLK_MEM];
 358
 359        if (mpc83xx_has_pci(type))
 360                gd->pci_clk = priv->speed[MPC83XX_CLK_PCI];
 361
 362        gd->cpu_clk = priv->speed[MPC83XX_CLK_CORE];
 363        gd->bus_clk = priv->speed[MPC83XX_CLK_CSB];
 364
 365        return 0;
 366}
 367
 368static int mpc83xx_clk_bind(struct udevice *dev)
 369{
 370        int ret;
 371        struct udevice *sys_child;
 372
 373        /*
 374         * Since there is no corresponding device tree entry, and since the
 375         * clock driver has to be present in either case, bind the sysreset
 376         * driver here.
 377         */
 378        ret = device_bind_driver(dev, "mpc83xx_sysreset", "sysreset",
 379                                 &sys_child);
 380        if (ret)
 381                debug("%s: No sysreset driver: ret=%d\n",
 382                      dev->name, ret);
 383
 384        return 0;
 385}
 386
 387U_BOOT_DRIVER(mpc83xx_clk) = {
 388        .name = "mpc83xx_clk",
 389        .id = UCLASS_CLK,
 390        .of_match = mpc83xx_clk_match,
 391        .ops = &mpc83xx_clk_ops,
 392        .probe = mpc83xx_clk_probe,
 393        .priv_auto      = sizeof(struct mpc83xx_clk_priv),
 394        .bind = mpc83xx_clk_bind,
 395};
 396
 397static int do_clocks(struct cmd_tbl *cmdtp, int flag, int argc,
 398                     char *const argv[])
 399{
 400        int i;
 401        char buf[32];
 402        struct udevice *clk;
 403        int ret;
 404        struct mpc83xx_clk_priv *priv;
 405
 406        ret = uclass_first_device_err(UCLASS_CLK, &clk);
 407        if (ret) {
 408                debug("%s: Could not get clock device\n", __func__);
 409                return ret;
 410        }
 411
 412        for (i = 0; i < MPC83XX_CLK_COUNT; i++) {
 413                if (!is_clk_valid(clk, i))
 414                        continue;
 415
 416                priv = dev_get_priv(clk);
 417
 418                printf("%s = %s MHz\n", names[i], strmhz(buf, priv->speed[i]));
 419        }
 420
 421        return 0;
 422}
 423
 424U_BOOT_CMD(clocks,      1,      1,      do_clocks,
 425           "display values of SoC's clocks",
 426           ""
 427);
 428