diff options
Diffstat (limited to 'arch/arm/mach-lpc32xx/clock.c')
| -rw-r--r-- | arch/arm/mach-lpc32xx/clock.c | 183 | 
1 files changed, 119 insertions, 64 deletions
diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c index 1e027514096..b7ef51119d3 100644 --- a/arch/arm/mach-lpc32xx/clock.c +++ b/arch/arm/mach-lpc32xx/clock.c @@ -82,10 +82,12 @@   *   will also impact the individual peripheral rates.   */ +#include <linux/export.h>  #include <linux/kernel.h>  #include <linux/list.h>  #include <linux/errno.h>  #include <linux/device.h> +#include <linux/delay.h>  #include <linux/err.h>  #include <linux/clk.h>  #include <linux/amba/bus.h> @@ -97,9 +99,12 @@  #include "clock.h"  #include "common.h" +static DEFINE_SPINLOCK(global_clkregs_lock); + +static int usb_pll_enable, usb_pll_valid; +  static struct clk clk_armpll;  static struct clk clk_usbpll; -static DEFINE_MUTEX(clkm_lock);  /*   * Post divider values for PLLs based on selected register value @@ -127,7 +132,7 @@ static struct clk osc_32KHz = {  static int local_pll397_enable(struct clk *clk, int enable)  {  	u32 reg; -	unsigned long timeout = 1 + msecs_to_jiffies(10); +	unsigned long timeout = jiffies + msecs_to_jiffies(10);  	reg = __raw_readl(LPC32XX_CLKPWR_PLL397_CTRL); @@ -142,7 +147,7 @@ static int local_pll397_enable(struct clk *clk, int enable)  		/* Wait for PLL397 lock */  		while (((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) &  			LPC32XX_CLKPWR_SYSCTRL_PLL397_STS) == 0) && -			(timeout > jiffies)) +			time_before(jiffies, timeout))  			cpu_relax();  		if ((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) & @@ -156,7 +161,7 @@ static int local_pll397_enable(struct clk *clk, int enable)  static int local_oscmain_enable(struct clk *clk, int enable)  {  	u32 reg; -	unsigned long timeout = 1 + msecs_to_jiffies(10); +	unsigned long timeout = jiffies + msecs_to_jiffies(10);  	reg = __raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL); @@ -171,7 +176,7 @@ static int local_oscmain_enable(struct clk *clk, int enable)  		/* Wait for main oscillator to start */  		while (((__raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL) &  			LPC32XX_CLKPWR_MOSC_DISABLE) != 0) && -			(timeout > jiffies)) +			time_before(jiffies, timeout))  			cpu_relax();  		if ((__raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL) & @@ -382,30 +387,62 @@ static u32 local_clk_usbpll_setup(struct clk_pll_setup *pHCLKPllSetup)  static int local_usbpll_enable(struct clk *clk, int enable)  {  	u32 reg; -	int ret = -ENODEV; -	unsigned long timeout = 1 + msecs_to_jiffies(10); +	int ret = 0; +	unsigned long timeout = jiffies + msecs_to_jiffies(20);  	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); -	if (enable == 0) { -		reg &= ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 | -			LPC32XX_CLKPWR_USBCTRL_CLK_EN2); -		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); -	} else if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP) { +	__raw_writel(reg & ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN2 | +		LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP), +		LPC32XX_CLKPWR_USB_CTRL); +	__raw_writel(reg & ~LPC32XX_CLKPWR_USBCTRL_CLK_EN1, +		LPC32XX_CLKPWR_USB_CTRL); + +	if (enable && usb_pll_valid && usb_pll_enable) { +		ret = -ENODEV; +		/* +		 * If the PLL rate has been previously set, then the rate +		 * in the PLL register is valid and can be enabled here. +		 * Otherwise, it needs to be enabled as part of setrate. +		 */ + +		/* +		 * Gate clock into PLL +		 */  		reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1;  		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); -		/* Wait for PLL lock */ -		while ((timeout > jiffies) & (ret == -ENODEV)) { +		/* +		 * Enable PLL +		 */ +		reg |= LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP; +		__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + +		/* +		 * Wait for PLL to lock +		 */ +		while (time_before(jiffies, timeout) && (ret == -ENODEV)) {  			reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);  			if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_STS)  				ret = 0; +			else +				udelay(10);  		} +		/* +		 * Gate clock from PLL if PLL is locked +		 */  		if (ret == 0) { -			reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; -			__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); +			__raw_writel(reg | LPC32XX_CLKPWR_USBCTRL_CLK_EN2, +				LPC32XX_CLKPWR_USB_CTRL); +		} else { +			__raw_writel(reg & ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 | +				LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP), +				LPC32XX_CLKPWR_USB_CTRL);  		} +	} else if ((enable == 0) && usb_pll_valid  && usb_pll_enable) { +		usb_pll_valid = 0; +		usb_pll_enable = 0;  	}  	return ret; @@ -423,7 +460,7 @@ static unsigned long local_usbpll_round_rate(struct clk *clk,  	 */  	rate = rate * 1000; -	clkin = clk->parent->rate; +	clkin = clk->get_rate(clk);  	usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &  		LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;  	clkin = clkin / usbdiv; @@ -437,7 +474,8 @@ static unsigned long local_usbpll_round_rate(struct clk *clk,  static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)  { -	u32 clkin, reg, usbdiv; +	int ret = -ENODEV; +	u32 clkin, usbdiv;  	struct clk_pll_setup pllsetup;  	/* @@ -446,7 +484,7 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)  	 */  	rate = rate * 1000; -	clkin = clk->get_rate(clk); +	clkin = clk->get_rate(clk->parent);  	usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &  		LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;  	clkin = clkin / usbdiv; @@ -455,22 +493,25 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)  	if (local_clk_find_pll_cfg(clkin, rate, &pllsetup) == 0)  		return -EINVAL; +	/* +	 * Disable PLL clocks during PLL change +	 */  	local_usbpll_enable(clk, 0); - -	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); -	reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1; -	__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); - -	pllsetup.analog_on = 1; +	pllsetup.analog_on = 0;  	local_clk_usbpll_setup(&pllsetup); -	clk->rate = clk_check_pll_setup(clkin, &pllsetup); +	/* +	 * Start USB PLL and check PLL status +	 */ -	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); -	reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; -	__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); +	usb_pll_valid = 1; +	usb_pll_enable = 1; -	return 0; +	ret = local_usbpll_enable(clk, 1); +	if (ret >= 0) +		clk->rate = clk_check_pll_setup(clkin, &pllsetup); + +	return ret;  }  static struct clk clk_usbpll = { @@ -719,6 +760,41 @@ static struct clk clk_tsc = {  	.get_rate	= local_return_parent_rate,  }; +static int adc_onoff_enable(struct clk *clk, int enable) +{ +	u32 tmp; +	u32 divider; + +	/* Use PERIPH_CLOCK */ +	tmp = __raw_readl(LPC32XX_CLKPWR_ADC_CLK_CTRL_1); +	tmp |= LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL; +	/* +	 * Set clock divider so that we have equal to or less than +	 * 4.5MHz clock at ADC +	 */ +	divider = clk->get_rate(clk) / 4500000 + 1; +	tmp |= divider; +	__raw_writel(tmp, LPC32XX_CLKPWR_ADC_CLK_CTRL_1); + +	/* synchronize rate of this clock w/ actual HW setting */ +	clk->rate = clk->get_rate(clk->parent) / divider; + +	if (enable == 0) +		__raw_writel(0, clk->enable_reg); +	else +		__raw_writel(clk->enable_mask, clk->enable_reg); + +	return 0; +} + +static struct clk clk_adc = { +	.parent		= &clk_pclk, +	.enable		= adc_onoff_enable, +	.enable_reg	= LPC32XX_CLKPWR_ADC_CLK_CTRL, +	.enable_mask	= LPC32XX_CLKPWR_ADC32CLKCTRL_CLK_EN, +	.get_rate	= local_return_parent_rate, +}; +  static int mmc_onoff_enable(struct clk *clk, int enable)  {  	u32 tmp; @@ -891,20 +967,8 @@ static struct clk clk_lcd = {  	.enable_mask	= LPC32XX_CLKPWR_LCDCTRL_CLK_EN,  }; -static inline void clk_lock(void) -{ -	mutex_lock(&clkm_lock); -} - -static inline void clk_unlock(void) -{ -	mutex_unlock(&clkm_lock); -} -  static void local_clk_disable(struct clk *clk)  { -	WARN_ON(clk->usecount == 0); -  	/* Don't attempt to disable clock if it has no users */  	if (clk->usecount > 0) {  		clk->usecount--; @@ -947,10 +1011,11 @@ static int local_clk_enable(struct clk *clk)  int clk_enable(struct clk *clk)  {  	int ret; +	unsigned long flags; -	clk_lock(); +	spin_lock_irqsave(&global_clkregs_lock, flags);  	ret = local_clk_enable(clk); -	clk_unlock(); +	spin_unlock_irqrestore(&global_clkregs_lock, flags);  	return ret;  } @@ -961,9 +1026,11 @@ EXPORT_SYMBOL(clk_enable);   */  void clk_disable(struct clk *clk)  { -	clk_lock(); +	unsigned long flags; + +	spin_lock_irqsave(&global_clkregs_lock, flags);  	local_clk_disable(clk); -	clk_unlock(); +	spin_unlock_irqrestore(&global_clkregs_lock, flags);  }  EXPORT_SYMBOL(clk_disable); @@ -972,13 +1039,7 @@ EXPORT_SYMBOL(clk_disable);   */  unsigned long clk_get_rate(struct clk *clk)  { -	unsigned long rate; - -	clk_lock(); -	rate = clk->get_rate(clk); -	clk_unlock(); - -	return rate; +	return clk->get_rate(clk);  }  EXPORT_SYMBOL(clk_get_rate); @@ -994,11 +1055,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate)  	 * the actual rate set as part of the peripheral dividers  	 * instead of high level clock control  	 */ -	if (clk->set_rate) { -		clk_lock(); +	if (clk->set_rate)  		ret = clk->set_rate(clk, rate); -		clk_unlock(); -	}  	return ret;  } @@ -1009,15 +1067,11 @@ EXPORT_SYMBOL(clk_set_rate);   */  long clk_round_rate(struct clk *clk, unsigned long rate)  { -	clk_lock(); -  	if (clk->round_rate)  		rate = clk->round_rate(clk, rate);  	else  		rate = clk->get_rate(clk); -	clk_unlock(); -  	return rate;  }  EXPORT_SYMBOL(clk_round_rate); @@ -1075,10 +1129,11 @@ static struct clk_lookup lookups[] = {  	_REGISTER_CLOCK("dev:ssp1", NULL, clk_ssp1)  	_REGISTER_CLOCK("lpc32xx_keys.0", NULL, clk_kscan)  	_REGISTER_CLOCK("lpc32xx-nand.0", "nand_ck", clk_nand) -	_REGISTER_CLOCK("tbd", "i2s0_ck", clk_i2s0) -	_REGISTER_CLOCK("tbd", "i2s1_ck", clk_i2s1) +	_REGISTER_CLOCK("lpc32xx-adc", NULL, clk_adc) +	_REGISTER_CLOCK(NULL, "i2s0_ck", clk_i2s0) +	_REGISTER_CLOCK(NULL, "i2s1_ck", clk_i2s1)  	_REGISTER_CLOCK("ts-lpc32xx", NULL, clk_tsc) -	_REGISTER_CLOCK("dev:mmc0", "MCLK", clk_mmc) +	_REGISTER_CLOCK("dev:mmc0", NULL, clk_mmc)  	_REGISTER_CLOCK("lpc-net.0", NULL, clk_net)  	_REGISTER_CLOCK("dev:clcd", NULL, clk_lcd)  	_REGISTER_CLOCK("lpc32xx_udc", "ck_usbd", clk_usbd)  |