From patchwork Fri Jul 17 20:48:54 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sylvain Lemieux X-Patchwork-Id: 497285 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 E4EC0140D23 for ; Sat, 18 Jul 2015 07:37:02 +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=bLApQDu0; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id A5A694B664; Fri, 17 Jul 2015 23:36:27 +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 tBpv-5UXu92T; Fri, 17 Jul 2015 23:36:27 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B0FCB4B662; Fri, 17 Jul 2015 23:36:04 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8393A4B615 for ; Fri, 17 Jul 2015 22:49:21 +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 EBeL97SgZ0TV for ; Fri, 17 Jul 2015 22:49:21 +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-f182.google.com (mail-ig0-f182.google.com [209.85.213.182]) by theia.denx.de (Postfix) with ESMTPS id 0826D4A039 for ; Fri, 17 Jul 2015 22:49:17 +0200 (CEST) Received: by igvi1 with SMTP id i1so43389899igv.1 for ; Fri, 17 Jul 2015 13:49:16 -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=7lNdc9LNV1PSbGYYP3nYSyKuChniUfmWYx8pfjyIIR4=; b=bLApQDu0xy6egl9Kdm1PpLW1gvKS7DOoXvPIen3OHnurInhiN5Sbhr7dkANmUkJBnM 9swi2rWot72stPxbj3em50hA4op5Q3XBEuZv8tySA215m8MsQlHfIAHGPDYtNk7S8Paa GJ/odZWHvisOY/n5H59Rdojkdu4WoiHowVL1fBc1540FkmB7Ej81ur9b0BeFd41J3JJf 050twwFvMor7gYvv6hKDm68bSE5atTYr3vzfydvXbOs+l9TskujgUVZnpKQ9k4DediEM /tMJ8c3rlQs+/uWf0Z7QbEpdXr731krl0Xo5RM55FTucX4sdMGm6IVqHPQ82h2yuQv2O Y0LA== X-Received: by 10.50.73.165 with SMTP id m5mr406441igv.60.1437166156316; Fri, 17 Jul 2015 13:49:16 -0700 (PDT) Received: from CABRO3AP00510.americas.tsp.ad ([74.51.240.241]) by smtp.gmail.com with ESMTPSA id b140sm8186291ioe.9.2015.07.17.13.49.14 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 17 Jul 2015 13:49:15 -0700 (PDT) From: slemieux.tyco@gmail.com To: u-boot@lists.denx.de Date: Fri, 17 Jul 2015 16:48:54 -0400 Message-Id: <1437166134-21204-4-git-send-email-slemieux.tyco@gmail.com> X-Mailer: git-send-email 1.8.3.1 X-Mailman-Approved-At: Fri, 17 Jul 2015 23:35:50 +0200 Cc: scottwood@freescale.com, marex@denx.de, vz@mleia.com Subject: [U-Boot] [PATCH 3/3] 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 DMA driver from legacy LPCLinux NXP BSP. The files taken from the legacy patch are: - lpc32xx USB driver - lpc3250 header file USB registers definition. Updated 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 use of volatile in register definition taken from "lpc3250.h" header file. 5) Update driver for latest u-boot USB interface (board_usb init & board_usb_cleanup). 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. Signed-off-by: Sylvain Lemieux --- arch/arm/include/asm/arch-lpc32xx/clk.h | 12 ++ drivers/usb/host/Makefile | 1 + drivers/usb/host/ohci-lpc32xx.c | 304 ++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 drivers/usb/host/ohci-lpc32xx.c 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/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..a93ccdf --- /dev/null +++ b/drivers/usb/host/ohci-lpc32xx.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2008-2015 by NXP Semiconductors + * All rights reserved. + * + * @Author: Based on code by Kevin Wells + * @Descr: Embedded Artists LPC3250 OEM Board support functions + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + + +/* 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_SUSPEND_REG (1 << 1) +#define MC1_DAT_SE0 (1 << 2) +#define MC1_TRANSPARENT (1 << 3) +#define MC1_BDIS_ACON_EN (1 << 4) +#define MC1_OE_INT_EN (1 << 5) +#define MC1_UART_EN (1 << 6) +#define MC1_MASK 0x7f + +#define MC2_GLOBAL_PWR_DN (1 << 0) +#define MC2_SPD_SUSP_CTRL (1 << 1) +#define MC2_BI_DI (1 << 2) +#define MC2_TRANSP_BDIR0 (1 << 3) +#define MC2_TRANSP_BDIR1 (1 << 4) +#define MC2_AUDIO_EN (1 << 5) +#define MC2_PSW_EN (1 << 6) +#define MC2_EN2V7 (1 << 7) + +#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_ID_PULLDOWN (1 << 4) +#define OTG1_VBUS_DRV (1 << 5) +#define OTG1_VBUS_DISCHRG (1 << 6) +#define OTG1_VBUS_CHRG (1 << 7) + +#define OTG_B_SESS_END (1 << 6) +#define OTG_B_SESS_VLD (1 << 7) + +#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_OTG_CONTROL_2 0x10 +#define ISP1301_I2C_INTERRUPT_SOURCE 0x8 +#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 + +/* i2c_stat register definitions */ +#define I2C_TDI (1 << 0) /* Transaction Done Interrupt */ + +/* i2c_ctrl register definitions */ +#define I2C_RESET (1 << 8) /* Soft Reset */ + +#define I2C_START_BIT (1 << 8) +#define I2C_STOP_BIT (1 << 9) + +#define I2C_READ 0x01 +#define I2C_WRITE 0x00 +#define DUMMY_BYTE 0x55 + + +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 i2c_wait_reset(int timeout) +{ + while (timeout > 0 && (readl(&otg->otg_i2c.otg_i2c_ctrl) & I2C_RESET)) { + udelay(1000); + timeout--; + } + + return timeout <= 0; +} + +static int isp1301_set_value(int reg, int value) +{ + int n = 0; + + /* send isp1301 address */ + writel((ISP1301_I2C_ADDR << 1) | I2C_START_BIT, + &otg->otg_i2c.otg_i2c_txrx); + + /* offset to write to */ + writel((reg & 0xff) | I2C_WRITE, &otg->otg_i2c.otg_i2c_txrx); + /* value to write */ + writel((value & 0xff) | I2C_STOP_BIT, &otg->otg_i2c.otg_i2c_txrx); + + /* wait for transmit done (TDI) */ + while (((readl(&otg->otg_i2c.otg_i2c_stat) & I2C_TDI) != I2C_TDI) && + n++ < 100000) + ; + + if (n >= 100000) { + printf("isp1301_set_value: ERROR TDI not set\n"); + return -1; + } + + /* clear TDI */ + setbits_le32(&otg->otg_i2c.otg_i2c_stat, I2C_TDI); + + return 0; +} + +static void isp1301_configure(void) +{ + unsigned int half_period = (get_periph_clk_rate() / 100000) / 2; + writel(half_period, &otg->otg_i2c.otg_i2c_clk_hi); + writel(half_period, &otg->otg_i2c.otg_i2c_clk_lo); + + setbits_le32(&otg->otg_i2c.otg_i2c_ctrl, I2C_RESET); + i2c_wait_reset(100); + + /* 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 board_usb_init(int index, enum usb_init_type init) +{ + /* Remove warning. */ + (void)index; + + if (init != USB_INIT_HOST) + return -1; + + /* 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); + + while (readl(&otg->otg_clk_sts) != OTG_CLK_I2C_EN) + ; + } + + /* 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); + + while ((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)) + ; + + 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_board_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 board_usb_cleanup(int index, enum usb_init_type init) +{ + usb_board_stop(); + + return 0; +}