diff options
author | Roshni Shah <shah.roshni@yahoo.com> | 2011-03-14 06:49:42 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-03-02 16:59:46 -0500 |
commit | 2731b2eadeaa141e6f305fa8086106608112bbaa (patch) | |
tree | 2702d45bac84073cd580ccb1bd3eafb9a000d3b6 /arch/arm/mach-mx25/clock.c | |
parent | 6d23f5084c975be637f7d748db82116bf84d3872 (diff) |
Add support for the i.MX53 QSB2.6.35.3-mx53-early-201103141049
This patch seems to have originated from the 11.01.00 release
from Freescale, which is no longer available except through the
gitweb interface from Freescale.
http://opensource.freescale.com/git?p=imx/linux-2.6-imx.git;a=commit;h=27fdf7bae11978d21e8aba09bb635f49b07edd4a
Diffstat (limited to 'arch/arm/mach-mx25/clock.c')
-rw-r--r-- | arch/arm/mach-mx25/clock.c | 1674 |
1 files changed, 1672 insertions, 2 deletions
diff --git a/arch/arm/mach-mx25/clock.c b/arch/arm/mach-mx25/clock.c index 155014993b13..d9723cc7cf47 100644 --- a/arch/arm/mach-mx25/clock.c +++ b/arch/arm/mach-mx25/clock.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 by Sascha Hauer, Pengutronix + * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,6 +17,8 @@ * MA 02110-1301, USA. */ +#include <linux/module.h> +#include <linux/spinlock.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/list.h> @@ -28,6 +31,7 @@ #include <mach/hardware.h> #include <mach/common.h> #include <mach/mx25.h> +#include "crm_regs.h" #define CRM_BASE MX25_IO_ADDRESS(MX25_CRM_BASE_ADDR) @@ -52,6 +56,1515 @@ #define CCM_LTR2 0x48 #define CCM_LTR3 0x4c +#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_cgcr_enable(struct clk *clk); +static void clk_cgcr_disable(struct clk *clk); +static unsigned long get_rate_ipg(struct clk *clk); + +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 unsigned long _clk_osc24m_get_rate(struct clk *clk) +{ + return OSC24M_CLK_FREQ; +} + +static unsigned long _clk_osc32k_get_rate(struct clk *clk) +{ + return OSC32K_CLK_FREQ; +} + +static unsigned long _clk_pll_get_rate(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_get_rate(clk->parent); + + 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); + + return temp; +} + +static unsigned long _clk_cpu_round_rate(struct clk *clk, unsigned long rate) +{ + int div = clk_get_rate(clk->parent) / rate; + + if (clk_get_rate(clk->parent) % rate) + div++; + + if (div > 4) + div = 4; + + return clk_get_rate(clk->parent) / 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); +#else + div = clk_get_rate(clk->parent) / rate; + + if (div > 4 || div < 1 || ((clk_get_rate(clk->parent) / div) != rate)) + return -EINVAL; + div--; + + reg = + (cctl & ~MXC_CCM_CCTL_ARM_MASK) | (div << MXC_CCM_CCTL_ARM_OFFSET); + __raw_writel(reg, MXC_CCM_CCTL); +#endif + + return 0; +} + +static unsigned long _clk_cpu_get_rate(struct clk *clk) +{ + unsigned long div; + unsigned long cctl = __raw_readl(MXC_CCM_CCTL); + unsigned long rate; + + div = (cctl & MXC_CCM_CCTL_ARM_MASK) >> MXC_CCM_CCTL_ARM_OFFSET; + + rate = clk_get_rate(clk->parent) / (div + 1); + + if (cctl & MXC_CCM_CCTL_ARM_SRC) { + rate *= 3; + rate /= 4; + } + + return rate; +} + +static unsigned long _clk_ahb_get_rate(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; + + return clk_get_rate(clk->parent) / (div + 1); +} + +static void *pcdr_a[4] = { + MXC_CCM_PCDR0, MXC_CCM_PCDR1, MXC_CCM_PCDR2, MXC_CCM_PCDR3 +}; +static unsigned long _clk_perclkx_get_rate(struct clk *clk) +{ + unsigned long perclk_pdf; + unsigned long pcdr; + + pcdr = __raw_readl(pcdr_a[clk->id >> 2]); + + perclk_pdf = + (pcdr >> ((clk->id & 3) << 3)) & MXC_CCM_PCDR1_PERDIV1_MASK; + + return clk_get_rate(clk->parent) / (perclk_pdf + 1); +} + +static unsigned long _clk_perclkx_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + if (parent_rate % rate) + div++; + + if (div > 64) + div = 64; + + return parent_rate / div; +} + +static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long reg; + unsigned long div; + unsigned long parent_rate; + + if (clk->id < 0 || clk->id > 15) + return -EINVAL; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + if (div > 64 || div < 1 || ((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]); + + 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 unsigned long _clk_ipg_get_rate(struct clk *clk) +{ + return clk_get_rate(clk->parent) / 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) +{ + return clk->parent->set_rate(clk->parent, rate); +} + +/* Top-level clocks */ + +static struct clk osc24m_clk = { + .get_rate = _clk_osc24m_get_rate, + .flags = RATE_PROPAGATES, +}; + +static struct clk osc32k_clk = { + .get_rate = _clk_osc32k_get_rate, + .flags = RATE_PROPAGATES, +}; + +static struct clk mpll_clk = { + .parent = &osc24m_clk, + .get_rate = _clk_pll_get_rate, + .flags = RATE_PROPAGATES, +}; + +static struct clk upll_clk = { + .parent = &osc24m_clk, + .get_rate = _clk_pll_get_rate, + .enable = _clk_upll_enable, + .disable = _clk_upll_disable, + .flags = RATE_PROPAGATES, +}; + +static unsigned long _clk_24610k_get_rate(struct clk *clk) +{ + long long temp = clk_get_rate(clk->parent) * 2461LL; + + do_div(temp, 24000); + + return temp; /* Always (UPLL * 24.61 / 240) */ +} + +static struct clk upll_24610k_clk = { + .parent = &upll_clk, + .get_rate = _clk_24610k_get_rate, + .flags = RATE_PROPAGATES, +}; + +/* Mid-level clocks */ + +static struct clk cpu_clk = { /* ARM clock */ + .parent = &mpll_clk, + .set_rate = _clk_cpu_set_rate, + .get_rate = _clk_cpu_get_rate, + .round_rate = _clk_cpu_round_rate, + .flags = RATE_PROPAGATES, +}; + +static struct clk ahb_clk = { /* a.k.a. HCLK */ + .parent = &cpu_clk, + .get_rate = _clk_ahb_get_rate, + .flags = RATE_PROPAGATES, +}; + +static struct clk ipg_clk = { + .parent = &ahb_clk, + .get_rate = _clk_ipg_get_rate, + .flags = RATE_PROPAGATES, +}; + + +/* Bottom-level clocks */ + +struct clk usb_ahb_clk = { + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_USBOTG_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk rtic_clk = { + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_RTIC_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk emi_clk = { + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_EMI_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk brom_clk = { + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_BROM_OFFSET, + .disable = clk_cgcr_disable, +}; + +static struct clk per_clk[] = { + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES, + }, + /* 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, + .get_rate = _clk_perclkx_get_rate, + .enable = _perclk_enable, + .disable = _perclk_disable, + .flags = RATE_PROPAGATES,}, +}; + +struct clk nfc_clk = { + .id = 0, + .parent = &per_clk[8], +}; + +struct clk audmux_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_AUDMUX_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk ata_clk[] = { + { + /* ata_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_ATA_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &ata_clk[1],}, + { + /* ata_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_ATA_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk can_clk[] = { + { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CAN1_OFFSET, + .disable = clk_cgcr_disable,}, + { + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CAN2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk csi_clk[] = { + { + /* csi_clk */ + .id = 0, + .parent = &per_clk[0], + .secondary = &csi_clk[1],}, + { + /* csi_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CSI_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &csi_clk[2],}, + { + /* csi_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_CSI_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk cspi_clk[] = { + { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CSPI1_OFFSET, + .get_rate = get_rate_ipg, + .disable = clk_cgcr_disable,}, + { + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CSPI2_OFFSET, + .get_rate = get_rate_ipg, + .disable = clk_cgcr_disable,}, + { + .id = 2, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_CSPI3_OFFSET, + .get_rate = get_rate_ipg, + .disable = clk_cgcr_disable,}, +}; + +struct clk dryice_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_DRYICE_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk ect_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_ECT_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk epit1_clk[] = { + { + /* epit_clk */ + .id = 0, + .parent = &per_clk[1], + .secondary = &epit1_clk[1],}, + { + /* epit_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_EPIT1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk epit2_clk[] = { + { + /* epit_clk */ + .id = 1, + .parent = &per_clk[1], + .secondary = &epit2_clk[1],}, + { + /* epit_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_EPIT2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk esai_clk[] = { + { + /* esai_clk */ + .id = 0, + .parent = &per_clk[2], + .secondary = &esai_clk[1],}, + { + /* esai_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_ESAI_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &esai_clk[2],}, + { + /* esai_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_ESAI_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk esdhc1_clk[] = { + { + /* esdhc_clk */ + .id = 0, + .parent = &per_clk[3], + .secondary = &esdhc1_clk[1],}, + { + /* esdhc_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_ESDHC1_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &esdhc1_clk[2],}, + { + /* esdhc_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk esdhc2_clk[] = { + { + /* esdhc_clk */ + .id = 1, + .parent = &per_clk[4], + .secondary = &esdhc2_clk[1],}, + { + /* esdhc_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_ESDHC2_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &esdhc2_clk[2],}, + { + /* esdhc_ahb_clk */ + .id = 1, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk fec_clk[] = { + { + /* fec_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_FEC_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &fec_clk[1],}, + { + /* fec_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_FEC_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk gpio_clk[] = { + { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPIO1_OFFSET, + .disable = clk_cgcr_disable,}, + { + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPIO2_OFFSET, + .disable = clk_cgcr_disable,}, + { + .id = 2, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPIO3_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +static struct clk gpt1_clk[] = { + { + /* gpt_clk */ + .id = 0, + .parent = &per_clk[5], + .secondary = &gpt1_clk[1],}, + { + /* gpt_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPT1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +static struct clk gpt2_clk[] = { + { + /* gpt_clk */ + .id = 1, + .parent = &per_clk[5], + .secondary = &gpt1_clk[1],}, + { + /* gpt_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPT2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +static struct clk gpt3_clk[] = { + { + /* gpt_clk */ + .id = 2, + .parent = &per_clk[5], + .secondary = &gpt1_clk[1],}, + { + /* gpt_ipg_clk */ + .id = 2, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPT3_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +static struct clk gpt4_clk[] = { + { + /* gpt_clk */ + .id = 3, + .parent = &per_clk[5], + .secondary = &gpt1_clk[1],}, + { + /* gpt_ipg_clk */ + .id = 3, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_GPT4_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk i2c_clk[] = { + { + .id = 0, + .parent = &per_clk[6],}, + { + .id = 1, + .parent = &per_clk[6],}, + { + .id = 2, + .parent = &per_clk[6],}, +}; + +struct clk iim_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_IIM_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk iomuxc_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_IOMUXC_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk kpp_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_KPP_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk lcdc_clk[] = { + { + /* lcdc_clk */ + .id = 0, + .parent = &per_clk[7], + .secondary = &lcdc_clk[1], + .round_rate = _clk_parent_round_rate, + .set_rate = _clk_parent_set_rate,}, + { + /* lcdc_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_LCDC_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &lcdc_clk[2],}, + { + /* lcdc_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_LCDC_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk owire_clk[] = { + { + /* owire_clk */ + .id = 0, + .parent = &per_clk[9], + .secondary = &owire_clk[1],}, + { + /* owire_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_OWIRE_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk pwm1_clk[] = { + { + /* pwm_clk */ + .id = 0, + .parent = &per_clk[10], + .secondary = &pwm1_clk[1],}, + { + /* pwm_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR1, + .enable_shift = MXC_CCM_CGCR1_PWM1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk pwm2_clk[] = { + { + /* pwm_clk */ + .id = 1, + .parent = &per_clk[10], + .secondary = &pwm2_clk[1],}, + { + /* pwm_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_PWM2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk pwm3_clk[] = { + { + /* pwm_clk */ + .id = 2, + .parent = &per_clk[10], + .secondary = &pwm3_clk[1],}, + { + /* pwm_ipg_clk */ + .id = 2, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk pwm4_clk[] = { + { + /* pwm_clk */ + .id = 3, + .parent = &per_clk[10], + .secondary = &pwm4_clk[1],}, + { + /* pwm_ipg_clk */ + .id = 3, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk rng_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_RNGB_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk scc_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SCC_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk sdma_clk[] = { + { + /* sdma_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SDMA_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &sdma_clk[1],}, + { + /* sdma_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_SDMA_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk sim1_clk[] = { + { + /* sim1_clk */ + .id = 0, + .parent = &per_clk[11], + .secondary = &sim1_clk[1],}, + { + /* sim_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SIM1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk sim2_clk[] = { + { + /* sim2_clk */ + .id = 1, + .parent = &per_clk[12], + .secondary = &sim2_clk[1],}, + { + /* sim_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SIM2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk slcdc_clk[] = { + { + /* slcdc_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SLCDC_OFFSET, + .disable = clk_cgcr_disable, + .secondary = &slcdc_clk[1],}, + { + /* slcdc_ahb_clk */ + .id = 0, + .parent = &ahb_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR0, + .enable_shift = MXC_CCM_CGCR0_HCLK_SLCDC_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk spba_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SPBA_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk ssi1_clk[] = { + { + /* ssi_clk */ + .id = 0, + .parent = &per_clk[13], + .secondary = &ssi1_clk[1],}, + { + /* ssi_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SSI1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk ssi2_clk[] = { + { + /* ssi_clk */ + .id = 1, + .parent = &per_clk[14], + .secondary = &ssi2_clk[1],}, + { + /* ssi_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_SSI2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk tsc_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_TCHSCRN_OFFSET, + .disable = clk_cgcr_disable, +}; + +struct clk uart1_clk[] = { + { + /* uart_clk */ + .id = 0, + .parent = &per_clk[15], + .secondary = &uart1_clk[1],}, + { + /* uart_ipg_clk */ + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_UART1_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk uart2_clk[] = { + { + /* uart_clk */ + .id = 1, + .parent = &per_clk[15], + .secondary = &uart2_clk[1],}, + { + /* uart_ipg_clk */ + .id = 1, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_UART2_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk uart3_clk[] = { + { + /* uart_clk */ + .id = 2, + .parent = &per_clk[15], + .secondary = &uart3_clk[1],}, + { + /* uart_ipg_clk */ + .id = 2, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_UART3_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk uart4_clk[] = { + { + /* uart_clk */ + .id = 3, + .parent = &per_clk[15], + .secondary = &uart4_clk[1],}, + { + /* uart_ipg_clk */ + .id = 3, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_UART4_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk uart5_clk[] = { + { + /* uart_clk */ + .id = 4, + .parent = &per_clk[15], + .secondary = &uart5_clk[1],}, + { + /* uart_ipg_clk */ + .id = 4, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_UART5_OFFSET, + .disable = clk_cgcr_disable,}, +}; + +struct clk wdog_clk = { + .id = 0, + .parent = &ipg_clk, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_CGCR2, + .enable_shift = MXC_CCM_CGCR2_WDOG_OFFSET, + .disable = clk_cgcr_disable, +}; + +static unsigned long _clk_usb_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + if (parent_rate % rate) + div++; + + if (div > 64) + return -EINVAL; + + return parent_rate / div; +} + +static int _clk_usb_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long reg; + unsigned long div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + + if (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 unsigned long _clk_usb_get_rate(struct clk *clk) +{ + unsigned long div = + __raw_readl(MXC_CCM_CCTL) & MXC_CCM_CCTL_USB_DIV_MASK; + + div >>= MXC_CCM_CCTL_USB_DIV_OFFSET; + + return clk_get_rate(clk->parent) / (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 = { + .parent = &upll_clk, + .get_rate = _clk_usb_get_rate, + .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; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + if (parent_rate % rate) + div++; + + if (div > 64) + return -EINVAL; + + return parent_rate / div; +} + +static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long reg; + unsigned long div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + div = parent_rate / rate; + + if ((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 unsigned long _clk_clko_get_rate(struct clk *clk) +{ + unsigned long div = + __raw_readl(MXC_CCM_MCR) & MXC_CCM_MCR_CLKO_DIV_MASK; + + div >>= MXC_CCM_MCR_CLKO_DIV_OFFSET; + + return clk_get_rate(clk->parent) / (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 = { + .get_rate = _clk_clko_get_rate, + .set_rate = _clk_clko_set_rate, + .round_rate = _clk_clko_round_rate, + .set_parent = _clk_clko_set_parent, + .enable = clk_cgcr_enable, + .enable_reg = MXC_CCM_MCR, + .enable_shift = MXC_CCM_MCR_CLKO_EN_OFFSET, + .disable = clk_cgcr_disable, +}; + static unsigned long get_rate_mpll(void) { ulong mpctl = __raw_readl(CRM_BASE + CCM_MPCTL); @@ -59,12 +1572,14 @@ static unsigned long get_rate_mpll(void) return mxc_decode_pll(mpctl, 24000000); } +# if 0 static unsigned long get_rate_upll(void) { ulong mpctl = __raw_readl(CRM_BASE + CCM_UPCTL); return mxc_decode_pll(mpctl, 24000000); } +#endif unsigned long get_rate_arm(struct clk *clk) { @@ -89,6 +1604,7 @@ static unsigned long get_rate_ipg(struct clk *clk) return get_rate_ahb(NULL) >> 1; } +#if 0 static unsigned long get_rate_per(int per) { unsigned long ofs = (per & 0x3) * 8; @@ -133,6 +1649,7 @@ static unsigned long get_rate_otg(struct clk *clk) { return 48000000; /* FIXME */ } +#endif static int clk_cgcr_enable(struct clk *clk) { @@ -154,6 +1671,7 @@ static void clk_cgcr_disable(struct clk *clk) __raw_writel(reg, clk->enable_reg); } +#if 0 #define DEFINE_CLOCK(name, i, er, es, gr, sr, s) \ static struct clk name = { \ .id = i, \ @@ -191,6 +1709,7 @@ DEFINE_CLOCK(i2c_clk, 0, CCM_CGCR0, 6, get_rate_i2c, NULL, NULL); DEFINE_CLOCK(fec_clk, 0, CCM_CGCR1, 15, get_rate_ipg, NULL, &fec_ahb_clk); DEFINE_CLOCK(dryice_clk, 0, CCM_CGCR1, 8, get_rate_ipg, NULL, NULL); DEFINE_CLOCK(lcdc_clk, 0, CCM_CGCR1, 29, get_rate_lcdc, NULL, &lcdc_per_clk); +#endif #define _REGISTER_CLOCK(d, n, c) \ { \ @@ -200,6 +1719,7 @@ DEFINE_CLOCK(lcdc_clk, 0, CCM_CGCR1, 29, get_rate_lcdc, NULL, &lcdc_per_clk); }, static struct clk_lookup lookups[] = { +#if 0 _REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk) _REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk) _REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk) @@ -225,24 +1745,174 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("fec.0", NULL, fec_clk) _REGISTER_CLOCK("imxdi_rtc.0", NULL, dryice_clk) _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk) +#endif + _REGISTER_CLOCK(NULL, "osc24m_clk", osc24m_clk) + _REGISTER_CLOCK(NULL, "osc32k_clk", osc32k_clk) + _REGISTER_CLOCK(NULL, "mpll_clk", mpll_clk) + _REGISTER_CLOCK(NULL, "upll_clk", upll_clk) + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) + _REGISTER_CLOCK(NULL, "ahb_clk", ahb_clk) + _REGISTER_CLOCK(NULL, "ipg_clk", ipg_clk) + _REGISTER_CLOCK(NULL, "usb_ahb_clk", usb_ahb_clk) + _REGISTER_CLOCK("mxcintuart.0", NULL, uart1_clk[0]) + _REGISTER_CLOCK("mxcintuart.1", NULL, uart2_clk[0]) + _REGISTER_CLOCK("mxcintuart.2", NULL, uart3_clk[0]) + _REGISTER_CLOCK("mxcintuart.3", NULL, uart4_clk[0]) + _REGISTER_CLOCK("mxcintuart.4", NULL, uart5_clk[0]) + _REGISTER_CLOCK(NULL, "usb_ahb_clk", usb_ahb_clk) + _REGISTER_CLOCK(NULL, "usb_clk", usb_clk) + _REGISTER_CLOCK("mxc_nandv2_flash.0", NULL, nfc_clk) + _REGISTER_CLOCK("spi_imx.0", NULL, cspi_clk[0]) + _REGISTER_CLOCK("spi_imx.1", NULL, cspi_clk[1]) + _REGISTER_CLOCK("spi_imx.2", NULL, cspi_clk[2]) + _REGISTER_CLOCK("mxc_pwm.0", NULL, pwm1_clk[0]) + _REGISTER_CLOCK("mxc_pwm.1", NULL, pwm2_clk[0]) + _REGISTER_CLOCK("mxc_pwm.2", NULL, pwm3_clk[0]) + _REGISTER_CLOCK("mxc_pwm.3", NULL, pwm4_clk[0]) + _REGISTER_CLOCK("mxc_keypad.0", NULL, kpp_clk) + _REGISTER_CLOCK("imx_adc.0", NULL, tsc_clk) + _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk[0]) + _REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk[1]) + _REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk[2]) + _REGISTER_CLOCK("fec.0", NULL, fec_clk[0]) + _REGISTER_CLOCK(NULL, "dryice_clk", dryice_clk) + _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk[0]) + _REGISTER_CLOCK("mxc_sdma.0", "sdma_ipg_clk", sdma_clk[0]) + _REGISTER_CLOCK("mxc_sdma.0", "sdma_ahb_clk", sdma_clk[1]) + _REGISTER_CLOCK("mxsdhci.0", NULL, esdhc1_clk[0]) + _REGISTER_CLOCK("mxsdhci.1", NULL, esdhc2_clk[0]) + _REGISTER_CLOCK(NULL, "gpt", gpt1_clk[0]) + _REGISTER_CLOCK(NULL, "gpt", gpt2_clk[0]) + _REGISTER_CLOCK(NULL, "gpt", gpt3_clk[0]) + _REGISTER_CLOCK(NULL, "gpt", gpt4_clk[0]) + _REGISTER_CLOCK(NULL, "clko_clk", clko_clk) + _REGISTER_CLOCK("pata_fsl", NULL, ata_clk[0]) + _REGISTER_CLOCK("FlexCAN.0", NULL, can_clk[0]) + _REGISTER_CLOCK("mxc_esai.0", NULL, esai_clk[0]) + _REGISTER_CLOCK("mxc_iim.0", NULL, iim_clk) + _REGISTER_CLOCK("mxc_w1.0", NULL, owire_clk[0]) + _REGISTER_CLOCK(NULL, "scc_clk", scc_clk) + _REGISTER_CLOCK("mxc_sim.0", NULL, sim1_clk[0]) + _REGISTER_CLOCK("mxc_sim.0", NULL, sim2_clk[0]) + _REGISTER_CLOCK("mxc_ssi.0", NULL, ssi1_clk[0]) + _REGISTER_CLOCK("mxc_ssi.0", NULL, ssi2_clk[0]) + _REGISTER_CLOCK("mxc_ipu", "csi_clk", csi_clk[0]) + _REGISTER_CLOCK(NULL, "lcdc_clk", lcdc_clk[0]) + _REGISTER_CLOCK(NULL, "slcdc_clk", lcdc_clk[0]) + _REGISTER_CLOCK(NULL, "rng_clk", rng_clk) + _REGISTER_CLOCK(NULL, "audmux_clk", audmux_clk) + _REGISTER_CLOCK(NULL, "ect_clk", ect_clk) + _REGISTER_CLOCK(NULL, "epit1_clk", epit1_clk[0]) + _REGISTER_CLOCK(NULL, "epit2_clk", epit2_clk[0]) + _REGISTER_CLOCK(NULL, "gpio_clk", gpio_clk[0]) + _REGISTER_CLOCK(NULL, "iomuxc_clk", iomuxc_clk) + _REGISTER_CLOCK(NULL, "spba_clk", spba_clk) + _REGISTER_CLOCK(NULL, "wdog_clk", wdog_clk) + _REGISTER_CLOCK(NULL, "per_csi_clk", per_clk[0]) + _REGISTER_CLOCK(NULL, "per_epit_clk", per_clk[1]) + _REGISTER_CLOCK(NULL, "per_esai_clk", per_clk[2]) + _REGISTER_CLOCK(NULL, "per_esdhc1_clk", per_clk[3]) + _REGISTER_CLOCK(NULL, "per_esdhc2_clk", per_clk[4]) + _REGISTER_CLOCK(NULL, "per_gpt_clk", per_clk[5]) + _REGISTER_CLOCK(NULL, "per_i2c_clk", per_clk[6]) + _REGISTER_CLOCK(NULL, "per_lcdc_clk", per_clk[7]) + _REGISTER_CLOCK(NULL, "per_nfc_clk", per_clk[8]) + _REGISTER_CLOCK(NULL, "per_owire_clk", per_clk[9]) + _REGISTER_CLOCK(NULL, "per_pwm_clk", per_clk[10]) + _REGISTER_CLOCK(NULL, "per_sim1_clk", per_clk[11]) + _REGISTER_CLOCK(NULL, "per_sim2_clk", per_clk[12]) + _REGISTER_CLOCK(NULL, "per_ssi1_clk", per_clk[13]) + _REGISTER_CLOCK(NULL, "per_ssi2_clk", per_clk[14]) + _REGISTER_CLOCK(NULL, "per_uart_clk", per_clk[15]) }; +static struct mxc_clk mxc_clks[ARRAY_SIZE(lookups)]; + +/*! + * 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.get_rate(&upll_clk); + per_clk[5].get_rate(&per_clk[5]); + per_clk[5].enable(&per_clk[5]); + + return clk_get_rate(&per_clk[5]); +} + int __init mx25_clocks_init(void) { + int i; + unsigned long upll_rate; + unsigned long ahb_rate; + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + for (i = 0; i < ARRAY_SIZE(lookups); i++) { + clkdev_add(&lookups[i]); + mxc_clks[i].reg_clk = lookups[i].clk; + if (lookups[i].con_id != NULL) + strcpy(mxc_clks[i].name, lookups[i].con_id); + else + strcpy(mxc_clks[i].name, lookups[i].dev_id); + clk_register(&mxc_clks[i]); + } + /* Turn off all clocks except the ones we need to survive, namely: * EMI, GPIO1-3 (CCM_CGCR1[18:16]), GPT1, IOMUXC (CCM_CGCR1[27]), IIM, * SCC */ __raw_writel((1 << 19), CRM_BASE + CCM_CGCR0); - __raw_writel((0xf << 16) | (3 << 26), CRM_BASE + CCM_CGCR1); + + __raw_writel((1 << MXC_CCM_CGCR1_GPT1_OFFSET) | + (1 << MXC_CCM_CGCR1_IIM_OFFSET), + CRM_BASE + CCM_CGCR1); + __raw_writel((1 << 5), CRM_BASE + CCM_CGCR2); /* Clock source for lcdc is upll */ __raw_writel(__raw_readl(CRM_BASE+0x64) | (1 << 7), CRM_BASE + 0x64); - mxc_timer_init(&gpt_clk, MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54); + /* 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); + + /* GPT clock must be derived from AHB clock */ + ahb_rate = clk_get_rate(&ahb_clk); + clk_set_rate(&per_clk[5], ahb_rate / 10); + + /* LCDC clock must be derived from UPLL clock */ + upll_rate = clk_get_rate(&upll_clk); + clk_set_parent(&per_clk[7], &upll_clk); + clk_set_rate(&per_clk[7], upll_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_rate / 6); + + /* sim clock */ + clk_set_rate(&per_clk[11], ahb_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_rate / 5); + + pr_info("Clock input source is %ld\n", clk_get_rate(&osc24m_clk)); + + 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(MX25_GPT1_BASE_ADDR), + MX25_INT_GPT1); return 0; } |