Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

clock.c

/*
 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/* based on mach-mx27/clock.c */

#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <mach/clock.h>
#include <mach/common.h>
#include "crm_regs.h"

#define OSC24M_CLK_FREQ     24000000      /* 24M reference clk */
#define OSC32K_CLK_FREQ     32768   /* 32.768k oscillator in */

#if defined CONFIG_CPU_FREQ_IMX
#define AHB_CLK_DEFAULT 133000000
#define ARM_SRC_DEFAULT 532000000
#endif

static struct clk mpll_clk;
static struct clk upll_clk;
static struct clk ahb_clk;
static struct clk upll_24610k_clk;
int cpu_wp_nr;

static int _clk_enable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(clk->enable_reg);
      reg |= 1 << clk->enable_shift;
      __raw_writel(reg, clk->enable_reg);

      return 0;
}

static void _clk_disable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(clk->enable_reg);
      reg &= ~(1 << clk->enable_shift);
      __raw_writel(reg, clk->enable_reg);
}

static int _clk_upll_enable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(MXC_CCM_CCTL);
      reg &= ~MXC_CCM_CCTL_UPLL_DISABLE;
      __raw_writel(reg, MXC_CCM_CCTL);

      while ((__raw_readl(MXC_CCM_UPCTL) & MXC_CCM_UPCTL_LF) == 0) ;

      return 0;
}

static void _clk_upll_disable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(MXC_CCM_CCTL);
      reg |= MXC_CCM_CCTL_UPLL_DISABLE;
      __raw_writel(reg, MXC_CCM_CCTL);
}

static int _perclk_enable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(MXC_CCM_CGCR0);
      reg |= 1 << clk->id;
      __raw_writel(reg, MXC_CCM_CGCR0);

      return 0;
}

static void _perclk_disable(struct clk *clk)
{
      unsigned long reg;

      reg = __raw_readl(MXC_CCM_CGCR0);
      reg &= ~(1 << clk->id);
      __raw_writel(reg, MXC_CCM_CGCR0);
}

static void _clk_pll_recalc(struct clk *clk)
{
      unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0;
      unsigned long ref_clk;
      unsigned long reg;
      unsigned long long temp;

      ref_clk = clk->parent->rate;

      if (clk == &mpll_clk) {
            reg = __raw_readl(MXC_CCM_MPCTL);
            pdf = (reg & MXC_CCM_MPCTL_PD_MASK) >> MXC_CCM_MPCTL_PD_OFFSET;
            mfd =
                (reg & MXC_CCM_MPCTL_MFD_MASK) >> MXC_CCM_MPCTL_MFD_OFFSET;
            mfi =
                (reg & MXC_CCM_MPCTL_MFI_MASK) >> MXC_CCM_MPCTL_MFI_OFFSET;
            mfn =
                (reg & MXC_CCM_MPCTL_MFN_MASK) >> MXC_CCM_MPCTL_MFN_OFFSET;
      } else if (clk == &upll_clk) {
            reg = __raw_readl(MXC_CCM_UPCTL);
            pdf = (reg & MXC_CCM_UPCTL_PD_MASK) >> MXC_CCM_UPCTL_PD_OFFSET;
            mfd =
                (reg & MXC_CCM_UPCTL_MFD_MASK) >> MXC_CCM_UPCTL_MFD_OFFSET;
            mfi =
                (reg & MXC_CCM_UPCTL_MFI_MASK) >> MXC_CCM_UPCTL_MFI_OFFSET;
            mfn =
                (reg & MXC_CCM_UPCTL_MFN_MASK) >> MXC_CCM_UPCTL_MFN_OFFSET;
      } else {
            BUG();            /* oops */
      }

      mfi = (mfi <= 5) ? 5 : mfi;
      temp = 2LL * ref_clk * mfn;
      do_div(temp, mfd + 1);
      temp = 2LL * ref_clk * mfi + temp;
      do_div(temp, pdf + 1);

      clk->rate = temp;
}

static unsigned long _clk_cpu_round_rate(struct clk *clk, unsigned long rate)
{
      int div = clk->parent->rate / rate;

      if (clk->parent->rate % rate)
            div++;

      if (div > 4)
            div = 4;

      return clk->parent->rate / div;
}

static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
{
      unsigned long div = 0x0, reg = 0x0;
      unsigned long cctl = __raw_readl(MXC_CCM_CCTL);

#if defined CONFIG_CPU_FREQ_IMX
      struct cpu_wp *cpu_wp;
      unsigned long ahb_clk_div = 0;
      unsigned long arm_src = 0;
      int i;

      cpu_wp = get_cpu_wp(&cpu_wp_nr);
      for (i = 0; i < cpu_wp_nr; i++) {
            if (cpu_wp[i].cpu_rate == rate) {
                  div = cpu_wp[i].cpu_podf;
                  ahb_clk_div = cpu_wp[i].cpu_rate / AHB_CLK_DEFAULT - 1;
                  arm_src =
                      (cpu_wp[i].pll_rate == ARM_SRC_DEFAULT) ? 0 : 1;
                  break;
            }
      }
      if (i == cpu_wp_nr)
            return -EINVAL;
      reg = (cctl & ~MXC_CCM_CCTL_ARM_MASK) |
          (div << MXC_CCM_CCTL_ARM_OFFSET);
      reg = (reg & ~MXC_CCM_CCTL_AHB_MASK) |
          (ahb_clk_div << MXC_CCM_CCTL_AHB_OFFSET);
      reg = (reg & ~MXC_CCM_CCTL_ARM_SRC) |
          (arm_src << MXC_CCM_CCTL_ARM_SRC_OFFSET);
      __raw_writel(reg, MXC_CCM_CCTL);
      clk->rate = rate;
#else
      div = clk->parent->rate / rate;

      if (div > 4 || div < 1 || ((clk->parent->rate / div) != rate))
            return -EINVAL;
      div--;

      reg =
          (cctl & ~MXC_CCM_CCTL_ARM_MASK) | (div << MXC_CCM_CCTL_ARM_OFFSET);
      __raw_writel(reg, MXC_CCM_CCTL);
      clk->rate = rate;
#endif

      return 0;
}

static void _clk_cpu_recalc(struct clk *clk)
{
      unsigned long div;
      unsigned long cctl = __raw_readl(MXC_CCM_CCTL);

      div = (cctl & MXC_CCM_CCTL_ARM_MASK) >> MXC_CCM_CCTL_ARM_OFFSET;

      clk->rate = clk->parent->rate / (div + 1);

      if (cctl & MXC_CCM_CCTL_ARM_SRC) {
            clk->rate *= 3;
            clk->rate /= 4;
      }
}

static void _clk_ahb_recalc(struct clk *clk)
{
      unsigned long div;
      unsigned long cctl = __raw_readl(MXC_CCM_CCTL);

      div = (cctl & MXC_CCM_CCTL_AHB_MASK) >> MXC_CCM_CCTL_AHB_OFFSET;

      clk->rate = clk->parent->rate / (div + 1);
}

static void *pcdr_a[4] = {
      MXC_CCM_PCDR0, MXC_CCM_PCDR1, MXC_CCM_PCDR2, MXC_CCM_PCDR3
};
static void _clk_perclkx_recalc(struct clk *clk)
{
      unsigned long perclk_pdf;
      unsigned long pcdr;

      if (clk->id < 0 || clk->id > 15)
            return;

      pcdr = __raw_readl(pcdr_a[clk->id >> 2]);

      perclk_pdf =
          (pcdr >> ((clk->id & 3) << 3)) & MXC_CCM_PCDR1_PERDIV1_MASK;

      clk->rate = clk->parent->rate / (perclk_pdf + 1);
}

static unsigned long _clk_perclkx_round_rate(struct clk *clk,
                                   unsigned long rate)
{
      unsigned long div;

      div = clk->parent->rate / rate;
      if (clk->parent->rate % rate)
            div++;

      if (div > 64)
            div = 64;

      return clk->parent->rate / div;
}

static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate)
{
      unsigned long reg;
      unsigned long div;

      if (clk->id < 0 || clk->id > 15)
            return -EINVAL;

      div = clk->parent->rate / rate;
      if (div > 64 || div < 1 || ((clk->parent->rate / div) != rate))
            return -EINVAL;
      div--;

      reg =
          __raw_readl(pcdr_a[clk->id >> 2]) & ~(MXC_CCM_PCDR1_PERDIV1_MASK <<
                                      ((clk->id & 3) << 3));
      reg |= div << ((clk->id & 3) << 3);
      __raw_writel(reg, pcdr_a[clk->id >> 2]);

      clk->rate = rate;

      return 0;
}

static int _clk_perclkx_set_parent(struct clk *clk, struct clk *parent)
{
      unsigned long mcr;

      if (parent != &upll_clk && parent != &ahb_clk)
            return -EINVAL;

      clk->parent = parent;
      mcr = __raw_readl(MXC_CCM_MCR);
      if (parent == &upll_clk)
            mcr |= (1 << clk->id);
      else
            mcr &= ~(1 << clk->id);

      __raw_writel(mcr, MXC_CCM_MCR);

      return 0;
}

static int _clk_perclkx_set_parent3(struct clk *clk, struct clk *parent)
{
      unsigned long mcr = __raw_readl(MXC_CCM_MCR);
      int bit;

      if (parent != &upll_clk && parent != &ahb_clk &&
          parent != &upll_24610k_clk)
            return -EINVAL;

      switch (clk->id) {
      case 2:
            bit = MXC_CCM_MCR_ESAI_CLK_MUX_OFFSET;
            break;
      case 13:
            bit = MXC_CCM_MCR_SSI1_CLK_MUX_OFFSET;
            break;
      case 14:
            bit = MXC_CCM_MCR_SSI2_CLK_MUX_OFFSET;
            break;
      default:
            return -EINVAL;
      }

      if (parent == &upll_24610k_clk) {
            mcr |= 1 << bit;
            __raw_writel(mcr, MXC_CCM_MCR);
            clk->parent = parent;
      } else {
            mcr &= ~(1 << bit);
            __raw_writel(mcr, MXC_CCM_MCR);
            return _clk_perclkx_set_parent(clk, parent);
      }

      return 0;
}

static void _clk_ipg_recalc(struct clk *clk)
{
      clk->rate = clk->parent->rate / 2;  /* Always AHB / 2 */
}

static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate)
{
      return clk->parent->round_rate(clk->parent, rate);
}

static int _clk_parent_set_rate(struct clk *clk, unsigned long rate)
{
      int ret;

      ret = clk->parent->set_rate(clk->parent, rate);
      if (ret == 0)
            clk->rate = rate;
      return ret;
}

/* Top-level clocks */

static struct clk osc24m_clk = {
      .name = "osc24m",
      .rate = OSC24M_CLK_FREQ,
      .flags = RATE_PROPAGATES,
};

static struct clk osc32k_clk = {
      .name = "osc32k",
      .rate = OSC32K_CLK_FREQ,
      .flags = RATE_PROPAGATES,
};

static struct clk mpll_clk = {
      .name = "mpll",
      .parent = &osc24m_clk,
      .recalc = _clk_pll_recalc,
      .flags = RATE_PROPAGATES,
};

static struct clk upll_clk = {
      .name = "upll",
      .parent = &osc24m_clk,
      .recalc = _clk_pll_recalc,
      .enable = _clk_upll_enable,
      .disable = _clk_upll_disable,
      .flags = RATE_PROPAGATES,
};

static void _clk_24610k_recalc(struct clk *clk)
{
      long long temp = clk->parent->rate * 2461LL;

      do_div(temp, 24000);

      clk->rate = temp; /* Always (UPLL * 24.61 / 240) */
}

static struct clk upll_24610k_clk = {
      .name = "upll_24610k",
      .parent = &upll_clk,
      .recalc = _clk_24610k_recalc,
      .flags = RATE_PROPAGATES,
};

/* Mid-level clocks */

static struct clk cpu_clk = { /* ARM clock */
      .name = "cpu_clk",
      .parent = &mpll_clk,
      .set_rate = _clk_cpu_set_rate,
      .recalc = _clk_cpu_recalc,
      .round_rate = _clk_cpu_round_rate,
      .flags = RATE_PROPAGATES,
};

static struct clk ahb_clk = { /* a.k.a. HCLK */
      .name = "ahb_clk",
      .parent = &cpu_clk,
      .recalc = _clk_ahb_recalc,
      .flags = RATE_PROPAGATES,
};

static struct clk ipg_clk = {
      .name = "ipg_clk",
      .parent = &ahb_clk,
      .recalc = _clk_ipg_recalc,
      .flags = RATE_PROPAGATES,
};

/* Bottom-level clocks */

struct clk usb_ahb_clk = {
      .name = "usb_ahb_clk",
      .id = 0,
      .parent = &ahb_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR0,
      .enable_shift = MXC_CCM_CGCR0_HCLK_USBOTG_OFFSET,
      .disable = _clk_disable,
};

struct clk rtic_clk = {
      .name = "rtic_clk",
      .id = 0,
      .parent = &ahb_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR0,
      .enable_shift = MXC_CCM_CGCR0_HCLK_RTIC_OFFSET,
      .disable = _clk_disable,
};

struct clk emi_clk = {
      .name = "emi_clk",
      .id = 0,
      .parent = &ahb_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR0,
      .enable_shift = MXC_CCM_CGCR0_HCLK_EMI_OFFSET,
      .disable = _clk_disable,
};

struct clk brom_clk = {
      .name = "brom_clk",
      .id = 0,
      .parent = &ahb_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR0,
      .enable_shift = MXC_CCM_CGCR0_HCLK_BROM_OFFSET,
      .disable = _clk_disable,
};

static struct clk per_clk[] = {
      {
       .name = "per_csi_clk",
       .id = 0,
       .parent = &upll_clk,   /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_epit_clk",
       .id = 1,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_esai_clk",
       .id = 2,
       .parent = &ahb_clk,    /* can be AHB or UPLL or 24.61MHz */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent3,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_esdhc1_clk",
       .id = 3,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_esdhc2_clk",
       .id = 4,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_gpt_clk",
       .id = 5,
       .parent = &ahb_clk,    /* Must be AHB */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_i2c_clk",
       .id = 6,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_lcdc_clk",
       .id = 7,
       .parent = &upll_clk,   /* Must be UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_nfc_clk",
       .id = 8,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_owire_clk",
       .id = 9,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_pwm_clk",
       .id = 10,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_sim1_clk",
       .id = 11,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_sim2_clk",
       .id = 12,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_ssi1_clk",
       .id = 13,
       .parent = &ahb_clk,    /* can be AHB or UPLL or 24.61MHz */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent3,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_ssi2_clk",
       .id = 14,
       .parent = &ahb_clk,    /* can be AHB or UPLL or 24.61MHz */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent3,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
      {
       .name = "per_uart_clk",
       .id = 15,
       .parent = &ahb_clk,    /* can be AHB or UPLL */
       .round_rate = _clk_perclkx_round_rate,
       .set_rate = _clk_perclkx_set_rate,
       .set_parent = _clk_perclkx_set_parent,
       .recalc = _clk_perclkx_recalc,
       .enable = _perclk_enable,
       .disable = _perclk_disable,
       .flags = RATE_PROPAGATES,},
};

struct clk nfc_clk = {
      .name = "nfc_clk",
      .id = 0,
      .parent = &per_clk[8],
};

struct clk audmux_clk = {
      .name = "audmux_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_AUDMUX_OFFSET,
      .disable = _clk_disable,
};

struct clk ata_clk[] = {
      {
       .name = "ata_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_ATA_OFFSET,
       .disable = _clk_disable,
       .secondary = &ata_clk[1],},
      {
       .name = "ata_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_ATA_OFFSET,
       .disable = _clk_disable,},
};

struct clk can_clk[] = {
      {
       .name = "can_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CAN1_OFFSET,
       .disable = _clk_disable,},
      {
       .name = "can_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CAN2_OFFSET,
       .disable = _clk_disable,},
};

struct clk csi_clk[] = {
      {
       .name = "csi_clk",
       .id = 0,
       .parent = &per_clk[0],
       .secondary = &csi_clk[1],},
      {
       .name = "csi_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CSI_OFFSET,
       .disable = _clk_disable,
       .secondary = &csi_clk[2],},
      {
       .name = "csi_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_CSI_OFFSET,
       .disable = _clk_disable,},
};

struct clk cspi_clk[] = {
      {
       .name = "cspi_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CSPI1_OFFSET,
       .disable = _clk_disable,},
      {
       .name = "cspi_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CSPI2_OFFSET,
       .disable = _clk_disable,},
      {
       .name = "cspi_clk",
       .id = 2,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_CSPI3_OFFSET,
       .disable = _clk_disable,},
};

struct clk dryice_clk = {
      .name = "dryice_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_DRYICE_OFFSET,
      .disable = _clk_disable,
};

struct clk ect_clk = {
      .name = "ect_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_ECT_OFFSET,
      .disable = _clk_disable,
};

struct clk epit1_clk[] = {
      {
       .name = "epit_clk",
       .id = 0,
       .parent = &per_clk[1],
       .secondary = &epit1_clk[1],},
      {
       .name = "epit_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_EPIT1_OFFSET,
       .disable = _clk_disable,},
};

struct clk epit2_clk[] = {
      {
       .name = "epit_clk",
       .id = 1,
       .parent = &per_clk[1],
       .secondary = &epit2_clk[1],},
      {
       .name = "epit_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_EPIT2_OFFSET,
       .disable = _clk_disable,},
};

struct clk esai_clk[] = {
      {
       .name = "esai_clk",
       .id = 0,
       .parent = &per_clk[2],
       .secondary = &esai_clk[1],},
      {
       .name = "esai_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_ESAI_OFFSET,
       .disable = _clk_disable,
       .secondary = &esai_clk[2],},
      {
       .name = "esai_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_ESAI_OFFSET,
       .disable = _clk_disable,},
};

struct clk esdhc1_clk[] = {
      {
       .name = "esdhc_clk",
       .id = 0,
       .parent = &per_clk[3],
       .secondary = &esdhc1_clk[1],},
      {
       .name = "esdhc_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_ESDHC1_OFFSET,
       .disable = _clk_disable,
       .secondary = &esdhc1_clk[2],},
      {
       .name = "esdhc_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC1_OFFSET,
       .disable = _clk_disable,},
};

struct clk esdhc2_clk[] = {
      {
       .name = "esdhc_clk",
       .id = 1,
       .parent = &per_clk[4],
       .secondary = &esdhc2_clk[1],},
      {
       .name = "esdhc_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_ESDHC2_OFFSET,
       .disable = _clk_disable,
       .secondary = &esdhc2_clk[2],},
      {
       .name = "esdhc_ahb_clk",
       .id = 1,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC2_OFFSET,
       .disable = _clk_disable,},
};

struct clk fec_clk[] = {
      {
       .name = "fec_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_FEC_OFFSET,
       .disable = _clk_disable,
       .secondary = &fec_clk[1],},
      {
       .name = "fec_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_FEC_OFFSET,
       .disable = _clk_disable,},
};

struct clk gpio_clk[] = {
      {
       .name = "gpio_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPIO1_OFFSET,
       .disable = _clk_disable,},
      {
       .name = "gpio_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPIO2_OFFSET,
       .disable = _clk_disable,},
      {
       .name = "gpio_clk",
       .id = 2,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPIO3_OFFSET,
       .disable = _clk_disable,},
};

static struct clk gpt1_clk[] = {
      {
       .name = "gpt_clk",
       .id = 0,
       .parent = &per_clk[5],
       .secondary = &gpt1_clk[1],},
      {
       .name = "gpt_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPT1_OFFSET,
       .disable = _clk_disable,},
};

static struct clk gpt2_clk[] = {
      {
       .name = "gpt_clk",
       .id = 1,
       .parent = &per_clk[5],
       .secondary = &gpt1_clk[1],},
      {
       .name = "gpt_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPT2_OFFSET,
       .disable = _clk_disable,},
};

static struct clk gpt3_clk[] = {
      {
       .name = "gpt_clk",
       .id = 2,
       .parent = &per_clk[5],
       .secondary = &gpt1_clk[1],},
      {
       .name = "gpt_ipg_clk",
       .id = 2,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPT3_OFFSET,
       .disable = _clk_disable,},
};

static struct clk gpt4_clk[] = {
      {
       .name = "gpt_clk",
       .id = 3,
       .parent = &per_clk[5],
       .secondary = &gpt1_clk[1],},
      {
       .name = "gpt_ipg_clk",
       .id = 3,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_GPT4_OFFSET,
       .disable = _clk_disable,},
};

struct clk i2c_clk[] = {
      {
       .name = "i2c_clk",
       .id = 0,
       .parent = &per_clk[6],},
      {
       .name = "i2c_clk",
       .id = 1,
       .parent = &per_clk[6],},
      {
       .name = "i2c_clk",
       .id = 2,
       .parent = &per_clk[6],},
};

struct clk iim_clk = {
      .name = "iim_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_IIM_OFFSET,
      .disable = _clk_disable,
};

struct clk iomuxc_clk = {
      .name = "iomuxc_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_IOMUXC_OFFSET,
      .disable = _clk_disable,
};

struct clk kpp_clk = {
      .name = "kpp_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR1,
      .enable_shift = MXC_CCM_CGCR1_KPP_OFFSET,
      .disable = _clk_disable,
};

struct clk lcdc_clk[] = {
      {
       .name = "lcdc_clk",
       .id = 0,
       .parent = &per_clk[7],
       .secondary = &lcdc_clk[1],
       .round_rate = _clk_parent_round_rate,
       .set_rate = _clk_parent_set_rate,},
      {
       .name = "lcdc_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_LCDC_OFFSET,
       .disable = _clk_disable,
       .secondary = &lcdc_clk[2],},
      {
       .name = "lcdc_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_LCDC_OFFSET,
       .disable = _clk_disable,},
};

struct clk owire_clk[] = {
      {
       .name = "owire_clk",
       .id = 0,
       .parent = &per_clk[9],
       .secondary = &owire_clk[1],},
      {
       .name = "owire_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_OWIRE_OFFSET,
       .disable = _clk_disable,},
};

struct clk pwm1_clk[] = {
      {
       .name = "pwm_clk",
       .id = 0,
       .parent = &per_clk[10],
       .secondary = &pwm1_clk[1],},
      {
       .name = "pwm_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR1,
       .enable_shift = MXC_CCM_CGCR1_PWM1_OFFSET,
       .disable = _clk_disable,},
};

struct clk pwm2_clk[] = {
      {
       .name = "pwm_clk",
       .id = 1,
       .parent = &per_clk[10],
       .secondary = &pwm2_clk[1],},
      {
       .name = "pwm_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_PWM2_OFFSET,
       .disable = _clk_disable,},
};

struct clk pwm3_clk[] = {
      {
       .name = "pwm_clk",
       .id = 2,
       .parent = &per_clk[10],
       .secondary = &pwm3_clk[1],},
      {
       .name = "pwm_ipg_clk",
       .id = 2,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET,
       .disable = _clk_disable,},
};

struct clk pwm4_clk[] = {
      {
       .name = "pwm_clk",
       .id = 3,
       .parent = &per_clk[10],
       .secondary = &pwm4_clk[1],},
      {
       .name = "pwm_ipg_clk",
       .id = 3,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET,
       .disable = _clk_disable,},
};

struct clk rng_clk = {
      .name = "rng_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR2,
      .enable_shift = MXC_CCM_CGCR2_RNGB_OFFSET,
      .disable = _clk_disable,
};

struct clk scc_clk = {
      .name = "scc_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR2,
      .enable_shift = MXC_CCM_CGCR2_SCC_OFFSET,
      .disable = _clk_disable,
};

struct clk sdma_clk[] = {
      {
       .name = "sdma_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SDMA_OFFSET,
       .disable = _clk_disable,
       .secondary = &sdma_clk[1],},
      {
       .name = "sdma_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_SDMA_OFFSET,
       .disable = _clk_disable,},
};

struct clk sim1_clk[] = {
      {
       .name = "sim1_clk",
       .id = 0,
       .parent = &per_clk[11],
       .secondary = &sim1_clk[1],},
      {
       .name = "sim_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SIM1_OFFSET,
       .disable = _clk_disable,},
};

struct clk sim2_clk[] = {
      {
       .name = "sim2_clk",
       .id = 1,
       .parent = &per_clk[12],
       .secondary = &sim2_clk[1],},
      {
       .name = "sim_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SIM2_OFFSET,
       .disable = _clk_disable,},
};

struct clk slcdc_clk[] = {
      {
       .name = "slcdc_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SLCDC_OFFSET,
       .disable = _clk_disable,
       .secondary = &slcdc_clk[1],},
      {
       .name = "slcdc_ahb_clk",
       .id = 0,
       .parent = &ahb_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR0,
       .enable_shift = MXC_CCM_CGCR0_HCLK_SLCDC_OFFSET,
       .disable = _clk_disable,},
};

struct clk spba_clk = {
      .name = "spba_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR2,
      .enable_shift = MXC_CCM_CGCR2_SPBA_OFFSET,
      .disable = _clk_disable,
};

struct clk ssi1_clk[] = {
      {
       .name = "ssi_clk",
       .id = 0,
       .parent = &per_clk[13],
       .secondary = &ssi1_clk[1],},
      {
       .name = "ssi_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SSI1_OFFSET,
       .disable = _clk_disable,},
};

struct clk ssi2_clk[] = {
      {
       .name = "ssi_clk",
       .id = 1,
       .parent = &per_clk[14],
       .secondary = &ssi2_clk[1],},
      {
       .name = "ssi_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_SSI2_OFFSET,
       .disable = _clk_disable,},
};

struct clk tchscrn_clk = {
      .name = "tchscrn_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR2,
      .enable_shift = MXC_CCM_CGCR2_TCHSCRN_OFFSET,
      .disable = _clk_disable,
};

struct clk uart1_clk[] = {
      {
       .name = "uart_clk",
       .id = 0,
       .parent = &per_clk[15],
       .secondary = &uart1_clk[1],},
      {
       .name = "uart_ipg_clk",
       .id = 0,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_UART1_OFFSET,
       .disable = _clk_disable,},
};

struct clk uart2_clk[] = {
      {
       .name = "uart_clk",
       .id = 1,
       .parent = &per_clk[15],
       .secondary = &uart2_clk[1],},
      {
       .name = "uart_ipg_clk",
       .id = 1,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_UART2_OFFSET,
       .disable = _clk_disable,},
};

struct clk uart3_clk[] = {
      {
       .name = "uart_clk",
       .id = 2,
       .parent = &per_clk[15],
       .secondary = &uart3_clk[1],},
      {
       .name = "uart_ipg_clk",
       .id = 2,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_UART3_OFFSET,
       .disable = _clk_disable,},
};

struct clk uart4_clk[] = {
      {
       .name = "uart_clk",
       .id = 3,
       .parent = &per_clk[15],
       .secondary = &uart4_clk[1],},
      {
       .name = "uart_ipg_clk",
       .id = 3,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_UART4_OFFSET,
       .disable = _clk_disable,},
};

struct clk uart5_clk[] = {
      {
       .name = "uart_clk",
       .id = 4,
       .parent = &per_clk[15],
       .secondary = &uart5_clk[1],},
      {
       .name = "uart_ipg_clk",
       .id = 4,
       .parent = &ipg_clk,
       .enable = _clk_enable,
       .enable_reg = MXC_CCM_CGCR2,
       .enable_shift = MXC_CCM_CGCR2_UART5_OFFSET,
       .disable = _clk_disable,},
};

struct clk wdog_clk = {
      .name = "wdog_clk",
      .id = 0,
      .parent = &ipg_clk,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_CGCR2,
      .enable_shift = MXC_CCM_CGCR2_WDOG_OFFSET,
      .disable = _clk_disable,
};

static unsigned long _clk_usb_round_rate(struct clk *clk, unsigned long rate)
{
      unsigned long div;

      div = clk->parent->rate / rate;
      if (clk->parent->rate % rate)
            div++;

      if (div > 64)
            return -EINVAL;

      return clk->parent->rate / div;
}

static int _clk_usb_set_rate(struct clk *clk, unsigned long rate)
{
      unsigned long reg;
      unsigned long div;

      div = clk->parent->rate / rate;

      if (clk->parent->rate / div != rate)
            return -EINVAL;
      if (div > 64)
            return -EINVAL;

      reg = __raw_readl(MXC_CCM_CCTL) & ~MXC_CCM_CCTL_USB_DIV_MASK;
      reg |= (div - 1) << MXC_CCM_CCTL_USB_DIV_OFFSET;
      __raw_writel(reg, MXC_CCM_CCTL);

      return 0;
}

static void _clk_usb_recalc(struct clk *clk)
{
      unsigned long div =
          __raw_readl(MXC_CCM_CCTL) & MXC_CCM_CCTL_USB_DIV_MASK;

      div >>= MXC_CCM_CCTL_USB_DIV_OFFSET;

      clk->rate = clk->parent->rate / (div + 1);
}

static int _clk_usb_set_parent(struct clk *clk, struct clk *parent)
{
      unsigned long mcr;

      if (parent != &upll_clk && parent != &ahb_clk)
            return -EINVAL;

      clk->parent = parent;
      mcr = __raw_readl(MXC_CCM_MCR);
      if (parent == &ahb_clk)
            mcr |= (1 << MXC_CCM_MCR_USB_CLK_MUX_OFFSET);
      else
            mcr &= ~(1 << MXC_CCM_MCR_USB_CLK_MUX_OFFSET);

      __raw_writel(mcr, MXC_CCM_MCR);

      return 0;
}

static struct clk usb_clk = {
      .name = "usb_clk",
      .parent = &upll_clk,
      .recalc = _clk_usb_recalc,
      .set_rate = _clk_usb_set_rate,
      .round_rate = _clk_usb_round_rate,
      .set_parent = _clk_usb_set_parent,
};

/* CLKO */

static unsigned long _clk_clko_round_rate(struct clk *clk, unsigned long rate)
{
      unsigned long div;

      div = clk->parent->rate / rate;
      if (clk->parent->rate % rate)
            div++;

      if (div > 64)
            return -EINVAL;

      return clk->parent->rate / div;
}

static int _clk_clko_set_rate(struct clk *clk, unsigned long rate)
{
      unsigned long reg;
      unsigned long div;

      div = clk->parent->rate / rate;

      if ((clk->parent->rate / div) != rate)
            return -EINVAL;
      if (div > 64)
            return -EINVAL;

      reg = __raw_readl(MXC_CCM_MCR) & ~MXC_CCM_MCR_CLKO_DIV_MASK;
      reg |= (div - 1) << MXC_CCM_MCR_CLKO_DIV_OFFSET;
      __raw_writel(reg, MXC_CCM_MCR);

      return 0;
}

static void _clk_clko_recalc(struct clk *clk)
{
      unsigned long div =
          __raw_readl(MXC_CCM_MCR) & MXC_CCM_MCR_CLKO_DIV_MASK;

      div >>= MXC_CCM_MCR_CLKO_DIV_OFFSET;

      clk->rate = clk->parent->rate / (div + 1);
}

static struct clk *clko_sources[] = {
      &osc32k_clk,            /* 0x0 */
      &osc24m_clk,            /* 0x1 */
      &cpu_clk,         /* 0x2 */
      &ahb_clk,         /* 0x3 */
      &ipg_clk,         /* 0x4 */
      NULL,             /* 0x5 */
      NULL,             /* 0x6 */
      NULL,             /* 0x7 */
      NULL,             /* 0x8 */
      NULL,             /* 0x9 */
      &per_clk[0],            /* 0xA */
      &per_clk[2],            /* 0xB */
      &per_clk[13],           /* 0xC */
      &per_clk[14],           /* 0xD */
      &usb_clk,         /* 0xE */
      NULL,             /* 0xF */
};

#define NR_CLKO_SOURCES (sizeof(clko_sources) / sizeof(struct clk *))

static int _clk_clko_set_parent(struct clk *clk, struct clk *parent)
{
      unsigned long reg =
          __raw_readl(MXC_CCM_MCR) & ~MXC_CCM_MCR_CLKO_SEL_MASK;
      struct clk **src;
      int i;

      for (i = 0, src = clko_sources; i < NR_CLKO_SOURCES; i++, src++)
            if (*src == parent)
                  break;

      if (i == NR_CLKO_SOURCES)
            return -EINVAL;

      clk->parent = parent;

      reg |= i << MXC_CCM_MCR_CLKO_SEL_OFFSET;

      __raw_writel(reg, MXC_CCM_MCR);

      return 0;
}

static struct clk clko_clk = {
      .name = "clko_clk",
      .recalc = _clk_clko_recalc,
      .set_rate = _clk_clko_set_rate,
      .round_rate = _clk_clko_round_rate,
      .set_parent = _clk_clko_set_parent,
      .enable = _clk_enable,
      .enable_reg = MXC_CCM_MCR,
      .enable_shift = MXC_CCM_MCR_CLKO_EN_OFFSET,
      .disable = _clk_disable,
};

static struct clk *mxc_clks[] = {
      &osc24m_clk,
      &osc32k_clk,
      &mpll_clk,
      &upll_clk,
      &cpu_clk,
      &ahb_clk,
      &ipg_clk,
      &usb_ahb_clk,
      &per_clk[0],
      &per_clk[1],
      &per_clk[2],
      &per_clk[3],
      &per_clk[4],
      &per_clk[5],
      &per_clk[6],
      &per_clk[7],
      &per_clk[8],
      &per_clk[9],
      &per_clk[10],
      &per_clk[11],
      &per_clk[12],
      &per_clk[13],
      &per_clk[14],
      &per_clk[15],
      &nfc_clk,
      &audmux_clk,
      &ata_clk[0],
      &ata_clk[1],
      &can_clk[0],
      &can_clk[1],
      &csi_clk[0],
      &csi_clk[1],
      &csi_clk[2],
      &cspi_clk[0],
      &cspi_clk[1],
      &cspi_clk[2],
      &dryice_clk,
      &ect_clk,
      &epit1_clk[0],
      &epit1_clk[1],
      &epit2_clk[0],
      &epit2_clk[1],
      &esai_clk[0],
      &esai_clk[1],
      &esai_clk[2],
      &esdhc1_clk[0],
      &esdhc1_clk[1],
      &esdhc1_clk[2],
      &esdhc2_clk[0],
      &esdhc2_clk[1],
      &esdhc2_clk[2],
      &fec_clk[0],
      &fec_clk[1],
      &gpio_clk[0],
      &gpio_clk[1],
      &gpio_clk[2],
      &gpt1_clk[0],
      &gpt1_clk[1],
      &gpt2_clk[0],
      &gpt2_clk[1],
      &gpt3_clk[0],
      &gpt3_clk[1],
      &gpt4_clk[0],
      &gpt4_clk[1],
      &i2c_clk[0],
      &i2c_clk[1],
      &i2c_clk[2],
      &iim_clk,
      &iomuxc_clk,
      &kpp_clk,
      &lcdc_clk[0],
      &lcdc_clk[1],
      &lcdc_clk[2],
      &owire_clk[0],
      &owire_clk[1],
      &pwm1_clk[0],
      &pwm1_clk[1],
      &pwm2_clk[0],
      &pwm2_clk[1],
      &pwm3_clk[0],
      &pwm3_clk[1],
      &pwm4_clk[0],
      &pwm4_clk[1],
      &rng_clk,
      &scc_clk,
      &sdma_clk[0],
      &sdma_clk[1],
      &sim1_clk[0],
      &sim1_clk[1],
      &sim2_clk[0],
      &sim2_clk[1],
      &slcdc_clk[0],
      &slcdc_clk[1],
      &spba_clk,
      &ssi1_clk[0],
      &ssi1_clk[1],
      &ssi2_clk[0],
      &ssi2_clk[1],
      &tchscrn_clk,
      &uart1_clk[0],
      &uart1_clk[1],
      &uart2_clk[0],
      &uart2_clk[1],
      &uart3_clk[0],
      &uart3_clk[1],
      &uart4_clk[0],
      &uart4_clk[1],
      &uart5_clk[0],
      &uart5_clk[1],
      &wdog_clk,
      &usb_clk,
      &clko_clk,
};

/*!
 * Function to get timer clock rate early in boot process before clock tree is
 * initialized.
 *
 * @return  Clock rate for timer
 */
unsigned long __init clk_early_get_timer_rate(void)
{
      upll_clk.recalc(&upll_clk);
      per_clk[5].recalc(&per_clk[5]);
      per_clk[5].enable(&per_clk[5]);

      return per_clk[5].rate;
}

extern void propagate_rate(struct clk *tclk);

int __init mx25_clocks_init(unsigned long fref)
{
      int i;
      struct clk **clkp;

      for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
            clk_register(*clkp);

      /* Turn off all possible clocks */
      __raw_writel((1 << MXC_CCM_CGCR0_HCLK_EMI_OFFSET), MXC_CCM_CGCR0);

      __raw_writel((1 << MXC_CCM_CGCR1_GPT1_OFFSET) |
                 (1 << MXC_CCM_CGCR1_IIM_OFFSET), MXC_CCM_CGCR1);
      __raw_writel(1 << MXC_CCM_CGCR2_SCC_OFFSET, MXC_CCM_CGCR2);

      /* Init all perclk sources to ahb clock*/
      for (i = 0; i < (sizeof(per_clk) / sizeof(struct clk)); i++)
            per_clk[i].set_parent(&per_clk[i], &ahb_clk);

      /* This will propagate to all children and init all the clock rates */
      propagate_rate(&osc24m_clk);
      propagate_rate(&osc32k_clk);

      /* GPT clock must be derived from AHB clock */
      clk_set_rate(&per_clk[5], ahb_clk.rate / 10);

      /* LCDC clock must be derived from UPLL clock */
      clk_set_parent(&per_clk[7], &upll_clk);
      clk_set_rate(&per_clk[7], upll_clk.rate);

      /* the NFC clock must be derived from AHB clock */
      clk_set_parent(&per_clk[8], &ahb_clk);
      clk_set_rate(&per_clk[8], ahb_clk.rate / 6);

      /* sim clock */
      clk_set_rate(&per_clk[11], ahb_clk.rate / 2);

      /* the csi clock must be derived from UPLL clock */
      clk_set_parent(&per_clk[0], &upll_clk);
      clk_set_rate(&per_clk[0], upll_clk.rate / 5);

      pr_info("Clock input source is %ld\n", osc24m_clk.rate);

      clk_enable(&emi_clk);
      clk_enable(&gpio_clk[0]);
      clk_enable(&gpio_clk[1]);
      clk_enable(&gpio_clk[2]);
      clk_enable(&iim_clk);
      clk_enable(&gpt1_clk[0]);
      clk_enable(&iomuxc_clk);
      clk_enable(&scc_clk);

      mxc_timer_init(&gpt1_clk[0], IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT1);
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index