uboot/drivers/clk/imx/clk-imx8.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2018 NXP
   4 * Peng Fan <peng.fan@nxp.com>
   5 */
   6
   7#include <common.h>
   8#include <clk-uclass.h>
   9#include <dm.h>
  10#include <asm/arch/sci/sci.h>
  11#include <asm/arch/clock.h>
  12#include <dt-bindings/clock/imx8qxp-clock.h>
  13#include <dt-bindings/soc/imx_rsrc.h>
  14#include <misc.h>
  15
  16struct imx8_clks {
  17        ulong id;
  18        const char *name;
  19};
  20
  21#if CONFIG_IS_ENABLED(CMD_CLK)
  22static struct imx8_clks imx8_clk_names[] = {
  23        { IMX8QXP_A35_DIV, "A35_DIV" },
  24        { IMX8QXP_I2C0_CLK, "I2C0" },
  25        { IMX8QXP_I2C1_CLK, "I2C1" },
  26        { IMX8QXP_I2C2_CLK, "I2C2" },
  27        { IMX8QXP_I2C3_CLK, "I2C3" },
  28        { IMX8QXP_UART0_CLK, "UART0" },
  29        { IMX8QXP_UART1_CLK, "UART1" },
  30        { IMX8QXP_UART2_CLK, "UART2" },
  31        { IMX8QXP_UART3_CLK, "UART3" },
  32        { IMX8QXP_SDHC0_CLK, "SDHC0" },
  33        { IMX8QXP_SDHC1_CLK, "SDHC1" },
  34        { IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" },
  35        { IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" },
  36        { IMX8QXP_ENET0_REF_DIV, "ENET0_REF" },
  37        { IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" },
  38        { IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" },
  39        { IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" },
  40        { IMX8QXP_ENET1_REF_DIV, "ENET1_REF" },
  41        { IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" },
  42};
  43#endif
  44
  45static ulong imx8_clk_get_rate(struct clk *clk)
  46{
  47        sc_pm_clk_t pm_clk;
  48        ulong rate;
  49        u16 resource;
  50        int ret;
  51
  52        debug("%s(#%lu)\n", __func__, clk->id);
  53
  54        switch (clk->id) {
  55        case IMX8QXP_A35_DIV:
  56                resource = SC_R_A35;
  57                pm_clk = SC_PM_CLK_CPU;
  58                break;
  59        case IMX8QXP_I2C0_CLK:
  60                resource = SC_R_I2C_0;
  61                pm_clk = SC_PM_CLK_PER;
  62                break;
  63        case IMX8QXP_I2C1_CLK:
  64                resource = SC_R_I2C_1;
  65                pm_clk = SC_PM_CLK_PER;
  66                break;
  67        case IMX8QXP_I2C2_CLK:
  68                resource = SC_R_I2C_2;
  69                pm_clk = SC_PM_CLK_PER;
  70                break;
  71        case IMX8QXP_I2C3_CLK:
  72                resource = SC_R_I2C_3;
  73                pm_clk = SC_PM_CLK_PER;
  74                break;
  75        case IMX8QXP_SDHC0_IPG_CLK:
  76        case IMX8QXP_SDHC0_CLK:
  77        case IMX8QXP_SDHC0_DIV:
  78                resource = SC_R_SDHC_0;
  79                pm_clk = SC_PM_CLK_PER;
  80                break;
  81        case IMX8QXP_SDHC1_IPG_CLK:
  82        case IMX8QXP_SDHC1_CLK:
  83        case IMX8QXP_SDHC1_DIV:
  84                resource = SC_R_SDHC_1;
  85                pm_clk = SC_PM_CLK_PER;
  86                break;
  87        case IMX8QXP_UART0_IPG_CLK:
  88        case IMX8QXP_UART0_CLK:
  89                resource = SC_R_UART_0;
  90                pm_clk = SC_PM_CLK_PER;
  91                break;
  92        case IMX8QXP_UART1_CLK:
  93                resource = SC_R_UART_1;
  94                pm_clk = SC_PM_CLK_PER;
  95                break;
  96        case IMX8QXP_UART2_CLK:
  97                resource = SC_R_UART_2;
  98                pm_clk = SC_PM_CLK_PER;
  99                break;
 100        case IMX8QXP_UART3_CLK:
 101                resource = SC_R_UART_3;
 102                pm_clk = SC_PM_CLK_PER;
 103                break;
 104        case IMX8QXP_ENET0_IPG_CLK:
 105        case IMX8QXP_ENET0_AHB_CLK:
 106        case IMX8QXP_ENET0_REF_DIV:
 107        case IMX8QXP_ENET0_PTP_CLK:
 108                resource = SC_R_ENET_0;
 109                pm_clk = SC_PM_CLK_PER;
 110                break;
 111        case IMX8QXP_ENET1_IPG_CLK:
 112        case IMX8QXP_ENET1_AHB_CLK:
 113        case IMX8QXP_ENET1_REF_DIV:
 114        case IMX8QXP_ENET1_PTP_CLK:
 115                resource = SC_R_ENET_1;
 116                pm_clk = SC_PM_CLK_PER;
 117                break;
 118        default:
 119                if (clk->id < IMX8QXP_UART0_IPG_CLK ||
 120                    clk->id >= IMX8QXP_CLK_END) {
 121                        printf("%s(Invalid clk ID #%lu)\n",
 122                               __func__, clk->id);
 123                        return -EINVAL;
 124                }
 125                return -ENOTSUPP;
 126        };
 127
 128        ret = sc_pm_get_clock_rate(-1, resource, pm_clk,
 129                                   (sc_pm_clock_rate_t *)&rate);
 130        if (ret) {
 131                printf("%s err %d\n", __func__, ret);
 132                return ret;
 133        }
 134
 135        return rate;
 136}
 137
 138static ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate)
 139{
 140        sc_pm_clk_t pm_clk;
 141        u32 new_rate = rate;
 142        u16 resource;
 143        int ret;
 144
 145        debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
 146
 147        switch (clk->id) {
 148        case IMX8QXP_I2C0_CLK:
 149                resource = SC_R_I2C_0;
 150                pm_clk = SC_PM_CLK_PER;
 151                break;
 152        case IMX8QXP_I2C1_CLK:
 153                resource = SC_R_I2C_1;
 154                pm_clk = SC_PM_CLK_PER;
 155                break;
 156        case IMX8QXP_I2C2_CLK:
 157                resource = SC_R_I2C_2;
 158                pm_clk = SC_PM_CLK_PER;
 159                break;
 160        case IMX8QXP_I2C3_CLK:
 161                resource = SC_R_I2C_3;
 162                pm_clk = SC_PM_CLK_PER;
 163                break;
 164        case IMX8QXP_UART0_CLK:
 165                resource = SC_R_UART_0;
 166                pm_clk = SC_PM_CLK_PER;
 167                break;
 168        case IMX8QXP_UART1_CLK:
 169                resource = SC_R_UART_1;
 170                pm_clk = SC_PM_CLK_PER;
 171                break;
 172        case IMX8QXP_UART2_CLK:
 173                resource = SC_R_UART_2;
 174                pm_clk = SC_PM_CLK_PER;
 175                break;
 176        case IMX8QXP_UART3_CLK:
 177                resource = SC_R_UART_3;
 178                pm_clk = SC_PM_CLK_PER;
 179                break;
 180        case IMX8QXP_SDHC0_IPG_CLK:
 181        case IMX8QXP_SDHC0_CLK:
 182        case IMX8QXP_SDHC0_DIV:
 183                resource = SC_R_SDHC_0;
 184                pm_clk = SC_PM_CLK_PER;
 185                break;
 186        case IMX8QXP_SDHC1_SEL:
 187        case IMX8QXP_SDHC0_SEL:
 188                return 0;
 189        case IMX8QXP_SDHC1_IPG_CLK:
 190        case IMX8QXP_SDHC1_CLK:
 191        case IMX8QXP_SDHC1_DIV:
 192                resource = SC_R_SDHC_1;
 193                pm_clk = SC_PM_CLK_PER;
 194                break;
 195        case IMX8QXP_ENET0_IPG_CLK:
 196        case IMX8QXP_ENET0_AHB_CLK:
 197        case IMX8QXP_ENET0_REF_DIV:
 198        case IMX8QXP_ENET0_PTP_CLK:
 199                resource = SC_R_ENET_0;
 200                pm_clk = SC_PM_CLK_PER;
 201                break;
 202        case IMX8QXP_ENET1_IPG_CLK:
 203        case IMX8QXP_ENET1_AHB_CLK:
 204        case IMX8QXP_ENET1_REF_DIV:
 205        case IMX8QXP_ENET1_PTP_CLK:
 206                resource = SC_R_ENET_1;
 207                pm_clk = SC_PM_CLK_PER;
 208                break;
 209        default:
 210                if (clk->id < IMX8QXP_UART0_IPG_CLK ||
 211                    clk->id >= IMX8QXP_CLK_END) {
 212                        printf("%s(Invalid clk ID #%lu)\n",
 213                               __func__, clk->id);
 214                        return -EINVAL;
 215                }
 216                return -ENOTSUPP;
 217        };
 218
 219        ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate);
 220        if (ret) {
 221                printf("%s err %d\n", __func__, ret);
 222                return ret;
 223        }
 224
 225        return new_rate;
 226}
 227
 228static int __imx8_clk_enable(struct clk *clk, bool enable)
 229{
 230        sc_pm_clk_t pm_clk;
 231        u16 resource;
 232        int ret;
 233
 234        debug("%s(#%lu)\n", __func__, clk->id);
 235
 236        switch (clk->id) {
 237        case IMX8QXP_I2C0_CLK:
 238                resource = SC_R_I2C_0;
 239                pm_clk = SC_PM_CLK_PER;
 240                break;
 241        case IMX8QXP_I2C1_CLK:
 242                resource = SC_R_I2C_1;
 243                pm_clk = SC_PM_CLK_PER;
 244                break;
 245        case IMX8QXP_I2C2_CLK:
 246                resource = SC_R_I2C_2;
 247                pm_clk = SC_PM_CLK_PER;
 248                break;
 249        case IMX8QXP_I2C3_CLK:
 250                resource = SC_R_I2C_3;
 251                pm_clk = SC_PM_CLK_PER;
 252                break;
 253        case IMX8QXP_UART0_CLK:
 254                resource = SC_R_UART_0;
 255                pm_clk = SC_PM_CLK_PER;
 256                break;
 257        case IMX8QXP_UART1_CLK:
 258                resource = SC_R_UART_1;
 259                pm_clk = SC_PM_CLK_PER;
 260                break;
 261        case IMX8QXP_UART2_CLK:
 262                resource = SC_R_UART_2;
 263                pm_clk = SC_PM_CLK_PER;
 264                break;
 265        case IMX8QXP_UART3_CLK:
 266                resource = SC_R_UART_3;
 267                pm_clk = SC_PM_CLK_PER;
 268                break;
 269        case IMX8QXP_SDHC0_IPG_CLK:
 270        case IMX8QXP_SDHC0_CLK:
 271        case IMX8QXP_SDHC0_DIV:
 272                resource = SC_R_SDHC_0;
 273                pm_clk = SC_PM_CLK_PER;
 274                break;
 275        case IMX8QXP_SDHC1_IPG_CLK:
 276        case IMX8QXP_SDHC1_CLK:
 277        case IMX8QXP_SDHC1_DIV:
 278                resource = SC_R_SDHC_1;
 279                pm_clk = SC_PM_CLK_PER;
 280                break;
 281        case IMX8QXP_ENET0_IPG_CLK:
 282        case IMX8QXP_ENET0_AHB_CLK:
 283        case IMX8QXP_ENET0_REF_DIV:
 284        case IMX8QXP_ENET0_PTP_CLK:
 285                resource = SC_R_ENET_0;
 286                pm_clk = SC_PM_CLK_PER;
 287                break;
 288        case IMX8QXP_ENET1_IPG_CLK:
 289        case IMX8QXP_ENET1_AHB_CLK:
 290        case IMX8QXP_ENET1_REF_DIV:
 291        case IMX8QXP_ENET1_PTP_CLK:
 292                resource = SC_R_ENET_1;
 293                pm_clk = SC_PM_CLK_PER;
 294                break;
 295        default:
 296                if (clk->id < IMX8QXP_UART0_IPG_CLK ||
 297                    clk->id >= IMX8QXP_CLK_END) {
 298                        printf("%s(Invalid clk ID #%lu)\n",
 299                               __func__, clk->id);
 300                        return -EINVAL;
 301                }
 302                return -ENOTSUPP;
 303        }
 304
 305        ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0);
 306        if (ret) {
 307                printf("%s err %d\n", __func__, ret);
 308                return ret;
 309        }
 310
 311        return 0;
 312}
 313
 314static int imx8_clk_disable(struct clk *clk)
 315{
 316        return __imx8_clk_enable(clk, 0);
 317}
 318
 319static int imx8_clk_enable(struct clk *clk)
 320{
 321        return __imx8_clk_enable(clk, 1);
 322}
 323
 324#if CONFIG_IS_ENABLED(CMD_CLK)
 325int soc_clk_dump(void)
 326{
 327        struct udevice *dev;
 328        struct clk clk;
 329        unsigned long rate;
 330        int i, ret;
 331
 332        ret = uclass_get_device_by_driver(UCLASS_CLK,
 333                                          DM_GET_DRIVER(imx8_clk), &dev);
 334        if (ret)
 335                return ret;
 336
 337        printf("Clk\t\tHz\n");
 338
 339        for (i = 0; i < ARRAY_SIZE(imx8_clk_names); i++) {
 340                clk.id = imx8_clk_names[i].id;
 341                ret = clk_request(dev, &clk);
 342                if (ret < 0) {
 343                        debug("%s clk_request() failed: %d\n", __func__, ret);
 344                        continue;
 345                }
 346
 347                ret = clk_get_rate(&clk);
 348                rate = ret;
 349
 350                clk_free(&clk);
 351
 352                if (ret == -ENOTSUPP) {
 353                        printf("clk ID %lu not supported yet\n",
 354                               imx8_clk_names[i].id);
 355                        continue;
 356                }
 357                if (ret < 0) {
 358                        printf("%s %lu: get_rate err: %d\n",
 359                               __func__, imx8_clk_names[i].id, ret);
 360                        continue;
 361                }
 362
 363                printf("%s(%3lu):\t%lu\n",
 364                       imx8_clk_names[i].name, imx8_clk_names[i].id, rate);
 365        }
 366
 367        return 0;
 368}
 369#endif
 370
 371static struct clk_ops imx8_clk_ops = {
 372        .set_rate = imx8_clk_set_rate,
 373        .get_rate = imx8_clk_get_rate,
 374        .enable = imx8_clk_enable,
 375        .disable = imx8_clk_disable,
 376};
 377
 378static int imx8_clk_probe(struct udevice *dev)
 379{
 380        return 0;
 381}
 382
 383static const struct udevice_id imx8_clk_ids[] = {
 384        { .compatible = "fsl,imx8qxp-clk" },
 385        { },
 386};
 387
 388U_BOOT_DRIVER(imx8_clk) = {
 389        .name = "clk_imx8",
 390        .id = UCLASS_CLK,
 391        .of_match = imx8_clk_ids,
 392        .ops = &imx8_clk_ops,
 393        .probe = imx8_clk_probe,
 394        .flags = DM_FLAG_PRE_RELOC,
 395};
 396