Commit 48a5dedf authored by Roland Stigge's avatar Roland Stigge

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: default avatarRoland Stigge <stigge@antcom.de>
parent a7e4c6a7
...@@ -87,6 +87,7 @@ ...@@ -87,6 +87,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
...@@ -100,6 +101,8 @@ ...@@ -100,6 +101,8 @@
static DEFINE_SPINLOCK(global_clkregs_lock); static DEFINE_SPINLOCK(global_clkregs_lock);
static int usb_pll_enable, usb_pll_valid;
static struct clk clk_armpll; static struct clk clk_armpll;
static struct clk clk_usbpll; static struct clk clk_usbpll;
...@@ -384,30 +387,62 @@ static u32 local_clk_usbpll_setup(struct clk_pll_setup *pHCLKPllSetup) ...@@ -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) static int local_usbpll_enable(struct clk *clk, int enable)
{ {
u32 reg; u32 reg;
int ret = -ENODEV; int ret = 0;
unsigned long timeout = jiffies + msecs_to_jiffies(10); unsigned long timeout = jiffies + msecs_to_jiffies(20);
reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
if (enable == 0) { __raw_writel(reg & ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN2 |
reg &= ~(LPC32XX_CLKPWR_USBCTRL_CLK_EN1 | LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP),
LPC32XX_CLKPWR_USBCTRL_CLK_EN2); LPC32XX_CLKPWR_USB_CTRL);
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); __raw_writel(reg & ~LPC32XX_CLKPWR_USBCTRL_CLK_EN1,
} else if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_PWRUP) { 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; reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1;
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); __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)) { while (time_before(jiffies, timeout) && (ret == -ENODEV)) {
reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_STS) if (reg & LPC32XX_CLKPWR_USBCTRL_PLL_STS)
ret = 0; ret = 0;
else
udelay(10);
} }
/*
* Gate clock from PLL if PLL is locked
*/
if (ret == 0) { if (ret == 0) {
reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2; __raw_writel(reg | LPC32XX_CLKPWR_USBCTRL_CLK_EN2,
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); 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; return ret;
...@@ -425,7 +460,7 @@ static unsigned long local_usbpll_round_rate(struct clk *clk, ...@@ -425,7 +460,7 @@ static unsigned long local_usbpll_round_rate(struct clk *clk,
*/ */
rate = rate * 1000; rate = rate * 1000;
clkin = clk->parent->rate; clkin = clk->get_rate(clk);
usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) & usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &
LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1; LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;
clkin = clkin / usbdiv; clkin = clkin / usbdiv;
...@@ -439,7 +474,8 @@ static unsigned long local_usbpll_round_rate(struct clk *clk, ...@@ -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) 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; struct clk_pll_setup pllsetup;
/* /*
...@@ -448,7 +484,7 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate) ...@@ -448,7 +484,7 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate)
*/ */
rate = rate * 1000; rate = rate * 1000;
clkin = clk->get_rate(clk); clkin = clk->get_rate(clk->parent);
usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) & usbdiv = (__raw_readl(LPC32XX_CLKPWR_USBCLK_PDIV) &
LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1; LPC32XX_CLKPWR_USBPDIV_PLL_MASK) + 1;
clkin = clkin / usbdiv; clkin = clkin / usbdiv;
...@@ -457,22 +493,25 @@ static int local_usbpll_set_rate(struct clk *clk, unsigned long rate) ...@@ -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) if (local_clk_find_pll_cfg(clkin, rate, &pllsetup) == 0)
return -EINVAL; return -EINVAL;
/*
* Disable PLL clocks during PLL change
*/
local_usbpll_enable(clk, 0); local_usbpll_enable(clk, 0);
pllsetup.analog_on = 0;
local_clk_usbpll_setup(&pllsetup);
reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); /*
reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1; * Start USB PLL and check PLL status
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL); */
pllsetup.analog_on = 1; usb_pll_valid = 1;
local_clk_usbpll_setup(&pllsetup); usb_pll_enable = 1;
ret = local_usbpll_enable(clk, 1);
if (ret >= 0)
clk->rate = clk_check_pll_setup(clkin, &pllsetup); clk->rate = clk_check_pll_setup(clkin, &pllsetup);
reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL); return ret;
reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2;
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
return 0;
} }
static struct clk clk_usbpll = { static struct clk clk_usbpll = {
......
...@@ -159,6 +159,32 @@ struct platform_device lpc32xx_adc_device = { ...@@ -159,6 +159,32 @@ struct platform_device lpc32xx_adc_device = {
.resource = adc_resources, .resource = adc_resources,
}; };
/*
* USB support
*/
/* The dmamask must be set for OHCI to work */
static u64 ohci_dmamask = ~(u32) 0;
static struct resource ohci_resources[] = {
{
.start = IO_ADDRESS(LPC32XX_USB_BASE),
.end = IO_ADDRESS(LPC32XX_USB_BASE + 0x100 - 1),
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_LPC32XX_USB_HOST,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device lpc32xx_ohci_device = {
.name = "usb-ohci",
.id = -1,
.dev = {
.dma_mask = &ohci_dmamask,
.coherent_dma_mask = 0xFFFFFFFF,
},
.num_resources = ARRAY_SIZE(ohci_resources),
.resource = ohci_resources,
};
/* /*
* Returns the unique ID for the device * Returns the unique ID for the device
*/ */
......
...@@ -31,6 +31,7 @@ extern struct platform_device lpc32xx_i2c2_device; ...@@ -31,6 +31,7 @@ extern struct platform_device lpc32xx_i2c2_device;
extern struct platform_device lpc32xx_tsc_device; extern struct platform_device lpc32xx_tsc_device;
extern struct platform_device lpc32xx_adc_device; extern struct platform_device lpc32xx_adc_device;
extern struct platform_device lpc32xx_rtc_device; extern struct platform_device lpc32xx_rtc_device;
extern struct platform_device lpc32xx_ohci_device;
/* /*
* Other arch specific structures and functions * Other arch specific structures and functions
......
...@@ -279,6 +279,7 @@ static struct platform_device *phy3250_devs[] __initdata = { ...@@ -279,6 +279,7 @@ static struct platform_device *phy3250_devs[] __initdata = {
&lpc32xx_watchdog_device, &lpc32xx_watchdog_device,
&lpc32xx_gpio_led_device, &lpc32xx_gpio_led_device,
&lpc32xx_adc_device, &lpc32xx_adc_device,
&lpc32xx_ohci_device,
}; };
static struct amba_device *amba_devs[] __initdata = { static struct amba_device *amba_devs[] __initdata = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment