|
@@ -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 = {
|
|
@@ -926,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--;
|
|
@@ -982,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;
|
|
|
}
|
|
@@ -996,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);
|
|
|
|
|
@@ -1007,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);
|
|
|
|
|
@@ -1029,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;
|
|
|
}
|
|
@@ -1044,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);
|
|
@@ -1110,12 +1129,12 @@ 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(NULL, "i2s0_ck", clk_i2s0)
|
|
|
+ _REGISTER_CLOCK(NULL, "i2s1_ck", clk_i2s1)
|
|
|
_REGISTER_CLOCK("lpc32xx-adc", NULL, clk_adc)
|
|
|
- _REGISTER_CLOCK("tbd", "i2s0_ck", clk_i2s0)
|
|
|
- _REGISTER_CLOCK("tbd", "i2s1_ck", clk_i2s1)
|
|
|
_REGISTER_CLOCK("ts-lpc32xx", NULL, clk_tsc)
|
|
|
- _REGISTER_CLOCK("dev:mmc0", "MCLK", clk_mmc)
|
|
|
- _REGISTER_CLOCK("lpc-net.0", NULL, clk_net)
|
|
|
+ _REGISTER_CLOCK("dev:mmc0", NULL, clk_mmc)
|
|
|
+ _REGISTER_CLOCK("lpc-eth.0", NULL, clk_net)
|
|
|
_REGISTER_CLOCK("dev:clcd", NULL, clk_lcd)
|
|
|
_REGISTER_CLOCK("lpc32xx_udc", "ck_usbd", clk_usbd)
|
|
|
_REGISTER_CLOCK("lpc32xx_rtc", NULL, clk_rtc)
|