uboot/arch/arm/cpu/arm926ejs/mxs/clock.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Freescale i.MX23/i.MX28 clock setup code
   4 *
   5 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
   6 * on behalf of DENX Software Engineering GmbH
   7 *
   8 * Based on code from LTIB:
   9 * Copyright (C) 2010 Freescale Semiconductor, Inc.
  10 */
  11
  12#include <common.h>
  13#include <linux/errno.h>
  14#include <asm/io.h>
  15#include <asm/arch/clock.h>
  16#include <asm/arch/imx-regs.h>
  17
  18/*
  19 * The PLL frequency is 480MHz and XTAL frequency is 24MHz
  20 *   iMX23: datasheet section 4.2
  21 *   iMX28: datasheet section 10.2
  22 */
  23#define PLL_FREQ_KHZ    480000
  24#define PLL_FREQ_COEF   18
  25#define XTAL_FREQ_KHZ   24000
  26
  27#define PLL_FREQ_MHZ    (PLL_FREQ_KHZ / 1000)
  28#define XTAL_FREQ_MHZ   (XTAL_FREQ_KHZ / 1000)
  29
  30#if defined(CONFIG_MX23)
  31#define MXC_SSPCLK_MAX MXC_SSPCLK0
  32#elif defined(CONFIG_MX28)
  33#define MXC_SSPCLK_MAX MXC_SSPCLK3
  34#endif
  35
  36static uint32_t mxs_get_pclk(void)
  37{
  38        struct mxs_clkctrl_regs *clkctrl_regs =
  39                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  40
  41        uint32_t clkctrl, clkseq, div;
  42        uint8_t clkfrac, frac;
  43
  44        clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
  45
  46        /* No support of fractional divider calculation */
  47        if (clkctrl &
  48                (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
  49                return 0;
  50        }
  51
  52        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
  53
  54        /* XTAL Path */
  55        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
  56                div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
  57                        CLKCTRL_CPU_DIV_XTAL_OFFSET;
  58                return XTAL_FREQ_MHZ / div;
  59        }
  60
  61        /* REF Path */
  62        clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
  63        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
  64        div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
  65        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
  66}
  67
  68static uint32_t mxs_get_hclk(void)
  69{
  70        struct mxs_clkctrl_regs *clkctrl_regs =
  71                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  72
  73        uint32_t div;
  74        uint32_t clkctrl;
  75
  76        clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
  77
  78        /* No support of fractional divider calculation */
  79        if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
  80                return 0;
  81
  82        div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
  83        return mxs_get_pclk() / div;
  84}
  85
  86static uint32_t mxs_get_emiclk(void)
  87{
  88        struct mxs_clkctrl_regs *clkctrl_regs =
  89                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  90
  91        uint32_t clkctrl, clkseq, div;
  92        uint8_t clkfrac, frac;
  93
  94        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
  95        clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi);
  96
  97        /* XTAL Path */
  98        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) {
  99                div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >>
 100                        CLKCTRL_EMI_DIV_XTAL_OFFSET;
 101                return XTAL_FREQ_MHZ / div;
 102        }
 103
 104        /* REF Path */
 105        clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
 106        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
 107        div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK;
 108        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
 109}
 110
 111static uint32_t mxs_get_gpmiclk(void)
 112{
 113        struct mxs_clkctrl_regs *clkctrl_regs =
 114                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 115#if defined(CONFIG_MX23)
 116        uint8_t *reg =
 117                &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU];
 118#elif defined(CONFIG_MX28)
 119        uint8_t *reg =
 120                &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI];
 121#endif
 122        uint32_t clkctrl, clkseq, div;
 123        uint8_t clkfrac, frac;
 124
 125        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
 126        clkctrl = readl(&clkctrl_regs->hw_clkctrl_gpmi);
 127
 128        /* XTAL Path */
 129        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_GPMI) {
 130                div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
 131                return XTAL_FREQ_MHZ / div;
 132        }
 133
 134        /* REF Path */
 135        clkfrac = readb(reg);
 136        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
 137        div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
 138        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
 139}
 140
 141/*
 142 * Set IO clock frequency, in kHz
 143 */
 144void mxs_set_ioclk(enum mxs_ioclock io, uint32_t freq)
 145{
 146        struct mxs_clkctrl_regs *clkctrl_regs =
 147                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 148        uint32_t div;
 149        int io_reg;
 150
 151        if (freq == 0)
 152                return;
 153
 154        if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
 155                return;
 156
 157        div = (PLL_FREQ_KHZ * PLL_FREQ_COEF) / freq;
 158
 159        if (div < 18)
 160                div = 18;
 161
 162        if (div > 35)
 163                div = 35;
 164
 165        io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
 166        writeb(CLKCTRL_FRAC_CLKGATE,
 167                &clkctrl_regs->hw_clkctrl_frac0_set[io_reg]);
 168        writeb(CLKCTRL_FRAC_CLKGATE | (div & CLKCTRL_FRAC_FRAC_MASK),
 169                &clkctrl_regs->hw_clkctrl_frac0[io_reg]);
 170        writeb(CLKCTRL_FRAC_CLKGATE,
 171                &clkctrl_regs->hw_clkctrl_frac0_clr[io_reg]);
 172}
 173
 174/*
 175 * Get IO clock, returns IO clock in kHz
 176 */
 177static uint32_t mxs_get_ioclk(enum mxs_ioclock io)
 178{
 179        struct mxs_clkctrl_regs *clkctrl_regs =
 180                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 181        uint8_t ret;
 182        int io_reg;
 183
 184        if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
 185                return 0;
 186
 187        io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
 188
 189        ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) &
 190                CLKCTRL_FRAC_FRAC_MASK;
 191
 192        return (PLL_FREQ_KHZ * PLL_FREQ_COEF) / ret;
 193}
 194
 195/*
 196 * Configure SSP clock frequency, in kHz
 197 */
 198void mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
 199{
 200        struct mxs_clkctrl_regs *clkctrl_regs =
 201                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 202        uint32_t clk, clkreg;
 203
 204        if (ssp > MXC_SSPCLK_MAX)
 205                return;
 206
 207        clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
 208                        (ssp * sizeof(struct mxs_register_32));
 209
 210        clrbits_le32(clkreg, CLKCTRL_SSP_CLKGATE);
 211        while (readl(clkreg) & CLKCTRL_SSP_CLKGATE)
 212                ;
 213
 214        if (xtal)
 215                clk = XTAL_FREQ_KHZ;
 216        else
 217                clk = mxs_get_ioclk(ssp >> 1);
 218
 219        if (freq > clk)
 220                return;
 221
 222        /* Calculate the divider and cap it if necessary */
 223        clk /= freq;
 224        if (clk > CLKCTRL_SSP_DIV_MASK)
 225                clk = CLKCTRL_SSP_DIV_MASK;
 226
 227        clrsetbits_le32(clkreg, CLKCTRL_SSP_DIV_MASK, clk);
 228        while (readl(clkreg) & CLKCTRL_SSP_BUSY)
 229                ;
 230
 231        if (xtal)
 232                writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
 233                        &clkctrl_regs->hw_clkctrl_clkseq_set);
 234        else
 235                writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
 236                        &clkctrl_regs->hw_clkctrl_clkseq_clr);
 237}
 238
 239/*
 240 * Return SSP frequency, in kHz
 241 */
 242static uint32_t mxs_get_sspclk(enum mxs_sspclock ssp)
 243{
 244        struct mxs_clkctrl_regs *clkctrl_regs =
 245                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 246        uint32_t clkreg;
 247        uint32_t clk, tmp;
 248
 249        if (ssp > MXC_SSPCLK_MAX)
 250                return 0;
 251
 252        tmp = readl(&clkctrl_regs->hw_clkctrl_clkseq);
 253        if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp))
 254                return XTAL_FREQ_KHZ;
 255
 256        clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
 257                        (ssp * sizeof(struct mxs_register_32));
 258
 259        tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK;
 260
 261        if (tmp == 0)
 262                return 0;
 263
 264        clk = mxs_get_ioclk(ssp >> 1);
 265
 266        return clk / tmp;
 267}
 268
 269/*
 270 * Set SSP/MMC bus frequency, in kHz)
 271 */
 272void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq)
 273{
 274        struct mxs_ssp_regs *ssp_regs;
 275        const enum mxs_sspclock clk = mxs_ssp_clock_by_bus(bus);
 276        const uint32_t sspclk = mxs_get_sspclk(clk);
 277        uint32_t reg;
 278        uint32_t divide, rate, tgtclk;
 279
 280        ssp_regs = mxs_ssp_regs_by_bus(bus);
 281
 282        /*
 283         * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
 284         * CLOCK_DIVIDE has to be an even value from 2 to 254, and
 285         * CLOCK_RATE could be any integer from 0 to 255.
 286         */
 287        for (divide = 2; divide < 254; divide += 2) {
 288                rate = sspclk / freq / divide;
 289                if (rate <= 256)
 290                        break;
 291        }
 292
 293        tgtclk = sspclk / divide / rate;
 294        while (tgtclk > freq) {
 295                rate++;
 296                tgtclk = sspclk / divide / rate;
 297        }
 298        if (rate > 256)
 299                rate = 256;
 300
 301        /* Always set timeout the maximum */
 302        reg = SSP_TIMING_TIMEOUT_MASK |
 303                (divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) |
 304                ((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET);
 305        writel(reg, &ssp_regs->hw_ssp_timing);
 306
 307        debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
 308                bus, tgtclk, freq);
 309}
 310
 311void mxs_set_lcdclk(uint32_t __maybe_unused lcd_base, uint32_t freq)
 312{
 313        struct mxs_clkctrl_regs *clkctrl_regs =
 314                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 315        uint32_t fp, x, k_rest, k_best, x_best, tk;
 316        int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
 317
 318        if (freq == 0)
 319                return;
 320
 321#if defined(CONFIG_MX23)
 322        writel(CLKCTRL_CLKSEQ_BYPASS_PIX, &clkctrl_regs->hw_clkctrl_clkseq_clr);
 323#elif defined(CONFIG_MX28)
 324        writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, &clkctrl_regs->hw_clkctrl_clkseq_clr);
 325#endif
 326
 327        /*
 328         *             /               18 \     1       1
 329         * freq kHz = | 480000000 Hz * --  | * --- * ------
 330         *             \                x /     k     1000
 331         *
 332         *      480000000 Hz   18
 333         *      ------------ * --
 334         *        freq kHz      x
 335         * k = -------------------
 336         *             1000
 337         */
 338
 339        fp = ((PLL_FREQ_KHZ * 1000) / freq) * 18;
 340
 341        for (x = 18; x <= 35; x++) {
 342                tk = fp / x;
 343                if ((tk / 1000 == 0) || (tk / 1000 > 255))
 344                        continue;
 345
 346                k_rest = tk % 1000;
 347
 348                if (k_rest < (k_best_l % 1000)) {
 349                        k_best_l = tk;
 350                        x_best_l = x;
 351                }
 352
 353                if (k_rest > (k_best_t % 1000)) {
 354                        k_best_t = tk;
 355                        x_best_t = x;
 356                }
 357        }
 358
 359        if (1000 - (k_best_t % 1000) > (k_best_l % 1000)) {
 360                k_best = k_best_l;
 361                x_best = x_best_l;
 362        } else {
 363                k_best = k_best_t;
 364                x_best = x_best_t;
 365        }
 366
 367        k_best /= 1000;
 368
 369#if defined(CONFIG_MX23)
 370        writeb(CLKCTRL_FRAC_CLKGATE,
 371                &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_PIX]);
 372        writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
 373                &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_PIX]);
 374        writeb(CLKCTRL_FRAC_CLKGATE,
 375                &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_PIX]);
 376
 377        writel(CLKCTRL_PIX_CLKGATE,
 378                &clkctrl_regs->hw_clkctrl_pix_set);
 379        clrsetbits_le32(&clkctrl_regs->hw_clkctrl_pix,
 380                        CLKCTRL_PIX_DIV_MASK | CLKCTRL_PIX_CLKGATE,
 381                        k_best << CLKCTRL_PIX_DIV_OFFSET);
 382
 383        while (readl(&clkctrl_regs->hw_clkctrl_pix) & CLKCTRL_PIX_BUSY)
 384                ;
 385#elif defined(CONFIG_MX28)
 386        writeb(CLKCTRL_FRAC_CLKGATE,
 387                &clkctrl_regs->hw_clkctrl_frac1_set[CLKCTRL_FRAC1_PIX]);
 388        writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
 389                &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_PIX]);
 390        writeb(CLKCTRL_FRAC_CLKGATE,
 391                &clkctrl_regs->hw_clkctrl_frac1_clr[CLKCTRL_FRAC1_PIX]);
 392
 393        writel(CLKCTRL_DIS_LCDIF_CLKGATE,
 394                &clkctrl_regs->hw_clkctrl_lcdif_set);
 395        clrsetbits_le32(&clkctrl_regs->hw_clkctrl_lcdif,
 396                        CLKCTRL_DIS_LCDIF_DIV_MASK | CLKCTRL_DIS_LCDIF_CLKGATE,
 397                        k_best << CLKCTRL_DIS_LCDIF_DIV_OFFSET);
 398
 399        while (readl(&clkctrl_regs->hw_clkctrl_lcdif) & CLKCTRL_DIS_LCDIF_BUSY)
 400                ;
 401#endif
 402}
 403
 404uint32_t mxc_get_clock(enum mxc_clock clk)
 405{
 406        switch (clk) {
 407        case MXC_ARM_CLK:
 408                return mxs_get_pclk() * 1000000;
 409        case MXC_GPMI_CLK:
 410                return mxs_get_gpmiclk() * 1000000;
 411        case MXC_AHB_CLK:
 412        case MXC_IPG_CLK:
 413                return mxs_get_hclk() * 1000000;
 414        case MXC_EMI_CLK:
 415                return mxs_get_emiclk();
 416        case MXC_IO0_CLK:
 417                return mxs_get_ioclk(MXC_IOCLK0);
 418        case MXC_IO1_CLK:
 419                return mxs_get_ioclk(MXC_IOCLK1);
 420        case MXC_XTAL_CLK:
 421                return XTAL_FREQ_KHZ * 1000;
 422        case MXC_SSP0_CLK:
 423                return mxs_get_sspclk(MXC_SSPCLK0);
 424#ifdef CONFIG_MX28
 425        case MXC_SSP1_CLK:
 426                return mxs_get_sspclk(MXC_SSPCLK1);
 427        case MXC_SSP2_CLK:
 428                return mxs_get_sspclk(MXC_SSPCLK2);
 429        case MXC_SSP3_CLK:
 430                return mxs_get_sspclk(MXC_SSPCLK3);
 431#endif
 432        }
 433
 434        return 0;
 435}
 436