Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+63 −24
Original line number Diff line number Diff line
@@ -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);
	pllsetup.analog_on = 0;
	local_clk_usbpll_setup(&pllsetup);

	reg = __raw_readl(LPC32XX_CLKPWR_USB_CTRL);
	reg |= LPC32XX_CLKPWR_USBCTRL_CLK_EN1;
	__raw_writel(reg, LPC32XX_CLKPWR_USB_CTRL);
	/*
	 * Start USB PLL and check PLL status
	 */

	pllsetup.analog_on = 1;
	local_clk_usbpll_setup(&pllsetup);
	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 = {
+26 −0
Original line number Diff line number Diff line
@@ -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
 */
+1 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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 = {