[U-Boot] [PATCH v5 5/5] usb: lpc32xx: add host USB driver

slemieux.tyco at gmail.com slemieux.tyco at gmail.com
Wed Aug 5 20:31:57 CEST 2015


From: Sylvain Lemieux <slemieux at tycoint.com>

Incorporate USB driver from legacy LPCLinux NXP BSP.
The files taken from the legacy patch are:
- lpc32xx USB driver
- lpc3250 header file USB registers definition.

The legacy driver was updated and clean-up as part of the integration with the latest u-boot.

Signed-off-by: Sylvain Lemieux <slemieux at tycoint.com>
---
Changes from v4 to v5:
* Addressed Marek's comments on LPC32xx USB driver:
  - update "wait_for_bit()" implementation to use "get_timer()".
  - use return value of "wait_for_bit()" instead of
    returning -1.
* Updated multiline comments style.

Changes from v3 to v4:
* Addressed Marek's comments on LPC32xx USB driver:
  - use same "wait_for_bit()" implementation as
    drivers/usb/host/dwc2.c
  - use a const variable to define a mask to make
    the code more clear to read.
* Fixed legacy USB driver; use "otg_clk_sts" register
  to verify clock status instead of control register.

Changes from v2 to v3:
* Addressed Marek's comments on LPC32xx USB driver:
  - use "get_timer()" to handle timeout (usbpll_setup).
  - submit i2c driver update into a separate patch.
  - use "u32" for 4 bytes registers definition.
  - Move pin mux code to setup file (i.e. "device.c").
* Updated ISP1301 register definition (set & clear) instead 
  of using an extra mask for the clear address.

Changes from v1 to v2:
* Addressed Marek's comments on LPC32xx USB driver:
  - use "get_timer()" to handle timeout.
  - Split USB and I2C driver.
* Updated LPC32xx I2C driver to support the I2C that is part
  of the USB module.
* Removed ISP1301 USB transceiver I2C registers definition
  that are not used.
* Use "cpu" initialization & stop functions API instead of the "board" API.

Update to the legacy driver to integrate with the latest u-boot:
1) Fixed checkpatch script output in legacy code.
2) Use LPC32xx definition from "cpu.h" and "clk.h".
3) Incorporate USB specific register definition from "lpc3250.h"
   header file from legacy BSP patch from LPCLinux.
4) Use u-boot API for register access to remove the volatile
   in register definition taken from "lpc3250.h" header file.
5) Update driver for latest u-boot USB API.
6) Use the peripheral clock to compute the I2C divider.

The legacy BSP patch (u-boot-2009.03_lpc32x0-v1.07.patch.tar.bz2)
was downloaded from the LPCLinux Web site.

 arch/arm/cpu/arm926ejs/lpc32xx/devices.c      |   6 +
 arch/arm/include/asm/arch-lpc32xx/clk.h       |  12 ++
 arch/arm/include/asm/arch-lpc32xx/sys_proto.h |   1 +
 drivers/usb/host/Makefile                     |   1 +
 drivers/usb/host/ohci-lpc32xx.c               | 250 ++++++++++++++++++++++++++
 5 files changed, 270 insertions(+)
 create mode 100644 drivers/usb/host/ohci-lpc32xx.c

diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
index 0d2ef7a..3da0952 100644
--- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
+++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c
@@ -82,6 +82,12 @@ void lpc32xx_i2c_init(unsigned int devnum)
 	writel(ctrl, &clk->i2cclk_ctrl);
 }
 
+void lpc32xx_usb_init(void)
+{
+	/* Do not route the UART 5 Tx/Rx pins to the USB D+ and USB D- pins. */
+	clrbits_le32(&ctrl->ctrl, UART_CTRL_UART5_USB_MODE);
+}
+
 U_BOOT_DEVICE(lpc32xx_gpios) = {
 	.name = "gpio_lpc32xx"
 };
diff --git a/arch/arm/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h
index 663f6bc..d21310e 100644
--- a/arch/arm/include/asm/arch-lpc32xx/clk.h
+++ b/arch/arm/include/asm/arch-lpc32xx/clk.h
@@ -167,6 +167,18 @@ struct clk_pm_regs {
 /* SDRAMCLK register bits */
 #define CLK_SDRAM_DDR_SEL		(1 << 1)
 
+/* USB control register definitions */
+#define CLK_USBCTRL_PLL_STS		(1 << 0)
+#define CLK_USBCTRL_FDBK_PLUS1(n)	(((n) & 0xFF) << 1)
+#define CLK_USBCTRL_POSTDIV_2POW(n)	(((n) & 0x3) << 11)
+#define CLK_USBCTRL_PLL_PWRUP		(1 << 16)
+#define CLK_USBCTRL_CLK_EN1		(1 << 17)
+#define CLK_USBCTRL_CLK_EN2		(1 << 18)
+#define CLK_USBCTRL_BUS_KEEPER		(0x1 << 19)
+#define CLK_USBCTRL_USBHSTND_EN		(1 << 21)
+#define CLK_USBCTRL_USBDVND_EN		(1 << 22)
+#define CLK_USBCTRL_HCLK_EN		(1 << 24)
+
 unsigned int get_sys_clk_rate(void);
 unsigned int get_hclk_pll_rate(void);
 unsigned int get_hclk_clk_div(void);
diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
index 7f997d9..ca7790a 100644
--- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
+++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h
@@ -14,6 +14,7 @@ void lpc32xx_mac_init(void);
 void lpc32xx_mlc_nand_init(void);
 void lpc32xx_slc_nand_init(void);
 void lpc32xx_dma_init(void);
+void lpc32xx_usb_init(void);
 void lpc32xx_i2c_init(unsigned int devnum);
 void lpc32xx_ssp_init(void);
 #if defined(CONFIG_SPL_BUILD)
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 4d35d3e..9dfdc94 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o
 obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o
 obj-$(CONFIG_USB_OHCI_EP93XX) += ohci-ep93xx.o
 obj-$(CONFIG_USB_OHCI_SUNXI) += ohci-sunxi.o
+obj-$(CONFIG_USB_OHCI_LPC32XX) += ohci-lpc32xx.o
 
 # echi
 obj-$(CONFIG_USB_EHCI) += ehci-hcd.o
diff --git a/drivers/usb/host/ohci-lpc32xx.c b/drivers/usb/host/ohci-lpc32xx.c
new file mode 100644
index 0000000..4ef1867
--- /dev/null
+++ b/drivers/usb/host/ohci-lpc32xx.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2008-2015 by NXP Semiconductors
+ * All rights reserved.
+ *
+ * @Author: Based on code by Kevin Wells
+ * @Descr: USB driver - Embedded Artists LPC3250 OEM Board support functions
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <usb.h>
+#include <i2c.h>
+
+/*
+ * Notes:
+ *
+ * Only bitfield masks/values which are actually used by the driver
+ * are defined.
+ */
+
+/* OTG I2C controller module register structures */
+struct otgi2c_regs {
+	u32 otg_i2c_txrx;   /* OTG I2C Tx/Rx Data FIFO */
+	u32 otg_i2c_stat;   /* OTG I2C Status Register */
+	u32 otg_i2c_ctrl;   /* OTG I2C Control Register */
+	u32 otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
+	u32 otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
+};
+
+/* OTG controller module register structures */
+struct otg_regs {
+	u32 reserved1[64];
+	u32 otg_int_sts;    /* OTG int status register */
+	u32 otg_int_enab;   /* OTG int enable register */
+	u32 otg_int_set;    /* OTG int set register */
+	u32 otg_int_clr;    /* OTG int clear register */
+	u32 otg_sts_ctrl;   /* OTG status/control register */
+	u32 otg_timer;      /* OTG timer register */
+	u32 reserved2[122];
+	struct otgi2c_regs otg_i2c;
+	u32 reserved3[824];
+	u32 otg_clk_ctrl;   /* OTG clock control reg */
+	u32 otg_clk_sts;    /* OTG clock status reg */
+};
+
+/* otg_sts_ctrl register definitions */
+#define OTG_HOST_EN			(1 << 0) /* Enable host mode */
+
+/* otg_clk_ctrl and otg_clk_sts register definitions */
+#define OTG_CLK_AHB_EN			(1 << 4) /* Enable AHB clock */
+#define OTG_CLK_OTG_EN			(1 << 3) /* Enable OTG clock */
+#define OTG_CLK_I2C_EN			(1 << 2) /* Enable I2C clock */
+#define OTG_CLK_HOST_EN			(1 << 0) /* Enable host clock */
+
+/* ISP1301 USB transceiver I2C registers */
+#define MC1_SPEED_REG			(1 << 0)
+#define MC1_DAT_SE0			(1 << 2)
+#define MC1_UART_EN			(1 << 6)
+
+#define MC2_SPD_SUSP_CTRL		(1 << 1)
+#define MC2_BI_DI			(1 << 2)
+#define MC2_PSW_EN			(1 << 6)
+
+#define OTG1_DP_PULLUP			(1 << 0)
+#define OTG1_DM_PULLUP			(1 << 1)
+#define OTG1_DP_PULLDOWN		(1 << 2)
+#define OTG1_DM_PULLDOWN		(1 << 3)
+#define OTG1_VBUS_DRV			(1 << 5)
+
+#define ISP1301_I2C_ADDR		CONFIG_USB_ISP1301_I2C_ADDR
+
+#define ISP1301_I2C_MODE_CONTROL_1_SET		0x04
+#define ISP1301_I2C_MODE_CONTROL_1_CLR		0x05
+#define ISP1301_I2C_MODE_CONTROL_2_SET		0x12
+#define ISP1301_I2C_MODE_CONTROL_2_CLR		0x13
+#define ISP1301_I2C_OTG_CONTROL_1_SET		0x06
+#define ISP1301_I2C_OTG_CONTROL_1_CLR		0x07
+#define ISP1301_I2C_INTERRUPT_LATCH_CLR		0x0B
+#define ISP1301_I2C_INTERRUPT_FALLING_CLR	0x0D
+#define ISP1301_I2C_INTERRUPT_RISING_CLR	0x0F
+
+static struct otg_regs *otg = (struct otg_regs *)USB_BASE;
+static struct clk_pm_regs *clk_pwr = (struct clk_pm_regs *)CLK_PM_BASE;
+
+static int wait_for_bit(void *reg, const u32 mask, bool set)
+{
+	u32 val;
+	unsigned long start = get_timer(0);
+
+	while (1) {
+		val = readl(reg);
+		if (!set)
+			val = ~val;
+
+		if ((val & mask) == mask)
+			return 0;
+
+		if (get_timer(start) > 1000)
+			break;
+
+		udelay(1);
+	}
+
+	debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n",
+	      __func__, reg, mask, set);
+
+	return -ETIMEDOUT;
+}
+
+static int isp1301_set_value(int reg, u8 value)
+{
+	return i2c_write(ISP1301_I2C_ADDR, reg, 1, &value, 1);
+}
+
+static void isp1301_configure(void)
+{
+	i2c_set_bus_num(I2C_2);
+
+	/*
+	 * LPC32XX only supports DAT_SE0 USB mode
+	 * This sequence is important
+	 */
+
+	/* Disable transparent UART mode first */
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, MC1_UART_EN);
+
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, ~MC1_SPEED_REG);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_SPEED_REG);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_CLR, ~0);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_SET,
+			  MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL);
+
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR, ~0);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_DAT_SE0);
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET,
+			  OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR,
+			  OTG1_DM_PULLUP | OTG1_DP_PULLUP);
+	isp1301_set_value(ISP1301_I2C_INTERRUPT_LATCH_CLR, ~0);
+	isp1301_set_value(ISP1301_I2C_INTERRUPT_FALLING_CLR, ~0);
+	isp1301_set_value(ISP1301_I2C_INTERRUPT_RISING_CLR, ~0);
+
+	/* Enable usb_need_clk clock after transceiver is initialized */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBDVND_EN);
+}
+
+static int usbpll_setup(void)
+{
+	u32 ret;
+
+	/* make sure clocks are disabled */
+	clrbits_le32(&clk_pwr->usb_ctrl,
+		     CLK_USBCTRL_CLK_EN1 | CLK_USBCTRL_CLK_EN2);
+
+	/* start PLL clock input */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN1);
+
+	/* Setup PLL. */
+	setbits_le32(&clk_pwr->usb_ctrl,
+		     CLK_USBCTRL_FDBK_PLUS1(192 - 1));
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01));
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
+
+	ret = wait_for_bit(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS, 1);
+	if (ret)
+		return ret;
+
+	/* enable PLL output */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);
+
+	return 0;
+}
+
+int usb_cpu_init(void)
+{
+	unsigned long start;
+	u32 ret;
+
+	/*
+	 * USB pins routing setup is done by "lpc32xx_usb_init()" and should
+	 * be call by board "board_init()" or "misc_init_r()" functions.
+	 */
+
+	/* enable AHB slave USB clock */
+	setbits_le32(&clk_pwr->usb_ctrl,
+		     CLK_USBCTRL_HCLK_EN | CLK_USBCTRL_BUS_KEEPER);
+
+	/* enable I2C clock in OTG block if it isn't */
+	if ((readl(&otg->otg_clk_sts) & OTG_CLK_I2C_EN) != OTG_CLK_I2C_EN) {
+		writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl);
+
+		start = get_timer(0);
+		while (1) {
+			if (readl(&otg->otg_clk_sts) == OTG_CLK_I2C_EN)
+				break;
+
+			if (get_timer(start) > 100)
+				return -1;
+
+			udelay(1);
+		}
+	}
+
+	/* Configure ISP1301 */
+	isp1301_configure();
+
+	/* setup USB clocks and PLL */
+	ret = usbpll_setup();
+	if (ret)
+		return ret;
+
+	/* enable usb_host_need_clk */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBHSTND_EN);
+
+	/* enable all needed USB clocks */
+	const u32 mask = OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
+			 OTG_CLK_I2C_EN | OTG_CLK_HOST_EN;
+	writel(mask, &otg->otg_clk_ctrl);
+
+	ret = wait_for_bit(&otg->otg_clk_sts, mask, 1);
+	if (ret)
+		return ret;
+
+	setbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);
+
+	return 0;
+}
+
+int usb_cpu_stop(void)
+{
+	/* vbus off */
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);
+
+	clrbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
+
+	clrbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_HCLK_EN);
+
+	return 0;
+}
+
+int usb_cpu_init_fail(void)
+{
+	return usb_cpu_stop();
+}
-- 
1.8.3.1



More information about the U-Boot mailing list