diff options
Diffstat (limited to 'arch/arm/mach-u300/clock.c')
-rw-r--r-- | arch/arm/mach-u300/clock.c | 1487 |
1 files changed, 1487 insertions, 0 deletions
diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c new file mode 100644 index 00000000000..5cd04d6751b --- /dev/null +++ b/arch/arm/mach-u300/clock.c @@ -0,0 +1,1487 @@ +/* + * + * arch/arm/mach-u300/clock.c + * + * + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Define clocks in the app platform. + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/io.h> + +#include <asm/clkdev.h> +#include <mach/hardware.h> +#include <mach/syscon.h> + +#include "clock.h" + +/* + * TODO: + * - move all handling of the CCR register into this file and create + * a spinlock for the CCR register + * - switch to the clkdevice lookup mechanism that maps clocks to + * device ID:s instead when it becomes available in kernel 2.6.29. + * - implement rate get/set for all clocks that need it. + */ + +/* + * Syscon clock I/O registers lock so clock requests don't collide + * NOTE: this is a local lock only used to lock access to clock and + * reset registers in syscon. + */ +static DEFINE_SPINLOCK(syscon_clkreg_lock); +static DEFINE_SPINLOCK(syscon_resetreg_lock); + +/* + * The clocking hierarchy currently looks like this. + * NOTE: the idea is NOT to show how the clocks are routed on the chip! + * The ideas is to show dependencies, so a clock higher up in the + * hierarchy has to be on in order for another clock to be on. Now, + * both CPU and DMA can actually be on top of the hierarchy, and that + * is not modeled currently. Instead we have the backbone AMBA bus on + * top. This bus cannot be programmed in any way but conceptually it + * needs to be active for the bridges and devices to transport data. + * + * Please be aware that a few clocks are hw controlled, which mean that + * the hw itself can turn on/off or change the rate of the clock when + * needed! + * + * AMBA bus + * | + * +- CPU + * +- NANDIF NAND Flash interface + * +- SEMI Shared Memory interface + * +- ISP Image Signal Processor (U335 only) + * +- CDS (U335 only) + * +- DMA Direct Memory Access Controller + * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) + * +- APEX + * +- VIDEO_ENC AVE2/3 Video Encoder + * +- XGAM Graphics Accelerator Controller + * +- AHB + * | + * +- ahb:0 AHB Bridge + * | | + * | +- ahb:1 INTCON Interrupt controller + * | +- ahb:3 MSPRO Memory Stick Pro controller + * | +- ahb:4 EMIF External Memory interface + * | + * +- fast:0 FAST bridge + * | | + * | +- fast:1 MMCSD MMC/SD card reader controller + * | +- fast:2 I2S0 PCM I2S channel 0 controller + * | +- fast:3 I2S1 PCM I2S channel 1 controller + * | +- fast:4 I2C0 I2C channel 0 controller + * | +- fast:5 I2C1 I2C channel 1 controller + * | +- fast:6 SPI SPI controller + * | +- fast:7 UART1 Secondary UART (U335 only) + * | + * +- slow:0 SLOW bridge + * | + * +- slow:1 SYSCON (not possible to control) + * +- slow:2 WDOG Watchdog + * +- slow:3 UART0 primary UART + * +- slow:4 TIMER_APP Application timer - used in Linux + * +- slow:5 KEYPAD controller + * +- slow:6 GPIO controller + * +- slow:7 RTC controller + * +- slow:8 BT Bus Tracer (not used currently) + * +- slow:9 EH Event Handler (not used currently) + * +- slow:a TIMER_ACC Access style timer (not used currently) + * +- slow:b PPM (U335 only, what is that?) + */ + +/* + * Reset control functions. We remember if a block has been + * taken out of reset and don't remove the reset assertion again + * and vice versa. Currently we only remove resets so the + * enablement function is defined out. + */ +static void syscon_block_reset_enable(struct clk *clk) +{ + u16 val; + unsigned long iflags; + + /* Not all blocks support resetting */ + if (!clk->res_reg || !clk->res_mask) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(clk->res_reg); + val |= clk->res_mask; + writew(val, clk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + clk->reset = true; +} + +static void syscon_block_reset_disable(struct clk *clk) +{ + u16 val; + unsigned long iflags; + + /* Not all blocks support resetting */ + if (!clk->res_reg || !clk->res_mask) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(clk->res_reg); + val &= ~clk->res_mask; + writew(val, clk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + clk->reset = false; +} + +int __clk_get(struct clk *clk) +{ + u16 val; + + /* The MMC and MSPRO clocks need some special set-up */ + if (!strcmp(clk->name, "MCLK")) { + /* Set default MMC clock divisor to 18.9 MHz */ + writew(0x0054U, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Disable MSPRO frequency */ + val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); + } + if (!strcmp(clk->name, "MSPRO")) { + val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Enable MSPRO frequency */ + val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); + } + return 1; +} +EXPORT_SYMBOL(__clk_get); + +void __clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(__clk_put); + +static void syscon_clk_disable(struct clk *clk) +{ + unsigned long iflags; + + /* Don't touch the hardware controlled clocks */ + if (clk->hw_ctrld) + return; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCDR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +static void syscon_clk_enable(struct clk *clk) +{ + unsigned long iflags; + + /* Don't touch the hardware controlled clocks */ + if (clk->hw_ctrld) + return; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCER); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +static u16 syscon_clk_get_rate(void) +{ + u16 val; + unsigned long iflags; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); + return val; +} + +#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER +static void enable_i2s0_vcxo(void) +{ + u16 val; + unsigned long iflags; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + /* Set I2S0 to use the VCXO 26 MHz clock */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val |= U300_SYSCON_CCR_TURN_VCXO_ON; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val |= U300_SYSCON_CCR_I2S0_USE_VCXO; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); + val |= U300_SYSCON_CEFR_I2S0_CLK_EN; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +static void enable_i2s1_vcxo(void) +{ + u16 val; + unsigned long iflags; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + /* Set I2S1 to use the VCXO 26 MHz clock */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val |= U300_SYSCON_CCR_TURN_VCXO_ON; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val |= U300_SYSCON_CCR_I2S1_USE_VCXO; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); + val |= U300_SYSCON_CEFR_I2S1_CLK_EN; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +static void disable_i2s0_vcxo(void) +{ + u16 val; + unsigned long iflags; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + /* Disable I2S0 use of the VCXO 26 MHz clock */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_I2S0_USE_VCXO; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + /* Deactivate VCXO if noone else is using VCXO */ + if (!(val & U300_SYSCON_CCR_I2S1_USE_VCXO)) + val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); + val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +static void disable_i2s1_vcxo(void) +{ + u16 val; + unsigned long iflags; + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + /* Disable I2S1 use of the VCXO 26 MHz clock */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_I2S1_USE_VCXO; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + /* Deactivate VCXO if noone else is using VCXO */ + if (!(val & U300_SYSCON_CCR_I2S0_USE_VCXO)) + val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); + val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} +#endif /* CONFIG_MACH_U300_USE_I2S_AS_MASTER */ + + +static void syscon_clk_rate_set_mclk(unsigned long rate) +{ + u16 val; + u32 reg; + unsigned long iflags; + + switch (rate) { + case 18900000: + val = 0x0054; + break; + case 20800000: + val = 0x0044; + break; + case 23100000: + val = 0x0043; + break; + case 26000000: + val = 0x0033; + break; + case 29700000: + val = 0x0032; + break; + case 34700000: + val = 0x0022; + break; + case 41600000: + val = 0x0021; + break; + case 52000000: + val = 0x0011; + break; + case 104000000: + val = 0x0000; + break; + default: + printk(KERN_ERR "Trying to set MCLK to unknown speed! %ld\n", + rate); + return; + } + + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + reg = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & + ~U300_SYSCON_MMF0R_MASK; + writew(reg | val, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} + +void syscon_clk_rate_set_cpuclk(unsigned long rate) +{ + u16 val; + unsigned long iflags; + + switch (rate) { + case 13000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; + break; + case 52000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; + break; + case 104000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; + break; + case 208000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; + break; + default: + return; + } + spin_lock_irqsave(&syscon_clkreg_lock, iflags); + val |= readw(U300_SYSCON_VBASE + U300_SYSCON_CCR) & + ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); +} +EXPORT_SYMBOL(syscon_clk_rate_set_cpuclk); + +void clk_disable(struct clk *clk) +{ + unsigned long iflags; + + spin_lock_irqsave(&clk->lock, iflags); + if (clk->usecount > 0 && !(--clk->usecount)) { + /* some blocks lack clocking registers and cannot be disabled */ + if (clk->disable) + clk->disable(clk); + if (likely((u32)clk->parent)) + clk_disable(clk->parent); + } +#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER + if (unlikely(!strcmp(clk->name, "I2S0"))) + disable_i2s0_vcxo(); + if (unlikely(!strcmp(clk->name, "I2S1"))) + disable_i2s1_vcxo(); +#endif + spin_unlock_irqrestore(&clk->lock, iflags); +} +EXPORT_SYMBOL(clk_disable); + +int clk_enable(struct clk *clk) +{ + int ret = 0; + unsigned long iflags; + + spin_lock_irqsave(&clk->lock, iflags); + if (clk->usecount++ == 0) { + if (likely((u32)clk->parent)) + ret = clk_enable(clk->parent); + + if (unlikely(ret != 0)) + clk->usecount--; + else { + /* remove reset line (we never enable reset again) */ + syscon_block_reset_disable(clk); + /* clocks without enable function are always on */ + if (clk->enable) + clk->enable(clk); +#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER + if (unlikely(!strcmp(clk->name, "I2S0"))) + enable_i2s0_vcxo(); + if (unlikely(!strcmp(clk->name, "I2S1"))) + enable_i2s1_vcxo(); +#endif + } + } + spin_unlock_irqrestore(&clk->lock, iflags); + return ret; + +} +EXPORT_SYMBOL(clk_enable); + +/* Returns the clock rate in Hz */ +static unsigned long clk_get_rate_cpuclk(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + return 104000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + return 208000000; + default: + break; + } + return clk->rate; +} + +static unsigned long clk_get_rate_ahb_clk(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 6500000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 26000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + return 52000000; + default: + break; + } + return clk->rate; + +} + +static unsigned long clk_get_rate_emif_clk(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + return 104000000; + default: + break; + } + return clk->rate; + +} + +static unsigned long clk_get_rate_xgamclk(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 6500000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 26000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + return 52000000; + default: + break; + } + + return clk->rate; +} + +static unsigned long clk_get_rate_mclk(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + /* + * Here, the 208 MHz PLL gets shut down and the always + * on 13 MHz PLL used for RTC etc kicks into use + * instead. + */ + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + { + /* + * This clock is under program control. The register is + * divided in two nybbles, bit 7-4 gives cycles-1 to count + * high, bit 3-0 gives cycles-1 to count low. Distribute + * these with no more than 1 cycle difference between + * low and high and add low and high to get the actual + * divisor. The base PLL is 208 MHz. Writing 0x00 will + * divide by 1 and 1 so the highest frequency possible + * is 104 MHz. + * + * e.g. 0x54 => + * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz + */ + u16 val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & + U300_SYSCON_MMF0R_MASK; + switch (val) { + case 0x0054: + return 18900000; + case 0x0044: + return 20800000; + case 0x0043: + return 23100000; + case 0x0033: + return 26000000; + case 0x0032: + return 29700000; + case 0x0022: + return 34700000; + case 0x0021: + return 41600000; + case 0x0011: + return 52000000; + case 0x0000: + return 104000000; + default: + break; + } + } + default: + break; + } + + return clk->rate; +} + +static unsigned long clk_get_rate_i2s_i2c_spi(struct clk *clk) +{ + u16 val; + + val = syscon_clk_get_rate(); + + switch (val) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + return 26000000; + default: + break; + } + + return clk->rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + if (clk->get_rate) + return clk->get_rate(clk); + else + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + +static unsigned long clk_round_rate_mclk(struct clk *clk, unsigned long rate) +{ + if (rate >= 18900000) + return 18900000; + if (rate >= 20800000) + return 20800000; + if (rate >= 23100000) + return 23100000; + if (rate >= 26000000) + return 26000000; + if (rate >= 29700000) + return 29700000; + if (rate >= 34700000) + return 34700000; + if (rate >= 41600000) + return 41600000; + if (rate >= 52000000) + return 52000000; + return -EINVAL; +} + +static unsigned long clk_round_rate_cpuclk(struct clk *clk, unsigned long rate) +{ + if (rate >= 13000000) + return 13000000; + if (rate >= 52000000) + return 52000000; + if (rate >= 104000000) + return 104000000; + if (rate >= 208000000) + return 208000000; + return -EINVAL; +} + +/* + * This adjusts a requested rate to the closest exact rate + * a certain clock can provide. For a fixed clock it's + * mostly clk->rate. + */ +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + /* TODO: get apropriate switches for EMIFCLK, AHBCLK and MCLK */ + /* Else default to fixed value */ + + if (clk->round_rate) { + return (long) clk->round_rate(clk, rate); + } else { + printk(KERN_ERR "clock: Failed to round rate of %s\n", + clk->name); + } + return (long) clk->rate; +} +EXPORT_SYMBOL(clk_round_rate); + +static int clk_set_rate_mclk(struct clk *clk, unsigned long rate) +{ + syscon_clk_rate_set_mclk(clk_round_rate(clk, rate)); + return 0; +} + +static int clk_set_rate_cpuclk(struct clk *clk, unsigned long rate) +{ + syscon_clk_rate_set_cpuclk(clk_round_rate(clk, rate)); + return 0; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + /* TODO: set for EMIFCLK and AHBCLK */ + /* Else assume the clock is fixed and fail */ + if (clk->set_rate) { + return clk->set_rate(clk, rate); + } else { + printk(KERN_ERR "clock: Failed to set %s to %ld hz\n", + clk->name, rate); + return -EINVAL; + } +} +EXPORT_SYMBOL(clk_set_rate); + +/* + * Clock definitions. The clock parents are set to respective + * bridge and the clock framework makes sure that the clocks have + * parents activated and are brought out of reset when in use. + * + * Clocks that have hw_ctrld = true are hw controlled, and the hw + * can by itself turn these clocks on and off. + * So in other words, we don't really have to care about them. + */ + +static struct clk amba_clk = { + .name = "AMBA", + .rate = 52000000, /* this varies! */ + .hw_ctrld = true, + .reset = false, +}; + +/* + * These blocks are connected directly to the AMBA bus + * with no bridge. + */ + +static struct clk cpu_clk = { + .name = "CPU", + .parent = &amba_clk, + .rate = 208000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_CPU_RESET_EN, + .set_rate = clk_set_rate_cpuclk, + .get_rate = clk_get_rate_cpuclk, + .round_rate = clk_round_rate_cpuclk, +}; + +static struct clk nandif_clk = { + .name = "NANDIF", + .parent = &amba_clk, + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_NANDIF_RESET_EN, + .clk_val = U300_SYSCON_SBCER_NANDIF_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk semi_clk = { + .name = "SEMI", + .parent = &amba_clk, + .rate = 0, /* FIXME */ + /* It is not possible to reset SEMI */ + .hw_ctrld = false, + .reset = false, + .clk_val = U300_SYSCON_SBCER_SEMI_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +#ifdef CONFIG_MACH_U300_BS335 +static struct clk isp_clk = { + .name = "ISP", + .parent = &amba_clk, + .rate = 0, /* FIXME */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_ISP_RESET_EN, + .clk_val = U300_SYSCON_SBCER_ISP_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk cds_clk = { + .name = "CDS", + .parent = &amba_clk, + .rate = 0, /* FIXME */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_CDS_RESET_EN, + .clk_val = U300_SYSCON_SBCER_CDS_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; +#endif + +static struct clk dma_clk = { + .name = "DMA", + .parent = &amba_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_DMAC_RESET_EN, + .clk_val = U300_SYSCON_SBCER_DMAC_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk aaif_clk = { + .name = "AAIF", + .parent = &amba_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_AAIF_RESET_EN, + .clk_val = U300_SYSCON_SBCER_AAIF_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk apex_clk = { + .name = "APEX", + .parent = &amba_clk, + .rate = 0, /* FIXME */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_APEX_RESET_EN, + .clk_val = U300_SYSCON_SBCER_APEX_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk video_enc_clk = { + .name = "VIDEO_ENC", + .parent = &amba_clk, + .rate = 208000000, /* this varies! */ + .hw_ctrld = false, + .reset = false, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + /* This has XGAM in the name but refers to the video encoder */ + .res_mask = U300_SYSCON_RRR_XGAM_VC_SYNC_RESET_EN, + .clk_val = U300_SYSCON_SBCER_VIDEO_ENC_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk xgam_clk = { + .name = "XGAMCLK", + .parent = &amba_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_XGAM_RESET_EN, + .clk_val = U300_SYSCON_SBCER_XGAM_CLK_EN, + .get_rate = clk_get_rate_xgamclk, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +/* This clock is used to activate the video encoder */ +static struct clk ahb_clk = { + .name = "AHB", + .parent = &amba_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = false, /* This one is set to false due to HW bug */ + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_AHB_RESET_EN, + .clk_val = U300_SYSCON_SBCER_AHB_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_ahb_clk, +}; + + +/* + * Clocks on the AHB bridge + */ + +static struct clk ahb_subsys_clk = { + .name = "AHB_SUBSYS", + .parent = &amba_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = true, + .reset = false, + .clk_val = U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_ahb_clk, +}; + +static struct clk intcon_clk = { + .name = "INTCON", + .parent = &ahb_subsys_clk, + .rate = 52000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_INTCON_RESET_EN, + /* INTCON can be reset but not clock-gated */ +}; + +static struct clk mspro_clk = { + .name = "MSPRO", + .parent = &ahb_subsys_clk, + .rate = 0, /* FIXME */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_MSPRO_RESET_EN, + .clk_val = U300_SYSCON_SBCER_MSPRO_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk emif_clk = { + .name = "EMIF", + .parent = &ahb_subsys_clk, + .rate = 104000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, + .res_mask = U300_SYSCON_RRR_EMIF_RESET_EN, + .clk_val = U300_SYSCON_SBCER_EMIF_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_emif_clk, +}; + + +/* + * Clocks on the FAST bridge + */ +static struct clk fast_clk = { + .name = "FAST_BRIDGE", + .parent = &amba_clk, + .rate = 13000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_FAST_BRIDGE_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk mmcsd_clk = { + .name = "MCLK", + .parent = &fast_clk, + .rate = 18900000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_MMC_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_MMC_CLK_EN, + .get_rate = clk_get_rate_mclk, + .set_rate = clk_set_rate_mclk, + .round_rate = clk_round_rate_mclk, + .disable = syscon_clk_disable, + .enable = syscon_clk_enable, +}; + +static struct clk i2s0_clk = { + .name = "i2s0", + .parent = &fast_clk, + .rate = 26000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_PCM_I2S0_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_I2S0_CORE_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_i2s_i2c_spi, +}; + +static struct clk i2s1_clk = { + .name = "i2s1", + .parent = &fast_clk, + .rate = 26000000, /* this varies! */ + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_PCM_I2S1_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_I2S1_CORE_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_i2s_i2c_spi, +}; + +static struct clk i2c0_clk = { + .name = "I2C0", + .parent = &fast_clk, + .rate = 26000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_I2C0_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_I2C0_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_i2s_i2c_spi, +}; + +static struct clk i2c1_clk = { + .name = "I2C1", + .parent = &fast_clk, + .rate = 26000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_I2C1_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_I2C1_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_i2s_i2c_spi, +}; + +static struct clk spi_clk = { + .name = "SPI", + .parent = &fast_clk, + .rate = 26000000, /* this varies! */ + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_SPI_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_SPI_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .get_rate = clk_get_rate_i2s_i2c_spi, +}; + +#ifdef CONFIG_MACH_U300_BS335 +static struct clk uart1_clk = { + .name = "UART1", + .parent = &fast_clk, + .rate = 13000000, + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, + .res_mask = U300_SYSCON_RFR_UART1_RESET_ENABLE, + .clk_val = U300_SYSCON_SBCER_UART1_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; +#endif + + +/* + * Clocks on the SLOW bridge + */ +static struct clk slow_clk = { + .name = "SLOW_BRIDGE", + .parent = &amba_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_SLOW_BRIDGE_RESET_EN, + .clk_val = U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +/* TODO: implement SYSCON clock? */ + +static struct clk wdog_clk = { + .name = "WDOG", + .parent = &slow_clk, + .hw_ctrld = false, + .rate = 32768, + .reset = false, + /* This is always on, cannot be enabled/disabled or reset */ +}; + +/* This one is hardwired to PLL13 */ +static struct clk uart_clk = { + .name = "UARTCLK", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_UART_RESET_EN, + .clk_val = U300_SYSCON_SBCER_UART_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk keypad_clk = { + .name = "KEYPAD", + .parent = &slow_clk, + .rate = 32768, + .hw_ctrld = false, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_KEYPAD_RESET_EN, + .clk_val = U300_SYSCON_SBCER_KEYPAD_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk gpio_clk = { + .name = "GPIO", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_GPIO_RESET_EN, + .clk_val = U300_SYSCON_SBCER_GPIO_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk rtc_clk = { + .name = "RTC", + .parent = &slow_clk, + .rate = 32768, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_RTC_RESET_EN, + /* This clock is always on, cannot be enabled/disabled */ +}; + +static struct clk bustr_clk = { + .name = "BUSTR", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_BTR_RESET_EN, + .clk_val = U300_SYSCON_SBCER_BTR_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk evhist_clk = { + .name = "EVHIST", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_EH_RESET_EN, + .clk_val = U300_SYSCON_SBCER_EH_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk timer_clk = { + .name = "TIMER", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_ACC_TMR_RESET_EN, + .clk_val = U300_SYSCON_SBCER_ACC_TMR_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +static struct clk app_timer_clk = { + .name = "TIMER_APP", + .parent = &slow_clk, + .rate = 13000000, + .hw_ctrld = true, + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_APP_TMR_RESET_EN, + .clk_val = U300_SYSCON_SBCER_APP_TMR_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; + +#ifdef CONFIG_MACH_U300_BS335 +static struct clk ppm_clk = { + .name = "PPM", + .parent = &slow_clk, + .rate = 0, /* FIXME */ + .hw_ctrld = true, /* TODO: Look up if it is hw ctrld or not */ + .reset = true, + .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, + .res_mask = U300_SYSCON_RSR_PPM_RESET_EN, + .clk_val = U300_SYSCON_SBCER_PPM_CLK_EN, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, +}; +#endif + +#define DEF_LOOKUP(devid, clkref) \ + { \ + .dev_id = devid, \ + .clk = clkref, \ + } + +/* + * Here we only define clocks that are meaningful to + * look up through clockdevice. + */ +static struct clk_lookup lookups[] = { + /* Connected directly to the AMBA bus */ + DEF_LOOKUP("amba", &amba_clk), + DEF_LOOKUP("cpu", &cpu_clk), + DEF_LOOKUP("nandif", &nandif_clk), + DEF_LOOKUP("semi", &semi_clk), +#ifdef CONFIG_MACH_U300_BS335 + DEF_LOOKUP("isp", &isp_clk), + DEF_LOOKUP("cds", &cds_clk), +#endif + DEF_LOOKUP("dma", &dma_clk), + DEF_LOOKUP("aaif", &aaif_clk), + DEF_LOOKUP("apex", &apex_clk), + DEF_LOOKUP("video_enc", &video_enc_clk), + DEF_LOOKUP("xgam", &xgam_clk), + DEF_LOOKUP("ahb", &ahb_clk), + /* AHB bridge clocks */ + DEF_LOOKUP("ahb", &ahb_subsys_clk), + DEF_LOOKUP("intcon", &intcon_clk), + DEF_LOOKUP("mspro", &mspro_clk), + DEF_LOOKUP("pl172", &emif_clk), + /* FAST bridge clocks */ + DEF_LOOKUP("fast", &fast_clk), + DEF_LOOKUP("mmci", &mmcsd_clk), + /* + * The .0 and .1 identifiers on these comes from the platform device + * .id field and are assigned when the platform devices are registered. + */ + DEF_LOOKUP("i2s.0", &i2s0_clk), + DEF_LOOKUP("i2s.1", &i2s1_clk), + DEF_LOOKUP("stddci2c.0", &i2c0_clk), + DEF_LOOKUP("stddci2c.1", &i2c1_clk), + DEF_LOOKUP("pl022", &spi_clk), +#ifdef CONFIG_MACH_U300_BS335 + DEF_LOOKUP("uart1", &uart1_clk), +#endif + /* SLOW bridge clocks */ + DEF_LOOKUP("slow", &slow_clk), + DEF_LOOKUP("wdog", &wdog_clk), + DEF_LOOKUP("uart0", &uart_clk), + DEF_LOOKUP("apptimer", &app_timer_clk), + DEF_LOOKUP("keypad", &keypad_clk), + DEF_LOOKUP("u300-gpio", &gpio_clk), + DEF_LOOKUP("rtc0", &rtc_clk), + DEF_LOOKUP("bustr", &bustr_clk), + DEF_LOOKUP("evhist", &evhist_clk), + DEF_LOOKUP("timer", &timer_clk), +#ifdef CONFIG_MACH_U300_BS335 + DEF_LOOKUP("ppm", &ppm_clk), +#endif +}; + +static void __init clk_register(void) +{ + int i; + + /* Register the lookups */ + for (i = 0; i < ARRAY_SIZE(lookups); i++) + clkdev_add(&lookups[i]); +} + +/* + * These are the clocks for cells registered as primecell drivers + * on the AMBA bus. These must be on during AMBA device registration + * since the bus probe will attempt to read magic configuration + * registers for these devices. If they are deactivated these probes + * will fail. + * + * + * Please note that on emif, both RAM and NAND is connected in dual + * RAM phones. On single RAM phones, ram is on semi and NAND on emif. + * + */ +void u300_clock_primecells(void) +{ + clk_enable(&intcon_clk); + clk_enable(&uart_clk); +#ifdef CONFIG_MACH_U300_BS335 + clk_enable(&uart1_clk); +#endif + clk_enable(&spi_clk); + + clk_enable(&mmcsd_clk); + +} +EXPORT_SYMBOL(u300_clock_primecells); + +void u300_unclock_primecells(void) +{ + + clk_disable(&intcon_clk); + clk_disable(&uart_clk); +#ifdef CONFIG_MACH_U300_BS335 + clk_disable(&uart1_clk); +#endif + clk_disable(&spi_clk); + clk_disable(&mmcsd_clk); + +} +EXPORT_SYMBOL(u300_unclock_primecells); + +/* + * The interrupt controller is enabled before the clock API is registered. + */ +void u300_enable_intcon_clock(void) +{ + clk_enable(&intcon_clk); +} +EXPORT_SYMBOL(u300_enable_intcon_clock); + +/* + * The timer is enabled before the clock API is registered. + */ +void u300_enable_timer_clock(void) +{ + clk_enable(&app_timer_clk); +} +EXPORT_SYMBOL(u300_enable_timer_clock); + +#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) +/* + * The following makes it possible to view the status (especially + * reference count and reset status) for the clocks in the platform + * by looking into the special file <debugfs>/u300_clocks + */ + +/* A list of all clocks in the platform */ +static struct clk *clks[] = { + /* Top node clock for the AMBA bus */ + &amba_clk, + /* Connected directly to the AMBA bus */ + &cpu_clk, + &nandif_clk, + &semi_clk, +#ifdef CONFIG_MACH_U300_BS335 + &isp_clk, + &cds_clk, +#endif + &dma_clk, + &aaif_clk, + &apex_clk, + &video_enc_clk, + &xgam_clk, + &ahb_clk, + + /* AHB bridge clocks */ + &ahb_subsys_clk, + &intcon_clk, + &mspro_clk, + &emif_clk, + /* FAST bridge clocks */ + &fast_clk, + &mmcsd_clk, + &i2s0_clk, + &i2s1_clk, + &i2c0_clk, + &i2c1_clk, + &spi_clk, +#ifdef CONFIG_MACH_U300_BS335 + &uart1_clk, +#endif + /* SLOW bridge clocks */ + &slow_clk, + &wdog_clk, + &uart_clk, + &app_timer_clk, + &keypad_clk, + &gpio_clk, + &rtc_clk, + &bustr_clk, + &evhist_clk, + &timer_clk, +#ifdef CONFIG_MACH_U300_BS335 + &ppm_clk, +#endif +}; + +static int u300_clocks_show(struct seq_file *s, void *data) +{ + struct clk *clk; + int i; + + seq_printf(s, "CLOCK DEVICE RESET STATE\t" \ + "ACTIVE\tUSERS\tHW CTRL FREQ\n"); + seq_printf(s, "---------------------------------------------" \ + "-----------------------------------------\n"); + for (i = 0; i < ARRAY_SIZE(clks); i++) { + clk = clks[i]; + if (clk != ERR_PTR(-ENOENT)) { + /* Format clock and device name nicely */ + char cdp[33]; + int chars; + + chars = snprintf(&cdp[0], 17, "%s", clk->name); + while (chars < 16) { + cdp[chars] = ' '; + chars++; + } + chars = snprintf(&cdp[16], 17, "%s", clk->dev ? + dev_name(clk->dev) : "N/A"); + while (chars < 16) { + cdp[chars+16] = ' '; + chars++; + } + cdp[32] = '\0'; + if (clk->get_rate) + seq_printf(s, + "%s%s\t%s\t%d\t%s\t%lu Hz\n", + &cdp[0], + clk->reset ? + "ASSERTED" : "RELEASED", + clk->usecount ? "ON" : "OFF", + clk->usecount, + clk->hw_ctrld ? "YES" : "NO ", + clk->get_rate(clk)); + else + seq_printf(s, + "%s%s\t%s\t%d\t%s\t" \ + "(unknown rate)\n", + &cdp[0], + clk->reset ? + "ASSERTED" : "RELEASED", + clk->usecount ? "ON" : "OFF", + clk->usecount, + clk->hw_ctrld ? "YES" : "NO "); + } + } + return 0; +} + +static int u300_clocks_open(struct inode *inode, struct file *file) +{ + return single_open(file, u300_clocks_show, NULL); +} + +static const struct file_operations u300_clocks_operations = { + .open = u300_clocks_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void init_clk_read_procfs(void) +{ + /* Expose a simple debugfs interface to view all clocks */ + (void) debugfs_create_file("u300_clocks", S_IFREG | S_IRUGO, + NULL, NULL, &u300_clocks_operations); +} +#else +static inline void init_clk_read_procfs(void) +{ +} +#endif + +static int __init u300_clock_init(void) +{ + u16 val; + + /* + * FIXME: shall all this powermanagement stuff really live here??? + */ + + /* Set system to run at PLL208, max performance, a known state. */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); + /* Wait for the PLL208 to lock if not locked in yet */ + while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & + U300_SYSCON_CSR_PLL208_LOCK_IND)); + + /* Power management enable */ + val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMCR); + val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; + writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMCR); + + clk_register(); + + init_clk_read_procfs(); + + /* + * Some of these may be on when we boot the system so make sure they + * are turned OFF. + */ + syscon_block_reset_enable(&timer_clk); + timer_clk.disable(&timer_clk); + + /* + * These shall be turned on by default when we boot the system + * so make sure they are ON. (Adding CPU here is a bit too much.) + * These clocks will be claimed by drivers later. + */ + syscon_block_reset_disable(&semi_clk); + syscon_block_reset_disable(&emif_clk); + semi_clk.enable(&semi_clk); + emif_clk.enable(&emif_clk); + + return 0; +} +/* initialize clocking early to be available later in the boot */ +core_initcall(u300_clock_init); |