diff options
author | Mark Brown <broonie@linaro.org> | 2014-05-14 19:10:54 +0100 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-05-14 19:10:54 +0100 |
commit | db015176a6e484e94e66d381b7535109db680e61 (patch) | |
tree | 26c1baed955aa1698bea0ef3b7227d481c35ae53 /drivers | |
parent | 21f9b14820917d0a21e0d4402c1796b5021c775c (diff) | |
parent | 31c39ee311f03cc286a9b3d397eb92c050407cf0 (diff) |
Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-h20ahb.c | 341 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 11 | ||||
-rw-r--r-- | drivers/usb/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-ulpi.c | 1 |
6 files changed, 359 insertions, 4 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cc3767148043..8bc8e066c881 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -175,6 +175,13 @@ config USB_EHCI_HCD_SPEAR Enables support for the on-chip EHCI controller on ST SPEAr chips. +config USB_EHCI_HCD_SYNOPSYS + tristate "Support for Synopsys Host-AHB USB 2.0 controller" + depends on USB_EHCI_HCD + ---help--- + Enable support for onchip USB controllers based on DesignWare USB 2.0 + Host-AHB Controller IP from Synopsys. + config USB_EHCI_HCD_AT91 tristate "Support for Atmel on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_AT91 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 4fb73c156d72..70df7c8c6bcd 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o +obj-$(CONFIG_USB_EHCI_HCD_SYNOPSYS) += ehci-h20ahb.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o diff --git a/drivers/usb/host/ehci-h20ahb.c b/drivers/usb/host/ehci-h20ahb.c new file mode 100644 index 000000000000..3ee3c7aa6e5b --- /dev/null +++ b/drivers/usb/host/ehci-h20ahb.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2007-2013 Texas Instruments, Inc. + * Author: Vikram Pandita <vikram.pandita@ti.com> + * Author: Anand Gadiyar <gadiyar@ti.com> + * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * Author: Roger Quadros <rogerq@ti.com> + * + * Copyright (C) 2009 Nokia Corporation + * Contact: Felipe Balbi <felipe.balbi@nokia.com> + * + * Based on ehci-omap.c - driver for USBHOST on OMAP3/4 processors + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb/ulpi.h> +#include <linux/pm_runtime.h> +#include <linux/gpio.h> +#include <linux/clk.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> + +#include "ehci.h" + +#define H20AHB_HS_USB_PORTS 1 + +/* EHCI Synopsys-specific Register Set */ +#define EHCI_INSNREG04 (0xA0) +#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5) +#define EHCI_INSNREG05_ULPI (0xA4) +#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 +#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 +#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 +#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 +#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 +#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 + +#define DRIVER_DESC "H20AHB-EHCI Host Controller driver" + +static const char hcd_name[] = "ehci-h20ahb"; + +/*-------------------------------------------------------------------------*/ + +struct h20ahb_hcd { + struct usb_phy *phy[H20AHB_HS_USB_PORTS]; /* one PHY for each port */ + int nports; +}; + +static inline void ehci_write(void __iomem *base, u32 reg, u32 val) +{ + __raw_writel(val, base + reg); +} + +static inline u32 ehci_read(void __iomem *base, u32 reg) +{ + return __raw_readl(base + reg); +} + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +static struct hc_driver __read_mostly ehci_h20ahb_hc_driver; + +static const struct ehci_driver_overrides ehci_h20ahb_overrides __initdata = { + .extra_priv_size = sizeof(struct h20ahb_hcd), +}; + +static int ehci_h20ahb_phy_read(struct usb_phy *x, u32 reg) +{ + u32 val = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) | + (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) | + (3 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) | + (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT); + ehci_write(x->io_priv, 0, val); + while ((val = ehci_read(x->io_priv, 0)) & + (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT)); + return val & 0xff; +} + +static int ehci_h20ahb_phy_write(struct usb_phy *x, u32 val, u32 reg) +{ + u32 v = (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) | + (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) | + (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) | + (reg << EHCI_INSNREG05_ULPI_REGADD_SHIFT) | + (val & 0xff); + ehci_write(x->io_priv, 0, v); + while ((v = ehci_read(x->io_priv, 0)) & + (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT)); + return 0; +} + +static struct usb_phy_io_ops ehci_h20ahb_phy_io_ops = { + .read = ehci_h20ahb_phy_read, + .write = ehci_h20ahb_phy_write, +}; + + +/** + * ehci_hcd_h20ahb_probe - initialize Synopsis-based HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int ehci_hcd_h20ahb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct usb_hcd *hcd; + void __iomem *regs; + int ret; + int irq; + int i; + struct h20ahb_hcd *h20ahb; + + if (usb_disabled()) + return -ENODEV; + + /* if (!dev->parent) { + dev_err(dev, "Missing parent device\n"); + return -ENODEV; + }*/ + + /* For DT boot, get platform data from parent. i.e. usbhshost */ + /*if (dev->of_node) { + pdata = dev_get_platdata(dev->parent); + dev->platform_data = pdata; + } + + if (!pdata) { + dev_err(dev, "Missing platform data\n"); + return -ENODEV; + }*/ + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "EHCI irq failed\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = -ENODEV; + hcd = usb_create_hcd(&ehci_h20ahb_hc_driver, dev, + dev_name(dev)); + if (!hcd) { + dev_err(dev, "Failed to create HCD\n"); + return -ENOMEM; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = regs; + hcd_to_ehci(hcd)->caps = regs; + + h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv; + h20ahb->nports = 1; + + platform_set_drvdata(pdev, hcd); + + /* get the PHY devices if needed */ + for (i = 0 ; i < h20ahb->nports ; i++) { + struct usb_phy *phy; + + /* get the PHY device */ +#if 0 + if (dev->of_node) + phy = devm_usb_get_phy_by_phandle(dev, "phys", i); + else + phy = devm_usb_get_phy_dev(dev, i); +#endif + phy = otg_ulpi_create(&ehci_h20ahb_phy_io_ops, 0); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + dev_err(dev, "Can't get PHY device for port %d: %d\n", + i, ret); + goto err_phy; + } + phy->dev = dev; + usb_add_phy_dev(phy); + + h20ahb->phy[i] = phy; + phy->io_priv = hcd->regs + EHCI_INSNREG05_ULPI; + +#if 0 + usb_phy_init(h20ahb->phy[i]); + /* bring PHY out of suspend */ + usb_phy_set_suspend(h20ahb->phy[i], 0); +#endif + } + + /* make the first port's phy the one used by hcd as well */ + hcd->phy = h20ahb->phy[0]; + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + /* + * An undocumented "feature" in the H20AHB EHCI controller, + * causes suspended ports to be taken out of suspend when + * the USBCMD.Run/Stop bit is cleared (for example when + * we do ehci_bus_suspend). + * This breaks suspend-resume if the root-hub is allowed + * to suspend. Writing 1 to this undocumented register bit + * disables this feature and restores normal behavior. + */ + ehci_write(regs, EHCI_INSNREG04, + EHCI_INSNREG04_DISABLE_UNSUSPEND); + + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret) { + dev_err(dev, "failed to add hcd with err %d\n", ret); + goto err_pm_runtime; + } + device_wakeup_enable(hcd->self.controller); + + /* + * Bring PHYs out of reset for non PHY modes. + * Even though HSIC mode is a PHY-less mode, the reset + * line exists between the chips and can be modelled + * as a PHY device for reset control. + */ + for (i = 0; i < h20ahb->nports; i++) { + usb_phy_init(h20ahb->phy[i]); + /* bring PHY out of suspend */ + usb_phy_set_suspend(h20ahb->phy[i], 0); + } + + return 0; + +err_pm_runtime: + pm_runtime_put_sync(dev); + +err_phy: + for (i = 0; i < h20ahb->nports; i++) { + if (h20ahb->phy[i]) + usb_phy_shutdown(h20ahb->phy[i]); + } + + usb_put_hcd(hcd); + + return ret; +} + + +/** + * ehci_hcd_h20ahb_remove - shutdown processing for EHCI HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usb_ehci_hcd_h20ahb_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static int ehci_hcd_h20ahb_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct h20ahb_hcd *h20ahb = (struct h20ahb_hcd *)hcd_to_ehci(hcd)->priv; + int i; + + usb_remove_hcd(hcd); + + for (i = 0; i < h20ahb->nports; i++) { + if (h20ahb->phy[i]) + usb_phy_shutdown(h20ahb->phy[i]); + } + + usb_put_hcd(hcd); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + return 0; +} + +static const struct of_device_id h20ahb_ehci_dt_ids[] = { + { .compatible = "snps,ehci-h20ahb" }, + { } +}; + +MODULE_DEVICE_TABLE(of, h20ahb_ehci_dt_ids); + +static struct platform_driver ehci_hcd_h20ahb_driver = { + .probe = ehci_hcd_h20ahb_probe, + .remove = ehci_hcd_h20ahb_remove, + .shutdown = usb_hcd_platform_shutdown, + /*.suspend = ehci_hcd_h20ahb_suspend, */ + /*.resume = ehci_hcd_h20ahb_resume, */ + .driver = { + .name = hcd_name, + .of_match_table = h20ahb_ehci_dt_ids, + } +}; + +/*-------------------------------------------------------------------------*/ + +static int __init ehci_h20ahb_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_h20ahb_hc_driver, &ehci_h20ahb_overrides); + return platform_driver_register(&ehci_hcd_h20ahb_driver); +} +module_init(ehci_h20ahb_init); + +static void __exit ehci_h20ahb_cleanup(void) +{ + platform_driver_unregister(&ehci_hcd_h20ahb_driver); +} +module_exit(ehci_h20ahb_cleanup); + +MODULE_ALIAS("platform:ehci-h20ahb"); +MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4518b8189a9e..4150380df705 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -591,11 +591,16 @@ static int ehci_run (struct usb_hcd *hcd) */ hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_64BIT_ADDR(hcc_params)) { - ehci_writel(ehci, 0, &ehci->regs->segment); -#if 0 -// this is deeply broken on almost all architectures +#if CONFIG_ARM64 + ehci_writel(ehci, ehci->periodic_dma >> 32, &ehci->regs->segment); + /* + * this is deeply broken on almost all architectures + * but arm64 can use it so enable it + */ if (!dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64))) ehci_info(ehci, "enabled 64bit DMA\n"); +#else + ehci_writel(ehci, 0, &ehci->regs->segment); #endif } diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3312ad2bb67f..0dd0da0d0ac5 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -206,7 +206,7 @@ config USB_RCAR_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" - depends on ARM + depends on ARM || ARM64 help Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index 17ea3f271bd8..4e3877c329f2 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -48,6 +48,7 @@ static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"), + ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"), ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), }; |