ARM: LPC32xx: USB Support
authorRoland Stigge <stigge@antcom.de>
Mon, 12 Mar 2012 21:23:43 +0000 (22:23 +0100)
committerRoland Stigge <stigge@antcom.de>
Tue, 13 Mar 2012 20:15:50 +0000 (21:15 +0100)
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>
arch/arm/mach-lpc32xx/clock.c
arch/arm/mach-lpc32xx/common.c
arch/arm/mach-lpc32xx/common.h
arch/arm/mach-lpc32xx/phy3250.c

index ab8c21647422a1b33dd45b087dc006efb7151bb6..370a5471798eb9544236fc34f66707d854a45c8e 100644 (file)
@@ -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>
 
 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 = {
index 6c76bb36559ba0ea32356b6e34cb4a6d735a1ad7..11c900857b1b26043835bfd2c784b00a5283bbe9 100644 (file)
@@ -159,6 +159,32 @@ struct platform_device lpc32xx_adc_device = {
        .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
  */
index 68f2e46d98ad3ba0b159895c72707598490d7538..e1880b02b76b8cf52810923fd1da40bf7b962394 100644 (file)
@@ -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
index 4590a8b52e72fc20e8c665c50a0303262ae7a8e4..53f12301be8a92dc5d7d82136475b30ee4a9e672 100644 (file)
@@ -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 = {
This page took 0.034951 seconds and 5 git commands to generate.