diff options
Diffstat (limited to 'drivers/usb/host')
58 files changed, 1526 insertions, 385 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 060e0e2b1ae..353cdd488b9 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -130,7 +130,7 @@ config USB_FSL_MPH_DR_OF tristate config USB_EHCI_FSL - bool "Support for Freescale on-chip EHCI USB controller" + bool "Support for Freescale PPC on-chip EHCI USB controller" depends on USB_EHCI_HCD && FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF @@ -138,7 +138,7 @@ config USB_EHCI_FSL Variation of ARC USB block used in some Freescale chips. config USB_EHCI_MXC - bool "Support for Freescale on-chip EHCI USB controller" + bool "Support for Freescale i.MX on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- @@ -194,6 +194,15 @@ config USB_EHCI_S5P help Enable support for the S5P SOC's on-chip EHCI controller. +config USB_EHCI_MV + bool "EHCI support for Marvell on-chip controller" + depends on USB_EHCI_HCD + select USB_EHCI_ROOT_HUB_TT + ---help--- + Enables support for Marvell (including PXA and MMP series) on-chip + USB SPH and OTG controller. SPH is a single port host, and it can + only be EHCI host. OTG is controller that can switch to host mode. + config USB_W90X900_EHCI bool "W90X900(W90P910) EHCI support" depends on USB_EHCI_HCD && ARCH_W90X900 @@ -210,7 +219,7 @@ config USB_CNS3XXX_EHCI config USB_EHCI_ATH79 bool "EHCI support for AR7XXX/AR9XXX SoCs" - depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X) + depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X) select USB_EHCI_ROOT_HUB_TT default y ---help--- @@ -371,6 +380,12 @@ config USB_OHCI_SH Enables support for the on-chip OHCI controller on the SuperH. If you use the PCI OHCI controller, this option is not necessary. +config USB_OHCI_EXYNOS + boolean "OHCI support for Samsung EXYNOS SoC Series" + depends on USB_OHCI_HCD && ARCH_EXYNOS + help + Enable support for the Samsung Exynos SOC's on-chip OHCI controller. + config USB_CNS3XXX_OHCI bool "Cavium CNS3XXX OHCI Module" depends on USB_OHCI_HCD && ARCH_CNS3XXX @@ -531,7 +546,7 @@ config USB_RENESAS_USBHS_HCD config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on PCI && USB + depends on PCI && USB && UWB select USB_WUSB select UWB_WHCI help @@ -544,7 +559,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on USB + depends on USB && UWB select USB_WUSB select UWB_HWA help diff --git a/drivers/usb/host/alchemy-common.c b/drivers/usb/host/alchemy-common.c index b4192c964d0..936af8359fb 100644 --- a/drivers/usb/host/alchemy-common.c +++ b/drivers/usb/host/alchemy-common.c @@ -52,9 +52,263 @@ USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ USBCFG_OME) +/* Au1300 USB config registers */ +#define USB_DWC_CTRL1 0x00 +#define USB_DWC_CTRL2 0x04 +#define USB_VBUS_TIMER 0x10 +#define USB_SBUS_CTRL 0x14 +#define USB_MSR_ERR 0x18 +#define USB_DWC_CTRL3 0x1C +#define USB_DWC_CTRL4 0x20 +#define USB_OTG_STATUS 0x28 +#define USB_DWC_CTRL5 0x2C +#define USB_DWC_CTRL6 0x30 +#define USB_DWC_CTRL7 0x34 +#define USB_PHY_STATUS 0xC0 +#define USB_INT_STATUS 0xC4 +#define USB_INT_ENABLE 0xC8 + +#define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ +#define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ +#define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ + +#define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ +#define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ +#define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ + +#define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) +#define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) +#define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) +#define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) + +#define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ + +#define USB_INTEN_FORCE 0x20 +#define USB_INTEN_PHY 0x10 +#define USB_INTEN_UDC 0x08 +#define USB_INTEN_EHCI 0x04 +#define USB_INTEN_OHCI1 0x02 +#define USB_INTEN_OHCI0 0x01 static DEFINE_SPINLOCK(alchemy_usb_lock); +static inline void __au1300_usb_phyctl(void __iomem *base, int enable) +{ + unsigned long r, s; + + r = __raw_readl(base + USB_DWC_CTRL2); + s = __raw_readl(base + USB_DWC_CTRL3); + + s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | + USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; + + if (enable) { + /* simply enable all PHYs */ + r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | + USB_DWC_CTRL2_PHYRS; + __raw_writel(r, base + USB_DWC_CTRL2); + wmb(); + } else if (!s) { + /* no USB block active, do disable all PHYs */ + r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | + USB_DWC_CTRL2_PHYRS); + __raw_writel(r, base + USB_DWC_CTRL2); + wmb(); + } +} + +static inline void __au1300_ohci_control(void __iomem *base, int enable, int id) +{ + unsigned long r; + + if (enable) { + __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ + r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN + : USB_DWC_CTRL3_OHCI1_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); /* power up the PHYs */ + + r = __raw_readl(base + USB_INT_ENABLE); + r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + /* reset the OHCI start clock bit */ + __raw_writel(0, base + USB_DWC_CTRL7); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN + : USB_DWC_CTRL3_OHCI1_CKEN); + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_ehci_control(void __iomem *base, int enable) +{ + unsigned long r; + + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL3); + r |= USB_DWC_CTRL3_EHCI0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_HSTRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + + r = __raw_readl(base + USB_INT_ENABLE); + r |= USB_INTEN_EHCI; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~USB_INTEN_EHCI; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_HSTRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~USB_DWC_CTRL3_EHCI0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_udc_control(void __iomem *base, int enable) +{ + unsigned long r; + + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_DCRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + + r = __raw_readl(base + USB_INT_ENABLE); + r |= USB_INTEN_UDC; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + } else { + r = __raw_readl(base + USB_INT_ENABLE); + r &= ~USB_INTEN_UDC; + __raw_writel(r, base + USB_INT_ENABLE); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_DCRS; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline void __au1300_otg_control(void __iomem *base, int enable) +{ + unsigned long r; + if (enable) { + r = __raw_readl(base + USB_DWC_CTRL3); + r |= USB_DWC_CTRL3_OTG0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL1); + r &= ~USB_DWC_CTRL1_OTGD; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + __au1300_usb_phyctl(base, enable); + } else { + r = __raw_readl(base + USB_DWC_CTRL1); + r |= USB_DWC_CTRL1_OTGD; + __raw_writel(r, base + USB_DWC_CTRL1); + wmb(); + + r = __raw_readl(base + USB_DWC_CTRL3); + r &= ~USB_DWC_CTRL3_OTG0_CKEN; + __raw_writel(r, base + USB_DWC_CTRL3); + wmb(); + + __au1300_usb_phyctl(base, enable); + } +} + +static inline int au1300_usb_control(int block, int enable) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + int ret = 0; + + switch (block) { + case ALCHEMY_USB_OHCI0: + __au1300_ohci_control(base, enable, 0); + break; + case ALCHEMY_USB_OHCI1: + __au1300_ohci_control(base, enable, 1); + break; + case ALCHEMY_USB_EHCI0: + __au1300_ehci_control(base, enable); + break; + case ALCHEMY_USB_UDC0: + __au1300_udc_control(base, enable); + break; + case ALCHEMY_USB_OTG0: + __au1300_otg_control(base, enable); + break; + default: + ret = -ENODEV; + } + return ret; +} + +static inline void au1300_usb_init(void) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + + /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 + * here at all: Port 2 routing (EHCI or UDC) must be set either + * by boot firmware or platform init code; I can't autodetect + * a sane setting. + */ + __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ + wmb(); + __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ + wmb(); + __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ + wmb(); + __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ + wmb(); + /* set coherent access bit */ + __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); + wmb(); +} static inline void __au1200_ohci_control(void __iomem *base, int enable) { @@ -233,6 +487,9 @@ int alchemy_usb_control(int block, int enable) case ALCHEMY_CPU_AU1200: ret = au1200_usb_control(block, enable); break; + case ALCHEMY_CPU_AU1300: + ret = au1300_usb_control(block, enable); + break; default: ret = -ENODEV; } @@ -281,6 +538,20 @@ static void au1200_usb_pm(int susp) } } +static void au1300_usb_pm(int susp) +{ + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); + /* remember Port2 routing */ + if (susp) { + alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); + } else { + au1300_usb_init(); + __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); + wmb(); + } +} + static void alchemy_usb_pm(int susp) { switch (alchemy_get_cputype()) { @@ -295,6 +566,9 @@ static void alchemy_usb_pm(int susp) case ALCHEMY_CPU_AU1200: au1200_usb_pm(susp); break; + case ALCHEMY_CPU_AU1300: + au1300_usb_pm(susp); + break; } } @@ -328,6 +602,9 @@ static int __init alchemy_usb_init(void) case ALCHEMY_CPU_AU1200: au1200_usb_init(); break; + case ALCHEMY_CPU_AU1300: + au1300_usb_init(); + break; } register_syscore_ops(&alchemy_usb_pm_ops); diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index afb6743cf09..f1424f9bc36 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -33,6 +33,10 @@ static const struct platform_device_id ehci_ath79_id_table[] = { .driver_data = EHCI_ATH79_IP_V2, }, { + .name = "ar933x-ehci", + .driver_data = EHCI_ATH79_IP_V2, + }, + { /* terminating entry */ }, }; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 18bafa99fe5..bf7441afed1 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -23,6 +23,7 @@ static int au1xxx_ehci_setup(struct usb_hcd *hcd) int ret = ehci_init(hcd); ehci->need_io_watchdog = 0; + ehci_reset(ehci); return ret; } diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index e90344a1763..c26a82e83f6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -125,7 +125,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, */ if (pdata->init && pdata->init(pdev)) { retval = -ENODEV; - goto err3; + goto err4; } /* Enable USB controller, 83xx or 8536 */ @@ -239,7 +239,7 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); } -static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) +static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) { struct usb_hcd *hcd = ehci_to_hcd(ehci); struct fsl_usb2_platform_data *pdata; @@ -299,12 +299,19 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) #endif out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); } + + if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & CTRL_PHY_CLK_VALID)) { + printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n"); + return -ENODEV; + } + return 0; } /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_fsl_reinit(struct ehci_hcd *ehci) { - ehci_fsl_usb_setup(ehci); + if (ehci_fsl_usb_setup(ehci)) + return -ENODEV; ehci_port_power(ehci, 0); return 0; diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 49180622116..bdf43e2adc5 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -45,5 +45,6 @@ #define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ #define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define CTRL_PHY_CLK_VALID (1 << 17) #define SNOOP_SIZE_2GB 0x1e #endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3ff9f82f726..a007a9fe0f8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -48,6 +48,10 @@ #include <asm/system.h> #include <asm/unaligned.h> +#if defined(CONFIG_PPC_PS3) +#include <asm/firmware.h> +#endif + /*-------------------------------------------------------------------------*/ /* @@ -108,7 +112,7 @@ module_param (park, uint, S_IRUGO); MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets"); /* for flakey hardware, ignore overcurrent indicators */ -static int ignore_oc = 0; +static bool ignore_oc = 0; module_param (ignore_oc, bool, S_IRUGO); MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); @@ -230,12 +234,58 @@ static int ehci_halt (struct ehci_hcd *ehci) STS_HALT, STS_HALT, 16 * 125); } +#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3) + +/* + * The EHCI controller of the Cell Super Companion Chip used in the + * PS3 will stop the root hub after all root hub ports are suspended. + * When in this condition handshake will return -ETIMEDOUT. The + * STS_HLT bit will not be set, so inspection of the frame index is + * used here to test for the condition. If the condition is found + * return success to allow the USB suspend to complete. + */ + +static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, + void __iomem *ptr, u32 mask, u32 done, + int usec) +{ + unsigned int old_index; + int error; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ETIMEDOUT; + + old_index = ehci_read_frame_index(ehci); + + error = handshake(ehci, ptr, mask, done, usec); + + if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index) + return 0; + + return error; +} + +#else + +static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, + void __iomem *ptr, u32 mask, u32 done, + int usec) +{ + return -ETIMEDOUT; +} + +#endif + static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, u32 mask, u32 done, int usec) { int error; error = handshake(ehci, ptr, mask, done, usec); + if (error == -ETIMEDOUT) + error = handshake_for_broken_root_hub(ehci, ptr, mask, done, + usec); + if (error) { ehci_halt(ehci); ehci->rh_state = EHCI_RH_HALTED; @@ -620,6 +670,7 @@ static int ehci_init(struct usb_hcd *hcd) hw = ehci->async->hw; hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); + hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */ hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); hw->hw_qtd_next = EHCI_LIST_END(ehci); ehci->async->qh_state = QH_STATE_LINKED; @@ -677,22 +728,13 @@ static int ehci_init(struct usb_hcd *hcd) static int ehci_run (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int retval; u32 temp; u32 hcc_params; hcd->uses_new_polling = 1; /* EHCI spec section 4.1 */ - /* - * TDI driver does the ehci_reset in their reset callback. - * Don't reset here, because configuration settings will - * vanish. - */ - if (!ehci_is_TDI(ehci) && (retval = ehci_reset(ehci)) != 0) { - ehci_mem_cleanup(ehci); - return retval; - } + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); @@ -1324,11 +1366,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_pxa168_driver #endif -#ifdef CONFIG_NLM_XLR +#ifdef CONFIG_CPU_XLR #include "ehci-xls.c" #define PLATFORM_DRIVER ehci_xls_driver #endif +#ifdef CONFIG_USB_EHCI_MV +#include "ehci-mv.c" +#define PLATFORM_DRIVER ehci_mv_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c new file mode 100644 index 00000000000..52a604fb932 --- /dev/null +++ b/drivers/usb/host/ehci-mv.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie <chao.xie@marvell.com> + * Neil Zhang <zhangwm@marvell.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/usb/otg.h> +#include <linux/platform_data/mv_usb.h> + +#define CAPLENGTH_MASK (0xff) + +struct ehci_hcd_mv { + struct usb_hcd *hcd; + + /* Which mode does this ehci running OTG/Host ? */ + int mode; + + void __iomem *phy_regs; + void __iomem *cap_regs; + void __iomem *op_regs; + + struct otg_transceiver *otg; + + struct mv_usb_platform_data *pdata; + + /* clock source and total clock number */ + unsigned int clknum; + struct clk *clk[0]; +}; + +static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) +{ + unsigned int i; + + for (i = 0; i < ehci_mv->clknum; i++) + clk_enable(ehci_mv->clk[i]); +} + +static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) +{ + unsigned int i; + + for (i = 0; i < ehci_mv->clknum; i++) + clk_disable(ehci_mv->clk[i]); +} + +static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) +{ + int retval; + + ehci_clock_enable(ehci_mv); + if (ehci_mv->pdata->phy_init) { + retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs); + if (retval) + return retval; + } + + return 0; +} + +static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) +{ + if (ehci_mv->pdata->phy_deinit) + ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs); + ehci_clock_disable(ehci_mv); +} + +static int mv_ehci_reset(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct device *dev = hcd->self.controller; + struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); + int retval; + + if (ehci_mv == NULL) { + dev_err(dev, "Can not find private ehci data\n"); + return -ENODEV; + } + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) { + dev_err(dev, "ehci_init failed %d\n", retval); + return retval; + } + + hcd->has_tt = 1; + ehci->sbrn = 0x20; + + retval = ehci_reset(ehci); + if (retval) { + dev_err(dev, "ehci_reset failed %d\n", retval); + return retval; + } + + return 0; +} + +static const struct hc_driver mv_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = mv_ehci_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +}; + +static int mv_ehci_probe(struct platform_device *pdev) +{ + struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct ehci_hcd_mv *ehci_mv; + struct resource *r; + int clk_i, retval = -ENODEV; + u32 offset; + size_t size; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform_data\n"); + return -ENODEV; + } + + if (usb_disabled()) + return -ENODEV; + + hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci"); + if (!hcd) + return -ENOMEM; + + size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum; + ehci_mv = kzalloc(size, GFP_KERNEL); + if (ehci_mv == NULL) { + dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n"); + retval = -ENOMEM; + goto err_put_hcd; + } + + platform_set_drvdata(pdev, ehci_mv); + ehci_mv->pdata = pdata; + ehci_mv->hcd = hcd; + + ehci_mv->clknum = pdata->clknum; + for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) { + ehci_mv->clk[clk_i] = + clk_get(&pdev->dev, pdata->clkname[clk_i]); + if (IS_ERR(ehci_mv->clk[clk_i])) { + dev_err(&pdev->dev, "error get clck \"%s\"\n", + pdata->clkname[clk_i]); + retval = PTR_ERR(ehci_mv->clk[clk_i]); + goto err_put_clk; + } + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); + if (r == NULL) { + dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); + retval = -ENODEV; + goto err_put_clk; + } + + ehci_mv->phy_regs = ioremap(r->start, resource_size(r)); + if (ehci_mv->phy_regs == 0) { + dev_err(&pdev->dev, "failed to map phy I/O memory\n"); + retval = -EFAULT; + goto err_put_clk; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); + if (!r) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + retval = -ENODEV; + goto err_iounmap_phyreg; + } + + ehci_mv->cap_regs = ioremap(r->start, resource_size(r)); + if (ehci_mv->cap_regs == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + retval = -EFAULT; + goto err_iounmap_phyreg; + } + + retval = mv_ehci_enable(ehci_mv); + if (retval) { + dev_err(&pdev->dev, "init phy error %d\n", retval); + goto err_iounmap_capreg; + } + + offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; + ehci_mv->op_regs = + (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); + + hcd->rsrc_start = r->start; + hcd->rsrc_len = r->end - r->start + 1; + hcd->regs = ehci_mv->op_regs; + + hcd->irq = platform_get_irq(pdev, 0); + if (!hcd->irq) { + dev_err(&pdev->dev, "Cannot get irq."); + retval = -ENODEV; + goto err_disable_clk; + } + + ehci = hcd_to_ehci(hcd); + ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; + ehci->regs = (struct ehci_regs *) ehci_mv->op_regs; + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + ehci_mv->mode = pdata->mode; + if (ehci_mv->mode == MV_USB_MODE_OTG) { +#ifdef CONFIG_USB_OTG_UTILS + ehci_mv->otg = otg_get_transceiver(); + if (!ehci_mv->otg) { + dev_err(&pdev->dev, + "unable to find transceiver\n"); + retval = -ENODEV; + goto err_disable_clk; + } + + retval = otg_set_host(ehci_mv->otg, &hcd->self); + if (retval < 0) { + dev_err(&pdev->dev, + "unable to register with transceiver\n"); + retval = -ENODEV; + goto err_put_transceiver; + } + /* otg will enable clock before use as host */ + mv_ehci_disable(ehci_mv); +#else + dev_info(&pdev->dev, "MV_USB_MODE_OTG " + "must have CONFIG_USB_OTG_UTILS enabled\n"); + goto err_disable_clk; +#endif + } else { + if (pdata->set_vbus) + pdata->set_vbus(1); + + retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + if (retval) { + dev_err(&pdev->dev, + "failed to add hcd with err %d\n", retval); + goto err_set_vbus; + } + } + + if (pdata->private_init) + pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); + + dev_info(&pdev->dev, + "successful find EHCI device with regs 0x%p irq %d" + " working in %s mode\n", hcd->regs, hcd->irq, + ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); + + return 0; + +err_set_vbus: + if (pdata->set_vbus) + pdata->set_vbus(0); +#ifdef CONFIG_USB_OTG_UTILS +err_put_transceiver: + if (ehci_mv->otg) + otg_put_transceiver(ehci_mv->otg); +#endif +err_disable_clk: + mv_ehci_disable(ehci_mv); +err_iounmap_capreg: + iounmap(ehci_mv->cap_regs); +err_iounmap_phyreg: + iounmap(ehci_mv->phy_regs); +err_put_clk: + for (clk_i--; clk_i >= 0; clk_i--) + clk_put(ehci_mv->clk[clk_i]); + platform_set_drvdata(pdev, NULL); + kfree(ehci_mv); +err_put_hcd: + usb_put_hcd(hcd); + + return retval; +} + +static int mv_ehci_remove(struct platform_device *pdev) +{ + struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_mv->hcd; + int clk_i; + + if (hcd->rh_registered) + usb_remove_hcd(hcd); + + if (ehci_mv->otg) { + otg_set_host(ehci_mv->otg, NULL); + otg_put_transceiver(ehci_mv->otg); + } + + if (ehci_mv->mode == MV_USB_MODE_HOST) { + if (ehci_mv->pdata->set_vbus) + ehci_mv->pdata->set_vbus(0); + + mv_ehci_disable(ehci_mv); + } + + iounmap(ehci_mv->cap_regs); + iounmap(ehci_mv->phy_regs); + + for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) + clk_put(ehci_mv->clk[clk_i]); + + platform_set_drvdata(pdev, NULL); + + kfree(ehci_mv); + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("mv-ehci"); + +static const struct platform_device_id ehci_id_table[] = { + {"pxa-u2oehci", PXA_U2OEHCI}, + {"pxa-sph", PXA_SPH}, + {"mmp3-hsic", MMP3_HSIC}, + {"mmp3-fsic", MMP3_FSIC}, + {}, +}; + +static void mv_ehci_shutdown(struct platform_device *pdev) +{ + struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_mv->hcd; + + if (!hcd->rh_registered) + return; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver ehci_mv_driver = { + .probe = mv_ehci_probe, + .remove = mv_ehci_remove, + .shutdown = mv_ehci_shutdown, + .driver = { + .name = "mv-ehci", + .bus = &platform_bus_type, + }, + .id_table = ehci_id_table, +}; diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index ba1f5136113..c0104882c72 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -155,6 +155,8 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + ehci_reset(ehci); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index e39b0297bad..bba9850f32f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -41,6 +41,7 @@ #include <linux/usb/ulpi.h> #include <plat/usb.h> #include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> /* EHCI Register Set */ #define EHCI_INSNREG04 (0xA0) @@ -190,11 +191,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } - ret = omap_usbhs_enable(dev); - if (ret) { - dev_err(dev, "failed to start usbhs with err %d\n", ret); - goto err_enable; - } + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); /* * An undocumented "feature" in the OMAP3 EHCI controller, @@ -228,6 +226,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); + ehci_reset(omap_ehci); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); @@ -240,11 +240,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return 0; err_add_hcd: - omap_usbhs_disable(dev); - -err_enable: disable_put_regulator(pdata); - usb_put_hcd(hcd); + pm_runtime_put_sync(dev); err_io: iounmap(regs); @@ -266,10 +263,12 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) struct usb_hcd *hcd = dev_get_drvdata(dev); usb_remove_hcd(hcd); - omap_usbhs_disable(dev); disable_put_regulator(dev->platform_data); iounmap(hcd->regs); usb_put_hcd(hcd); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return 0; } diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index a68a2a5c4b8..6c6a5a3b4ea 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -172,7 +172,7 @@ static const struct hc_driver ehci_orion_hc_driver = { static void __init ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, - struct mbus_dram_target_info *dram) + const struct mbus_dram_target_info *dram) { int i; @@ -182,7 +182,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, } for (i = 0; i < dram->num_cs; i++) { - struct mbus_dram_window *cs = dram->cs + i; + const struct mbus_dram_window *cs = dram->cs + i; wrl(USB_WINDOW_CTRL(i), ((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | @@ -194,6 +194,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) { struct orion_ehci_data *pd = pdev->dev.platform_data; + const struct mbus_dram_target_info *dram; struct resource *res; struct usb_hcd *hcd; struct ehci_hcd *ehci; @@ -259,8 +260,9 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) /* * (Re-)program MBUS remapping windows if we are asked to. */ - if (pd != NULL && pd->dram != NULL) - ehci_orion_conf_mbus_windows(hcd, pd->dram); + dram = mv_mbus_dram_info(); + if (dram) + ehci_orion_conf_mbus_windows(hcd, dram); /* * setup Orion USB controller. diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index f4b627d343a..01bb7241d6e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -276,6 +276,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); + if (pdev->vendor == PCI_VENDOR_ID_STMICRO + && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) + ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test @@ -526,6 +529,9 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0), .driver_data = (unsigned long) &ehci_pci_hc_driver, + }, { + PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST), + .driver_data = (unsigned long) &ehci_pci_hc_driver, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 2dc32da75cf..a20e496eb47 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -21,6 +21,34 @@ #include <asm/firmware.h> #include <asm/ps3.h> +static void ps3_ehci_setup_insnreg(struct ehci_hcd *ehci) +{ + /* PS3 HC internal setup register offsets. */ + + enum ps3_ehci_hc_insnreg { + ps3_ehci_hc_insnreg01 = 0x084, + ps3_ehci_hc_insnreg02 = 0x088, + ps3_ehci_hc_insnreg03 = 0x08c, + }; + + /* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its + * internal INSNREGXX setup regs back to the chip default values + * on Host Controller Reset (CMD_RESET) or Light Host Controller + * Reset (CMD_LRESET). The work-around for this is for the HC + * driver to re-initialise these regs when ever the HC is reset. + */ + + /* Set burst transfer counts to 256 out, 32 in. */ + + writel_be(0x01000020, (void __iomem *)ehci->regs + + ps3_ehci_hc_insnreg01); + + /* Enable burst transfer counts. */ + + writel_be(0x00000001, (void __iomem *)ehci->regs + + ps3_ehci_hc_insnreg03); +} + static int ps3_ehci_hc_reset(struct usb_hcd *hcd) { int result; @@ -49,6 +77,8 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) ehci_reset(ehci); + ps3_ehci_setup_insnreg(ehci); + return result; } diff --git a/drivers/usb/host/ehci-pxa168.c b/drivers/usb/host/ehci-pxa168.c index ac0c16e8f53..8d0e7a22e71 100644 --- a/drivers/usb/host/ehci-pxa168.c +++ b/drivers/usb/host/ehci-pxa168.c @@ -299,7 +299,7 @@ static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; ehci->sbrn = 0x20; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 4e4066c35a0..36ca5077cdf 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -373,6 +373,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) retry_xacterr: if ((token & QTD_STS_ACTIVE) == 0) { + /* Report Data Buffer Error: non-fatal but useful */ + if (token & QTD_STS_DBE) + ehci_dbg(ehci, + "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", + urb, + usb_endpoint_num(&urb->ep->desc), + usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out", + urb->transfer_buffer_length, + qtd, + qh); + /* on STALL, error, and short reads this urb must * complete and all its qtds must be recycled. */ @@ -647,7 +658,7 @@ qh_urb_transaction ( /* * data transfer stage: buffer setup */ - i = urb->num_sgs; + i = urb->num_mapped_sgs; if (len > 0 && i > 0) { sg = urb->sg; buf = sg_dma_address(sg); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 024b65c4990..293f7412992 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -14,8 +14,6 @@ #include <linux/clk.h> #include <linux/platform_device.h> -#include <mach/regs-pmu.h> -#include <plat/cpu.h> #include <plat/ehci.h> #include <plat/usb-phy.h> @@ -136,6 +134,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci_reset(ehci); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 56a32033adb..a60679cbbf8 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1475,6 +1475,7 @@ iso_stream_schedule ( * jump until after the queue is primed. */ else { + int done = 0; start = SCHEDULE_SLOP + (now & ~0x07); /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ @@ -1492,18 +1493,18 @@ iso_stream_schedule ( if (stream->highspeed) { if (itd_slot_ok(ehci, mod, start, stream->usecs, period)) - break; + done = 1; } else { if ((start % 8) >= 6) continue; if (sitd_slot_ok(ehci, mod, stream, start, sched, period)) - break; + done = 1; } - } while (start > next); + } while (start > next && !done); /* no room in the schedule */ - if (start == next) { + if (!done) { ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n", urb, now, now + mod); status = -ENOSPC; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index db9d1b4bfbd..dbc7fe8ca9e 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -21,7 +21,12 @@ #include <linux/platform_data/tegra_usb.h> #include <linux/irq.h> #include <linux/usb/otg.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> + #include <mach/usb_phy.h> +#include <mach/iomap.h> #define TEGRA_USB_DMA_ALIGN 32 @@ -574,6 +579,35 @@ static const struct hc_driver tegra_ehci_hc_driver = { .port_handed_over = ehci_port_handed_over, }; +static int setup_vbus_gpio(struct platform_device *pdev) +{ + int err = 0; + int gpio; + + if (!pdev->dev.of_node) + return 0; + + gpio = of_get_named_gpio(pdev->dev.of_node, "nvidia,vbus-gpio", 0); + if (!gpio_is_valid(gpio)) + return 0; + + err = gpio_request(gpio, "vbus_gpio"); + if (err) { + dev_err(&pdev->dev, "can't request vbus gpio %d", gpio); + return err; + } + err = gpio_direction_output(gpio, 1); + if (err) { + dev_err(&pdev->dev, "can't enable vbus\n"); + return err; + } + gpio_set_value(gpio, 1); + + return err; +} + +static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32); + static int tegra_ehci_probe(struct platform_device *pdev) { struct resource *res; @@ -590,6 +624,15 @@ static int tegra_ehci_probe(struct platform_device *pdev) return -EINVAL; } + /* 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. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &tegra_ehci_dma_mask; + + setup_vbus_gpio(pdev); + tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL); if (!tegra) return -ENOMEM; @@ -640,6 +683,28 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_io; } + /* This is pretty ugly and needs to be fixed when we do only + * device-tree probing. Old code relies on the platform_device + * numbering that we lack for device-tree-instantiated devices. + */ + if (instance < 0) { + switch (res->start) { + case TEGRA_USB_BASE: + instance = 0; + break; + case TEGRA_USB2_BASE: + instance = 1; + break; + case TEGRA_USB3_BASE: + instance = 2; + break; + default: + err = -ENODEV; + dev_err(&pdev->dev, "unknown usb instance\n"); + goto fail_phy; + } + } + tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config, TEGRA_USB_PHY_MODE_HOST); if (IS_ERR(tegra->phy)) { @@ -773,6 +838,11 @@ static void tegra_ehci_hcd_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +static struct of_device_id tegra_ehci_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-ehci", }, + { }, +}; + static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, .remove = tegra_ehci_remove, @@ -783,5 +853,6 @@ static struct platform_driver tegra_ehci_driver = { .shutdown = tegra_ehci_hcd_shutdown, .driver = { .name = "tegra-ehci", + .of_match_table = tegra_ehci_of_match, } }; diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 54d1ab8aec4..c1eda73916c 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -132,6 +132,8 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci_port_power(ehci, 1); + ehci_reset(ehci); + ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret == 0) { diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index d661cf7de14..3d2e26cbb34 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -78,6 +78,8 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, if (irq < 0) goto err4; + ehci_reset(ehci); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 32793ce3d9e..9c2cc463389 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -183,7 +183,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) } irq = irq_of_parse_and_map(dn, 0); - if (irq == NO_IRQ) { + if (!irq) { printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); rv = -EBUSY; goto err_irq; diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c index b4fb511d24b..72f08196f8c 100644 --- a/drivers/usb/host/ehci-xls.c +++ b/drivers/usb/host/ehci-xls.c @@ -69,7 +69,7 @@ int ehci_xls_probe_internal(const struct hc_driver *driver, } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 4ed6d19f2a5..d2623747b48 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -824,17 +824,7 @@ static struct platform_driver of_fhci_driver = { .remove = __devexit_p(of_fhci_remove), }; -static int __init fhci_module_init(void) -{ - return platform_driver_register(&of_fhci_driver); -} -module_init(fhci_module_init); - -static void __exit fhci_module_exit(void) -{ - platform_driver_unregister(&of_fhci_driver); -} -module_exit(fhci_module_exit); +module_platform_driver(of_fhci_driver); MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver"); MODULE_AUTHOR("Shlomi Gridish <gridish@freescale.com>, " diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 9037035ad1e..7916e56a725 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -297,17 +297,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = { .remove = __devexit_p(fsl_usb2_mph_dr_of_remove), }; -static int __init fsl_usb2_mph_dr_init(void) -{ - return platform_driver_register(&fsl_usb2_mph_dr_driver); -} -module_init(fsl_usb2_mph_dr_init); - -static void __exit fsl_usb2_mph_dr_exit(void) -{ - platform_driver_unregister(&fsl_usb2_mph_dr_driver); -} -module_exit(fsl_usb2_mph_dr_exit); +module_platform_driver(fsl_usb2_mph_dr_driver); MODULE_DESCRIPTION("FSL MPH DR OF devices driver"); MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 9bfac657572..104730dabd2 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -481,7 +481,7 @@ static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, encryption_value = 0; } - /* Set the encryption type for commmunicating with the device */ + /* Set the encryption type for communicating with the device */ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), USB_REQ_SET_ENCRYPTION, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, @@ -776,7 +776,6 @@ static int hwahc_probe(struct usb_interface *usb_iface, goto error_alloc; } usb_hcd->wireless = 1; - set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags); wusbhc = usb_hcd_to_wusbhc(usb_hcd); hwahc = container_of(wusbhc, struct hwahc, wusbhc); hwahc_init(hwahc); @@ -837,18 +836,7 @@ static struct usb_driver hwahc_driver = { .id_table = hwahc_id_table, }; -static int __init hwahc_driver_init(void) -{ - return usb_register(&hwahc_driver); -} -module_init(hwahc_driver_init); - -static void __exit hwahc_driver_exit(void) -{ - usb_deregister(&hwahc_driver); -} -module_exit(hwahc_driver_exit); - +module_usb_driver(hwahc_driver); MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver"); diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 2ee18cfa1ef..ff471c1c165 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -473,7 +473,7 @@ static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep) /* End handling */ /* =========================================== */ -/* Endpoint now idle - release it's ETD(s) or asssign to queued request */ +/* Endpoint now idle - release its ETD(s) or assign to queued request */ static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv) { int i; @@ -1924,18 +1924,7 @@ static struct platform_driver imx21_hcd_driver = { .resume = NULL, }; -static int __init imx21_hcd_init(void) -{ - return platform_driver_register(&imx21_hcd_driver); -} - -static void __exit imx21_hcd_cleanup(void) -{ - platform_driver_unregister(&imx21_hcd_driver); -} - -module_init(imx21_hcd_init); -module_exit(imx21_hcd_cleanup); +module_platform_driver(imx21_hcd_driver); MODULE_DESCRIPTION("i.MX21 USB Host controller"); MODULE_AUTHOR("Martin Fuzzey"); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 27dfab80ed8..fc72d44bf78 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -32,6 +32,13 @@ static struct kmem_cache *qtd_cachep; static struct kmem_cache *qh_cachep; static struct kmem_cache *urb_listitem_cachep; +enum queue_head_types { + QH_CONTROL, + QH_BULK, + QH_INTERRUPT, + QH_END +}; + struct isp1760_hcd { u32 hcs_params; spinlock_t lock; @@ -40,7 +47,7 @@ struct isp1760_hcd { struct slotinfo int_slots[32]; int int_done_map; struct memory_chunk memory_pool[BLOCKS]; - struct list_head controlqhs, bulkqhs, interruptqhs; + struct list_head qh_list[QH_END]; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -406,12 +413,12 @@ static int priv_init(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 hcc_params; + int i; spin_lock_init(&priv->lock); - INIT_LIST_HEAD(&priv->interruptqhs); - INIT_LIST_HEAD(&priv->controlqhs); - INIT_LIST_HEAD(&priv->bulkqhs); + for (i = 0; i < QH_END; i++) + INIT_LIST_HEAD(&priv->qh_list[i]); /* * hw default: 1K periodic list heads, one per frame. @@ -930,9 +937,9 @@ void schedule_ptds(struct usb_hcd *hcd) struct isp1760_hcd *priv; struct isp1760_qh *qh, *qh_next; struct list_head *ep_queue; - struct usb_host_endpoint *ep; LIST_HEAD(urb_list); struct urb_listitem *urb_listitem, *urb_listitem_next; + int i; if (!hcd) { WARN_ON(1); @@ -944,28 +951,13 @@ void schedule_ptds(struct usb_hcd *hcd) /* * check finished/retired xfers, transfer payloads, call urb_done() */ - ep_queue = &priv->interruptqhs; - while (ep_queue) { + for (i = 0; i < QH_END; i++) { + ep_queue = &priv->qh_list[i]; list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) { - ep = list_entry(qh->qtd_list.next, struct isp1760_qtd, - qtd_list)->urb->ep; collect_qtds(hcd, qh, &urb_list); - if (list_empty(&qh->qtd_list)) { + if (list_empty(&qh->qtd_list)) list_del(&qh->qh_list); - if (ep->hcpriv == NULL) { - /* Endpoint has been disabled, so we - can free the associated queue head. */ - qh_free(qh); - } - } } - - if (ep_queue == &priv->interruptqhs) - ep_queue = &priv->controlqhs; - else if (ep_queue == &priv->controlqhs) - ep_queue = &priv->bulkqhs; - else - ep_queue = NULL; } list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list, @@ -998,17 +990,10 @@ void schedule_ptds(struct usb_hcd *hcd) * * I'm sure this scheme could be improved upon! */ - ep_queue = &priv->controlqhs; - while (ep_queue) { + for (i = 0; i < QH_END; i++) { + ep_queue = &priv->qh_list[i]; list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) enqueue_qtds(hcd, qh); - - if (ep_queue == &priv->controlqhs) - ep_queue = &priv->interruptqhs; - else if (ep_queue == &priv->interruptqhs) - ep_queue = &priv->bulkqhs; - else - ep_queue = NULL; } } @@ -1543,16 +1528,16 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: - ep_queue = &priv->controlqhs; + ep_queue = &priv->qh_list[QH_CONTROL]; break; case PIPE_BULK: - ep_queue = &priv->bulkqhs; + ep_queue = &priv->qh_list[QH_BULK]; break; case PIPE_INTERRUPT: if (urb->interval < 0) return -EINVAL; /* FIXME: Check bandwidth */ - ep_queue = &priv->interruptqhs; + ep_queue = &priv->qh_list[QH_INTERRUPT]; break; case PIPE_ISOCHRONOUS: dev_err(hcd->self.controller, "%s: isochronous USB packets " @@ -1714,8 +1699,8 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, { struct isp1760_hcd *priv = hcd_to_priv(hcd); unsigned long spinflags; - struct isp1760_qh *qh; - struct isp1760_qtd *qtd; + struct isp1760_qh *qh, *qh_iter; + int i; spin_lock_irqsave(&priv->lock, spinflags); @@ -1723,14 +1708,17 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, if (!qh) goto out; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) - if (qtd->status != QTD_RETIRE) { - dequeue_urb_from_qtd(hcd, qh, qtd); - qtd->urb->status = -ECONNRESET; - } + WARN_ON(!list_empty(&qh->qtd_list)); + for (i = 0; i < QH_END; i++) + list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list) + if (qh_iter == qh) { + list_del(&qh_iter->qh_list); + i = QH_END; + break; + } + qh_free(qh); ep->hcpriv = NULL; - /* Cannot free qh here since it will be parsed by schedule_ptds() */ schedule_ptds(hcd); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index a7dc1e1d45f..4592dc17a9f 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -18,7 +18,7 @@ #include "isp1760-hcd.h" -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) #include <linux/slab.h> #include <linux/of.h> #include <linux/of_platform.h> @@ -31,7 +31,7 @@ #include <linux/pci.h> #endif -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) struct isp1760 { struct usb_hcd *hcd; int rst_gpio; @@ -47,23 +47,27 @@ static int of_isp1760_probe(struct platform_device *dev) int virq; resource_size_t res_len; int ret; - const unsigned int *prop; unsigned int devflags = 0; enum of_gpio_flags gpio_flags; + u32 bus_width = 0; drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; ret = of_address_to_resource(dp, 0, &memory); - if (ret) - return -ENXIO; + if (ret) { + ret = -ENXIO; + goto free_data; + } res_len = resource_size(&memory); res = request_mem_region(memory.start, res_len, dev_name(&dev->dev)); - if (!res) - return -EBUSY; + if (!res) { + ret = -EBUSY; + goto free_data; + } if (of_irq_map_one(dp, 0, &oirq)) { ret = -ENODEV; @@ -77,8 +81,8 @@ static int of_isp1760_probe(struct platform_device *dev) devflags |= ISP1760_FLAG_ISP1761; /* Some systems wire up only 16 of the 32 data lines */ - prop = of_get_property(dp, "bus-width", NULL); - if (prop && *prop == 16) + of_property_read_u32(dp, "bus-width", &bus_width); + if (bus_width == 16) devflags |= ISP1760_FLAG_BUS_WIDTH_16; if (of_get_property(dp, "port1-otg", NULL) != NULL) @@ -125,6 +129,7 @@ free_gpio: gpio_free(drvdata->rst_gpio); release_reg: release_mem_region(memory.start, res_len); +free_data: kfree(drvdata); return ret; } @@ -437,7 +442,7 @@ static int __init isp1760_init(void) ret = platform_driver_register(&isp1760_plat_driver); if (!ret) any_ret = 0; -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) ret = platform_driver_register(&isp1760_of_driver); if (!ret) any_ret = 0; @@ -457,7 +462,7 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { platform_driver_unregister(&isp1760_plat_driver); -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) platform_driver_unregister(&isp1760_of_driver); #endif #ifdef CONFIG_PCI diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 95a9fec38e8..77afabc77f9 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -139,8 +139,23 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, } iclk = clk_get(&pdev->dev, "ohci_clk"); + if (IS_ERR(iclk)) { + dev_err(&pdev->dev, "failed to get ohci_clk\n"); + retval = PTR_ERR(iclk); + goto err3; + } fclk = clk_get(&pdev->dev, "uhpck"); + if (IS_ERR(fclk)) { + dev_err(&pdev->dev, "failed to get uhpck\n"); + retval = PTR_ERR(fclk); + goto err4; + } hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + dev_err(&pdev->dev, "failed to get hclk\n"); + retval = PTR_ERR(hclk); + goto err5; + } at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -153,9 +168,12 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, at91_stop_hc(pdev); clk_put(hclk); + err5: clk_put(fclk); + err4: clk_put(iclk); + err3: iounmap(hcd->regs); err2: @@ -223,10 +241,11 @@ static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int if (port < 0 || port >= 2) return; - if (pdata->vbus_pin[port] <= 0) + if (!gpio_is_valid(pdata->vbus_pin[port])) return; - gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); + gpio_set_value(pdata->vbus_pin[port], + !pdata->vbus_pin_active_low[port] ^ enable); } static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) @@ -234,10 +253,11 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) if (port < 0 || port >= 2) return -EINVAL; - if (pdata->vbus_pin[port] <= 0) + if (!gpio_is_valid(pdata->vbus_pin[port])) return -EINVAL; - return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; + return gpio_get_value(pdata->vbus_pin[port]) ^ + !pdata->vbus_pin_active_low[port]; } /* @@ -465,7 +485,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { - if (pdata->vbus_pin[i] <= 0) + if (!gpio_is_valid(pdata->vbus_pin[i])) continue; gpio_request(pdata->vbus_pin[i], "ohci_vbus"); ohci_at91_usb_set_power(pdata, i, 1); @@ -474,7 +494,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { int ret; - if (pdata->overcurrent_pin[i] <= 0) + if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); @@ -499,14 +519,14 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { - if (pdata->vbus_pin[i] <= 0) + if (!gpio_is_valid(pdata->vbus_pin[i])) continue; ohci_at91_usb_set_power(pdata, i, 0); gpio_free(pdata->vbus_pin[i]); } for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { - if (pdata->overcurrent_pin[i] <= 0) + if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); gpio_free(pdata->overcurrent_pin[i]); diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 9b66df8278f..4ea63b2cac4 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -89,7 +89,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = { static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) { - int ret; + int ret, unit; struct usb_hcd *hcd; if (usb_disabled()) @@ -120,7 +120,9 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) goto err2; } - if (alchemy_usb_control(ALCHEMY_USB_OHCI0, 1)) { + unit = (hcd->rsrc_start == AU1300_USB_OHCI1_PHYS_ADDR) ? + ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; + if (alchemy_usb_control(unit, 1)) { printk(KERN_INFO "%s: controller init failed!\n", pdev->name); ret = -ENODEV; goto err3; @@ -135,7 +137,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) return ret; } - alchemy_usb_control(ALCHEMY_USB_OHCI0, 0); + alchemy_usb_control(unit, 0); err3: iounmap(hcd->regs); err2: @@ -148,9 +150,12 @@ err1: static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); + int unit; + unit = (hcd->rsrc_start == AU1300_USB_OHCI1_PHYS_ADDR) ? + ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; usb_remove_hcd(hcd); - alchemy_usb_control(ALCHEMY_USB_OHCI0, 0); + alchemy_usb_control(unit, 0); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); @@ -173,12 +178,9 @@ static int ohci_hcd_au1xxx_drv_suspend(struct device *dev) * mark HW unaccessible, bail out if RH has been resumed. Use * the spinlock to properly synchronize with possible pending * RH suspend or resume activity. - * - * This is still racy as hcd->state is manipulated outside of - * any locks =P But that will be a different fix. */ spin_lock_irqsave(&ohci->lock, flags); - if (hcd->state != HC_STATE_SUSPENDED) { + if (ohci->rh_state != OHCI_RH_SUSPENDED) { rc = -EINVAL; goto bail; } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index d7d34492934..e4bcb62b930 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -82,6 +82,14 @@ urb_print(struct urb * urb, char * str, int small, int status) ohci_dbg(ohci,format, ## arg ); \ } while (0); +/* Version for use where "next" is the address of a local variable */ +#define ohci_dbg_nosw(ohci, next, size, format, arg...) \ + do { \ + unsigned s_len; \ + s_len = scnprintf(*next, *size, format, ## arg); \ + *size -= s_len; *next += s_len; \ + } while (0); + static void ohci_dump_intr_mask ( struct ohci_hcd *ohci, @@ -127,6 +135,19 @@ static char *hcfs2string (int state) return "?"; } +static const char *rh_state_string(struct ohci_hcd *ohci) +{ + switch (ohci->rh_state) { + case OHCI_RH_HALTED: + return "halted"; + case OHCI_RH_SUSPENDED: + return "suspended"; + case OHCI_RH_RUNNING: + return "running"; + } + return "?"; +} + // dump control and status registers static void ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) @@ -136,9 +157,10 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) temp = ohci_readl (controller, ®s->revision) & 0xff; ohci_dbg_sw (controller, next, size, - "OHCI %d.%d, %s legacy support registers\n", + "OHCI %d.%d, %s legacy support registers, rh state %s\n", 0x03 & (temp >> 4), (temp & 0x0f), - (temp & 0x0100) ? "with" : "NO"); + (temp & 0x0100) ? "with" : "NO", + rh_state_string(controller)); temp = ohci_readl (controller, ®s->control); ohci_dbg_sw (controller, next, size, @@ -639,7 +661,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* dump driver info, then registers in spec order */ - ohci_dbg_sw (ohci, &next, &size, + ohci_dbg_nosw(ohci, &next, &size, "bus %s, device %s\n" "%s\n" "%s\n", @@ -658,7 +680,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* hcca */ if (ohci->hcca) - ohci_dbg_sw (ohci, &next, &size, + ohci_dbg_nosw(ohci, &next, &size, "hcca frame 0x%04x\n", ohci_frame_no(ohci)); /* other registers mostly affect frame timings */ diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index dc45d489d00..3d63574d2c7 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -179,8 +179,6 @@ static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_ ohci->next_statechange = jiffies; ep93xx_stop_hc(&pdev->dev); - hcd->state = HC_STATE_SUSPENDED; - return 0; } diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c new file mode 100644 index 00000000000..55aa35aa3d7 --- /dev/null +++ b/drivers/usb/host/ohci-exynos.c @@ -0,0 +1,274 @@ +/* + * SAMSUNG EXYNOS USB HOST OHCI Controller + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Jingoo Han <jg1.han@samsung.com> + * + * 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. + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <mach/ohci.h> +#include <plat/usb-phy.h> + +struct exynos_ohci_hcd { + struct device *dev; + struct usb_hcd *hcd; + struct clk *clk; +}; + +static int ohci_exynos_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci); + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver exynos_ohci_hc_driver = { + .description = hcd_name, + .product_desc = "EXYNOS OHCI Host Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + + .irq = ohci_irq, + .flags = HCD_MEMORY|HCD_USB11, + + .start = ohci_exynos_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + .get_frame_number = ohci_get_frame, + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int __devinit exynos_ohci_probe(struct platform_device *pdev) +{ + struct exynos4_ohci_platdata *pdata; + struct exynos_ohci_hcd *exynos_ohci; + struct usb_hcd *hcd; + struct ohci_hcd *ohci; + struct resource *res; + int irq; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL); + if (!exynos_ohci) + return -ENOMEM; + + exynos_ohci->dev = &pdev->dev; + + hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); + err = -ENOMEM; + goto fail_hcd; + } + + exynos_ohci->hcd = hcd; + exynos_ohci->clk = clk_get(&pdev->dev, "usbhost"); + + if (IS_ERR(exynos_ohci->clk)) { + dev_err(&pdev->dev, "Failed to get usbhost clock\n"); + err = PTR_ERR(exynos_ohci->clk); + goto fail_clk; + } + + err = clk_enable(exynos_ohci->clk); + if (err) + goto fail_clken; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get I/O memory\n"); + err = -ENXIO; + goto fail_io; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = ioremap(res->start, resource_size(res)); + if (!hcd->regs) { + dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + err = -ENOMEM; + goto fail_io; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "Failed to get IRQ\n"); + err = -ENODEV; + goto fail; + } + + if (pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + ohci = hcd_to_ohci(hcd); + ohci_hcd_init(ohci); + + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) { + dev_err(&pdev->dev, "Failed to add USB HCD\n"); + goto fail; + } + + platform_set_drvdata(pdev, exynos_ohci); + + return 0; + +fail: + iounmap(hcd->regs); +fail_io: + clk_disable(exynos_ohci->clk); +fail_clken: + clk_put(exynos_ohci->clk); +fail_clk: + usb_put_hcd(hcd); +fail_hcd: + kfree(exynos_ohci); + return err; +} + +static int __devexit exynos_ohci_remove(struct platform_device *pdev) +{ + struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = exynos_ohci->hcd; + + usb_remove_hcd(hcd); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + iounmap(hcd->regs); + + clk_disable(exynos_ohci->clk); + clk_put(exynos_ohci->clk); + + usb_put_hcd(hcd); + kfree(exynos_ohci); + + return 0; +} + +static void exynos_ohci_shutdown(struct platform_device *pdev) +{ + struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = exynos_ohci->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +#ifdef CONFIG_PM +static int exynos_ohci_suspend(struct device *dev) +{ + struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); + struct usb_hcd *hcd = exynos_ohci->hcd; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct platform_device *pdev = to_platform_device(dev); + struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + unsigned long flags; + int rc = 0; + + /* + * Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. + * + * This is still racy as hcd->state is manipulated outside of + * any locks =P But that will be a different fix. + */ + spin_lock_irqsave(&ohci->lock, flags); + if (hcd->state != HC_STATE_SUSPENDED && hcd->state != HC_STATE_HALT) { + rc = -EINVAL; + goto fail; + } + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); +fail: + spin_unlock_irqrestore(&ohci->lock, flags); + + return rc; +} + +static int exynos_ohci_resume(struct device *dev) +{ + struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); + struct usb_hcd *hcd = exynos_ohci->hcd; + struct platform_device *pdev = to_platform_device(dev); + struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + + if (pdata && pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + /* Mark hardware accessible again as we are out of D3 state by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + ohci_finish_controller_resume(hcd); + + return 0; +} +#else +#define exynos_ohci_suspend NULL +#define exynos_ohci_resume NULL +#endif + +static const struct dev_pm_ops exynos_ohci_pm_ops = { + .suspend = exynos_ohci_suspend, + .resume = exynos_ohci_resume, +}; + +static struct platform_driver exynos_ohci_driver = { + .probe = exynos_ohci_probe, + .remove = __devexit_p(exynos_ohci_remove), + .shutdown = exynos_ohci_shutdown, + .driver = { + .name = "exynos-ohci", + .owner = THIS_MODULE, + .pm = &exynos_ohci_pm_ops, + } +}; + +MODULE_ALIAS("platform:exynos-ohci"); +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index b2639191549..34b9edd8665 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -115,13 +115,13 @@ static inline void sb800_prefetch(struct ohci_hcd *ohci, int on) /* Some boards misreport power switching/overcurrent */ -static int distrust_firmware = 1; +static bool distrust_firmware = 1; module_param (distrust_firmware, bool, 0); MODULE_PARM_DESC (distrust_firmware, "true to distrust firmware power/overcurrent setup"); /* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */ -static int no_handshake = 0; +static bool no_handshake = 0; module_param (no_handshake, bool, 0); MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); @@ -209,7 +209,7 @@ static int ohci_urb_enqueue ( retval = -ENODEV; goto fail; } - if (!HC_IS_RUNNING(hcd->state)) { + if (ohci->rh_state != OHCI_RH_RUNNING) { retval = -ENODEV; goto fail; } @@ -274,7 +274,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) rc = usb_hcd_check_unlink_urb(hcd, urb, status); if (rc) { ; /* Do nothing */ - } else if (HC_IS_RUNNING(hcd->state)) { + } else if (ohci->rh_state == OHCI_RH_RUNNING) { urb_priv_t *urb_priv; /* Unless an IRQ completed the unlink while it was being @@ -321,7 +321,7 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) rescan: spin_lock_irqsave (&ohci->lock, flags); - if (!HC_IS_RUNNING (hcd->state)) { + if (ohci->rh_state != OHCI_RH_RUNNING) { sanitize: ed->state = ED_IDLE; if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) @@ -377,6 +377,7 @@ static void ohci_usb_reset (struct ohci_hcd *ohci) ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); ohci->hc_control &= OHCI_CTRL_RWC; ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); + ohci->rh_state = OHCI_RH_HALTED; } /* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and @@ -500,7 +501,7 @@ static int ohci_init (struct ohci_hcd *ohci) if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; - disable (ohci); + ohci->rh_state = OHCI_RH_HALTED; ohci->regs = hcd->regs; /* REVISIT this BIOS handshake is now moved into PCI "quirks", and @@ -575,7 +576,7 @@ static int ohci_run (struct ohci_hcd *ohci) int first = ohci->fminterval == 0; struct usb_hcd *hcd = ohci_to_hcd(ohci); - disable (ohci); + ohci->rh_state = OHCI_RH_HALTED; /* boot firmware should have set this up (5.1.1.3.1) */ if (first) { @@ -688,7 +689,7 @@ retry: ohci->hc_control &= OHCI_CTRL_RWC; ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); - hcd->state = HC_STATE_RUNNING; + ohci->rh_state = OHCI_RH_RUNNING; /* wake on ConnectStatusChange, matching external hubs */ ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status); @@ -725,7 +726,6 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((val >> 23) & 0x1fe); - hcd->state = HC_STATE_RUNNING; if (quirk_zfmicro(ohci)) { /* Create timer to watch for bad queue state on ZF Micro */ @@ -761,7 +761,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) * of dead, unclocked, or unplugged (CardBus...) devices */ if (ints == ~(u32)0) { - disable (ohci); + ohci->rh_state = OHCI_RH_HALTED; ohci_dbg (ohci, "device removed!\n"); usb_hc_died(hcd); return IRQ_HANDLED; @@ -771,7 +771,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ints &= ohci_readl(ohci, ®s->intrenable); /* interrupt for some other device? */ - if (ints == 0 || unlikely(hcd->state == HC_STATE_HALT)) + if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED)) return IRQ_NOTMINE; if (ints & OHCI_INTR_UE) { @@ -786,8 +786,8 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) schedule_work (&ohci->nec_work); } else { - disable (ohci); ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); + ohci->rh_state = OHCI_RH_HALTED; usb_hc_died(hcd); } @@ -871,11 +871,11 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && !ohci->ed_to_check - && HC_IS_RUNNING(hcd->state)) + && ohci->rh_state == OHCI_RH_RUNNING) ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); spin_unlock (&ohci->lock); - if (HC_IS_RUNNING(hcd->state)) { + if (ohci->rh_state == OHCI_RH_RUNNING) { ohci_writel (ohci, ints, ®s->intrstatus); ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); // flush those writes @@ -929,7 +929,7 @@ static int ohci_restart (struct ohci_hcd *ohci) struct urb_priv *priv; spin_lock_irq(&ohci->lock); - disable (ohci); + ohci->rh_state = OHCI_RH_HALTED; /* Recycle any "live" eds/tds (and urbs). */ if (!list_empty (&ohci->pending)) @@ -1005,6 +1005,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_s3c2410_driver #endif +#ifdef CONFIG_USB_OHCI_EXYNOS +#include "ohci-exynos.c" +#define PLATFORM_DRIVER exynos_ohci_driver +#endif + #ifdef CONFIG_USB_OHCI_HCD_OMAP1 #include "ohci-omap.c" #define OMAP1_PLATFORM_DRIVER ohci_hcd_omap_driver @@ -1111,7 +1116,7 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_ath79_driver #endif -#ifdef CONFIG_NLM_XLR +#ifdef CONFIG_CPU_XLR #include "ohci-xls.c" #define PLATFORM_DRIVER ohci_xls_driver #endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 2f00040fc40..836772dfabd 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -111,6 +111,7 @@ __acquires(ohci->lock) if (!autostop) { ohci->next_statechange = jiffies + msecs_to_jiffies (5); ohci->autostop = 0; + ohci->rh_state = OHCI_RH_SUSPENDED; } done: @@ -140,7 +141,7 @@ __acquires(ohci->lock) if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { /* this can happen after resuming a swsusp snapshot */ - if (hcd->state == HC_STATE_RESUMING) { + if (ohci->rh_state != OHCI_RH_RUNNING) { ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", ohci->hc_control); status = -EBUSY; @@ -274,6 +275,7 @@ skip_resume: (void) ohci_readl (ohci, &ohci->regs->control); } + ohci->rh_state = OHCI_RH_RUNNING; return 0; } @@ -336,11 +338,8 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) /* If needed, reinitialize and suspend the root hub */ if (need_reinit) { spin_lock_irq(&ohci->lock); - hcd->state = HC_STATE_RESUMING; ohci_rh_resume(ohci); - hcd->state = HC_STATE_QUIESCING; ohci_rh_suspend(ohci, 0); - hcd->state = HC_STATE_SUSPENDED; spin_unlock_irq(&ohci->lock); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index e4b8782cc6e..db3968656d2 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -516,7 +516,6 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) ohci->next_statechange = jiffies; omap_ohci_clock_power(0); - ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 516ebc4d6cc..1b8133b6e45 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <plat/usb.h> +#include <linux/pm_runtime.h> /*-------------------------------------------------------------------------*/ @@ -134,7 +135,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) int irq; if (usb_disabled()) - goto err_end; + return -ENODEV; if (!dev->parent) { dev_err(dev, "Missing parent device\n"); @@ -172,11 +173,8 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = regs; - ret = omap_usbhs_enable(dev); - if (ret) { - dev_dbg(dev, "failed to start ohci\n"); - goto err_end; - } + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -189,9 +187,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) return 0; err_add_hcd: - omap_usbhs_disable(dev); - -err_end: + pm_runtime_put_sync(dev); usb_put_hcd(hcd); err_io: @@ -220,9 +216,9 @@ static int __devexit ohci_hcd_omap3_remove(struct platform_device *pdev) iounmap(hcd->regs); usb_remove_hcd(hcd); - omap_usbhs_disable(dev); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); usb_put_hcd(hcd); - return 0; } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index bc01b064585..1843bb68ac7 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -308,12 +308,9 @@ static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) * mark HW unaccessible, bail out if RH has been resumed. Use * the spinlock to properly synchronize with possible pending * RH suspend or resume activity. - * - * This is still racy as hcd->state is manipulated outside of - * any locks =P But that will be a different fix. */ spin_lock_irqsave (&ohci->lock, flags); - if (hcd->state != HC_STATE_SUSPENDED) { + if (ohci->rh_state != OHCI_RH_SUSPENDED) { rc = -EINVAL; goto bail; } @@ -400,6 +397,10 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, + }, { + /* The device in the ConneXT I/O hub has no class reg */ + PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI), + .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, pci_ids); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 29dfefe1c72..6313e4439f3 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -502,8 +502,6 @@ static int ohci_hcd_pxa27x_drv_suspend(struct device *dev) ohci->ohci.next_statechange = jiffies; pxa27x_stop_hc(ohci, dev); - hcd->state = HC_STATE_SUSPENDED; - return 0; } diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 15dc51ded61..c5a1ea9145f 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -912,7 +912,7 @@ rescan_all: /* only take off EDs that the HC isn't using, accounting for * frame counter wraps and EDs with partially retired TDs */ - if (likely (HC_IS_RUNNING(ohci_to_hcd(ohci)->state))) { + if (likely(ohci->rh_state == OHCI_RH_RUNNING)) { if (tick_before (tick, ed->tick)) { skip_ed: last = &ed->ed_next; @@ -1012,7 +1012,7 @@ rescan_this: /* but if there's work queued, reschedule */ if (!list_empty (&ed->td_list)) { - if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state)) + if (ohci->rh_state == OHCI_RH_RUNNING) ed_schedule (ohci, ed); } @@ -1021,9 +1021,7 @@ rescan_this: } /* maybe reenable control and bulk lists */ - if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state) - && ohci_to_hcd(ohci)->state != HC_STATE_QUIESCING - && !ohci->ed_rm_list) { + if (ohci->rh_state == OHCI_RH_RUNNING && !ohci->ed_rm_list) { u32 command = 0, control = 0; if (ohci->ed_controltail) { diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index a1877c47601..56dcf069246 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -486,15 +486,66 @@ static int __devexit ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int ohci_hcd_s3c2410_drv_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct platform_device *pdev = to_platform_device(dev); + unsigned long flags; + int rc = 0; + + /* + * Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. + */ + spin_lock_irqsave(&ohci->lock, flags); + if (ohci->rh_state != OHCI_RH_SUSPENDED) { + rc = -EINVAL; + goto bail; + } + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + s3c2410_stop_hc(pdev); +bail: + spin_unlock_irqrestore(&ohci->lock, flags); + + return rc; +} + +static int ohci_hcd_s3c2410_drv_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + + s3c2410_start_hc(pdev, hcd); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + ohci_finish_controller_resume(hcd); + + return 0; +} +#else +#define ohci_hcd_s3c2410_drv_suspend NULL +#define ohci_hcd_s3c2410_drv_resume NULL +#endif + +static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = { + .suspend = ohci_hcd_s3c2410_drv_suspend, + .resume = ohci_hcd_s3c2410_drv_resume, +}; + static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe, .remove = __devexit_p(ohci_hcd_s3c2410_drv_remove), .shutdown = usb_hcd_platform_shutdown, - /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ - /*.resume = ohci_hcd_s3c2410_drv_resume, */ .driver = { .owner = THIS_MODULE, .name = "s3c2410-ohci", + .pm = &ohci_hcd_s3c2410_pm_ops, }, }; diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index afc4eb6bb9d..84686d90805 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -29,7 +29,6 @@ static int ohci_sh_start(struct usb_hcd *hcd) ohci_hcd_init(ohci); ohci_init(ohci); ohci_run(ohci); - hcd->state = HC_STATE_RUNNING; return 0; } diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 968cea2b6d4..5596ac2ba1c 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -224,7 +224,6 @@ static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) ohci->next_statechange = jiffies; sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0); - ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 69874654f3b..95c16489e88 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -203,7 +203,6 @@ static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, ohci->next_statechange = jiffies; spear_stop_ohci(ohci_p); - ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 06331d93117..120bfe6ede3 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -318,9 +318,6 @@ static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t s if (ret) return ret; } - - hcd->state = HC_STATE_SUSPENDED; - return 0; } diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c index a3a9c6f45b9..a2247867af8 100644 --- a/drivers/usb/host/ohci-xls.c +++ b/drivers/usb/host/ohci-xls.c @@ -40,7 +40,7 @@ static int ohci_xls_probe_internal(const struct hc_driver *driver, goto err1; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, driver->description)) { diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 0795b934d00..8ff6f7ea96f 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -344,6 +344,12 @@ typedef struct urb_priv { * a subset of what the full implementation needs. (Linus) */ +enum ohci_rh_state { + OHCI_RH_HALTED, + OHCI_RH_SUSPENDED, + OHCI_RH_RUNNING +}; + struct ohci_hcd { spinlock_t lock; @@ -384,6 +390,7 @@ struct ohci_hcd { /* * driver state */ + enum ohci_rh_state rh_state; int num_ports; int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ @@ -679,11 +686,6 @@ static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci, /*-------------------------------------------------------------------------*/ -static inline void disable (struct ohci_hcd *ohci) -{ - ohci_to_hcd(ohci)->state = HC_STATE_HALT; -} - #define FI 0x2edf /* 12000 bits per frame (-1) */ #define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) #define FIT (1 << 31) @@ -707,7 +709,7 @@ static inline void periodic_reinit (struct ohci_hcd *ohci) #define read_roothub(hc, register, mask) ({ \ u32 temp = ohci_readl (hc, &hc->regs->roothub.register); \ if (temp == -1) \ - disable (hc); \ + hc->rh_state = OHCI_RH_HALTED; \ else if (hc->flags & OHCI_QUIRK_AMD756) \ while (temp & mask) \ temp = ohci_readl (hc, &hc->regs->roothub.register); \ diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index dcd889803f0..015c7c62ed4 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -233,7 +233,7 @@ module_param(park, uint, S_IRUGO); MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); /* For flakey hardware, ignore overcurrent indicators */ -static int ignore_oc; +static bool ignore_oc; module_param(ignore_oc, bool, S_IRUGO); MODULE_PARM_DESC(ignore_oc, "ignore bogus hardware overcurrent indications"); @@ -3951,24 +3951,7 @@ static struct platform_driver oxu_driver = { } }; -static int __init oxu_module_init(void) -{ - int retval = 0; - - retval = platform_driver_register(&oxu_driver); - if (retval < 0) - return retval; - - return retval; -} - -static void __exit oxu_module_cleanup(void) -{ - platform_driver_unregister(&oxu_driver); -} - -module_init(oxu_module_init); -module_exit(oxu_module_cleanup); +module_platform_driver(oxu_driver); MODULE_DESCRIPTION("Oxford OXU210HP HCD driver - ver. " DRIVER_VERSION); MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index caf87428ca4..7732d69e49e 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -867,6 +867,22 @@ hc_init: static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { + /* Skip Netlogic mips SoC's internal PCI USB controller. + * This device does not need/support EHCI/OHCI handoff + */ + if (pdev->vendor == 0x184e) /* vendor Netlogic */ + return; + if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI && + pdev->class != PCI_CLASS_SERIAL_USB_OHCI && + pdev->class != PCI_CLASS_SERIAL_USB_EHCI && + pdev->class != PCI_CLASS_SERIAL_USB_XHCI) + return; + + if (pci_enable_device(pdev) < 0) { + dev_warn(&pdev->dev, "Can't enable PCI device, " + "BIOS handoff failed.\n"); + return; + } if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) @@ -875,5 +891,6 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) quirk_usb_disable_ehci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI) quirk_usb_handoff_xhci(pdev); + pci_disable_device(pdev); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 533d12cca37..16dd6a6abf0 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -74,7 +74,7 @@ MODULE_LICENSE("GPL"); #define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444) INT_MODULE_PARM(testing, 0); /* Some boards misreport power switching/overcurrent*/ -static int distrust_firmware = 1; +static bool distrust_firmware = 1; module_param(distrust_firmware, bool, 0); MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren" "t setup"); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c8ae199cfbb..6b5eb1017e2 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -59,7 +59,7 @@ #define DRIVER_DESC "USB Universal Host Controller Interface driver" /* for flakey hardware, ignore overcurrent indicators */ -static int ignore_oc; +static bool ignore_oc; module_param(ignore_oc, bool, S_IRUGO); MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications"); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index f6ca80ee4ce..d2c6f5ac462 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -943,7 +943,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, if (usb_pipein(urb->pipe)) status |= TD_CTRL_SPD; - i = urb->num_sgs; + i = urb->num_mapped_sgs; if (len > 0 && i > 0) { sg = urb->sg; data = sg_dma_address(sg); diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index d6e17542861..76083ae9213 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -124,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset) { qset->td_start = qset->td_end = qset->ntds = 0; - qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T); + qset->qh.link = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; qset->qh.err_count = 0; qset->qh.scratch[0] = 0; @@ -443,7 +443,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u remaining = urb->transfer_buffer_length; - for_each_sg(urb->sg, sg, urb->num_sgs, i) { + for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { dma_addr_t dma_addr; size_t dma_remaining; dma_addr_t sp, ep; @@ -561,7 +561,7 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, remaining = urb->transfer_buffer_length; - for_each_sg(urb->sg, sg, urb->num_sgs, i) { + for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { size_t len; size_t sg_remaining; void *orig; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 430e88fd3f6..557b6f32db8 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -57,17 +57,15 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; - /* Ugh, these should be #defines, FIXME */ - /* Using table 11-13 in USB 2.0 spec. */ temp = 0; - /* Bits 1:0 - support port power switching, or power always on */ + /* Bits 1:0 - support per-port power switching, or power always on */ if (HCC_PPC(xhci->hcc_params)) - temp |= 0x0001; + temp |= HUB_CHAR_INDV_PORT_LPSM; else - temp |= 0x0002; + temp |= HUB_CHAR_NO_LPSM; /* Bit 2 - root hubs are not part of a compound device */ /* Bits 4:3 - individual port over current protection */ - temp |= 0x0008; + temp |= HUB_CHAR_INDV_PORT_OCPM; /* Bits 6:5 - no TTs in root ports */ /* Bit 7 - no port indicators */ desc->wHubCharacteristics = cpu_to_le16(temp); @@ -86,16 +84,16 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, ports = xhci->num_usb2_ports; xhci_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = 0x29; + desc->bDescriptorType = USB_DT_HUB; temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; + desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp; /* The Device Removable bits are reported on a byte granularity. * If the port doesn't exist within that byte, the bit is set to 0. */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + portsc = xhci_readl(xhci, xhci->usb2_ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -137,8 +135,8 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, ports = xhci->num_usb3_ports; xhci_common_hub_descriptor(xhci, desc, ports); - desc->bDescriptorType = 0x2a; - desc->bDescLength = 12; + desc->bDescriptorType = USB_DT_SS_HUB; + desc->bDescLength = USB_DT_SS_HUB_SIZE; /* header decode latency should be zero for roothubs, * see section 4.23.5.2. diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 0e4b25fa3bc..383fc857491 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -42,15 +42,12 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag seg = kzalloc(sizeof *seg, flags); if (!seg) return NULL; - xhci_dbg(xhci, "Allocating priv segment structure at %p\n", seg); seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma); if (!seg->trbs) { kfree(seg); return NULL; } - xhci_dbg(xhci, "// Allocating segment at %p (virtual) 0x%llx (DMA)\n", - seg->trbs, (unsigned long long)dma); memset(seg->trbs, 0, SEGMENT_SIZE); seg->dma = dma; @@ -62,12 +59,9 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) { if (seg->trbs) { - xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n", - seg->trbs, (unsigned long long)seg->dma); dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma); seg->trbs = NULL; } - xhci_dbg(xhci, "Freeing priv segment structure at %p\n", seg); kfree(seg); } @@ -101,9 +95,6 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, val |= TRB_CHAIN; prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } - xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n", - (unsigned long long)prev->dma, - (unsigned long long)next->dma); } /* XXX: Do we need the hcd structure in all these functions? */ @@ -117,7 +108,6 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->first_seg) { first_seg = ring->first_seg; seg = first_seg->next; - xhci_dbg(xhci, "Freeing ring at %p\n", ring); while (seg != first_seg) { struct xhci_segment *next = seg->next; xhci_segment_free(xhci, seg); @@ -160,7 +150,6 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, struct xhci_segment *prev; ring = kzalloc(sizeof *(ring), flags); - xhci_dbg(xhci, "Allocating ring at %p\n", ring); if (!ring) return NULL; @@ -191,9 +180,6 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, /* See section 4.9.2.1 and 6.4.4.1 */ prev->trbs[TRBS_PER_SEGMENT-1].link.control |= cpu_to_le32(LINK_TOGGLE); - xhci_dbg(xhci, "Wrote link toggle flag to" - " segment %p (virtual), 0x%llx (DMA)\n", - prev, (unsigned long long)prev->dma); } xhci_initialize_ring_info(ring); return ring; @@ -1140,26 +1126,42 @@ static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, } /* - * Convert bInterval expressed in frames (in 1-255 range) to exponent of + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of * microframes, rounded down to nearest power of 2. */ -static unsigned int xhci_parse_frame_interval(struct usb_device *udev, - struct usb_host_endpoint *ep) +static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, + struct usb_host_endpoint *ep, unsigned int desc_interval, + unsigned int min_exponent, unsigned int max_exponent) { unsigned int interval; - interval = fls(8 * ep->desc.bInterval) - 1; - interval = clamp_val(interval, 3, 10); - if ((1 << interval) != 8 * ep->desc.bInterval) + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", ep->desc.bEndpointAddress, 1 << interval, - 8 * ep->desc.bInterval); + desc_interval); return interval; } +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval, 0, 15); +} + + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval * 8, 3, 10); +} + /* Return the polling or NAK interval. * * The polling interval is expressed in "microframes". If xHCI's Interval field @@ -1178,7 +1180,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_bulk(&ep->desc)) { - interval = ep->desc.bInterval; + interval = xhci_parse_microframe_interval(udev, ep); break; } /* Fall through - SS and HS isoc/int have same decoding */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9f1d4b15d81..b62037bff68 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -155,10 +155,6 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer while (last_trb(xhci, ring, ring->deq_seg, next)) { if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next)) { ring->cycle_state = (ring->cycle_state ? 0 : 1); - if (!in_interrupt()) - xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n", - ring, - (unsigned int) ring->cycle_state); } ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; @@ -231,10 +227,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { ring->cycle_state = (ring->cycle_state ? 0 : 1); - if (!in_interrupt()) - xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n", - ring, - (unsigned int) ring->cycle_state); } } ring->enq_seg = ring->enq_seg->next; @@ -560,12 +552,9 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, cpu_to_le32(TRB_CYCLE); cur_trb->generic.field[3] |= cpu_to_le32( TRB_TYPE(TRB_TR_NOOP)); - xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) " - "in seg %p (0x%llx dma)\n", - cur_trb, - (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb), - cur_seg, - (unsigned long long)cur_seg->dma); + xhci_dbg(xhci, "TRB to noop at offset 0x%llx\n", + (unsigned long long) + xhci_trb_virt_to_dma(cur_seg, cur_trb)); } if (cur_trb == cur_td->last_trb) break; @@ -705,9 +694,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, */ list_for_each(entry, &ep->cancelled_td_list) { cur_td = list_entry(entry, struct xhci_td, cancelled_td_list); - xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n", - cur_td->first_trb, - (unsigned long long)xhci_trb_virt_to_dma(cur_td->start_seg, cur_td->first_trb)); + xhci_dbg(xhci, "Removing canceled TD starting at 0x%llx (dma).\n", + (unsigned long long)xhci_trb_virt_to_dma( + cur_td->start_seg, cur_td->first_trb)); ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb); if (!ep_ring) { /* This shouldn't happen unless a driver is mucking @@ -1215,6 +1204,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci, * * Returns a zero-based port number, which is suitable for indexing into each of * the split roothubs' port arrays and bus state arrays. + * Add one to it in order to call xhci_find_slot_id_by_port. */ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, struct xhci_hcd *xhci, u32 port_id) @@ -1335,7 +1325,7 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_set_link_state(xhci, port_array, faked_port_index, XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index); + faked_port_index + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto cleanup; @@ -1627,7 +1617,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); - xhci_debug_trb(xhci, xhci->event_ring->dequeue); switch (trb_comp_code) { case COMP_SUCCESS: if (event_trb == ep_ring->dequeue) { @@ -1643,7 +1632,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, } break; case COMP_SHORT_TX: - xhci_warn(xhci, "WARN: short transfer on control ep\n"); if (td->urb->transfer_flags & URB_SHORT_NOT_OK) *status = -EREMOTEIO; else @@ -1946,6 +1934,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, xdev = xhci->devs[slot_id]; if (!xdev) { xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n"); + xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", + (unsigned long long) xhci_trb_virt_to_dma( + xhci->event_ring->deq_seg, + xhci->event_ring->dequeue), + lower_32_bits(le64_to_cpu(event->buffer)), + upper_32_bits(le64_to_cpu(event->buffer)), + le32_to_cpu(event->transfer_len), + le32_to_cpu(event->flags)); + xhci_dbg(xhci, "Event ring:\n"); + xhci_debug_segment(xhci, xhci->event_ring->deq_seg); return -ENODEV; } @@ -1959,6 +1957,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event for disabled endpoint " "or incorrect stream ring\n"); + xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", + (unsigned long long) xhci_trb_virt_to_dma( + xhci->event_ring->deq_seg, + xhci->event_ring->dequeue), + lower_32_bits(le64_to_cpu(event->buffer)), + upper_32_bits(le64_to_cpu(event->buffer)), + le32_to_cpu(event->transfer_len), + le32_to_cpu(event->flags)); + xhci_dbg(xhci, "Event ring:\n"); + xhci_debug_segment(xhci, xhci->event_ring->deq_seg); return -ENODEV; } @@ -1985,7 +1993,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_dbg(xhci, "Stopped on No-op or Link TRB\n"); break; case COMP_STALL: - xhci_warn(xhci, "WARN: Stalled endpoint\n"); + xhci_dbg(xhci, "Stalled endpoint\n"); ep->ep_state |= EP_HALTED; status = -EPIPE; break; @@ -1995,11 +2003,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; case COMP_SPLIT_ERR: case COMP_TX_ERR: - xhci_warn(xhci, "WARN: transfer error on endpoint\n"); + xhci_dbg(xhci, "Transfer error on endpoint\n"); status = -EPROTO; break; case COMP_BABBLE: - xhci_warn(xhci, "WARN: babble error on endpoint\n"); + xhci_dbg(xhci, "Babble error on endpoint\n"); status = -EOVERFLOW; break; case COMP_DB_ERR: @@ -2390,17 +2398,7 @@ hw_died: irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) { - irqreturn_t ret; - struct xhci_hcd *xhci; - - xhci = hcd_to_xhci(hcd); - set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - if (xhci->shared_hcd) - set_bit(HCD_FLAG_SAW_IRQ, &xhci->shared_hcd->flags); - - ret = xhci_irq(hcd); - - return ret; + return xhci_irq(hcd); } /**** Endpoint Ring Operations ****/ @@ -2488,11 +2486,6 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { ring->cycle_state = (ring->cycle_state ? 0 : 1); - if (!in_interrupt()) { - xhci_dbg(xhci, "queue_trb: Toggle cycle " - "state for ring %p = %i\n", - ring, (unsigned int)ring->cycle_state); - } } ring->enq_seg = ring->enq_seg->next; ring->enqueue = ring->enq_seg->trbs; @@ -2561,13 +2554,11 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) struct scatterlist *sg; sg = NULL; - num_sgs = urb->num_sgs; + num_sgs = urb->num_mapped_sgs; temp = urb->transfer_buffer_length; - xhci_dbg(xhci, "count sg list trbs: \n"); num_trbs = 0; for_each_sg(urb->sg, sg, num_sgs, i) { - unsigned int previous_total_trbs = num_trbs; unsigned int len = sg_dma_len(sg); /* Scatter gather list entries may cross 64KB boundaries */ @@ -2582,22 +2573,11 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) num_trbs++; running_total += TRB_MAX_BUFF_SIZE; } - xhci_dbg(xhci, " sg #%d: dma = %#llx, len = %#x (%d), num_trbs = %d\n", - i, (unsigned long long)sg_dma_address(sg), - len, len, num_trbs - previous_total_trbs); - len = min_t(int, len, temp); temp -= len; if (temp == 0) break; } - xhci_dbg(xhci, "\n"); - if (!in_interrupt()) - xhci_dbg(xhci, "ep %#x - urb len = %d, sglist used, " - "num_trbs = %d\n", - urb->ep->desc.bEndpointAddress, - urb->transfer_buffer_length, - num_trbs); return num_trbs; } @@ -2745,7 +2725,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return -EINVAL; num_trbs = count_sg_trbs_needed(xhci, urb); - num_sgs = urb->num_sgs; + num_sgs = urb->num_mapped_sgs; total_packet_count = roundup(urb->transfer_buffer_length, usb_endpoint_maxp(&urb->ep->desc)); @@ -2783,8 +2763,6 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trb_buff_len = min_t(int, trb_buff_len, this_sg_len); if (trb_buff_len > urb->transfer_buffer_length) trb_buff_len = urb->transfer_buffer_length; - xhci_dbg(xhci, "First length to xfer from 1st sglist entry = %u\n", - trb_buff_len); first_trb = true; /* Queue the first TRB, even if it's zero-length */ @@ -2816,11 +2794,6 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (usb_urb_dir_in(urb)) field |= TRB_ISP; - xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), " - "64KB boundary at %#x, end dma = %#x\n", - (unsigned int) addr, trb_buff_len, trb_buff_len, - (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), - (unsigned int) addr + trb_buff_len); if (TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) { xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n"); @@ -2926,15 +2899,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */ - if (!in_interrupt()) - xhci_dbg(xhci, "ep %#x - urb len = %#x (%d), " - "addr = %#llx, num_trbs = %d\n", - urb->ep->desc.bEndpointAddress, - urb->transfer_buffer_length, - urb->transfer_buffer_length, - (unsigned long long)urb->transfer_dma, - num_trbs); - ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, num_trbs, urb, 0, false, mem_flags); @@ -3055,9 +3019,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (!urb->setup_packet) return -EINVAL; - if (!in_interrupt()) - xhci_dbg(xhci, "Queueing ctrl tx for slot id %d, ep %d\n", - slot_id, ep_index); /* 1 TRB for setup, 1 for status */ num_trbs = 2; /* @@ -3249,15 +3210,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return -EINVAL; } - if (!in_interrupt()) - xhci_dbg(xhci, "ep %#x - urb len = %#x (%d)," - " addr = %#llx, num_tds = %d\n", - urb->ep->desc.bEndpointAddress, - urb->transfer_buffer_length, - urb->transfer_buffer_length, - (unsigned long long)urb->transfer_dma, - num_tds); - start_addr = (u64) urb->transfer_dma; start_trb = &ep_ring->enqueue->generic; start_cycle = ep_ring->cycle_state; @@ -3372,7 +3324,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check TD length */ if (running_total != td_len) { xhci_err(xhci, "ISOC TD length unmatch\n"); - return -EINVAL; + ret = -EINVAL; + goto cleanup; } } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index aa94c019579..c939f5fdef9 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -200,14 +200,14 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) ret = pci_enable_msi(pdev); if (ret) { - xhci_err(xhci, "failed to allocate MSI entry\n"); + xhci_dbg(xhci, "failed to allocate MSI entry\n"); return ret; } ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq, 0, "xhci_hcd", xhci_to_hcd(xhci)); if (ret) { - xhci_err(xhci, "disable MSI interrupt\n"); + xhci_dbg(xhci, "disable MSI interrupt\n"); pci_disable_msi(pdev); } @@ -270,7 +270,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); if (ret) { - xhci_err(xhci, "Failed to enable MSI-X\n"); + xhci_dbg(xhci, "Failed to enable MSI-X\n"); goto free_entries; } @@ -286,7 +286,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) return ret; disable_msix: - xhci_err(xhci, "disable MSI-X interrupt\n"); + xhci_dbg(xhci, "disable MSI-X interrupt\n"); xhci_free_irq(xhci); pci_disable_msix(pdev); free_entries: @@ -352,6 +352,11 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) /* hcd->irq is -1, we have MSI */ return 0; + if (!pdev->irq) { + xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); + return -EINVAL; + } + /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); @@ -711,7 +716,10 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) ring = xhci->cmd_ring; seg = ring->deq_seg; do { - memset(seg->trbs, 0, SEGMENT_SIZE); + memset(seg->trbs, 0, + sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); + seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= + cpu_to_le32(~TRB_CYCLE); seg = seg->next; } while (seg != ring->deq_seg); @@ -1330,9 +1338,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; } - xhci_dbg(xhci, "Cancel URB %p\n", urb); - xhci_dbg(xhci, "Event ring:\n"); - xhci_debug_ring(xhci, xhci->event_ring); ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; ep_ring = xhci_urb_to_transfer_ring(xhci, urb); @@ -1341,12 +1346,18 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; } - xhci_dbg(xhci, "Endpoint ring:\n"); - xhci_debug_ring(xhci, ep_ring); - urb_priv = urb->hcpriv; - - for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { + i = urb_priv->td_cnt; + if (i < urb_priv->length) + xhci_dbg(xhci, "Cancel URB %p, dev %s, ep 0x%x, " + "starting at offset 0x%llx\n", + urb, urb->dev->devpath, + urb->ep->desc.bEndpointAddress, + (unsigned long long) xhci_trb_virt_to_dma( + urb_priv->td[i]->start_seg, + urb_priv->td[i]->first_trb)); + + for (; i < urb_priv->length; i++) { td = urb_priv->td[i]; list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); } @@ -1617,6 +1628,7 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, /* FIXME: can we allocate more resources for the HC? */ break; case COMP_BW_ERR: + case COMP_2ND_BW_ERR: dev_warn(&udev->dev, "Not enough bandwidth " "for new device state.\n"); ret = -ENOSPC; @@ -2793,8 +2805,7 @@ static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci, if (ret < 0) return ret; - max_streams = USB_SS_MAX_STREAMS( - eps[i]->ss_ep_comp.bmAttributes); + max_streams = usb_ss_max_streams(&eps[i]->ss_ep_comp); if (max_streams < (*num_streams - 1)) { xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n", eps[i]->desc.bEndpointAddress, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3c8fbd2772e..fb99c837914 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1033,7 +1033,6 @@ struct xhci_transfer_event { /* Invalid Stream ID Error */ #define COMP_STRID_ERR 34 /* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ -/* FIXME - check for this */ #define COMP_2ND_BW_ERR 35 /* Split Transaction Error */ #define COMP_SPLIT_ERR 36 @@ -1356,7 +1355,7 @@ static inline unsigned int hcd_index(struct usb_hcd *hcd) return 1; } -/* There is one ehci_hci structure per controller */ +/* There is one xhci_hcd structure per controller */ struct xhci_hcd { struct usb_hcd *main_hcd; struct usb_hcd *shared_hcd; |