diff mbox

[U-Boot,v2,4/4] usb: lpc32xx: add host USB driver

Message ID 1438186450-4076-5-git-send-email-slemieux.tyco@gmail.com
State Superseded
Headers show

Commit Message

Sylvain Lemieux July 29, 2015, 4:14 p.m. UTC
From: Sylvain Lemieux <slemieux@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 to integrate with the latest u-boot.

Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com>
---
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/include/asm/arch-lpc32xx/clk.h |  12 ++
 drivers/i2c/lpc32xx_i2c.c               |  20 ++-
 drivers/usb/host/Makefile               |   1 +
 drivers/usb/host/ohci-lpc32xx.c         | 245 ++++++++++++++++++++++++++++++++
 4 files changed, 275 insertions(+), 3 deletions(-)
 create mode 100644 drivers/usb/host/ohci-lpc32xx.c

Comments

Marek Vasut July 29, 2015, 11:33 p.m. UTC | #1
On Wednesday, July 29, 2015 at 06:14:10 PM, slemieux.tyco@gmail.com wrote:

Hi!

> From: Sylvain Lemieux <slemieux@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 to integrate with the latest u-boot.
> 
> Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com>
> ---
> 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.

[...]

I like this , but please split the change to the i2c driver from the addition
of the USB driver into two separate patches.

>  unsigned int get_hclk_clk_div(void);
> diff --git a/drivers/i2c/lpc32xx_i2c.c b/drivers/i2c/lpc32xx_i2c.c
> index 98106fa..be166b0 100644
> --- a/drivers/i2c/lpc32xx_i2c.c
> +++ b/drivers/i2c/lpc32xx_i2c.c
> @@ -1,7 +1,7 @@
>  /*
>   * LPC32xx I2C interface driver
>   *
> - * (C) Copyright 2014  DENX Software Engineering GmbH
> + * (C) Copyright 2014-2015  DENX Software Engineering GmbH
>   * Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
>   *
>   * SPDX-License-Identifier:	GPL-2.0+

[...]

> 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..fb51d42
> --- /dev/null
> +++ b/drivers/usb/host/ohci-lpc32xx.c
> @@ -0,0 +1,245 @@
> +/*
> + * 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 <asm/io.h>
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/clk.h>
> +#include <usb.h>
> +#include <i2c.h>
> +
> +
> +/* OTG I2C controller module register structures */
> +struct otgi2c_regs {
> +	unsigned int otg_i2c_txrx;   /* OTG I2C Tx/Rx Data FIFO */
> +	unsigned int otg_i2c_stat;   /* OTG I2C Status Register */
> +	unsigned int otg_i2c_ctrl;   /* OTG I2C Control Register */
> +	unsigned int otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
> +	unsigned int otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
> +};

Please replace that unsigned int with u32, here we are certain that
the register is 4-byte big.

> +/* OTG controller module register structures */
> +struct otg_regs {
> +	unsigned int reserved1[64];
> +	unsigned int otg_int_sts;    /* OTG int status register */
> +	unsigned int otg_int_enab;   /* OTG int enable register */
> +	unsigned int otg_int_set;    /* OTG int set register */
> +	unsigned int otg_int_clr;    /* OTG int clear register */
> +	unsigned int otg_sts_ctrl;   /* OTG status/control register */
> +	unsigned int otg_timer;      /* OTG timer register */
> +	unsigned int reserved2[122];
> +	struct otgi2c_regs otg_i2c;
> +	unsigned int reserved3[824];
> +	unsigned int otg_clk_ctrl;   /* OTG clock control reg */
> +	unsigned int 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 */
> +
> +/* UART control structure */
> +struct uartctrl_regs {
> +	unsigned int ctrl;     /* General UART control register */
> +	unsigned int clkmode;  /* UART clock control register */
> +	unsigned int loop;     /* UART loopmode enable/disable */
> +};

Huh? Is this an UART driver now ? :)

> +/* UART ctrl register definitions */
> +#define UART_U5_ROUTE_TO_USB		(1 << 0)
> +
> +/* 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	0x4
> +#define ISP1301_I2C_MODE_CONTROL_2	0x12
> +#define ISP1301_I2C_OTG_CONTROL_1	0x6
> +#define ISP1301_I2C_INTERRUPT_LATCH	0xA
> +#define ISP1301_I2C_INTERRUPT_FALLING	0xC
> +#define ISP1301_I2C_INTERRUPT_RISING	0xE
> +#define ISP1301_I2C_REG_CLEAR_ADDR	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
> +			   | ISP1301_I2C_REG_CLEAR_ADDR), MC1_UART_EN);

The parenthesis around (ISP... | ISP...) are unnecessary :)

[...]

> +static int usbpll_setup(void)
> +{
> +	int n = 0;
> +
> +	/* 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);
> +	while ((readl(clk_pwr->usb_ctrl) & CLK_USBCTRL_PLL_STS) == 0) {
> +		if (n++ >= 100000) {
> +			printf("usbpll_setup: ERROR PLL doesn't lock\n");
> +			return -1;
> +		}

Please use get_timer(0) kind of loop:

#define DELAY 1000 /* delay in mS */

ulong start = get_timer(0);
while (1) {
    if (cond) { handle the cond }
    if (get_timer(start) > DELAY) { timeout }
    udelay(1); /* Poke WDT */
}

You actually use that below already in usb_cpu_init() .

> +	}
> +
> +	/* enable PLL output */
> +	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);
> +
> +	return 0;
> +}
> +
> +int usb_cpu_init(void)
> +{
> +	unsigned long start;
> +
> +	/* 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);
> +		}
> +	}
[...]
LEMIEUX, SYLVAIN July 30, 2015, 2:13 p.m. UTC | #2
Hi Marek,

Thanks for the feedback. I will do the change and submit a new revision of the patch.

For your question regarding reference to UART 5 inside the USB driver;
* It is possible to also  route the UART5 Tx/Rx pin to the USB D+ and D- pins;
   the driver ensure this is not selected.
   For details, refer to UM10326, "LPC32x0 and LPC32x0/01 User manual - Rev. 3"
   table 459 and paragraph 17.2.3.2.6.
* I will add comments in the code.

Sylvain Lemieux

> From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Marek Vasut
>
> On Wednesday, July 29, 2015 at 06:14:10 PM, slemieux.tyco@gmail.com wrote:
>
> Hi!
>
> > From: Sylvain Lemieux <slemieux@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 to integrate with the latest u-boot.
> >
> > Signed-off-by: Sylvain Lemieux <slemieux@tycoint.com>
> > ---
> > 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.
>
> [...]
>
> I like this , but please split the change to the i2c driver from the addition
> of the USB driver into two separate patches.
>
> >  unsigned int get_hclk_clk_div(void);
> > diff --git a/drivers/i2c/lpc32xx_i2c.c b/drivers/i2c/lpc32xx_i2c.c
> > index 98106fa..be166b0 100644
> > --- a/drivers/i2c/lpc32xx_i2c.c
> > +++ b/drivers/i2c/lpc32xx_i2c.c
> > @@ -1,7 +1,7 @@
> >  /*
> >   * LPC32xx I2C interface driver
> >   *
> > - * (C) Copyright 2014  DENX Software Engineering GmbH
> > + * (C) Copyright 2014-2015  DENX Software Engineering GmbH
> >   * Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
> >   *
> >   * SPDX-License-Identifier:        GPL-2.0+
>
> [...]
>
> > 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..fb51d42
> > --- /dev/null
> > +++ b/drivers/usb/host/ohci-lpc32xx.c
> > @@ -0,0 +1,245 @@
> > +/*
> > + * 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 <asm/io.h>
> > +#include <asm/arch/cpu.h>
> > +#include <asm/arch/clk.h>
> > +#include <usb.h>
> > +#include <i2c.h>
> > +
> > +
> > +/* OTG I2C controller module register structures */
> > +struct otgi2c_regs {
> > +   unsigned int otg_i2c_txrx;   /* OTG I2C Tx/Rx Data FIFO */
> > +   unsigned int otg_i2c_stat;   /* OTG I2C Status Register */
> > +   unsigned int otg_i2c_ctrl;   /* OTG I2C Control Register */
> > +   unsigned int otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
> > +   unsigned int otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
> > +};
>
> Please replace that unsigned int with u32, here we are certain that
> the register is 4-byte big.
>
> > +/* OTG controller module register structures */
> > +struct otg_regs {
> > +   unsigned int reserved1[64];
> > +   unsigned int otg_int_sts;    /* OTG int status register */
> > +   unsigned int otg_int_enab;   /* OTG int enable register */
> > +   unsigned int otg_int_set;    /* OTG int set register */
> > +   unsigned int otg_int_clr;    /* OTG int clear register */
> > +   unsigned int otg_sts_ctrl;   /* OTG status/control register */
> > +   unsigned int otg_timer;      /* OTG timer register */
> > +   unsigned int reserved2[122];
> > +   struct otgi2c_regs otg_i2c;
> > +   unsigned int reserved3[824];
> > +   unsigned int otg_clk_ctrl;   /* OTG clock control reg */
> > +   unsigned int 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 */
> > +
> > +/* UART control structure */
> > +struct uartctrl_regs {
> > +   unsigned int ctrl;     /* General UART control register */
> > +   unsigned int clkmode;  /* UART clock control register */
> > +   unsigned int loop;     /* UART loopmode enable/disable */
> > +};
>
> Huh? Is this an UART driver now ? :)
>
> > +/* UART ctrl register definitions */
> > +#define UART_U5_ROUTE_TO_USB               (1 << 0)
> > +
> > +/* 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 0x4
> > +#define ISP1301_I2C_MODE_CONTROL_2 0x12
> > +#define ISP1301_I2C_OTG_CONTROL_1  0x6
> > +#define ISP1301_I2C_INTERRUPT_LATCH        0xA
> > +#define ISP1301_I2C_INTERRUPT_FALLING      0xC
> > +#define ISP1301_I2C_INTERRUPT_RISING       0xE
> > +#define ISP1301_I2C_REG_CLEAR_ADDR 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
> > +                      | ISP1301_I2C_REG_CLEAR_ADDR), MC1_UART_EN);
>
> The parenthesis around (ISP... | ISP...) are unnecessary :)
>
> [...]
>
> > +static int usbpll_setup(void)
> > +{
> > +   int n = 0;
> > +
> > +   /* 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);
> > +   while ((readl(clk_pwr->usb_ctrl) & CLK_USBCTRL_PLL_STS) == 0) {
> > +           if (n++ >= 100000) {
> > +                   printf("usbpll_setup: ERROR PLL doesn't lock\n");
> > +                   return -1;
> > +           }
>
> Please use get_timer(0) kind of loop:
>
> #define DELAY 1000 /* delay in mS */
>
> ulong start = get_timer(0);
> while (1) {
>     if (cond) { handle the cond }
>     if (get_timer(start) > DELAY) { timeout }
>     udelay(1); /* Poke WDT */
> }
>
> You actually use that below already in usb_cpu_init() .
>
> > +   }
> > +
> > +   /* enable PLL output */
> > +   setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);
> > +
> > +   return 0;
> > +}
> > +
> > +int usb_cpu_init(void)
> > +{
> > +   unsigned long start;
> > +
> > +   /* 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);
> > +           }
> > +   }
> [...]
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
Marek Vasut July 30, 2015, 2:20 p.m. UTC | #3
On Thursday, July 30, 2015 at 04:13:14 PM, LEMIEUX, SYLVAIN wrote:
> Hi Marek,

Hi!

> Thanks for the feedback. I will do the change and submit a new revision of
> the patch.
> 
> For your question regarding reference to UART 5 inside the USB driver;
> * It is possible to also  route the UART5 Tx/Rx pin to the USB D+ and D-
> pins; the driver ensure this is not selected.
>    For details, refer to UM10326, "LPC32x0 and LPC32x0/01 User manual -
> Rev. 3" table 459 and paragraph 17.2.3.2.6.
> * I will add comments in the code.

Well in that case, you should pull out a separate pinmux code and do the
pinmuxing in the board setup code, definitelly not in the USB driver.

Best regards,
Marek Vasut
diff mbox

Patch

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/drivers/i2c/lpc32xx_i2c.c b/drivers/i2c/lpc32xx_i2c.c
index 98106fa..be166b0 100644
--- a/drivers/i2c/lpc32xx_i2c.c
+++ b/drivers/i2c/lpc32xx_i2c.c
@@ -1,7 +1,7 @@ 
 /*
  * LPC32xx I2C interface driver
  *
- * (C) Copyright 2014  DENX Software Engineering GmbH
+ * (C) Copyright 2014-2015  DENX Software Engineering GmbH
  * Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
  *
  * SPDX-License-Identifier:	GPL-2.0+
@@ -60,7 +60,8 @@  struct lpc32xx_i2c_registers {
 
 static struct lpc32xx_i2c_registers *lpc32xx_i2c[] = {
 	(struct lpc32xx_i2c_registers *)I2C1_BASE,
-	(struct lpc32xx_i2c_registers *)I2C2_BASE
+	(struct lpc32xx_i2c_registers *)I2C2_BASE,
+	(struct lpc32xx_i2c_registers *)(USB_BASE + 0x300)
 };
 
 /* Set I2C bus speed */
@@ -68,11 +69,17 @@  static unsigned int lpc32xx_i2c_set_bus_speed(struct i2c_adapter *adap,
 			unsigned int speed)
 {
 	int half_period;
+	int clk_rate;
 
 	if (speed == 0)
 		return -EINVAL;
 
-	half_period = (get_hclk_clk_rate() / speed) / 2;
+	if (adap->hwadapnr == 2)
+		/* OTG I2C clock source is different. */
+		clk_rate = get_periph_clk_rate();
+	else
+		clk_rate = get_hclk_clk_rate();
+	half_period = (clk_rate / speed) / 2;
 
 	if ((half_period > 255) || (half_period < 0))
 		return -EINVAL;
@@ -247,3 +254,10 @@  U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_1, _i2c_init, lpc32xx_i2c_probe,
 			 CONFIG_SYS_I2C_LPC32XX_SPEED,
 			 CONFIG_SYS_I2C_LPC32XX_SLAVE,
 			 1)
+
+U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_2, _i2c_init, NULL,
+			 lpc32xx_i2c_read, lpc32xx_i2c_write,
+			 lpc32xx_i2c_set_bus_speed,
+			 100000,
+			 0,
+			 2)
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..fb51d42
--- /dev/null
+++ b/drivers/usb/host/ohci-lpc32xx.c
@@ -0,0 +1,245 @@ 
+/*
+ * 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 <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clk.h>
+#include <usb.h>
+#include <i2c.h>
+
+
+/* OTG I2C controller module register structures */
+struct otgi2c_regs {
+	unsigned int otg_i2c_txrx;   /* OTG I2C Tx/Rx Data FIFO */
+	unsigned int otg_i2c_stat;   /* OTG I2C Status Register */
+	unsigned int otg_i2c_ctrl;   /* OTG I2C Control Register */
+	unsigned int otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
+	unsigned int otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
+};
+
+/* OTG controller module register structures */
+struct otg_regs {
+	unsigned int reserved1[64];
+	unsigned int otg_int_sts;    /* OTG int status register */
+	unsigned int otg_int_enab;   /* OTG int enable register */
+	unsigned int otg_int_set;    /* OTG int set register */
+	unsigned int otg_int_clr;    /* OTG int clear register */
+	unsigned int otg_sts_ctrl;   /* OTG status/control register */
+	unsigned int otg_timer;      /* OTG timer register */
+	unsigned int reserved2[122];
+	struct otgi2c_regs otg_i2c;
+	unsigned int reserved3[824];
+	unsigned int otg_clk_ctrl;   /* OTG clock control reg */
+	unsigned int 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 */
+
+/* UART control structure */
+struct uartctrl_regs {
+	unsigned int ctrl;     /* General UART control register */
+	unsigned int clkmode;  /* UART clock control register */
+	unsigned int loop;     /* UART loopmode enable/disable */
+};
+
+/* UART ctrl register definitions */
+#define UART_U5_ROUTE_TO_USB		(1 << 0)
+
+/* 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	0x4
+#define ISP1301_I2C_MODE_CONTROL_2	0x12
+#define ISP1301_I2C_OTG_CONTROL_1	0x6
+#define ISP1301_I2C_INTERRUPT_LATCH	0xA
+#define ISP1301_I2C_INTERRUPT_FALLING	0xC
+#define ISP1301_I2C_INTERRUPT_RISING	0xE
+#define ISP1301_I2C_REG_CLEAR_ADDR	1
+
+
+static struct otg_regs *otg = (struct otg_regs *)USB_BASE;
+static struct uartctrl_regs *uart_ctrl = (struct uartctrl_regs *)UART_CTRL_BASE;
+static struct clk_pm_regs *clk_pwr = (struct clk_pm_regs *)CLK_PM_BASE;
+
+
+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
+			   | ISP1301_I2C_REG_CLEAR_ADDR), MC1_UART_EN);
+
+	isp1301_set_value((ISP1301_I2C_MODE_CONTROL_1
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~MC1_SPEED_REG);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1, MC1_SPEED_REG);
+	isp1301_set_value((ISP1301_I2C_MODE_CONTROL_2
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2,
+			  (MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL));
+
+	isp1301_set_value((ISP1301_I2C_OTG_CONTROL_1
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1, MC1_DAT_SE0);
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1,
+			  (OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+	isp1301_set_value((ISP1301_I2C_OTG_CONTROL_1
+			   | ISP1301_I2C_REG_CLEAR_ADDR),
+			  (OTG1_DM_PULLUP | OTG1_DP_PULLUP));
+	isp1301_set_value((ISP1301_I2C_INTERRUPT_LATCH
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	isp1301_set_value((ISP1301_I2C_INTERRUPT_FALLING
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	isp1301_set_value((ISP1301_I2C_INTERRUPT_RISING
+			   | ISP1301_I2C_REG_CLEAR_ADDR), ~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)
+{
+	int n = 0;
+
+	/* 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);
+	while ((readl(clk_pwr->usb_ctrl) & CLK_USBCTRL_PLL_STS) == 0) {
+		if (n++ >= 100000) {
+			printf("usbpll_setup: ERROR PLL doesn't lock\n");
+			return -1;
+		}
+	}
+
+	/* enable PLL output */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);
+
+	return 0;
+}
+
+int usb_cpu_init(void)
+{
+	unsigned long start;
+
+	/* 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 */
+	if (usbpll_setup() == -1)
+		return -1;
+
+	/* enable usb_host_need_clk */
+	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBHSTND_EN);
+
+	/* enable all needed USB clocks */
+	writel(OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
+	       OTG_CLK_I2C_EN | OTG_CLK_HOST_EN,
+	       &otg->otg_clk_ctrl);
+
+	start = get_timer(0);
+	while (1) {
+		if ((readl(&otg->otg_clk_ctrl) &
+		     (OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
+		      OTG_CLK_I2C_EN | OTG_CLK_HOST_EN)) ==
+		    (OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
+		     OTG_CLK_I2C_EN | OTG_CLK_HOST_EN))
+			break;
+
+		if (get_timer(start) > 100)
+			return -1;
+
+		udelay(1);
+	}
+
+	setbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
+	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
+
+	clrbits_le32(&uart_ctrl->ctrl, UART_U5_ROUTE_TO_USB);
+
+	return 0;
+}
+
+int usb_cpu_stop(void)
+{
+	/* vbus off */
+	isp1301_set_value(
+		(ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+		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();
+}