From: Charlie Paul <cpaul.windri...@gmail.com> Driver changes to usb host to support the AXXIA 5500 board.
Signed-off-by: Charlie Paul <cpaul.windri...@gmail.com> --- drivers/usb/host/Kconfig | 19 ++ drivers/usb/host/ehci-ci13612.c | 385 +++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-ci13612.h | 48 +++++ drivers/usb/host/ehci-hcd.c | 5 + 4 files changed, 457 insertions(+) create mode 100644 drivers/usb/host/ehci-ci13612.c create mode 100644 drivers/usb/host/ehci-ci13612.h diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a6ed85b..370b2d8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -3,6 +3,12 @@ # comment "USB Host Controller Drivers" +config LSI_USB_SW_WORKAROUND + bool "LSI USB SW Workaround for ACP34xx devices" + default n + help + LSI USB SW Workaround for ACP34xx devices + config USB_C67X00_HCD tristate "Cypress C67x00 HCD support" help @@ -358,6 +364,19 @@ config USB_ISP1362_HCD To compile this driver as a module, choose M here: the module will be called isp1362-hcd. +config USB_CI13612_HCD + tristate "CI13612A HCD support" + depends on USB + ---help--- + The CI13612A is an USB host/OTG/device controller. Enable this + option if your board has this chip. If unsure, say N. + + This driver does not support isochronous transfers and doesn't + implement OTG nor USB device controllers. + + To compile this driver as a module, choose M here: the + module will be called CI13612-hcd. + config USB_FUSBH200_HCD tristate "FUSBH200 HCD support" depends on USB diff --git a/drivers/usb/host/ehci-ci13612.c b/drivers/usb/host/ehci-ci13612.c new file mode 100644 index 0000000..6425f88 --- /dev/null +++ b/drivers/usb/host/ehci-ci13612.c @@ -0,0 +1,385 @@ + /* + * drivers/usb/host/ehci-ci13612.c + * + * USB Host Controller Driver for LSI's ACP + * + * Copyright (C) 2010 LSI Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include "ehci-ci13612.h" + + +static int ci13612_ehci_halt(struct ehci_hcd *ehci); + +#ifdef CONFIG_LSI_USB_SW_WORKAROUND +static void ci13612_usb_setup(struct usb_hcd *hcd) +{ + int USB_TXFIFOTHRES, VUSB_HS_TX_BURST; + u32 devicemode; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 txfulltuning = 0; + + if ((of_find_compatible_node(NULL, NULL, "lsi,acp3500") + != NULL) + || (of_find_compatible_node(NULL, NULL, "lsi,axxia35xx") + != NULL)) { + writel(3, USB_SBUSCFG); + return; + } + + /* Fix for HW errata 0002832: Settings of VUSB_HS_TX_BURST and + * TXFILLTUNING. + * TXFIFOTHRES should satisfy + * TXFIFOTHRES * VUSB_HS_TX_BURST >= MAXIMUM PACKET SIZE of packet + * relationship. + */ + VUSB_HS_TX_BURST = readl(USB_HWTXBUF) & 0x0f; + USB_TXFIFOTHRES = (32 << 16); + txfulltuning = (txfulltuning & 0xffc0ffff) | USB_TXFIFOTHRES; + writel(txfulltuning, (void __iomem *)USB_TXFILLTUNING); + + /* Fix for HW errata 9000556154: When operating in device mode Use + * Unspecified Length Bursts by setting SBUSCFG to 0x0, or use stream + * disable mode by setting USBMODE.SDIS to 0x1. + */ + devicemode = ehci_readl(ehci, hcd->regs + 0x1A8); + + if ((devicemode & 0x3) == 0x2) { + /* device mode */ + writel(0x0, hcd->regs + 0x90); + } else if ((devicemode & 0x3) == 0x3) { + /* host mode */ + writel(0x6, hcd->regs + 0x90); + } + + pr_err("ehci-ci13612 (ci13612_usb_setup): VUSB_HS_TX_BURST = 0x%x,USB_TXFIFOTHRES = 0x%x\n", + VUSB_HS_TX_BURST, USB_TXFIFOTHRES); + return; +} +#endif + +/* called after powerup, by probe or system-pm "wakeup" */ +static int ehci_ci13612_reinit(struct ehci_hcd *ehci) +{ + +#ifdef CONFIG_LSI_USB_SW_WORKAROUND + /* S/W workarounds are not needed in AXM55xx */ + ci13612_usb_setup(ehci_to_hcd(ehci)); +#endif + return 0; +} + + +static int ci13612_ehci_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval = 0; + int len; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + len = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* configure other settings */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + hcd->has_tt = 1; + + ehci->sbrn = 0x20; + + /* Reset is only allowed on a stopped controller */ + ci13612_ehci_halt(ehci); + + /* reset controller */ + ehci_reset(ehci); + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + hcd->self.sg_tablesize = 0; + + retval = ehci_ci13612_reinit(ehci); + + return retval; +} + +#ifdef CONFIG_LSI_USB_SW_WORKAROUND +/* + * ci13612_fixup_usbcmd_rs + * + * Fix HW errata 0003256: Do not enable USBCMD.RS for some time after the USB + * reset has been completed (PORTSCx.PR=0). This ensures that the host does not + * send the SOF until the ULPI post reset processing has been completed. Note: + * This workaround reduces the likelihood of this problem occuring, but it may + * not entirely eliminate it. + */ +static int +ci13612_fixup_usbcmd_rs(struct ehci_hcd *ehci) +{ + u32 port_status; + /* This workaround is not applicable to 3500 */ + if ((of_find_compatible_node(NULL, NULL, "lsi,acp3500") + != NULL) + || (of_find_compatible_node(NULL, NULL, "lsi,axxia35xx") + != NULL)) { + return 0; + } + + port_status = ehci_readl(ehci, &ehci->regs->port_status[0]); + pr_info("ehci-ci13612: port_status = 0x%x\n", port_status); + if (port_status & 0x100) { + pr_err("ehci-ci13612: USB port is in reset status, not able to change HC status to run\n"); + return -EFAULT; + } + return 0; +} +#else +#define ci13612_fixup_usbcmd_rs(_ehci) (0) +#endif + + +#ifdef CONFIG_LSI_USB_SW_WORKAROUND +/* + * ci13612_fixup_txpburst + * + * Fix for HW errata 9000373951: You can adjust the burst size and fill the + * level to minimize under-run possibilities. In the failing case, the transfer + * size was 96 bytes, the burst size was 16, and the fill threshold level was + * set to 2. Because of this, the Host core issued the Out token when it + * requested the second burst of data. If the burst size had been changed to 8, + * and the fill level set to 3, then the core would have pre-fetched the 96 + * bytes before issuing the OUT token. + */ +static void +ci13612_fixup_txpburst(struct ehci_hcd *ehci) +{ + unsigned burst_size; + + /* This workaround is not applicable to 3500 */ + if ((of_find_compatible_node(NULL, NULL, "lsi,acp3500") + != NULL) + || (of_find_compatible_node(NULL, NULL, "lsi,axxia35xx") + != NULL)) { + return; + } + + burst_size = ehci_readl(ehci, &ehci->regs->reserved1[1]); + burst_size = (burst_size & 0xffff00ff) | 0x4000; /* TXPBURST */ + ehci_writel(ehci, burst_size, &ehci->regs->reserved1[1]); +} +#else +#define ci13612_fixup_txpburst(ehci) do { (void)ehci; } while (0) +#endif + +static int ci13612_ehci_run(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + u32 tmp; + + retval = ci13612_fixup_usbcmd_rs(ehci); + if (retval) + return retval; + + +#ifndef CONFIG_LSI_USB_SW_WORKAROUND + /* Setup AMBA interface to force INCR16 busts when possible */ + writel(3, USB_SBUSCFG); +#endif + + retval = ehci_run(hcd); + if (retval) + return retval; + + ci13612_fixup_txpburst(ehci); + +#ifndef CONFIG_LSI_USB_SW_WORKAROUND + /* Set ITC (bits [23:16]) to zero for interrupt on every micro-frame */ + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp &= 0xFFFF; + ehci_writel(ehci, tmp & 0xFFFF, &ehci->regs->command); +#endif + + return retval; +} + +static const struct hc_driver ci13612_hc_driver = { + .description = "ci13612_hcd", + .product_desc = "CI13612A EHCI USB Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = ci13612_ehci_init, + .start = ci13612_ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ci13612_ehci_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct usb_hcd *hcd; + void __iomem *gpreg_base; + int irq; + int retval; + struct resource *res; + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "error getting irq number\n"); + retval = irq; + goto fail_create_hcd; + } + + if (0 != irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH)) { + dev_dbg(&pdev->dev, "set_irq_type() failed\n"); + retval = -EBUSY; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Error: resource addr %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + +#ifndef CONFIG_LSI_USB_SW_WORKAROUND + /* Device using 32-bit addressing */ + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; +#endif + + hcd = usb_create_hcd(&ci13612_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + hcd->regs = of_iomap(np, 0); + if (!hcd->regs) { + dev_err(&pdev->dev, "of_iomap error\n"); + retval = -ENOMEM; + goto fail_put_hcd; + } + + gpreg_base = of_iomap(np, 1); + if (!gpreg_base) { + dev_warn(&pdev->dev, "of_iomap error can't map region 1\n"); + retval = -ENOMEM; + goto fail_put_hcd; + } else { + /* Set address bits [39:32] to zero */ + writel(0x0, gpreg_base + 0x8); +#ifndef CONFIG_LSI_USB_SW_WORKAROUND + /* hprot cachable and bufferable */ + writel(0xc, gpreg_base + 0x74); +#endif + iounmap(gpreg_base); + } + + retval = usb_add_hcd(hcd, irq, 0); + if (retval == 0) { + platform_set_drvdata(pdev, hcd); + return retval; + } + +fail_put_hcd: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); + return retval; +} + +static int ci13612_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int ci13612_ehci_halt(struct ehci_hcd *ehci) +{ + u32 temp; + + temp = ehci_readl(ehci, &ehci->regs->command); + temp &= ~CMD_RUN; + ehci_writel(ehci, temp, &ehci->regs->command); + + return ehci_handshake(ehci, &ehci->regs->status, + STS_HALT, STS_HALT, 16 * 125); +} + +MODULE_ALIAS("platform:ci13612-ehci"); + +static struct of_device_id ci13612_match[] = { + { + .type = "usb", + .compatible = "lsi,acp-usb", + }, + { + .type = "usb", + .compatible = "acp-usb", + }, + {}, +}; + +static struct platform_driver ci13612_ehci_driver = { + .probe = ci13612_ehci_probe, + .remove = ci13612_ehci_remove, + .driver = { + .name = "ci13612-ehci", + .of_match_table = ci13612_match, + }, + +}; diff --git a/drivers/usb/host/ehci-ci13612.h b/drivers/usb/host/ehci-ci13612.h new file mode 100644 index 0000000..7785bdd --- /dev/null +++ b/drivers/usb/host/ehci-ci13612.h @@ -0,0 +1,48 @@ +/* + * Host interface registers + */ + +/* define CI13612 USB registers here */ +#define CI13612_USB_BASE (hcd->regs) + +#define USB_ID (CI13612_USB_BASE + 0x0000) +#define USB_HWGENERAL (CI13612_USB_BASE + 0x0004) +#define USB_HWHOST (CI13612_USB_BASE + 0x0008) +#define USB_HWDEVICE (CI13612_USB_BASE + 0x000C) +#define USB_HWTXBUF (CI13612_USB_BASE + 0x0010) +#define USB_HWRXBUF (CI13612_USB_BASE + 0x0014) +#define USB_GPTIMER0LD (CI13612_USB_BASE + 0x0080) +#define USB_GPTIMER0CTRL (CI13612_USB_BASE + 0x0084) +#define USB_GPTIMER1LD (CI13612_USB_BASE + 0x0088) +#define USB_GPTIMER1CTRL (CI13612_USB_BASE + 0x008c) +#define USB_SBUSCFG (CI13612_USB_BASE + 0x0090) + +#define USB_CAPLENGTH (CI13612_USB_BASE + 0x0100) /* 8 bit */ +#define USB_HCIVERSION (CI13612_USB_BASE + 0x0102) /* 16 bit */ +#define USB_HCSPARAMS (CI13612_USB_BASE + 0x0104) +#define USB_HCCPARAMS (CI13612_USB_BASE + 0x0108) +#define USB_DCIVERSION (CI13612_USB_BASE + 0x0120) /* 16 bit */ +#define USB_DCCPARAMS (CI13612_USB_BASE + 0x0124) +#define USB_USBCMD (CI13612_USB_BASE + 0x0140) +#define USB_USBSTS (CI13612_USB_BASE + 0x0144) +#define USB_USBINTR (CI13612_USB_BASE + 0x0148) +#define USB_FRINDEX (CI13612_USB_BASE + 0x014C) +#define USB_DEVICEADDR (CI13612_USB_BASE + 0x0154) +#define USB_ENDPOINTLISTADDR (CI13612_USB_BASE + 0x0158) +#define USB_TTCTRL (CI13612_USB_BASE + 0x015C) +#define USB_BURSTSIZE (CI13612_USB_BASE + 0x0160) +#define USB_TXFILLTUNING (CI13612_USB_BASE + 0x0164) +#define USB_ICUSB (CI13612_USB_BASE + 0x016C) +#define USB_ULPI_VIEWPORT (CI13612_USB_BASE + 0x0170) +#define USB_ENDPTNAK (CI13612_USB_BASE + 0x0178) +#define USB_ENDPTNAKEN (CI13612_USB_BASE + 0x017C) +#define USB_CONFIGFLAG (CI13612_USB_BASE + 0x0180) +#define USB_PORTSC (CI13612_USB_BASE + 0x0184) +#define USB_OTGSC (CI13612_USB_BASE + 0x01A4) +#define USB_USBMODE (CI13612_USB_BASE + 0x01A8) +#define USB_ENDPTSETUPSTAT (CI13612_USB_BASE + 0x01AC) +#define USB_ENDPTPRIME (CI13612_USB_BASE + 0x01B0) +#define USB_ENDPTFLUSH (CI13612_USB_BASE + 0x01B4) +#define USB_ENDPTSTAT (CI13612_USB_BASE + 0x01B8) +#define USB_ENDPTCOMPLETE (CI13612_USB_BASE + 0x01BC) +#define USB_ENDPTCTRL(n) (CI13612_USB_BASE + 0x01C0 + (4 * (n))) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 488a308..194e31f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1298,6 +1298,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_sead3_driver #endif +#ifdef CONFIG_USB_CI13612_HCD +#include "ehci-ci13612.c" +#define PLATFORM_DRIVER ci13612_ehci_driver +#endif + static int __init ehci_hcd_init(void) { int retval = 0; -- 1.7.9.5 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto