[linux-yocto] [PATCH 04/70] usb/ehci-ci13612: add support for ci13612 host controller

Paul Butler butler.paul at gmail.com
Mon Jun 10 18:45:27 PDT 2013


From: Kevin Hao <kexin.hao at windriver.com>

Extracted from lsi.patch in lsi_acp_linux_3.8.1.28 tarball.

[Kevin: The main modifications include:
* Remove the ci13612.h which is needless.
* Adapt to the API changes in 3.4 kernel.]

usb/ehci-ci13612: use the of match method to probe the hcd
The of platform has already registered ehci host controller
as a platform device. We can probe this device correctly by
just adding the corresponding of match table. Also move the
codes which check whether the ehci is enabled to the ci13612
specific file.

usb/ehci-ci13612: use dynamic virtual address to map the hcd registers
Use of_iomap to map the hcd registers instead of assuming that there
always has a fix virtual address we can use to access these registers.
usb/host/ehci-ci13612: stop accessing the secondary register space
Don't make the second register area mandatory, since we don't have it
in our on boards dts yet.
drivers/usb/host/ehci-ci13612.c: replaced bugzilla comments
drivers/usb/host/ehci-ci13612.c: fix a burst size issue
Fix an issue that we found with the burst size on the AXI bus.
drivers/usb: ehci-ci13612.c replaced hard-coded address and irq to use dtb

Signed-off-by: Paul Butler <paul.butler at windriver.com>
---
 drivers/usb/host/Kconfig        |  13 ++
 drivers/usb/host/ehci-ci13612.c | 289 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-ci13612.h |  48 +++++++
 drivers/usb/host/ehci-hcd.c     |   6 +
 4 files changed, 356 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 f788eb8..7748646 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -286,6 +286,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_OHCI_HCD
 	tristate "OHCI HCD support"
 	depends on USB && USB_ARCH_HAS_OHCI
diff --git a/drivers/usb/host/ehci-ci13612.c b/drivers/usb/host/ehci-ci13612.c
new file mode 100644
index 0000000..6af6a4d
--- /dev/null
+++ b/drivers/usb/host/ehci-ci13612.c
@@ -0,0 +1,289 @@
+ /*
+  * 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 "ehci-ci13612.h"
+
+
+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;
+
+	/* 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 = inl(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);
+	}
+
+	printk(KERN_INFO
+	       "ehci-ci13612 (ci13612_usb_setup): VUSB_HS_TX_BURST = 0x%x,"
+		"USB_TXFIFOTHRES = 0x%x\n", VUSB_HS_TX_BURST, USB_TXFIFOTHRES);
+	return;
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_ci13612_reinit(struct ehci_hcd *ehci)
+{
+	ci13612_usb_setup(ehci_to_hcd(ehci));
+	ehci_port_power(ehci, 0);
+
+	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 and halt 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;
+}
+
+static int ehci_run_fix(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	u32 port_status;
+	unsigned burst_size;
+	int retval;
+
+	/* 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.
+	 */
+	port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+	printk(KERN_INFO "ehci_run: port_status = 0x%x\n", port_status);
+	if (port_status & 0x100) {
+		printk(KERN_ERR
+		       "USB port is in reset status, not able to change"
+			"host controller status to run\n");
+		return -EFAULT;
+	}
+
+	retval = ehci_run(hcd);
+	if (retval)
+		return retval;
+
+    /* 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.
+     */
+	burst_size = ehci_readl(ehci, &ehci->regs->reserved[1]);
+	burst_size = (burst_size & 0xffff00ff) | 0x4000;	/* TXPBURST */
+	ehci_writel(ehci, burst_size, &ehci->regs->reserved[1]);
+
+	return 0;
+}
+
+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 | HCD_LOCAL_MEM,
+	.reset			= ci13612_ehci_init,
+	.start			= ehci_run_fix,
+	.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 usb_hcd *hcd;
+	const struct hc_driver *driver = &ci13612_hc_driver;
+	void __iomem *gpreg_base;
+	int irq;
+	int retval;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+
+      /* Check if device is enabled */
+	if (!of_device_is_available(np)) {
+		printk(KERN_INFO "%s: Port disabled via device-tree\n",
+			np->full_name);
+		return -ENODEV;
+	}
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	/* Map the irq in the PPC476 to get the irq number */
+	irq = platform_get_irq(pdev, 0);
+
+	if (NO_IRQ == irq) {
+		dev_dbg(&pdev->dev, "error getting irq number\n");
+		retval = -EBUSY;
+		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;
+	}
+
+	hcd = usb_create_hcd(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 {
+		/* Setup GPREG for USB to enable the 6-bit address line */
+		writel(0x0, gpreg_base + 0x8);
+		iounmap(gpreg_base);
+	}
+
+	retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+	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;
+}
+
+MODULE_ALIAS("platform:ci13612-ehci");
+
+static struct of_device_id ci13612_match[] = {
+	{
+		.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..b257cf7
--- /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	((unsigned) 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 bb73df6..6bdfc8d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1385,6 +1385,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ehci_platform_driver
 #endif
 
+#ifdef CONFIG_USB_CI13612_HCD
+#include "ehci-ci13612.c"
+#define PLATFORM_DRIVER	ci13612_ehci_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
@@ -1481,6 +1486,7 @@ module_init(ehci_hcd_init);
 
 static void __exit ehci_hcd_cleanup(void)
 {
+
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
 #endif
-- 
1.8.3




More information about the linux-yocto mailing list