From patchwork Mon Aug 10 12:16:34 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylvain Lemieux X-Patchwork-Id: 505605 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3C96414016A for ; Mon, 10 Aug 2015 22:17:35 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=sX4n0h6x; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 037DD4BB21; Mon, 10 Aug 2015 14:17:22 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id PYk1IiRK0I8I; Mon, 10 Aug 2015 14:17:21 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 60E5D4BB6B; Mon, 10 Aug 2015 14:17:19 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6BA1D4BB25 for ; Mon, 10 Aug 2015 14:17:10 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3B3Rlc7nd0KW for ; Mon, 10 Aug 2015 14:17:10 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ig0-f173.google.com (mail-ig0-f173.google.com [209.85.213.173]) by theia.denx.de (Postfix) with ESMTPS id 1FBEE4BB2E for ; Mon, 10 Aug 2015 14:17:03 +0200 (CEST) Received: by igfj19 with SMTP id j19so48056593igf.1 for ; Mon, 10 Aug 2015 05:17:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=hS3pTyiYhpivm076p41dEiwQ/j2ddO2hVYkTzSPDahg=; b=sX4n0h6xQB2nzOtdbS+RTrmewg7eAg+qNUsGzKI9Z6CjGNu/vPtitrRPT8Gsb+VXMV B9bou/waoom7dfOKAZTcd5vM2S7MvnQrKEm/cnRSAtcaZqGqJJGzW5UilZyfT9kg2zH/ u4TUd+7GlkMjYaPG+DCbBWjiRAOuwCtQitJn4+whn3GrfPIqS7o3InJcgMvGq49Bf3Ty LUPAIKGvsEWw20zxU+6MWMi+fhBfiwIL7GPFScPRy9/e5ckjq6FQ3i3x5rHcRqBJ56ns 6DTetDmouTmwDaVrwDX1cYao++VFiC08ANVHsFr08aiYrE6AeLjfr5GayQDxp8aEeoZo pZhg== X-Received: by 10.50.97.71 with SMTP id dy7mr10762825igb.76.1439209021956; Mon, 10 Aug 2015 05:17:01 -0700 (PDT) Received: from CABRO3AP00510.americas.tsp.ad ([74.51.240.241]) by smtp.gmail.com with ESMTPSA id w4sm5693014igl.22.2015.08.10.05.17.00 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 10 Aug 2015 05:17:00 -0700 (PDT) From: slemieux.tyco@gmail.com To: u-boot@lists.denx.de Date: Mon, 10 Aug 2015 08:16:34 -0400 Message-Id: <1439208994-19072-4-git-send-email-slemieux.tyco@gmail.com> X-Mailer: git-send-email 1.8.3.1 Cc: scottwood@freescale.com, marex@denx.de, vz@mleia.com Subject: [U-Boot] [PATCH v6 5/5] usb: lpc32xx: add host USB driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Sylvain Lemieux 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 Acked-by: Marek Vasut Tested-by: Vladimir Zapolskiy --- Changes from v5 to v6: * Addressed Marek's comments on LPC32xx USB driver: - return "-ETIMEDOUT" on timeout. * Addressed Vladimir's comments on LPC32xx USB driver: - follow alphabetic order when adding "lpc32xx_usb_init()". - use "CONFIG_SYS_HZ" when verifying if timer expired. - use "wait_for_bit()" when verify the I2C OTG clock status. 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 | 235 ++++++++++++++++++++++++++ 5 files changed, 255 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 f0af851..d9fa280 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -72,6 +72,12 @@ void lpc32xx_slc_nand_init(void) writel(CLK_NAND_SLC | CLK_NAND_SLC_SELECT, &clk->flashclk_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); +} + void lpc32xx_i2c_init(unsigned int devnum) { /* Enable I2C interface */ 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 d6e5e68..eb8010f 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -16,6 +16,7 @@ void lpc32xx_mlc_nand_init(void); void lpc32xx_slc_nand_init(void); void lpc32xx_i2c_init(unsigned int devnum); void lpc32xx_ssp_init(void); +void lpc32xx_usb_init(void); #if defined(CONFIG_SPL_BUILD) void ddr_init(const struct emc_dram_settings *dram); #endif 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..acf356b --- /dev/null +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2008 by NXP Semiconductors + * @Author: Based on code by Kevin Wells + * @Descr: USB driver - Embedded Artists LPC3250 OEM Board support functions + * + * Copyright (c) 2015 Tyco Fire Protection Products. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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) > CONFIG_SYS_HZ) + 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) +{ + 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); + + ret = wait_for_bit(&otg->otg_clk_sts, OTG_CLK_I2C_EN, 1); + if (ret) + return ret; + } + + /* 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(); +}