diff options
Diffstat (limited to 'arch/arm/mach-mx28/clock.c')
-rw-r--r-- | arch/arm/mach-mx28/clock.c | 1818 |
1 files changed, 1818 insertions, 0 deletions
diff --git a/arch/arm/mach-mx28/clock.c b/arch/arm/mach-mx28/clock.c new file mode 100644 index 000000000000..9797d1f650d9 --- /dev/null +++ b/arch/arm/mach-mx28/clock.c @@ -0,0 +1,1818 @@ +/* + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/iram_alloc.h> +#include <linux/platform_device.h> + +#include <mach/clock.h> + +#include "regs-clkctrl.h" +#include "regs-digctl.h" +#include "emi_settings.h" + +#define HW_SAIF_CTRL (0x00000000) +#define HW_SAIF_STAT (0x00000010) +#define SAIF0_CTRL (IO_ADDRESS(SAIF0_PHYS_ADDR) + HW_SAIF_CTRL) +#define SAIF0_STAT (IO_ADDRESS(SAIF0_PHYS_ADDR) + HW_SAIF_STAT) +#define SAIF1_CTRL (IO_ADDRESS(SAIF1_PHYS_ADDR) + HW_SAIF_CTRL) +#define SAIF1_STAT (IO_ADDRESS(SAIF1_PHYS_ADDR) + HW_SAIF_STAT) +#define BM_SAIF_CTRL_RUN 0x00000001 +#define BM_SAIF_STAT_BUSY 0x00000001 +#define CLKCTRL_BASE_ADDR IO_ADDRESS(CLKCTRL_PHYS_ADDR) +#define DIGCTRL_BASE_ADDR IO_ADDRESS(DIGCTL_PHYS_ADDR) + +/* external clock input */ +static struct clk xtal_clk[]; +static unsigned long xtal_clk_rate[3] = { 24000000, 24000000, 32000 }; + +static unsigned long enet_mii_phy_rate; + +static inline int clk_is_busy(struct clk *clk) +{ + return __raw_readl(clk->busy_reg) & (1 << clk->busy_bits); +} + +static bool mx28_enable_h_autoslow(bool enable) +{ + bool currently_enabled; + + if (__raw_readl(CLKCTRL_BASE_ADDR+HW_CLKCTRL_HBUS) & + BM_CLKCTRL_HBUS_ASM_ENABLE) + currently_enabled = true; + else + currently_enabled = false; + + if (enable) + __raw_writel(BM_CLKCTRL_HBUS_ASM_ENABLE, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS_SET); + else + __raw_writel(BM_CLKCTRL_HBUS_ASM_ENABLE, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS_CLR); + return currently_enabled; +} + + +static void mx28_set_hbus_autoslow_flags(u16 mask) +{ + u32 reg; + + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); + reg &= 0xFFFF; + reg |= mask << 16; + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); +} + +static int mx28_raw_enable(struct clk *clk) +{ + unsigned int reg; + if (clk->enable_reg) { + reg = __raw_readl(clk->enable_reg); + reg &= ~clk->enable_bits; + __raw_writel(reg, clk->enable_reg); + } + return 0; +} + +static void mx28_raw_disable(struct clk *clk) +{ + unsigned int reg; + if (clk->enable_reg) { + reg = __raw_readl(clk->enable_reg); + reg |= clk->enable_bits; + __raw_writel(reg, clk->enable_reg); + } +} + +static unsigned int +mx28_get_frac_div(unsigned long root_rate, unsigned long rate, unsigned mask) +{ + unsigned long mult_rate; + unsigned int div; + mult_rate = rate * (mask + 1); + div = mult_rate / root_rate; + if ((mult_rate % root_rate) && (div < mask)) + div--; + return div; +} + +static unsigned long xtal_get_rate(struct clk *clk) +{ + int id = clk - xtal_clk; + return xtal_clk_rate[id]; +} + +static struct clk xtal_clk[] = { + { + .flags = RATE_FIXED, + .get_rate = xtal_get_rate, + }, + { + .flags = RATE_FIXED, + .get_rate = xtal_get_rate, + }, + { + .flags = RATE_FIXED, + .get_rate = xtal_get_rate, + }, +}; + +static struct clk ref_xtal_clk = { + .parent = &xtal_clk[0], +}; + +static unsigned long pll_get_rate(struct clk *clk); +static int pll_enable(struct clk *clk); +static void pll_disable(struct clk *clk); +static int pll_set_rate(struct clk *clk, unsigned long rate); +static struct clk pll_clk[] = { + { + .parent = &ref_xtal_clk, + .flags = RATE_FIXED, + .get_rate = pll_get_rate, + .set_rate = pll_set_rate, + .enable = pll_enable, + .disable = pll_disable, + }, + { + .parent = &ref_xtal_clk, + .flags = RATE_FIXED, + .get_rate = pll_get_rate, + .set_rate = pll_set_rate, + .enable = pll_enable, + .disable = pll_disable, + }, + { + .parent = &ref_xtal_clk, + .flags = RATE_FIXED, + .get_rate = pll_get_rate, + .set_rate = pll_set_rate, + .enable = pll_enable, + .disable = pll_disable, + } +}; + +static unsigned long pll_get_rate(struct clk *clk) +{ + unsigned int reg; + if (clk == (pll_clk + 2)) + return 50000000; + if (clk == pll_clk) { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL1); + reg = (reg & BM_CLKCTRL_PLL0CTRL0_DIV_SEL) >> + BP_CLKCTRL_PLL0CTRL0_DIV_SEL; + } else { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL1); + reg = (reg & BM_CLKCTRL_PLL1CTRL0_DIV_SEL) >> + BP_CLKCTRL_PLL1CTRL0_DIV_SEL; + } + switch (reg) { + case 0: + return 480000000; + case 1: + return 384000000; + case 2: + return 288000000; + default: + return -EINVAL; + } +} + +static int pll_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned int div, reg; + + if (clk == pll_clk + 2) + return -EINVAL; + + switch (rate) { + case 480000000: + div = 0; + break; + case 384000000: + div = 1; + break; + case 288000000: + div = 2; + break; + default: + return -EINVAL; + } + if (clk == pll_clk) { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL1); + reg &= ~BM_CLKCTRL_PLL0CTRL0_DIV_SEL; + reg |= BF_CLKCTRL_PLL0CTRL0_DIV_SEL(div); + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL1); + } else { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL1); + reg &= ~BM_CLKCTRL_PLL1CTRL0_DIV_SEL; + reg |= BF_CLKCTRL_PLL1CTRL0_DIV_SEL(div); + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL1); + } + return 0; +} + +static int pll_enable(struct clk *clk) +{ + int timeout = 100; + unsigned long reg; + switch (clk - pll_clk) { + case 0: + __raw_writel(BM_CLKCTRL_PLL0CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL0_SET); + do { + udelay(10); + reg = __raw_readl(CLKCTRL_BASE_ADDR + + HW_CLKCTRL_PLL0CTRL1); + timeout--; + } while ((timeout > 0) && !(reg & BM_CLKCTRL_PLL0CTRL1_LOCK)); + if (timeout <= 0) + return -EFAULT; + return 0; + case 1: + __raw_writel(BM_CLKCTRL_PLL1CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL0_SET); + do { + udelay(10); + reg = __raw_readl(CLKCTRL_BASE_ADDR + + HW_CLKCTRL_PLL1CTRL1); + timeout--; + } while ((timeout > 0) && !(reg & BM_CLKCTRL_PLL1CTRL1_LOCK)); + if (timeout <= 0) + return -EFAULT; + return 0; + case 2: + __raw_writel(BM_CLKCTRL_PLL2CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL2CTRL0_SET); + udelay(10); + __raw_writel(BM_CLKCTRL_PLL2CTRL0_CLKGATE, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL2CTRL0_CLR); + break; + } + return -ENODEV; +} + +static void pll_disable(struct clk *clk) +{ + switch (clk - pll_clk) { + case 0: + __raw_writel(BM_CLKCTRL_PLL0CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL0_CLR); + return; + case 1: + __raw_writel(BM_CLKCTRL_PLL1CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL0_CLR); + return; + case 2: + __raw_writel(BM_CLKCTRL_PLL2CTRL0_CLKGATE, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL2CTRL0_SET); + __raw_writel(BM_CLKCTRL_PLL2CTRL0_POWER, + CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL2CTRL0_CLR); + break; + } + return; +} + +static inline unsigned long +ref_clk_get_rate(unsigned long base, unsigned int div) +{ + unsigned long rate = base / 1000; + return 1000 * ((rate * 18) / div); +} + +static unsigned long ref_clk_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long base = clk->parent->get_rate(clk->parent); + unsigned long div = (base * 18) / rate; + return (base / div) * 18; +} + +static int ref_clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long base = clk->parent->get_rate(clk->parent); + unsigned long div = ((base/1000) * 18) / (rate/1000); + if (rate != ((base / div) * 18)) + return -EINVAL; + if (clk->scale_reg == 0) + return -EINVAL; + base = __raw_readl(clk->scale_reg); + base &= ~(0x3F << clk->scale_bits); + base |= (div << clk->scale_bits); + __raw_writel(base, clk->scale_reg); + return 0; +} + +static unsigned long ref_cpu_get_rate(struct clk *clk) +{ + unsigned int reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0) & + BM_CLKCTRL_FRAC0_CPUFRAC; + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + + +static struct clk ref_cpu_clk = { + .parent = &pll_clk[0], + .get_rate = ref_cpu_get_rate, + .round_rate = ref_clk_round_rate, + .set_rate = ref_clk_set_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .enable_bits = BM_CLKCTRL_FRAC0_CLKGATECPU, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .scale_bits = BP_CLKCTRL_FRAC0_CPUFRAC, +}; + +static unsigned long ref_emi_get_rate(struct clk *clk) +{ + unsigned int reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0) & + BM_CLKCTRL_FRAC0_EMIFRAC; + reg >>= BP_CLKCTRL_FRAC0_EMIFRAC; + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + +static struct clk ref_emi_clk = { + .parent = &pll_clk[0], + .get_rate = ref_emi_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .enable_bits = BM_CLKCTRL_FRAC0_CLKGATEEMI, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .scale_bits = BP_CLKCTRL_FRAC0_EMIFRAC, +}; + +static unsigned long ref_io_get_rate(struct clk *clk); +static struct clk ref_io_clk[] = { + { + .parent = &pll_clk[0], + .get_rate = ref_io_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .enable_bits = BM_CLKCTRL_FRAC0_CLKGATEIO0, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .scale_bits = BP_CLKCTRL_FRAC0_IO0FRAC, + }, + { + .parent = &pll_clk[0], + .get_rate = ref_io_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .enable_bits = BM_CLKCTRL_FRAC0_CLKGATEIO1, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .scale_bits = BP_CLKCTRL_FRAC0_IO1FRAC, + }, +}; + +static unsigned long ref_io_get_rate(struct clk *clk) +{ + unsigned int reg; + if (clk == ref_io_clk) { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0) & + BM_CLKCTRL_FRAC0_IO0FRAC; + reg >>= BP_CLKCTRL_FRAC0_IO0FRAC; + } else { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0) & + BM_CLKCTRL_FRAC0_IO1FRAC; + reg >>= BP_CLKCTRL_FRAC0_IO1FRAC; + } + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + +static unsigned long ref_pix_get_rate(struct clk *clk) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1) & + BM_CLKCTRL_FRAC1_PIXFRAC; + reg >>= BP_CLKCTRL_FRAC1_PIXFRAC; + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + +static struct clk ref_pix_clk = { + .parent = &pll_clk[0], + .get_rate = ref_pix_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .enable_bits = BM_CLKCTRL_FRAC1_CLKGATEPIX, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .scale_bits = BP_CLKCTRL_FRAC1_PIXFRAC, +}; + +static unsigned long ref_hsadc_get_rate(struct clk *clk) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1) & + BM_CLKCTRL_FRAC1_HSADCFRAC; + reg >>= BP_CLKCTRL_FRAC1_HSADCFRAC; + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + +static struct clk ref_hsadc_clk = { + .parent = &pll_clk[0], + .get_rate = ref_hsadc_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .enable_bits = BM_CLKCTRL_FRAC1_CLKGATEHSADC, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .scale_bits = BP_CLKCTRL_FRAC1_HSADCFRAC, +}; + +static unsigned long ref_gpmi_get_rate(struct clk *clk) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1) & + BM_CLKCTRL_FRAC1_GPMIFRAC; + reg >>= BP_CLKCTRL_FRAC1_GPMIFRAC; + return ref_clk_get_rate(clk->parent->get_rate(clk->parent), reg); +} + +static struct clk ref_gpmi_clk = { + .parent = &pll_clk[0], + .get_rate = ref_gpmi_get_rate, + .set_rate = ref_clk_set_rate, + .round_rate = ref_clk_round_rate, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .enable_bits = BM_CLKCTRL_FRAC1_CLKGATEGPMI, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1, + .scale_bits = BP_CLKCTRL_FRAC1_GPMIFRAC, +}; + +static unsigned long cpu_get_rate(struct clk *clk) +{ + unsigned long rate, div; + rate = (clk->parent->get_rate(clk->parent)); + div = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU) & + BM_CLKCTRL_CPU_DIV_CPU; + rate = rate/div; + return rate; + } + +static unsigned long cpu_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long frac_rate, root_rate = clk->parent->get_rate(clk->parent); + unsigned int div = root_rate / rate; + if (div == 0) + return root_rate; + if (clk->parent == &ref_cpu_clk) { + if (div > 0x3F) + div = 0x3F; + return root_rate / div; + } + + frac_rate = root_rate % rate; + div = root_rate / rate; + if ((div == 0) || (div >= 0x400)) + return root_rate; + if (frac_rate == 0) + return rate; + return rate; +} + +static struct clk h_clk; +static int cpu_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long root_rate = + clk->parent->parent->get_rate(clk->parent->parent); + int i; + u32 clkctrl_cpu = 1; + u32 c = clkctrl_cpu; + u32 clkctrl_frac = 1; + u32 val; + u32 reg_val, hclk_reg; + + if (rate < 24000) + return -EINVAL; + else if (rate == 24000) { + /* switch to the 24M source */ + clk_set_parent(clk, &ref_xtal_clk); + } else { + for ( ; c < 0x40; c++) { + u32 f = ((root_rate/1000)*18/c + (rate/1000)/2) / + (rate/1000); + int s1, s2; + + if (f < 18 || f > 35) + continue; + s1 = (root_rate/1000)*18/clkctrl_frac/clkctrl_cpu - + (rate/1000); + s2 = (root_rate/1000)*18/c/f - (rate/1000); + if (abs(s1) > abs(s2)) { + clkctrl_cpu = c; + clkctrl_frac = f; + } + if (s2 == 0) + break; + }; + if (c == 0x40) { + int d = (root_rate/1000)*18/clkctrl_frac/clkctrl_cpu - + (rate/1000); + if ((abs(d) > 100) || (clkctrl_frac < 18) || + (clkctrl_frac > 35)) + return -EINVAL; + } + + /* Set safe hbus clock divider. A divider of 3 ensure that + * the Vddd voltage required for the cpuclk is sufficiently + * high for the hbus clock. + */ + hclk_reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); + if ((hclk_reg & BP_CLKCTRL_HBUS_DIV) != 3) { + hclk_reg &= ~(BM_CLKCTRL_HBUS_DIV); + hclk_reg |= BF_CLKCTRL_HBUS_DIV(3); + + /* change hclk divider to safe value for any ref_cpu + * value. + */ + __raw_writel(hclk_reg, CLKCTRL_BASE_ADDR + + HW_CLKCTRL_HBUS); + } + + for (i = 10000; i; i--) + if (!clk_is_busy(&h_clk)) + break; + if (!i) { + printk(KERN_ERR "couldn't set up HCLK divisor\n"); + return -ETIMEDOUT; + } + + /* Set Frac div */ + val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0); + val &= ~(BM_CLKCTRL_FRAC0_CPUFRAC << BP_CLKCTRL_FRAC0_CPUFRAC); + val |= clkctrl_frac; + __raw_writel(val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0); + /* Do not gate */ + __raw_writel(BM_CLKCTRL_FRAC0_CLKGATECPU, CLKCTRL_BASE_ADDR + + HW_CLKCTRL_FRAC0_CLR); + + /* write clkctrl_cpu */ + reg_val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU); + reg_val &= ~0x3F; + reg_val |= clkctrl_cpu; + + __raw_writel(reg_val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU); + + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + if (!i) { + printk(KERN_ERR "couldn't set up CPU divisor\n"); + return -ETIMEDOUT; + } + } + return 0; +} + +static int cpu_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + + if (clk->bypass_reg) { + if (parent == clk->parent) + return 0; + if (parent == &ref_xtal_clk) { + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + SET_REGISTER); + ret = 0; + } + if (ret && (parent == &ref_cpu_clk)) { + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + CLR_REGISTER); + ret = 0; + } + if (!ret) + clk->parent = parent; + } + return ret; +} + +static struct clk cpu_clk = { + .parent = &ref_cpu_clk, + .get_rate = cpu_get_rate, + .round_rate = cpu_round_rate, + .set_rate = cpu_set_rate, + .set_parent = cpu_set_parent, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 18, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .busy_bits = 28, +}; + +static unsigned long uart_get_rate(struct clk *clk) +{ + unsigned int div; + div = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_XTAL) & + BM_CLKCTRL_XTAL_DIV_UART; + return clk->parent->get_rate(clk->parent) / div; +} + +static struct clk uart_clk = { + .parent = &ref_xtal_clk, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_XTAL, + .enable_bits = BM_CLKCTRL_XTAL_UART_CLK_GATE, + .get_rate = uart_get_rate, +}; + +static struct clk pwm_clk = { + .parent = &ref_xtal_clk, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_XTAL, + .enable_bits = BM_CLKCTRL_XTAL_PWM_CLK24M_GATE, +}; + +static unsigned long clk_32k_get_rate(struct clk *clk) +{ + return clk->parent->get_rate(clk->parent) / 750; +} + +static struct clk clk_32k = { + .parent = &ref_xtal_clk, + .flags = RATE_FIXED, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_XTAL, + .enable_bits = BM_CLKCTRL_XTAL_TIMROT_CLK32K_GATE, + .get_rate = clk_32k_get_rate, +}; + +static unsigned long lradc_get_rate(struct clk *clk) +{ + return clk->parent->get_rate(clk->parent) / 16; +} + +static struct clk lradc_clk = { + .parent = &clk_32k, + .flags = RATE_FIXED, + .get_rate = lradc_get_rate, +}; + +static unsigned long x_get_rate(struct clk *clk) +{ + unsigned long reg, div; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_XBUS); + div = reg & BM_CLKCTRL_XBUS_DIV; + if (!(reg & BM_CLKCTRL_XBUS_DIV_FRAC_EN)) + return clk->parent->get_rate(clk->parent) / div; + return (clk->parent->get_rate(clk->parent) / 0x400) * div; +} + +static unsigned long x_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned int root_rate, frac_rate; + unsigned int div; + root_rate = clk->parent->get_rate(clk->parent); + frac_rate = root_rate % rate; + div = root_rate / rate; + if ((div == 0) || (div >= 0x400)) + return root_rate; + if (frac_rate == 0) + return rate; + return rate; +} + +static int x_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long root_rate; + unsigned int reg, div; + root_rate = clk->parent->get_rate(clk->parent); + div = root_rate / rate; + if ((div == 0) || (div >= 0x400)) + return -EINVAL; + + if (root_rate % rate) { + div = mx28_get_frac_div(root_rate / 1000, rate / 1000, 0x3FF); + if (((root_rate / 0x400) * div) > rate) + return -EINVAL; + } + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_XBUS); + reg &= ~(BM_CLKCTRL_XBUS_DIV | BM_CLKCTRL_XBUS_DIV_FRAC_EN); + if (root_rate % rate) + reg |= BM_CLKCTRL_XBUS_DIV_FRAC_EN; + reg |= BF_CLKCTRL_XBUS_DIV(div); + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_XBUS); + return 0; +} + +static struct clk x_clk = { + .parent = &ref_xtal_clk, + .get_rate = x_get_rate, + .set_rate = x_set_rate, + .round_rate = x_round_rate, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_XBUS, + .scale_bits = BM_CLKCTRL_XBUS_BUSY, +}; + +static struct clk ana_clk = { + .parent = &ref_xtal_clk, +}; + +static unsigned long rtc_get_rate(struct clk *clk) +{ + if (clk->parent == &xtal_clk[2]) + return clk->parent->get_rate(clk->parent); + return clk->parent->get_rate(clk->parent) / 768; +} + +static struct clk rtc_clk = { + .parent = &ref_xtal_clk, + .get_rate = rtc_get_rate, +}; + +static struct clk flexcan_clk[] = { + { + .parent = &ref_xtal_clk, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FLEXCAN, + .enable_bits = BM_CLKCTRL_FLEXCAN_STOP_CAN0, + }, + { + .parent = &ref_xtal_clk, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FLEXCAN, + .enable_bits = BM_CLKCTRL_FLEXCAN_STOP_CAN1, + }, +}; + +static unsigned long h_get_rate(struct clk *clk) +{ + unsigned long reg, div; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); + div = reg & BM_CLKCTRL_HBUS_DIV; + return clk->parent->get_rate(clk->parent) / div; +} + +static unsigned long h_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned int root_rate, frac_rate; + unsigned int div; + root_rate = clk->parent->get_rate(clk->parent); + frac_rate = root_rate % rate; + div = root_rate / rate; + if ((div == 0) || (div >= 0x20)) + return root_rate; + if (frac_rate < (rate / 2)) + return rate; + else + return root_rate / (div + 1); +} + +static int h_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long root_rate; + unsigned long round_rate; + unsigned int reg, div; + root_rate = clk->parent->get_rate(clk->parent); + round_rate = h_round_rate(clk, rate); + div = root_rate / round_rate; + if ((div == 0) || (div >= 0x20)) + return -EINVAL; + + if (root_rate % round_rate) + return -EINVAL; + + if ((root_rate < rate) && (root_rate == 64000000)) + div = 3; + + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); + reg &= ~(BM_CLKCTRL_HBUS_DIV_FRAC_EN | BM_CLKCTRL_HBUS_DIV); + reg |= BF_CLKCTRL_HBUS_DIV(div); + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS); + + if (clk->busy_reg) { + int i; + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + if (!i) { + printk(KERN_ERR "couldn't set up AHB divisor\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static struct clk h_clk = { + .parent = &cpu_clk, + .get_rate = h_get_rate, + .set_rate = h_set_rate, + .round_rate = h_round_rate, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_HBUS, + .busy_bits = 31, +}; + +static struct clk ocrom_clk = { + .parent = &h_clk, +}; + +static unsigned long emi_get_rate(struct clk *clk) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_EMI); + if (clk->parent == &ref_emi_clk) + reg = (reg & BM_CLKCTRL_EMI_DIV_EMI); + else + reg = (reg & BM_CLKCTRL_EMI_DIV_XTAL) >> + BP_CLKCTRL_EMI_DIV_XTAL; + return clk->parent->get_rate(clk->parent) / reg; +} + +static int emi_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + if (clk->bypass_reg) { + if (parent == clk->parent) + return 0; + if (parent == &ref_xtal_clk) { + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + SET_REGISTER); + ret = 0; + } + if (ret && (parent == &ref_emi_clk)) { + __raw_writel(0 << clk->bypass_bits, + clk->bypass_reg + CLR_REGISTER); + ret = 0; + } + if (!ret) + clk->parent = parent; + } + return ret; +} + +static unsigned long emi_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long root_rate = clk->parent->get_rate(clk->parent); + unsigned int div = root_rate / rate; + if (div == 0) + return root_rate; + if (clk->parent == &ref_emi_clk) { + if (div > 0x3F) + div = 0x3F; + return root_rate / div; + } + if (div > 0xF) + div = 0xF; + return root_rate / div; +} + +static int emi_set_rate(struct clk *clk, unsigned long rate) +{ + int i; + struct mxs_emi_scaling_data emi; + unsigned long iram_phy; + void (*f) (struct mxs_emi_scaling_data *, unsigned int *); + f = iram_alloc((unsigned int)mxs_ram_freq_scale_end - + (unsigned int)mxs_ram_freq_scale, &iram_phy); + if (NULL == f) { + pr_err("%s Not enough iram\n", __func__); + return -ENOMEM; + } + memcpy(f, mxs_ram_freq_scale, + (unsigned int)mxs_ram_freq_scale_end - + (unsigned int)mxs_ram_freq_scale); +#ifdef CONFIG_MEM_mDDR + if (rate <= 24000000) { + emi.emi_div = 20; + emi.frac_div = 18; + emi.new_freq = 24; + mDDREmiController_24MHz(); + } else if (rate <= 133000000) { + emi.emi_div = 3; + emi.frac_div = 22; + emi.new_freq = 133; + mDDREmiController_133MHz(); + } else { + emi.emi_div = 2; + emi.frac_div = 22; + emi.new_freq = 200; + mDDREmiController_200MHz(); + } +#else + if (rate <= 133000000) { + emi.emi_div = 3; + emi.frac_div = 22; + emi.new_freq = 133; + DDR2EmiController_EDE1116_133MHz(); + } else if (rate <= 166000000) { + emi.emi_div = 2; + emi.frac_div = 27; + emi.new_freq = 166; + DDR2EmiController_EDE1116_166MHz(); + } else { + emi.emi_div = 2; + emi.frac_div = 22; + emi.new_freq = 200; + DDR2EmiController_EDE1116_200MHz(); + } +#endif + + local_irq_disable(); + local_fiq_disable(); + f(&emi, get_current_emidata()); + local_fiq_enable(); + local_irq_enable(); + iram_free(iram_phy, + (unsigned int)mxs_ram_freq_scale_end - + (unsigned int)mxs_ram_freq_scale); + + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + + if (!i) { + printk(KERN_ERR "couldn't set up EMI divisor\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static struct clk emi_clk = { + .parent = &ref_emi_clk, + .get_rate = emi_get_rate, + .set_rate = emi_set_rate, + .round_rate = emi_round_rate, + .set_parent = emi_set_parent, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_EMI, + .enable_bits = BM_CLKCTRL_EMI_CLKGATE, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC0, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_EMI, + .busy_bits = 28, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 7, +}; + +static unsigned long ssp_get_rate(struct clk *clk); + +static int ssp_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + int div = (clk_get_rate(clk->parent) + rate - 1) / rate; + u32 reg_frac; + const int mask = 0x1FF; + int try = 10; + int i = -1; + + if (div == 0 || div > mask) + goto out; + + reg_frac = __raw_readl(clk->scale_reg); + reg_frac &= ~(mask << clk->scale_bits); + + while (try--) { + __raw_writel(reg_frac | (div << clk->scale_bits), + clk->scale_reg); + + if (clk->busy_reg) { + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + } + if (i) + break; + } + + if (!i) + ret = -ETIMEDOUT; + else + ret = 0; + +out: + if (ret != 0) + pr_err("%s: error %d\n", __func__, ret); + return ret; +} + +static int ssp_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + + if (clk->bypass_reg) { + if (clk->parent == parent) + return 0; + if (parent == &ref_io_clk[0] || parent == &ref_io_clk[1]) + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + CLR_REGISTER); + else + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + SET_REGISTER); + clk->parent = parent; + ret = 0; + } + + return ret; +} + +static struct clk ssp_clk[] = { + { + .parent = &ref_io_clk[0], + .get_rate = ssp_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP0, + .enable_bits = BM_CLKCTRL_SSP0_CLKGATE, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP0, + .busy_bits = 29, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP0, + .scale_bits = 0, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 3, + .set_rate = ssp_set_rate, + .set_parent = ssp_set_parent, + }, + { + .parent = &ref_io_clk[0], + .get_rate = ssp_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP1, + .enable_bits = BM_CLKCTRL_SSP1_CLKGATE, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP1, + .busy_bits = 29, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP1, + .scale_bits = 0, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 4, + .set_rate = ssp_set_rate, + .set_parent = ssp_set_parent, + }, + { + .parent = &ref_io_clk[1], + .get_rate = ssp_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP2, + .enable_bits = BM_CLKCTRL_SSP2_CLKGATE, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP2, + .busy_bits = 29, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP2, + .scale_bits = 0, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 5, + .set_rate = ssp_set_rate, + .set_parent = ssp_set_parent, + }, + { + .parent = &ref_io_clk[1], + .get_rate = ssp_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP3, + .enable_bits = BM_CLKCTRL_SSP3_CLKGATE, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP3, + .busy_bits = 29, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP3, + .scale_bits = 0, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 6, + .set_rate = ssp_set_rate, + .set_parent = ssp_set_parent, + }, +}; + +static unsigned long ssp_get_rate(struct clk *clk) +{ + unsigned int reg, div; + switch (clk - ssp_clk) { + case 0: + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP0); + div = reg & BM_CLKCTRL_SSP0_DIV; + reg &= BM_CLKCTRL_SSP0_DIV_FRAC_EN; + break; + case 1: + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP1); + div = reg & BM_CLKCTRL_SSP1_DIV; + reg &= BM_CLKCTRL_SSP1_DIV_FRAC_EN; + break; + case 2: + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP2); + div = reg & BM_CLKCTRL_SSP2_DIV; + reg &= BM_CLKCTRL_SSP2_DIV_FRAC_EN; + break; + case 3: + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SSP3); + div = reg & BM_CLKCTRL_SSP3_DIV; + reg &= BM_CLKCTRL_SSP3_DIV_FRAC_EN; + break; + default: + return 0; + } + if (!reg) + return clk->parent->get_rate(clk->parent) / div; + return (clk->parent->get_rate(clk->parent) / 0x200) / div; +} + +static unsigned long lcdif_get_rate(struct clk *clk) +{ + long rate = clk->parent->get_rate(clk->parent); + long div; + + div = __raw_readl(clk->scale_reg); + if (!(div & BM_CLKCTRL_DIS_LCDIF_DIV_FRAC_EN)) { + div = (div >> clk->scale_bits) & BM_CLKCTRL_DIS_LCDIF_DIV; + return rate / (div ? div : 1); + } + + div = (div >> clk->scale_bits) & BM_CLKCTRL_DIS_LCDIF_DIV; + rate /= (BM_CLKCTRL_DIS_LCDIF_DIV >> clk->scale_bits) + 1; + rate *= div; + return rate; +} + +static int lcdif_set_rate(struct clk *clk, unsigned long rate) +{ + int reg_val; + + reg_val = __raw_readl(clk->scale_reg); + reg_val &= ~(BM_CLKCTRL_DIS_LCDIF_DIV | BM_CLKCTRL_DIS_LCDIF_CLKGATE); + reg_val |= (1 << BP_CLKCTRL_DIS_LCDIF_DIV) & BM_CLKCTRL_DIS_LCDIF_DIV; + __raw_writel(reg_val, clk->scale_reg); + if (clk->busy_reg) { + int i; + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + if (!i) + return -ETIMEDOUT; + } + + reg_val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ); + reg_val |= BM_CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF; + __raw_writel(reg_val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ); + + return 0; +} + +static int lcdif_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + if (clk->bypass_reg) { + if (parent == clk->parent) + return 0; + if (parent == &ref_xtal_clk) { + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + SET_REGISTER); + ret = 0; + } + if (ret && (parent == &ref_pix_clk)) { + __raw_writel(0 << clk->bypass_bits, + clk->bypass_reg + CLR_REGISTER); + ret = 0; + } + if (!ret) + clk->parent = parent; + } + return ret; +} + +static struct clk dis_lcdif_clk = { + .parent = &pll_clk[0], + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_DIS_LCDIF, + .scale_bits = 0, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_DIS_LCDIF, + .busy_bits = 29, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_DIS_LCDIF, + .enable_bits = 31, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 14, + .get_rate = lcdif_get_rate, + .set_rate = lcdif_set_rate, + .set_parent = lcdif_set_parent, + .flags = CPU_FREQ_TRIG_UPDATE, +}; + +static unsigned long hsadc_get_rate(struct clk *clk) +{ + unsigned int reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_HSADC); + reg = (reg & BM_CLKCTRL_HSADC_FREQDIV) >> BP_CLKCTRL_HSADC_FREQDIV; + return clk->parent->get_rate(clk->parent) / ((1 << reg) * 9); +} + +static int hsadc_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned int reg = clk->parent->get_rate(clk->parent); + if ((reg / rate) % 9) + return -EINVAL; + reg = reg / 9; + switch (reg) { + case 1: + reg = BM_CLKCTRL_HSADC_RESETB; + break; + case 2: + reg = 1 | BM_CLKCTRL_HSADC_RESETB; + break; + case 4: + reg = 2 | BM_CLKCTRL_HSADC_RESETB; + break; + case 8: + reg = 3 | BM_CLKCTRL_HSADC_RESETB; + break; + default: + return -EINVAL; + } + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_HSADC); + return 0; +} + +static unsigned long hsadc_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned int div; + unsigned int reg = clk->parent->get_rate(clk->parent); + div = ((reg / rate) + 8) / 9; + if (div <= 1) + return reg; + if (div > 4) + return reg >> 3; + if (div > 2) + return reg >> 2; + return reg >> 1; +} + +static struct clk hsadc_clk = { + .parent = &ref_hsadc_clk, + .get_rate = hsadc_get_rate, + .set_rate = hsadc_set_rate, + .round_rate = hsadc_round_rate, +}; + +static unsigned long gpmi_get_rate(struct clk *clk) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI) & + BM_CLKCTRL_GPMI_DIV; + return clk->parent->get_rate(clk->parent) / reg; +} + +static int gpmi_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + if (clk->bypass_reg) { + if (parent == clk->parent) + return 0; + if (parent == &ref_xtal_clk) { + __raw_writel(1 << clk->bypass_bits, + clk->bypass_reg + SET_REGISTER); + ret = 0; + } + if (ret && (parent == &ref_gpmi_clk)) { + __raw_writel(0 << clk->bypass_bits, + clk->bypass_reg + CLR_REGISTER); + ret = 0; + } + if (!ret) + clk->parent = parent; + } + return ret; +} + +static unsigned long gpmi_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned int root_rate, frac_rate; + unsigned int div; + root_rate = clk->parent->get_rate(clk->parent); + frac_rate = root_rate % rate; + div = root_rate / rate; + if ((div == 0) || (div >= 0x400)) + return root_rate; + if (frac_rate == 0) + return rate; + return rate; +} + +static int gpmi_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long root_rate; + unsigned int reg, div; + root_rate = clk->parent->get_rate(clk->parent); + div = root_rate / rate; + if ((div == 0) || (div >= 0x400)) + return -EINVAL; + + if (root_rate % rate) { + div = mx28_get_frac_div(root_rate / 1000, rate / 1000, 0x3FF); + if (((root_rate / 0x400) * div) > rate) + return -EINVAL; + } + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI); + reg &= ~(BM_CLKCTRL_GPMI_DIV | BM_CLKCTRL_GPMI_DIV_FRAC_EN); + if (root_rate % rate) + reg |= BM_CLKCTRL_GPMI_DIV_FRAC_EN; + reg |= BF_CLKCTRL_GPMI_DIV(div); + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI); + + do { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI); + } while (reg & BM_CLKCTRL_GPMI_BUSY); + return 0; +} + +static struct clk gpmi_clk = { + .parent = &ref_gpmi_clk, + .set_parent = gpmi_set_parent, + .get_rate = gpmi_get_rate, + .set_rate = gpmi_set_rate, + .round_rate = gpmi_round_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI, + .enable_bits = BM_CLKCTRL_GPMI_CLKGATE, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 2, +}; + +static unsigned long saif_get_rate(struct clk *clk); +static unsigned long saif_set_rate(struct clk *clk, unsigned int rate); +static unsigned long saif_set_parent(struct clk *clk, struct clk *parent); + +static struct clk saif_clk[] = { + { + .parent = &pll_clk[0], + .get_rate = saif_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0, + .enable_bits = BM_CLKCTRL_SAIF0_CLKGATE, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0, + .scale_bits = 0, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0, + .busy_bits = 29, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 0, + .set_rate = saif_set_rate, + .set_parent = saif_set_parent, + }, + { + .parent = &pll_clk[0], + .get_rate = saif_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1, + .enable_bits = BM_CLKCTRL_SAIF1_CLKGATE, + .scale_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1, + .scale_bits = 0, + .busy_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1, + .busy_bits = 29, + .bypass_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ, + .bypass_bits = 1, + .set_rate = saif_set_rate, + .set_parent = saif_set_parent, + }, +}; + +static unsigned long saif_get_rate(struct clk *clk) +{ + unsigned long reg, div; + if (clk == saif_clk) { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0); + div = reg & BM_CLKCTRL_SAIF0_DIV; + reg &= BM_CLKCTRL_SAIF0_DIV_FRAC_EN; + } else { + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1); + div = reg & BM_CLKCTRL_SAIF1_DIV; + reg &= BM_CLKCTRL_SAIF1_DIV_FRAC_EN; + } + if (!reg) + return clk->parent->get_rate(clk->parent) / div; + return (clk->parent->get_rate(clk->parent) / 0x10000) * div; +} + +static unsigned long saif_set_rate(struct clk *clk, unsigned int rate) +{ + u16 div = 0; + u32 clkctrl_saif; + u64 rates; + struct clk *parent = clk->parent; + + pr_debug("%s: rate %d, parent rate %d\n", __func__, rate, + clk_get_rate(parent)); + + if (rate > clk_get_rate(parent)) + return -EINVAL; + /*saif clock always use frac div*/ + rates = 65536 * (u64)rate; + rates = rates + (u64)(clk_get_rate(parent) / 2); + do_div(rates, clk_get_rate(parent)); + div = rates; + + pr_debug("%s: div calculated is %d\n", __func__, div); + if (!div) + return -EINVAL; + + clkctrl_saif = __raw_readl(clk->scale_reg); + clkctrl_saif &= ~BM_CLKCTRL_SAIF0_DIV_FRAC_EN; + clkctrl_saif &= ~BM_CLKCTRL_SAIF0_DIV; + clkctrl_saif |= div; + clkctrl_saif |= BM_CLKCTRL_SAIF0_DIV_FRAC_EN; + clkctrl_saif &= ~BM_CLKCTRL_SAIF0_CLKGATE; + __raw_writel(clkctrl_saif, clk->scale_reg); + if (clk->busy_reg) { + int i; + for (i = 10000; i; i--) + if (!clk_is_busy(clk)) + break; + if (!i) { + pr_err("couldn't set up SAIF clk divisor\n"); + return -ETIMEDOUT; + } + } + return 0; +} + +static unsigned long saif_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = -EINVAL; + int shift = 4; + /*bypass*/ + if (parent == &pll_clk[0]) + shift = 8; + if (clk->bypass_reg) { + __raw_writel(1 << clk->bypass_bits, clk->bypass_reg + shift); + ret = 0; + } + return ret; +} + +static int saif_mclk_enable(struct clk *clk) +{ + /*Check if enabled already*/ + if (__raw_readl(clk->busy_reg) & clk->busy_bits) + return 0; + /*Enable saif to enable mclk*/ + __raw_writel(0x1, clk->enable_reg); + mdelay(1); + __raw_writel(0x1, clk->enable_reg); + mdelay(1); + return 0; +} + +static int saif_mclk_disable(struct clk *clk) +{ + /*Check if disabled already*/ + if (!(__raw_readl(clk->busy_reg) & clk->busy_bits)) + return 0; + /*Disable saif to disable mclk*/ + __raw_writel(0x0, clk->enable_reg); + mdelay(1); + __raw_writel(0x0, clk->enable_reg); + mdelay(1); + return 0; +} + +static struct clk saif_mclk[] = { + { + .parent = &saif_clk[0], + .enable = saif_mclk_enable, + .disable = saif_mclk_disable, + .enable_reg = SAIF0_CTRL, + .enable_bits = BM_SAIF_CTRL_RUN, + .busy_reg = SAIF0_STAT, + .busy_bits = BM_SAIF_STAT_BUSY, + }, + { + .parent = &saif_clk[1], + .enable = saif_mclk_enable, + .disable = saif_mclk_disable, + .enable_reg = SAIF1_CTRL, + .enable_bits = BM_SAIF_CTRL_RUN, + .busy_reg = SAIF1_STAT, + .busy_bits = BM_SAIF_STAT_BUSY, + }, +}; + +static unsigned long pcmspdif_get_rate(struct clk *clk) +{ + return clk->parent->get_rate(clk->parent) / 4; +} + +static struct clk pcmspdif_clk = { + .parent = &pll_clk[0], + .get_rate = pcmspdif_get_rate, + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_SPDIF, + .enable_bits = BM_CLKCTRL_SPDIF_CLKGATE, +}; + +/* usb_clk for usb0 */ +static struct clk usb_clk0 = { + .parent = &pll_clk[0], + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = DIGCTRL_BASE_ADDR + HW_DIGCTL_CTRL, + .enable_bits = BM_DIGCTL_CTRL_USB0_CLKGATE, + .flags = CPU_FREQ_TRIG_UPDATE, +}; + +/* usb_clk for usb1 */ +static struct clk usb_clk1 = { + .parent = &pll_clk[1], + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = DIGCTRL_BASE_ADDR + HW_DIGCTL_CTRL, + .enable_bits = BM_DIGCTL_CTRL_USB1_CLKGATE, + .flags = CPU_FREQ_TRIG_UPDATE, +}; + +/* usb phy clock for usb0 */ +static struct clk usb_phy_clk0 = { + .parent = &pll_clk[0], + .enable = mx28_raw_disable, /* EN_USB_CLKS = 1 means ON */ + .disable = mx28_raw_enable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL0CTRL0_SET, + .enable_bits = BM_CLKCTRL_PLL0CTRL0_EN_USB_CLKS, + .flags = CPU_FREQ_TRIG_UPDATE, +}; + +/* usb phy clock for usb1 */ +static struct clk usb_phy_clk1 = { + .parent = &pll_clk[1], + .enable = mx28_raw_disable, + .disable = mx28_raw_enable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_PLL1CTRL0_SET, + .enable_bits = BM_CLKCTRL_PLL0CTRL0_EN_USB_CLKS, + .flags = CPU_FREQ_TRIG_UPDATE, +}; + +static struct clk enet_out_clk = { + .parent = &pll_clk[2], + .enable = mx28_raw_enable, + .disable = mx28_raw_disable, + .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_ENET, + .enable_bits = BM_CLKCTRL_ENET_DISABLE, +}; + +static struct clk_lookup onchip_clocks[] = { + { + .con_id = "xtal.0", + .clk = &xtal_clk[0], + }, + { + .con_id = "xtal.1", + .clk = &xtal_clk[1], + }, + { + .con_id = "xtal.2", + .clk = &xtal_clk[2], + }, + { + .con_id = "pll.0", + .clk = &pll_clk[0], + }, + { + .con_id = "pll.1", + .clk = &pll_clk[1], + }, + { + .con_id = "pll.2", + .clk = &pll_clk[2], + }, + { + .con_id = "ref_xtal", + .clk = &ref_xtal_clk, + }, + { + .con_id = "ref_cpu", + .clk = &ref_cpu_clk, + }, + { + .con_id = "ref_emi", + .clk = &ref_emi_clk, + }, + { + .con_id = "ref_io.0", + .clk = &ref_io_clk[0], + }, + { + .con_id = "ref_io.1", + .clk = &ref_io_clk[1], + }, + { + .con_id = "ref_pix", + .clk = &ref_pix_clk, + }, + { + .con_id = "ref_hsadc", + .clk = &ref_hsadc_clk, + }, + { + .con_id = "ref_gpmi", + .clk = &ref_gpmi_clk, + }, + { + .con_id = "ana", + .clk = &ana_clk, + }, + { + .con_id = "rtc", + .clk = &rtc_clk, + }, + { + .con_id = "cpu", + .clk = &cpu_clk, + }, + { + .con_id = "h", + .clk = &h_clk, + }, + { + .con_id = "x", + .clk = &x_clk, + }, + { + .con_id = "ocrom", + .clk = &ocrom_clk, + }, + { + .con_id = "clk_32k", + .clk = &clk_32k, + }, + { + .con_id = "uart", + .clk = &uart_clk, + }, + { + .con_id = "pwm", + .clk = &pwm_clk, + }, + { + .con_id = "lradc", + .clk = &lradc_clk, + }, + { + .con_id = "ssp.0", + .clk = &ssp_clk[0], + }, + { + .con_id = "ssp.1", + .clk = &ssp_clk[1], + }, + { + .con_id = "ssp.2", + .clk = &ssp_clk[2], + }, + { + .con_id = "ssp.3", + .clk = &ssp_clk[3], + }, + { + .con_id = "gpmi", + .clk = &gpmi_clk, + }, + { + .con_id = "spdif", + .clk = &pcmspdif_clk, + }, + { + .con_id = "saif.0", + .clk = &saif_clk[0], + }, + { + .con_id = "saif.1", + .clk = &saif_clk[1], + }, + { + .con_id = "emi", + .clk = &emi_clk, + }, + { + .con_id = "dis_lcdif", + .clk = &dis_lcdif_clk, + }, + { + .con_id = "hsadc", + .clk = &hsadc_clk, + }, + { + .con_id = "can_clk", + .dev_id = "FlexCAN.0", + .clk = &flexcan_clk[0], + }, + { + .con_id = "can_clk", + .dev_id = "FlexCAN.1", + .clk = &flexcan_clk[1], + }, + { + .con_id = "usb_clk0", + .clk = &usb_clk0, + }, + { + .con_id = "usb_clk1", + .clk = &usb_clk1, + }, + { + .con_id = "usb_phy_clk0", + .clk = &usb_phy_clk0, + }, + { + .con_id = "usb_phy_clk1", + .clk = &usb_phy_clk1, + }, + { + .con_id = "fec_clk", + .clk = &enet_out_clk, + }, + { + .con_id = "saif_mclk.0", + .clk = &saif_mclk[0], + }, + { + .con_id = "saif_mclk.1", + .clk = &saif_mclk[1], + } +}; + +static void mx28_clock_scan(void) +{ + unsigned long reg; + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ); + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_CPU) + cpu_clk.parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF) + dis_lcdif_clk.parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_EMI) + emi_clk.parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SSP3) + ssp_clk[3].parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SSP2) + ssp_clk[2].parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SSP1) + ssp_clk[1].parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SSP0) + ssp_clk[0].parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_GPMI) + gpmi_clk.parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SAIF1) + saif_clk[1].parent = &ref_xtal_clk; + if (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SAIF0) + saif_clk[0].parent = &ref_xtal_clk; +}; + +void __init mx28_set_input_clk(unsigned long xtal0, + unsigned long xtal1, + unsigned long xtal2, unsigned long enet) +{ + xtal_clk_rate[0] = xtal0; + xtal_clk_rate[1] = xtal1; + xtal_clk_rate[2] = xtal2; + enet_mii_phy_rate = enet; +} + +void mx28_enet_clk_hook(void) +{ + unsigned long reg; + + reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_ENET); + + reg &= ~BM_CLKCTRL_ENET_SLEEP; + reg |= BM_CLKCTRL_ENET_CLK_OUT_EN; + /* select clock for 1588 module */ + reg &= ~(BM_CLKCTRL_ENET_DIV_TIME | BM_CLKCTRL_ENET_TIME_SEL); + reg |= BF_CLKCTRL_ENET_TIME_SEL(BV_CLKCTRL_ENET_TIME_SEL__PLL) + | BF_CLKCTRL_ENET_DIV_TIME(12); + + __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_ENET); +} + +void __init mx28_clock_init(void) +{ + int i; + mx28_clock_scan(); + mx28_enet_clk_hook(); + for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) + clk_register(&onchip_clocks[i]); + + clk_enable(&cpu_clk); + clk_enable(&emi_clk); + + clk_en_public_h_asm_ctrl(mx28_enable_h_autoslow, + mx28_set_hbus_autoslow_flags); +} |