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>
This commit is contained in:
Родитель
a7e4c6a714
Коммит
48a5dedfa7
|
@ -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);
|
||||
|
||||
/*
|
||||
* Start USB PLL and check PLL status
|
||||
*/
|
||||
|
||||
usb_pll_valid = 1;
|
||||
usb_pll_enable = 1;
|
||||
|
||||
ret = local_usbpll_enable(clk, 1);
|
||||
if (ret >= 0)
|
||||
clk->rate = clk_check_pll_setup(clkin, &pllsetup);
|
||||
|
||||
reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
|
||||
reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN2;
|
||||
__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct clk clk_usbpll = {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Загрузка…
Ссылка в новой задаче