aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-lpc32xx/clock.c
diff options
context:
space:
mode:
authorRoland Stigge <stigge@antcom.de>2012-03-12 22:23:43 +0100
committerRoland Stigge <stigge@antcom.de>2012-03-13 21:15:50 +0100
commit48a5dedfa78899dad50b3b4ae30f07e27e6591ab (patch)
tree3dab55b1f4e4dd755b67887555489541f688aae8 /arch/arm/mach-lpc32xx/clock.c
parentMerge branch 'lpc32xx/drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc into lpc32xx/tmp (diff)
downloadlinux-dev-48a5dedfa78899dad50b3b4ae30f07e27e6591ab.tar.xz
linux-dev-48a5dedfa78899dad50b3b4ae30f07e27e6591ab.zip
ARM: LPC32xx: USB Support
This patch adds OHCI support to the LPC32xx ARM platform. Besides the trivial addition of platform "usb-ohci" support, fixes for the USB PLL have been added to arch/arm/mach-lpc32xx/clock.c, ported from NXP's lpclinux.com. (This is the mach-lpc32xx specific part, the USB subsystem specific changes are in a separate patch for the USB maintainers.) Signed-off-by: Roland Stigge <stigge@antcom.de>
Diffstat (limited to 'arch/arm/mach-lpc32xx/clock.c')
-rw-r--r--arch/arm/mach-lpc32xx/clock.c87
1 files changed, 63 insertions, 24 deletions
diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c
index ab8c21647422..370a5471798e 100644
--- a/arch/arm/mach-lpc32xx/clock.c
+++ b/arch/arm/mach-lpc32xx/clock.c
@@ -87,6 +87,7 @@
#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>
@@ -100,6 +101,8 @@
static DEFINE_SPINLOCK(global_clkregs_lock);
+static int usb_pll_enable, usb_pll_valid;
+
static struct clk clk_armpll;
static struct clk clk_usbpll;
@@ -384,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 = jiffies + 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 */
+ /*
+ * 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;
@@ -425,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;
@@ -439,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;
/*
@@ -448,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;
@@ -457,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 = {