diff options
Diffstat (limited to 'arch/arm/mach-ux500/clock.c')
| -rw-r--r-- | arch/arm/mach-ux500/clock.c | 104 | 
1 files changed, 98 insertions, 6 deletions
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c index 6544855af2f..fe84b9021c7 100644 --- a/arch/arm/mach-ux500/clock.c +++ b/arch/arm/mach-ux500/clock.c @@ -16,6 +16,7 @@  #include <asm/clkdev.h> +#include <plat/mtu.h>  #include <mach/hardware.h>  #include "clock.h" @@ -59,6 +60,9 @@  #define PRCM_DMACLK_MGT		0x074  #define PRCM_B2R2CLK_MGT	0x078  #define PRCM_TVCLK_MGT		0x07C +#define PRCM_TCR		0x1C8 +#define PRCM_TCR_STOPPED	(1 << 16) +#define PRCM_TCR_DOZE_MODE	(1 << 17)  #define PRCM_UNIPROCLK_MGT	0x278  #define PRCM_SSPCLK_MGT		0x280  #define PRCM_RNGCLK_MGT		0x284 @@ -120,10 +124,95 @@ void clk_disable(struct clk *clk)  }  EXPORT_SYMBOL(clk_disable); +/* + * The MTU has a separate, rather complex muxing setup + * with alternative parents (peripheral cluster or + * ULP or fixed 32768 Hz) depending on settings + */ +static unsigned long clk_mtu_get_rate(struct clk *clk) +{ +	void __iomem *addr = __io_address(U8500_PRCMU_BASE) +		+ PRCM_TCR; +	u32 tcr = readl(addr); +	int mtu = (int) clk->data; +	/* +	 * One of these is selected eventually +	 * TODO: Replace the constant with a reference +	 * to the ULP source once this is modeled. +	 */ +	unsigned long clk32k = 32768; +	unsigned long mturate; +	unsigned long retclk; + +	/* Get the rate from the parent as a default */ +	if (clk->parent_periph) +		mturate = clk_get_rate(clk->parent_periph); +	else if (clk->parent_cluster) +		mturate = clk_get_rate(clk->parent_cluster); +	else +		/* We need to be connected SOMEWHERE */ +		BUG(); + +	/* +	 * Are we in doze mode? +	 * In this mode the parent peripheral or the fixed 32768 Hz +	 * clock is fed into the block. +	 */ +	if (!(tcr & PRCM_TCR_DOZE_MODE)) { +		/* +		 * Here we're using the clock input from the APE ULP +		 * clock domain. But first: are the timers stopped? +		 */ +		if (tcr & PRCM_TCR_STOPPED) { +			clk32k = 0; +			mturate = 0; +		} else { +			/* Else default mode: 0 and 2.4 MHz */ +			clk32k = 0; +			if (cpu_is_u5500()) +				/* DB5500 divides by 8 */ +				mturate /= 8; +			else if (cpu_is_u8500ed()) { +				/* +				 * This clocking setting must not be used +				 * in the ED chip, it is simply not +				 * connected anywhere! +				 */ +				mturate = 0; +				BUG(); +			} else +				/* +				 * In this mode the ulp38m4 clock is divided +				 * by a factor 16, on the DB8500 typically +				 * 38400000 / 16 ~ 2.4 MHz. +				 * TODO: Replace the constant with a reference +				 * to the ULP source once this is modeled. +				 */ +				mturate = 38400000 / 16; +		} +	} + +	/* Return the clock selected for this MTU */ +	if (tcr & (1 << mtu)) +		retclk = clk32k; +	else +		retclk = mturate; + +	pr_info("MTU%d clock rate: %lu Hz\n", mtu, retclk); +	return retclk; +} +  unsigned long clk_get_rate(struct clk *clk)  {  	unsigned long rate; +	/* +	 * If there is a custom getrate callback for this clock, +	 * it will take precedence. +	 */ +	if (clk->get_rate) +		return clk->get_rate(clk); +  	if (clk->ops && clk->ops->get_rate)  		return clk->ops->get_rate(clk); @@ -341,8 +430,9 @@ static DEFINE_PRCC_CLK(5, usb_v1, 	0,  0, NULL);  /* Peripheral Cluster #6 */ -static DEFINE_PRCC_CLK(6, mtu1_v1, 	8, -1, NULL); -static DEFINE_PRCC_CLK(6, mtu0_v1, 	7, -1, NULL); +/* MTU ID in data */ +static DEFINE_PRCC_CLK_CUSTOM(6, mtu1_v1, 8, -1, NULL, clk_mtu_get_rate, 1); +static DEFINE_PRCC_CLK_CUSTOM(6, mtu0_v1, 7, -1, NULL, clk_mtu_get_rate, 0);  static DEFINE_PRCC_CLK(6, cfgreg_v1, 	6,  6, NULL);  static DEFINE_PRCC_CLK(6, dmc_ed, 	6,  6, NULL);  static DEFINE_PRCC_CLK(6, hash1, 	5, -1, NULL); @@ -357,8 +447,9 @@ static DEFINE_PRCC_CLK(6, rng_v1, 	0,  0, &clk_rngclk);  /* Peripheral Cluster #7 */  static DEFINE_PRCC_CLK(7, tzpc0_ed, 	4, -1, NULL); -static DEFINE_PRCC_CLK(7, mtu1_ed, 	3, -1, NULL); -static DEFINE_PRCC_CLK(7, mtu0_ed, 	2, -1, NULL); +/* MTU ID in data */ +static DEFINE_PRCC_CLK_CUSTOM(7, mtu1_ed, 3, -1, NULL, clk_mtu_get_rate, 1); +static DEFINE_PRCC_CLK_CUSTOM(7, mtu0_ed, 2, -1, NULL, clk_mtu_get_rate, 0);  static DEFINE_PRCC_CLK(7, wdg_ed, 	1, -1, NULL);  static DEFINE_PRCC_CLK(7, cfgreg_ed, 	0, -1, NULL); @@ -503,15 +594,17 @@ static struct clk_lookup u8500_v1_clks[] = {  	CLK(uiccclk,	"uicc",		NULL),  }; -static int __init clk_init(void) +int __init clk_init(void)  {  	if (cpu_is_u8500ed()) {  		clk_prcmu_ops.enable = clk_prcmu_ed_enable;  		clk_prcmu_ops.disable = clk_prcmu_ed_disable; +		clk_per6clk.rate = 100000000;  	} else if (cpu_is_u5500()) {  		/* Clock tree for U5500 not implemented yet */  		clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;  		clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL; +		clk_per6clk.rate = 26000000;  	}  	clkdev_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks)); @@ -522,4 +615,3 @@ static int __init clk_init(void)  	return 0;  } -arch_initcall(clk_init);  |