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 + */ + + usb_pll_valid = 1; + usb_pll_enable = 1; - reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); - reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; - __raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); + ret = local_usbpll_enable(clk, 1); + if (ret >= 0) + clk->rate = clk_check_pll_setup(clkin, &pllsetup); - return 0; + 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) |