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);  |