diff options
author | Roland Stigge <stigge@antcom.de> | 2012-03-12 22:23:43 +0100 |
---|---|---|
committer | Roland Stigge <stigge@antcom.de> | 2012-03-13 21:15:50 +0100 |
commit | 48a5dedfa78899dad50b3b4ae30f07e27e6591ab (patch) | |
tree | 3dab55b1f4e4dd755b67887555489541f688aae8 | |
parent | a7e4c6a7145ee375a688e1701ae4c975d6c6419a (diff) | |
download | op-kernel-dev-48a5dedfa78899dad50b3b4ae30f07e27e6591ab.zip op-kernel-dev-48a5dedfa78899dad50b3b4ae30f07e27e6591ab.tar.gz |
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>
-rw-r--r-- | arch/arm/mach-lpc32xx/clock.c | 87 | ||||
-rw-r--r-- | arch/arm/mach-lpc32xx/common.c | 26 | ||||
-rw-r--r-- | arch/arm/mach-lpc32xx/common.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-lpc32xx/phy3250.c | 1 |
4 files changed, 91 insertions, 24 deletions
diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c index ab8c216..370a547 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 = { diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c index 6c76bb3..11c9008 100644 --- a/arch/arm/mach-lpc32xx/common.c +++ b/arch/arm/mach-lpc32xx/common.c @@ -160,6 +160,32 @@ struct platform_device lpc32xx_adc_device = { }; /* + * 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 */ void lpc32xx_get_uid(u32 devid[4]) diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h index 68f2e46..e1880b0 100644 --- a/arch/arm/mach-lpc32xx/common.h +++ b/arch/arm/mach-lpc32xx/common.h @@ -31,6 +31,7 @@ extern struct platform_device lpc32xx_i2c2_device; extern struct platform_device lpc32xx_tsc_device; extern struct platform_device lpc32xx_adc_device; extern struct platform_device lpc32xx_rtc_device; +extern struct platform_device lpc32xx_ohci_device; /* * Other arch specific structures and functions diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c index 4590a8b..53f1230 100644 --- a/arch/arm/mach-lpc32xx/phy3250.c +++ b/arch/arm/mach-lpc32xx/phy3250.c @@ -279,6 +279,7 @@ static struct platform_device *phy3250_devs[] __initdata = { &lpc32xx_watchdog_device, &lpc32xx_gpio_led_device, &lpc32xx_adc_device, + &lpc32xx_ohci_device, }; static struct amba_device *amba_devs[] __initdata = { |