uboot/arch/arm/cpu/arm926ejs/mxs/clock.c
<<
>>
Prefs
   1/*
   2 * Freescale i.MX28 clock setup code
   3 *
   4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
   5 * on behalf of DENX Software Engineering GmbH
   6 *
   7 * Based on code from LTIB:
   8 * Copyright (C) 2010 Freescale Semiconductor, Inc.
   9 *
  10 * See file CREDITS for list of people who contributed to this
  11 * project.
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License as
  15 * published by the Free Software Foundation; either version 2 of
  16 * the License, or (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  26 * MA 02111-1307 USA
  27 */
  28
  29#include <common.h>
  30#include <asm/errno.h>
  31#include <asm/io.h>
  32#include <asm/arch/clock.h>
  33#include <asm/arch/imx-regs.h>
  34
  35/* The PLL frequency is always 480MHz, see section 10.2 in iMX28 datasheet. */
  36#define PLL_FREQ_KHZ    480000
  37#define PLL_FREQ_COEF   18
  38/* The XTAL frequency is always 24MHz, see section 10.2 in iMX28 datasheet. */
  39#define XTAL_FREQ_KHZ   24000
  40
  41#define PLL_FREQ_MHZ    (PLL_FREQ_KHZ / 1000)
  42#define XTAL_FREQ_MHZ   (XTAL_FREQ_KHZ / 1000)
  43
  44static uint32_t mx28_get_pclk(void)
  45{
  46        struct mxs_clkctrl_regs *clkctrl_regs =
  47                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  48
  49        uint32_t clkctrl, clkseq, div;
  50        uint8_t clkfrac, frac;
  51
  52        clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
  53
  54        /* No support of fractional divider calculation */
  55        if (clkctrl &
  56                (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
  57                return 0;
  58        }
  59
  60        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
  61
  62        /* XTAL Path */
  63        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
  64                div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
  65                        CLKCTRL_CPU_DIV_XTAL_OFFSET;
  66                return XTAL_FREQ_MHZ / div;
  67        }
  68
  69        /* REF Path */
  70        clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
  71        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
  72        div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
  73        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
  74}
  75
  76static uint32_t mx28_get_hclk(void)
  77{
  78        struct mxs_clkctrl_regs *clkctrl_regs =
  79                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  80
  81        uint32_t div;
  82        uint32_t clkctrl;
  83
  84        clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
  85
  86        /* No support of fractional divider calculation */
  87        if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
  88                return 0;
  89
  90        div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
  91        return mx28_get_pclk() / div;
  92}
  93
  94static uint32_t mx28_get_emiclk(void)
  95{
  96        struct mxs_clkctrl_regs *clkctrl_regs =
  97                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
  98
  99        uint32_t clkctrl, clkseq, div;
 100        uint8_t clkfrac, frac;
 101
 102        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
 103        clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi);
 104
 105        /* XTAL Path */
 106        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) {
 107                div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >>
 108                        CLKCTRL_EMI_DIV_XTAL_OFFSET;
 109                return XTAL_FREQ_MHZ / div;
 110        }
 111
 112        /* REF Path */
 113        clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
 114        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
 115        div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK;
 116        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
 117}
 118
 119static uint32_t mx28_get_gpmiclk(void)
 120{
 121        struct mxs_clkctrl_regs *clkctrl_regs =
 122                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 123
 124        uint32_t clkctrl, clkseq, div;
 125        uint8_t clkfrac, frac;
 126
 127        clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
 128        clkctrl = readl(&clkctrl_regs->hw_clkctrl_gpmi);
 129
 130        /* XTAL Path */
 131        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_GPMI) {
 132                div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
 133                return XTAL_FREQ_MHZ / div;
 134        }
 135
 136        /* REF Path */
 137        clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI]);
 138        frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
 139        div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
 140        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
 141}
 142
 143/*
 144 * Set IO clock frequency, in kHz
 145 */
 146void mx28_set_ioclk(enum mxs_ioclock io, uint32_t freq)
 147{
 148        struct mxs_clkctrl_regs *clkctrl_regs =
 149                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 150        uint32_t div;
 151        int io_reg;
 152
 153        if (freq == 0)
 154                return;
 155
 156        if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
 157                return;
 158
 159        div = (PLL_FREQ_KHZ * PLL_FREQ_COEF) / freq;
 160
 161        if (div < 18)
 162                div = 18;
 163
 164        if (div > 35)
 165                div = 35;
 166
 167        io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
 168        writeb(CLKCTRL_FRAC_CLKGATE,
 169                &clkctrl_regs->hw_clkctrl_frac0_set[io_reg]);
 170        writeb(CLKCTRL_FRAC_CLKGATE | (div & CLKCTRL_FRAC_FRAC_MASK),
 171                &clkctrl_regs->hw_clkctrl_frac0[io_reg]);
 172        writeb(CLKCTRL_FRAC_CLKGATE,
 173                &clkctrl_regs->hw_clkctrl_frac0_clr[io_reg]);
 174}
 175
 176/*
 177 * Get IO clock, returns IO clock in kHz
 178 */
 179static uint32_t mx28_get_ioclk(enum mxs_ioclock io)
 180{
 181        struct mxs_clkctrl_regs *clkctrl_regs =
 182                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 183        uint8_t ret;
 184        int io_reg;
 185
 186        if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
 187                return 0;
 188
 189        io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
 190
 191        ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) &
 192                CLKCTRL_FRAC_FRAC_MASK;
 193
 194        return (PLL_FREQ_KHZ * PLL_FREQ_COEF) / ret;
 195}
 196
 197/*
 198 * Configure SSP clock frequency, in kHz
 199 */
 200void mx28_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
 201{
 202        struct mxs_clkctrl_regs *clkctrl_regs =
 203                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 204        uint32_t clk, clkreg;
 205
 206        if (ssp > MXC_SSPCLK3)
 207                return;
 208
 209        clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
 210                        (ssp * sizeof(struct mxs_register_32));
 211
 212        clrbits_le32(clkreg, CLKCTRL_SSP_CLKGATE);
 213        while (readl(clkreg) & CLKCTRL_SSP_CLKGATE)
 214                ;
 215
 216        if (xtal)
 217                clk = XTAL_FREQ_KHZ;
 218        else
 219                clk = mx28_get_ioclk(ssp >> 1);
 220
 221        if (freq > clk)
 222                return;
 223
 224        /* Calculate the divider and cap it if necessary */
 225        clk /= freq;
 226        if (clk > CLKCTRL_SSP_DIV_MASK)
 227                clk = CLKCTRL_SSP_DIV_MASK;
 228
 229        clrsetbits_le32(clkreg, CLKCTRL_SSP_DIV_MASK, clk);
 230        while (readl(clkreg) & CLKCTRL_SSP_BUSY)
 231                ;
 232
 233        if (xtal)
 234                writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
 235                        &clkctrl_regs->hw_clkctrl_clkseq_set);
 236        else
 237                writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
 238                        &clkctrl_regs->hw_clkctrl_clkseq_clr);
 239}
 240
 241/*
 242 * Return SSP frequency, in kHz
 243 */
 244static uint32_t mx28_get_sspclk(enum mxs_sspclock ssp)
 245{
 246        struct mxs_clkctrl_regs *clkctrl_regs =
 247                (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 248        uint32_t clkreg;
 249        uint32_t clk, tmp;
 250
 251        if (ssp > MXC_SSPCLK3)
 252                return 0;
 253
 254        tmp = readl(&clkctrl_regs->hw_clkctrl_clkseq);
 255        if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp))
 256                return XTAL_FREQ_KHZ;
 257
 258        clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
 259                        (ssp * sizeof(struct mxs_register_32));
 260
 261        tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK;
 262
 263        if (tmp == 0)
 264                return 0;
 265
 266        clk = mx28_get_ioclk(ssp >> 1);
 267
 268        return clk / tmp;
 269}
 270
 271/*
 272 * Set SSP/MMC bus frequency, in kHz)
 273 */
 274void mx28_set_ssp_busclock(unsigned int bus, uint32_t freq)
 275{
 276        struct mxs_ssp_regs *ssp_regs;
 277        const uint32_t sspclk = mx28_get_sspclk(bus);
 278        uint32_t reg;
 279        uint32_t divide, rate, tgtclk;
 280
 281        ssp_regs = (struct mxs_ssp_regs *)(MXS_SSP0_BASE + (bus * 0x2000));
 282
 283        /*
 284         * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
 285         * CLOCK_DIVIDE has to be an even value from 2 to 254, and
 286         * CLOCK_RATE could be any integer from 0 to 255.
 287         */
 288        for (divide = 2; divide < 254; divide += 2) {
 289                rate = sspclk / freq / divide;
 290                if (rate <= 256)
 291                        break;
 292        }
 293
 294        tgtclk = sspclk / divide / rate;
 295        while (tgtclk > freq) {
 296                rate++;
 297                tgtclk = sspclk / divide / rate;
 298        }
 299        if (rate > 256)
 300                rate = 256;
 301
 302        /* Always set timeout the maximum */
 303        reg = SSP_TIMING_TIMEOUT_MASK |
 304                (divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) |
 305                ((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET);
 306        writel(reg, &ssp_regs->hw_ssp_timing);
 307
 308        debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
 309                bus, tgtclk, freq);
 310}
 311
 312uint32_t mxc_get_clock(enum mxc_clock clk)
 313{
 314        switch (clk) {
 315        case MXC_ARM_CLK:
 316                return mx28_get_pclk() * 1000000;
 317        case MXC_GPMI_CLK:
 318                return mx28_get_gpmiclk() * 1000000;
 319        case MXC_AHB_CLK:
 320        case MXC_IPG_CLK:
 321                return mx28_get_hclk() * 1000000;
 322        case MXC_EMI_CLK:
 323                return mx28_get_emiclk();
 324        case MXC_IO0_CLK:
 325                return mx28_get_ioclk(MXC_IOCLK0);
 326        case MXC_IO1_CLK:
 327                return mx28_get_ioclk(MXC_IOCLK1);
 328        case MXC_SSP0_CLK:
 329                return mx28_get_sspclk(MXC_SSPCLK0);
 330        case MXC_SSP1_CLK:
 331                return mx28_get_sspclk(MXC_SSPCLK1);
 332        case MXC_SSP2_CLK:
 333                return mx28_get_sspclk(MXC_SSPCLK2);
 334        case MXC_SSP3_CLK:
 335                return mx28_get_sspclk(MXC_SSPCLK3);
 336        case MXC_XTAL_CLK:
 337                return XTAL_FREQ_KHZ * 1000;
 338        }
 339
 340        return 0;
 341}
 342