linux/arch/arm/mach-imx/clock-imx1.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along
  14 * with this program; if not, write to the Free Software Foundation, Inc.,
  15 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/init.h>
  20#include <linux/list.h>
  21#include <linux/math64.h>
  22#include <linux/err.h>
  23#include <linux/clk.h>
  24#include <linux/io.h>
  25#include <linux/clkdev.h>
  26
  27#include <mach/clock.h>
  28#include <mach/hardware.h>
  29#include <mach/common.h>
  30
  31#define IO_ADDR_CCM(off)        (MX1_IO_ADDRESS(MX1_CCM_BASE_ADDR + (off)))
  32
  33/* CCM register addresses */
  34#define CCM_CSCR        IO_ADDR_CCM(0x0)
  35#define CCM_MPCTL0      IO_ADDR_CCM(0x4)
  36#define CCM_SPCTL0      IO_ADDR_CCM(0xc)
  37#define CCM_PCDR        IO_ADDR_CCM(0x20)
  38
  39#define CCM_CSCR_CLKO_OFFSET    29
  40#define CCM_CSCR_CLKO_MASK      (0x7 << 29)
  41#define CCM_CSCR_USB_OFFSET     26
  42#define CCM_CSCR_USB_MASK       (0x7 << 26)
  43#define CCM_CSCR_OSC_EN_SHIFT   17
  44#define CCM_CSCR_SYSTEM_SEL     (1 << 16)
  45#define CCM_CSCR_BCLK_OFFSET    10
  46#define CCM_CSCR_BCLK_MASK      (0xf << 10)
  47#define CCM_CSCR_PRESC          (1 << 15)
  48
  49#define CCM_PCDR_PCLK3_OFFSET   16
  50#define CCM_PCDR_PCLK3_MASK     (0x7f << 16)
  51#define CCM_PCDR_PCLK2_OFFSET   4
  52#define CCM_PCDR_PCLK2_MASK     (0xf << 4)
  53#define CCM_PCDR_PCLK1_OFFSET   0
  54#define CCM_PCDR_PCLK1_MASK     0xf
  55
  56#define IO_ADDR_SCM(off)        (MX1_IO_ADDRESS(MX1_SCM_BASE_ADDR + (off)))
  57
  58/* SCM register addresses */
  59#define SCM_GCCR        IO_ADDR_SCM(0xc)
  60
  61#define SCM_GCCR_DMA_CLK_EN_OFFSET      3
  62#define SCM_GCCR_CSI_CLK_EN_OFFSET      2
  63#define SCM_GCCR_MMA_CLK_EN_OFFSET      1
  64#define SCM_GCCR_USBD_CLK_EN_OFFSET     0
  65
  66static int _clk_enable(struct clk *clk)
  67{
  68        unsigned int reg;
  69
  70        reg = __raw_readl(clk->enable_reg);
  71        reg |= 1 << clk->enable_shift;
  72        __raw_writel(reg, clk->enable_reg);
  73
  74        return 0;
  75}
  76
  77static void _clk_disable(struct clk *clk)
  78{
  79        unsigned int reg;
  80
  81        reg = __raw_readl(clk->enable_reg);
  82        reg &= ~(1 << clk->enable_shift);
  83        __raw_writel(reg, clk->enable_reg);
  84}
  85
  86static int _clk_can_use_parent(const struct clk *clk_arr[], unsigned int size,
  87                               struct clk *parent)
  88{
  89        int i;
  90
  91        for (i = 0; i < size; i++)
  92                if (parent == clk_arr[i])
  93                        return i;
  94
  95        return -EINVAL;
  96}
  97
  98static unsigned long
  99_clk_simple_round_rate(struct clk *clk, unsigned long rate, unsigned int limit)
 100{
 101        int div;
 102        unsigned long parent_rate;
 103
 104        parent_rate = clk_get_rate(clk->parent);
 105
 106        div = parent_rate / rate;
 107        if (parent_rate % rate)
 108                div++;
 109
 110        if (div > limit)
 111                div = limit;
 112
 113        return parent_rate / div;
 114}
 115
 116static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate)
 117{
 118        return clk->parent->round_rate(clk->parent, rate);
 119}
 120
 121static int _clk_parent_set_rate(struct clk *clk, unsigned long rate)
 122{
 123        return clk->parent->set_rate(clk->parent, rate);
 124}
 125
 126static unsigned long clk16m_get_rate(struct clk *clk)
 127{
 128        return 16000000;
 129}
 130
 131static struct clk clk16m = {
 132        .get_rate = clk16m_get_rate,
 133        .enable = _clk_enable,
 134        .enable_reg = CCM_CSCR,
 135        .enable_shift = CCM_CSCR_OSC_EN_SHIFT,
 136        .disable = _clk_disable,
 137};
 138
 139/* in Hz */
 140static unsigned long clk32_rate;
 141
 142static unsigned long clk32_get_rate(struct clk *clk)
 143{
 144        return clk32_rate;
 145}
 146
 147static struct clk clk32 = {
 148        .get_rate = clk32_get_rate,
 149};
 150
 151static unsigned long clk32_premult_get_rate(struct clk *clk)
 152{
 153        return clk_get_rate(clk->parent) * 512;
 154}
 155
 156static struct clk clk32_premult = {
 157        .parent = &clk32,
 158        .get_rate = clk32_premult_get_rate,
 159};
 160
 161static const struct clk *prem_clk_clocks[] = {
 162        &clk32_premult,
 163        &clk16m,
 164};
 165
 166static int prem_clk_set_parent(struct clk *clk, struct clk *parent)
 167{
 168        int i;
 169        unsigned int reg = __raw_readl(CCM_CSCR);
 170
 171        i = _clk_can_use_parent(prem_clk_clocks, ARRAY_SIZE(prem_clk_clocks),
 172                                parent);
 173
 174        switch (i) {
 175        case 0:
 176                reg &= ~CCM_CSCR_SYSTEM_SEL;
 177                break;
 178        case 1:
 179                reg |= CCM_CSCR_SYSTEM_SEL;
 180                break;
 181        default:
 182                return i;
 183        }
 184
 185        __raw_writel(reg, CCM_CSCR);
 186
 187        return 0;
 188}
 189
 190static struct clk prem_clk = {
 191        .set_parent = prem_clk_set_parent,
 192};
 193
 194static unsigned long system_clk_get_rate(struct clk *clk)
 195{
 196        return mxc_decode_pll(__raw_readl(CCM_SPCTL0),
 197                              clk_get_rate(clk->parent));
 198}
 199
 200static struct clk system_clk = {
 201        .parent = &prem_clk,
 202        .get_rate = system_clk_get_rate,
 203};
 204
 205static unsigned long mcu_clk_get_rate(struct clk *clk)
 206{
 207        return mxc_decode_pll(__raw_readl(CCM_MPCTL0),
 208                              clk_get_rate(clk->parent));
 209}
 210
 211static struct clk mcu_clk = {
 212        .parent = &clk32_premult,
 213        .get_rate = mcu_clk_get_rate,
 214};
 215
 216static unsigned long fclk_get_rate(struct clk *clk)
 217{
 218        unsigned long fclk = clk_get_rate(clk->parent);
 219
 220        if (__raw_readl(CCM_CSCR) & CCM_CSCR_PRESC)
 221                fclk /= 2;
 222
 223        return fclk;
 224}
 225
 226static struct clk fclk = {
 227        .parent = &mcu_clk,
 228        .get_rate = fclk_get_rate,
 229};
 230
 231/*
 232 *  get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
 233 */
 234static unsigned long hclk_get_rate(struct clk *clk)
 235{
 236        return clk_get_rate(clk->parent) / (((__raw_readl(CCM_CSCR) &
 237                        CCM_CSCR_BCLK_MASK) >> CCM_CSCR_BCLK_OFFSET) + 1);
 238}
 239
 240static unsigned long hclk_round_rate(struct clk *clk, unsigned long rate)
 241{
 242        return _clk_simple_round_rate(clk, rate, 16);
 243}
 244
 245static int hclk_set_rate(struct clk *clk, unsigned long rate)
 246{
 247        unsigned int div;
 248        unsigned int reg;
 249        unsigned long parent_rate;
 250
 251        parent_rate = clk_get_rate(clk->parent);
 252
 253        div = parent_rate / rate;
 254
 255        if (div > 16 || div < 1 || ((parent_rate / div) != rate))
 256                return -EINVAL;
 257
 258        div--;
 259
 260        reg = __raw_readl(CCM_CSCR);
 261        reg &= ~CCM_CSCR_BCLK_MASK;
 262        reg |= div << CCM_CSCR_BCLK_OFFSET;
 263        __raw_writel(reg, CCM_CSCR);
 264
 265        return 0;
 266}
 267
 268static struct clk hclk = {
 269        .parent = &system_clk,
 270        .get_rate = hclk_get_rate,
 271        .round_rate = hclk_round_rate,
 272        .set_rate = hclk_set_rate,
 273};
 274
 275static unsigned long clk48m_get_rate(struct clk *clk)
 276{
 277        return clk_get_rate(clk->parent) / (((__raw_readl(CCM_CSCR) &
 278                        CCM_CSCR_USB_MASK) >> CCM_CSCR_USB_OFFSET) + 1);
 279}
 280
 281static unsigned long clk48m_round_rate(struct clk *clk, unsigned long rate)
 282{
 283        return _clk_simple_round_rate(clk, rate, 8);
 284}
 285
 286static int clk48m_set_rate(struct clk *clk, unsigned long rate)
 287{
 288        unsigned int div;
 289        unsigned int reg;
 290        unsigned long parent_rate;
 291
 292        parent_rate = clk_get_rate(clk->parent);
 293
 294        div = parent_rate / rate;
 295
 296        if (div > 8 || div < 1 || ((parent_rate / div) != rate))
 297                return -EINVAL;
 298
 299        div--;
 300
 301        reg = __raw_readl(CCM_CSCR);
 302        reg &= ~CCM_CSCR_USB_MASK;
 303        reg |= div << CCM_CSCR_USB_OFFSET;
 304        __raw_writel(reg, CCM_CSCR);
 305
 306        return 0;
 307}
 308
 309static struct clk clk48m = {
 310        .parent = &system_clk,
 311        .get_rate = clk48m_get_rate,
 312        .round_rate = clk48m_round_rate,
 313        .set_rate = clk48m_set_rate,
 314};
 315
 316/*
 317 *  get peripheral clock 1 ( UART[12], Timer[12], PWM )
 318 */
 319static unsigned long perclk1_get_rate(struct clk *clk)
 320{
 321        return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
 322                        CCM_PCDR_PCLK1_MASK) >> CCM_PCDR_PCLK1_OFFSET) + 1);
 323}
 324
 325static unsigned long perclk1_round_rate(struct clk *clk, unsigned long rate)
 326{
 327        return _clk_simple_round_rate(clk, rate, 16);
 328}
 329
 330static int perclk1_set_rate(struct clk *clk, unsigned long rate)
 331{
 332        unsigned int div;
 333        unsigned int reg;
 334        unsigned long parent_rate;
 335
 336        parent_rate = clk_get_rate(clk->parent);
 337
 338        div = parent_rate / rate;
 339
 340        if (div > 16 || div < 1 || ((parent_rate / div) != rate))
 341                return -EINVAL;
 342
 343        div--;
 344
 345        reg = __raw_readl(CCM_PCDR);
 346        reg &= ~CCM_PCDR_PCLK1_MASK;
 347        reg |= div << CCM_PCDR_PCLK1_OFFSET;
 348        __raw_writel(reg, CCM_PCDR);
 349
 350        return 0;
 351}
 352
 353/*
 354 *  get peripheral clock 2 ( LCD, SD, SPI[12] )
 355 */
 356static unsigned long perclk2_get_rate(struct clk *clk)
 357{
 358        return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
 359                        CCM_PCDR_PCLK2_MASK) >> CCM_PCDR_PCLK2_OFFSET) + 1);
 360}
 361
 362static unsigned long perclk2_round_rate(struct clk *clk, unsigned long rate)
 363{
 364        return _clk_simple_round_rate(clk, rate, 16);
 365}
 366
 367static int perclk2_set_rate(struct clk *clk, unsigned long rate)
 368{
 369        unsigned int div;
 370        unsigned int reg;
 371        unsigned long parent_rate;
 372
 373        parent_rate = clk_get_rate(clk->parent);
 374
 375        div = parent_rate / rate;
 376
 377        if (div > 16 || div < 1 || ((parent_rate / div) != rate))
 378                return -EINVAL;
 379
 380        div--;
 381
 382        reg = __raw_readl(CCM_PCDR);
 383        reg &= ~CCM_PCDR_PCLK2_MASK;
 384        reg |= div << CCM_PCDR_PCLK2_OFFSET;
 385        __raw_writel(reg, CCM_PCDR);
 386
 387        return 0;
 388}
 389
 390/*
 391 *  get peripheral clock 3 ( SSI )
 392 */
 393static unsigned long perclk3_get_rate(struct clk *clk)
 394{
 395        return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
 396                        CCM_PCDR_PCLK3_MASK) >> CCM_PCDR_PCLK3_OFFSET) + 1);
 397}
 398
 399static unsigned long perclk3_round_rate(struct clk *clk, unsigned long rate)
 400{
 401        return _clk_simple_round_rate(clk, rate, 128);
 402}
 403
 404static int perclk3_set_rate(struct clk *clk, unsigned long rate)
 405{
 406        unsigned int div;
 407        unsigned int reg;
 408        unsigned long parent_rate;
 409
 410        parent_rate = clk_get_rate(clk->parent);
 411
 412        div = parent_rate / rate;
 413
 414        if (div > 128 || div < 1 || ((parent_rate / div) != rate))
 415                return -EINVAL;
 416
 417        div--;
 418
 419        reg = __raw_readl(CCM_PCDR);
 420        reg &= ~CCM_PCDR_PCLK3_MASK;
 421        reg |= div << CCM_PCDR_PCLK3_OFFSET;
 422        __raw_writel(reg, CCM_PCDR);
 423
 424        return 0;
 425}
 426
 427static struct clk perclk[] = {
 428        {
 429                .id = 0,
 430                .parent = &system_clk,
 431                .get_rate = perclk1_get_rate,
 432                .round_rate = perclk1_round_rate,
 433                .set_rate = perclk1_set_rate,
 434        }, {
 435                .id = 1,
 436                .parent = &system_clk,
 437                .get_rate = perclk2_get_rate,
 438                .round_rate = perclk2_round_rate,
 439                .set_rate = perclk2_set_rate,
 440        }, {
 441                .id = 2,
 442                .parent = &system_clk,
 443                .get_rate = perclk3_get_rate,
 444                .round_rate = perclk3_round_rate,
 445                .set_rate = perclk3_set_rate,
 446        }
 447};
 448
 449static const struct clk *clko_clocks[] = {
 450        &perclk[0],
 451        &hclk,
 452        &clk48m,
 453        &clk16m,
 454        &prem_clk,
 455        &fclk,
 456};
 457
 458static int clko_set_parent(struct clk *clk, struct clk *parent)
 459{
 460        int i;
 461        unsigned int reg;
 462
 463        i = _clk_can_use_parent(clko_clocks, ARRAY_SIZE(clko_clocks), parent);
 464        if (i < 0)
 465                return i;
 466
 467        reg = __raw_readl(CCM_CSCR) & ~CCM_CSCR_CLKO_MASK;
 468        reg |= i << CCM_CSCR_CLKO_OFFSET;
 469        __raw_writel(reg, CCM_CSCR);
 470
 471        if (clko_clocks[i]->set_rate && clko_clocks[i]->round_rate) {
 472                clk->set_rate = _clk_parent_set_rate;
 473                clk->round_rate = _clk_parent_round_rate;
 474        } else {
 475                clk->set_rate = NULL;
 476                clk->round_rate = NULL;
 477        }
 478
 479        return 0;
 480}
 481
 482static struct clk clko_clk = {
 483        .set_parent = clko_set_parent,
 484};
 485
 486static struct clk dma_clk = {
 487        .parent = &hclk,
 488        .round_rate = _clk_parent_round_rate,
 489        .set_rate = _clk_parent_set_rate,
 490        .enable = _clk_enable,
 491        .enable_reg = SCM_GCCR,
 492        .enable_shift = SCM_GCCR_DMA_CLK_EN_OFFSET,
 493        .disable = _clk_disable,
 494};
 495
 496static struct clk csi_clk = {
 497        .parent = &hclk,
 498        .round_rate = _clk_parent_round_rate,
 499        .set_rate = _clk_parent_set_rate,
 500        .enable = _clk_enable,
 501        .enable_reg = SCM_GCCR,
 502        .enable_shift = SCM_GCCR_CSI_CLK_EN_OFFSET,
 503        .disable = _clk_disable,
 504};
 505
 506static struct clk mma_clk = {
 507        .parent = &hclk,
 508        .round_rate = _clk_parent_round_rate,
 509        .set_rate = _clk_parent_set_rate,
 510        .enable = _clk_enable,
 511        .enable_reg = SCM_GCCR,
 512        .enable_shift = SCM_GCCR_MMA_CLK_EN_OFFSET,
 513        .disable = _clk_disable,
 514};
 515
 516static struct clk usbd_clk = {
 517        .parent = &clk48m,
 518        .round_rate = _clk_parent_round_rate,
 519        .set_rate = _clk_parent_set_rate,
 520        .enable = _clk_enable,
 521        .enable_reg = SCM_GCCR,
 522        .enable_shift = SCM_GCCR_USBD_CLK_EN_OFFSET,
 523        .disable = _clk_disable,
 524};
 525
 526static struct clk gpt_clk = {
 527        .parent = &perclk[0],
 528        .round_rate = _clk_parent_round_rate,
 529        .set_rate = _clk_parent_set_rate,
 530};
 531
 532static struct clk uart_clk = {
 533        .parent = &perclk[0],
 534        .round_rate = _clk_parent_round_rate,
 535        .set_rate = _clk_parent_set_rate,
 536};
 537
 538static struct clk i2c_clk = {
 539        .parent = &hclk,
 540        .round_rate = _clk_parent_round_rate,
 541        .set_rate = _clk_parent_set_rate,
 542};
 543
 544static struct clk spi_clk = {
 545        .parent = &perclk[1],
 546        .round_rate = _clk_parent_round_rate,
 547        .set_rate = _clk_parent_set_rate,
 548};
 549
 550static struct clk sdhc_clk = {
 551        .parent = &perclk[1],
 552        .round_rate = _clk_parent_round_rate,
 553        .set_rate = _clk_parent_set_rate,
 554};
 555
 556static struct clk lcdc_clk = {
 557        .parent = &perclk[1],
 558        .round_rate = _clk_parent_round_rate,
 559        .set_rate = _clk_parent_set_rate,
 560};
 561
 562static struct clk mshc_clk = {
 563        .parent = &hclk,
 564        .round_rate = _clk_parent_round_rate,
 565        .set_rate = _clk_parent_set_rate,
 566};
 567
 568static struct clk ssi_clk = {
 569        .parent = &perclk[2],
 570        .round_rate = _clk_parent_round_rate,
 571        .set_rate = _clk_parent_set_rate,
 572};
 573
 574static struct clk rtc_clk = {
 575        .parent = &clk32,
 576};
 577
 578#define _REGISTER_CLOCK(d, n, c) \
 579        { \
 580                .dev_id = d, \
 581                .con_id = n, \
 582                .clk = &c, \
 583        },
 584static struct clk_lookup lookups[] __initdata = {
 585        _REGISTER_CLOCK(NULL, "dma", dma_clk)
 586        _REGISTER_CLOCK("mx1-camera.0", NULL, csi_clk)
 587        _REGISTER_CLOCK(NULL, "mma", mma_clk)
 588        _REGISTER_CLOCK("imx_udc.0", NULL, usbd_clk)
 589        _REGISTER_CLOCK(NULL, "gpt", gpt_clk)
 590        _REGISTER_CLOCK("imx-uart.0", NULL, uart_clk)
 591        _REGISTER_CLOCK("imx-uart.1", NULL, uart_clk)
 592        _REGISTER_CLOCK("imx-uart.2", NULL, uart_clk)
 593        _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
 594        _REGISTER_CLOCK("imx1-cspi.0", NULL, spi_clk)
 595        _REGISTER_CLOCK("imx-mmc.0", NULL, sdhc_clk)
 596        _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
 597        _REGISTER_CLOCK(NULL, "mshc", mshc_clk)
 598        _REGISTER_CLOCK(NULL, "ssi", ssi_clk)
 599        _REGISTER_CLOCK("mxc_rtc.0", NULL, rtc_clk)
 600};
 601
 602int __init mx1_clocks_init(unsigned long fref)
 603{
 604        unsigned int reg;
 605
 606        /* disable clocks we are able to */
 607        __raw_writel(0, SCM_GCCR);
 608
 609        clk32_rate = fref;
 610        reg = __raw_readl(CCM_CSCR);
 611
 612        /* detect clock reference for system PLL */
 613        if (reg & CCM_CSCR_SYSTEM_SEL) {
 614                prem_clk.parent = &clk16m;
 615        } else {
 616                /* ensure that oscillator is disabled */
 617                reg &= ~(1 << CCM_CSCR_OSC_EN_SHIFT);
 618                __raw_writel(reg, CCM_CSCR);
 619                prem_clk.parent = &clk32_premult;
 620        }
 621
 622        /* detect reference for CLKO */
 623        reg = (reg & CCM_CSCR_CLKO_MASK) >> CCM_CSCR_CLKO_OFFSET;
 624        clko_clk.parent = (struct clk *)clko_clocks[reg];
 625
 626        clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 627
 628        clk_enable(&hclk);
 629        clk_enable(&fclk);
 630
 631        mxc_timer_init(&gpt_clk, MX1_IO_ADDRESS(MX1_TIM1_BASE_ADDR),
 632                        MX1_TIM1_INT);
 633
 634        return 0;
 635}
 636