From: Kevin Hao <[email protected]> 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 <[email protected]> --- 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 _______________________________________________ linux-yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/linux-yocto
