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

Paul Butler butler.paul at gmail.com
Mon May 27 09:55:35 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.]

Signed-off-by: Kevin Hao <kexin.hao at windriver.com>
---
 drivers/usb/host/Kconfig        |  13 +++
 drivers/usb/host/ehci-ci13612.c | 213 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-ci13612.h |  54 ++++++++++
 drivers/usb/host/ehci-hcd.c     |  45 +++++++++
 4 files changed, 325 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..09fd1c3
--- /dev/null
+++ b/drivers/usb/host/ehci-ci13612.c
@@ -0,0 +1,213 @@
+ /*
+  * 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 "ehci-ci13612.h"
+
+
+/* Patch the code to fix the bugs in Bugzilla */
+static void ci13612_usb_setup(struct usb_hcd *hcd)
+{
+	int USB_TXFIFOTHRES, VUSB_HS_TX_BURST;
+
+	/* fix Bugzilla #31874 */
+	/* fix Bugzilla #32212 */
+	VUSB_HS_TX_BURST = inl(USB_HWTXBUF) & 0x0f;
+	USB_TXFIFOTHRES = (inl(USB_TXFILLTUNING) & 0x3f0000) >> 16;
+
+	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 Bugzilla 33669 */
+	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;
+
+	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 *USB_base = (void __iomem *) 0xF00A0000;
+	void __iomem *gpreg_base = (void __iomem *) 0xF000C000;
+	int irq;
+	int retval;
+
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	/* Map the irq in the PPC476 to get the irq number */
+	irq = irq_create_mapping(NULL, 31);
+
+	if (NO_IRQ == irq) {
+		dev_dbg(&pdev->dev, "error mapping 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;
+	}
+
+	hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto fail_create_hcd;
+	}
+
+	hcd->rsrc_start = ci13612_PHY_ADDR;
+	hcd->rsrc_len = 0x20000;
+
+	hcd->regs = USB_base;
+
+	/* Setup GPREG for USB to enable the 6-bit address line */
+	writel(0x0, gpreg_base + 0x8);
+
+	retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+	if (retval == 0) {
+		platform_set_drvdata(pdev, hcd);
+		return retval;
+	}
+
+	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);
+	usb_put_hcd(hcd);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:ci13612-ehci");
+
+static struct platform_driver ci13612_ehci_driver = {
+	.probe = ci13612_ehci_probe,
+	.remove = ci13612_ehci_remove,
+	.driver = {
+		.name = "ci13612-ehci",
+	},
+};
diff --git a/drivers/usb/host/ehci-ci13612.h b/drivers/usb/host/ehci-ci13612.h
new file mode 100644
index 0000000..bbba3f4
--- /dev/null
+++ b/drivers/usb/host/ehci-ci13612.h
@@ -0,0 +1,54 @@
+/*
+ * System definitions
+ */
+#define ci13612_PHY_ADDR	0x0020004A0000ULL
+#define ci13162_PHY_ADDR_SIZE	0x20000
+
+/*
+ * 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..c05a245 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1385,16 +1385,53 @@ 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)
 #error "missing bus glue for ehci-hcd"
 #endif
 
+static void ci13612_device_release(struct device *dev)
+{
+}
+
+static struct platform_device ci13612_device = {
+	.name		= "ci13612-ehci",
+	.id		= -1,
+	.dev		= {
+		.release = ci13612_device_release,
+       },
+};
+
 static int __init ehci_hcd_init(void)
 {
 	int retval = 0;
 
+#ifdef CONFIG_ACP
+
+	struct device_node *np = NULL;
+	int *enabled;
+
+	np = of_find_node_by_type(np, "usb");
+
+	while (np && !of_device_is_compatible(np, "acp-usb"))
+		np = of_find_node_by_type(np, "usb");
+
+	if (np)
+		enabled = of_get_property(np, "enabled", NULL);
+	else
+		return -ENODEV;
+
+	if ((NULL == *enabled) || (0 == *enabled))
+		return -ENODEV;
+
+#endif
+
 	if (usb_disabled())
 		return -ENODEV;
 
@@ -1442,6 +1479,10 @@ static int __init ehci_hcd_init(void)
 		goto clean3;
 #endif
 
+	retval = platform_device_register(&ci13612_device);
+	if (retval < 0)
+	goto err_device_register;
+
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	retval = platform_driver_register(&XILINX_OF_PLATFORM_DRIVER);
 	if (retval < 0)
@@ -1449,6 +1490,7 @@ static int __init ehci_hcd_init(void)
 #endif
 	return retval;
 
+err_device_register:
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	/* platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER); */
 clean4:
@@ -1481,6 +1523,9 @@ module_init(ehci_hcd_init);
 
 static void __exit ehci_hcd_cleanup(void)
 {
+
+platform_device_unregister(&ci13612_device);
+
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	platform_driver_unregister(&XILINX_OF_PLATFORM_DRIVER);
 #endif
-- 
1.8.3




More information about the linux-yocto mailing list