diff options
Diffstat (limited to 'usb')
39 files changed, 4941 insertions, 0 deletions
diff --git a/usb/usb-add-of_platform-glue-driver-for-fsl-usb-dr-controller.patch b/usb/usb-add-of_platform-glue-driver-for-fsl-usb-dr-controller.patch new file mode 100644 index 00000000000000..0f12d9a906c03e --- /dev/null +++ b/usb/usb-add-of_platform-glue-driver-for-fsl-usb-dr-controller.patch @@ -0,0 +1,261 @@ +From agust@denx.de Thu Jul 22 16:54:08 2010 +From: Anatolij Gustschin <agust@denx.de> +To: linux-usb@vger.kernel.org +Cc: linuxppc-dev@ozlabs.org, Greg Kroah-Hartman <gregkh@suse.de>, + David Brownell <dbrownell@users.sourceforge.net>, + Grant Likely <grant.likely@secretlab.ca>, + Detlev Zundel <dzu@denx.de>, Wolfgang Denk <wd@denx.de>, + Anatolij Gustschin <agust@denx.de>, + Kumar Gala <galak@kernel.crashing.org> +Subject: USB: add of_platform glue driver for FSL USB DR controller +Date: Thu, 22 Jul 2010 18:25:21 +0200 +Message-Id: <1279815922-27198-3-git-send-email-agust@denx.de> + +The driver creates platform devices based on the information +from USB nodes in the flat device tree. This is the replacement +for old arch fsl_soc usb code removed by the previous patch. +It uses usual of-style binding, available EHCI-HCD and UDC +drivers can be bound to the created devices. The new of-style +driver additionaly instantiates USB OTG platform device, as the +appropriate USB OTG driver will be added soon. + +Signed-off-by: Anatolij Gustschin <agust@denx.de> +Cc: Kumar Gala <galak@kernel.crashing.org> +Cc: Grant Likely <grant.likely@secretlab.ca> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Kconfig | 1 + drivers/usb/host/Kconfig | 5 + + drivers/usb/host/Makefile | 1 + drivers/usb/host/fsl-mph-dr-of.c | 189 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 196 insertions(+) + +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -158,6 +158,7 @@ config USB_GADGET_FSL_USB2 + boolean "Freescale Highspeed USB DR Peripheral Controller" + depends on FSL_SOC || ARCH_MXC + select USB_GADGET_DUALSPEED ++ select USB_FSL_MPH_DR_OF + help + Some of Freescale PowerPC processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -112,10 +112,15 @@ config XPS_USB_HCD_XILINX + support both high speed and full speed devices, or high speed + devices only. + ++config USB_FSL_MPH_DR_OF ++ bool ++ depends on PPC_OF ++ + config USB_EHCI_FSL + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && FSL_SOC + select USB_EHCI_ROOT_HUB_TT ++ select USB_FSL_MPH_DR_OF + ---help--- + Variation of ARC USB block used in some Freescale chips. + +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -33,4 +33,5 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a665 + obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o + obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o + obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o ++obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o + +--- /dev/null ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -0,0 +1,189 @@ ++/* ++ * Setup platform devices needed by the Freescale multi-port host ++ * and/or dual-role USB controller modules based on the description ++ * in flat device tree. ++ * ++ * 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/platform_device.h> ++#include <linux/fsl_devices.h> ++#include <linux/io.h> ++#include <linux/of_platform.h> ++ ++struct fsl_usb2_dev_data { ++ char *dr_mode; /* controller mode */ ++ char *drivers[3]; /* drivers to instantiate for this mode */ ++ enum fsl_usb2_operating_modes op_mode; /* operating mode */ ++}; ++ ++struct fsl_usb2_dev_data dr_mode_data[] __devinitdata = { ++ { ++ "host", ++ { "fsl-ehci", NULL, NULL, }, ++ FSL_USB2_DR_HOST, ++ }, ++ { ++ "otg", ++ { "fsl-ehci", "fsl-usb2-udc", "fsl-usb2-otg", }, ++ FSL_USB2_DR_OTG, ++ }, ++ { ++ "periferal", ++ { "fsl-usb2-udc", NULL, NULL, }, ++ FSL_USB2_DR_DEVICE, ++ }, ++}; ++ ++struct fsl_usb2_dev_data * __devinit get_dr_mode_data(struct device_node *np) ++{ ++ const unsigned char *prop; ++ int i; ++ ++ prop = of_get_property(np, "dr_mode", NULL); ++ if (prop) { ++ for (i = 0; i < ARRAY_SIZE(dr_mode_data); i++) { ++ if (!strcmp(prop, dr_mode_data[i].dr_mode)) ++ return &dr_mode_data[i]; ++ } ++ } ++ return &dr_mode_data[0]; /* mode not specified, use host */ ++} ++ ++static enum fsl_usb2_phy_modes __devinit determine_usb_phy(const char *phy_type) ++{ ++ if (!phy_type) ++ return FSL_USB2_PHY_NONE; ++ if (!strcasecmp(phy_type, "ulpi")) ++ return FSL_USB2_PHY_ULPI; ++ if (!strcasecmp(phy_type, "utmi")) ++ return FSL_USB2_PHY_UTMI; ++ if (!strcasecmp(phy_type, "utmi_wide")) ++ return FSL_USB2_PHY_UTMI_WIDE; ++ if (!strcasecmp(phy_type, "serial")) ++ return FSL_USB2_PHY_SERIAL; ++ ++ return FSL_USB2_PHY_NONE; ++} ++ ++struct platform_device * __devinit ++fsl_usb2_device_register(struct of_device *ofdev, ++ struct fsl_usb2_platform_data *pdata, ++ const char *name, int id) ++{ ++ struct platform_device *pdev; ++ const struct resource *res = ofdev->resource; ++ unsigned int num = ofdev->num_resources; ++ int retval; ++ ++ pdev = platform_device_alloc(name, id); ++ if (!pdev) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ pdev->dev.parent = &ofdev->dev; ++ ++ pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; ++ pdev->dev.dma_mask = &pdev->archdata.dma_mask; ++ *pdev->dev.dma_mask = *ofdev->dev.dma_mask; ++ ++ retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); ++ if (retval) ++ goto error; ++ ++ if (num) { ++ retval = platform_device_add_resources(pdev, res, num); ++ if (retval) ++ goto error; ++ } ++ ++ retval = platform_device_add(pdev); ++ if (retval) ++ goto error; ++ ++ return pdev; ++ ++error: ++ platform_device_put(pdev); ++ return ERR_PTR(retval); ++} ++ ++static int __devinit fsl_usb2_mph_dr_of_probe(struct of_device *ofdev, ++ const struct of_device_id *match) ++{ ++ struct device_node *np = ofdev->dev.of_node; ++ struct platform_device *usb_dev; ++ struct fsl_usb2_platform_data data, *pdata; ++ struct fsl_usb2_dev_data *dev_data; ++ const unsigned char *prop; ++ static unsigned int idx; ++ int i; ++ ++ if (!of_device_is_available(np)) ++ return -ENODEV; ++ ++ pdata = match->data; ++ if (!pdata) { ++ memset(&data, 0, sizeof(data)); ++ pdata = &data; ++ } ++ ++ dev_data = get_dr_mode_data(np); ++ ++ if (of_device_is_compatible(np, "fsl-usb2-mph")) { ++ prop = of_get_property(np, "port0", NULL); ++ if (prop) ++ pdata->port_enables |= FSL_USB2_PORT0_ENABLED; ++ ++ prop = of_get_property(np, "port1", NULL); ++ if (prop) ++ pdata->port_enables |= FSL_USB2_PORT1_ENABLED; ++ ++ pdata->operating_mode = FSL_USB2_MPH_HOST; ++ } else { ++ /* setup mode selected in the device tree */ ++ pdata->operating_mode = dev_data->op_mode; ++ } ++ ++ prop = of_get_property(np, "phy_type", NULL); ++ pdata->phy_mode = determine_usb_phy(prop); ++ ++ for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { ++ if (!dev_data->drivers[i]) ++ continue; ++ usb_dev = fsl_usb2_device_register(ofdev, pdata, ++ dev_data->drivers[i], idx); ++ if (IS_ERR(usb_dev)) { ++ dev_err(&ofdev->dev, "Can't register usb device\n"); ++ return PTR_ERR(usb_dev); ++ } ++ } ++ idx++; ++ return 0; ++} ++ ++static const struct of_device_id fsl_usb2_mph_dr_of_match[] = { ++ { .compatible = "fsl-usb2-mph", }, ++ { .compatible = "fsl-usb2-dr", }, ++ {}, ++}; ++ ++static struct of_platform_driver fsl_usb2_mph_dr_driver = { ++ .driver = { ++ .name = "fsl-usb2-mph-dr", ++ .owner = THIS_MODULE, ++ .of_match_table = fsl_usb2_mph_dr_of_match, ++ }, ++ .probe = fsl_usb2_mph_dr_of_probe, ++}; ++ ++static int __init fsl_usb2_mph_dr_init(void) ++{ ++ return of_register_platform_driver(&fsl_usb2_mph_dr_driver); ++} ++fs_initcall(fsl_usb2_mph_dr_init); diff --git a/usb/usb-add-usb-ehci-support-for-mpc5121-soc.patch b/usb/usb-add-usb-ehci-support-for-mpc5121-soc.patch new file mode 100644 index 00000000000000..6fa814f54f40e8 --- /dev/null +++ b/usb/usb-add-usb-ehci-support-for-mpc5121-soc.patch @@ -0,0 +1,503 @@ +From agust@denx.de Thu Jul 22 16:54:48 2010 +From: Anatolij Gustschin <agust@denx.de> +To: linux-usb@vger.kernel.org +Cc: linuxppc-dev@ozlabs.org, Greg Kroah-Hartman <gregkh@suse.de>, + David Brownell <dbrownell@users.sourceforge.net>, + Grant Likely <grant.likely@secretlab.ca>, + Detlev Zundel <dzu@denx.de>, Wolfgang Denk <wd@denx.de>, + Anatolij Gustschin <agust@denx.de> +Subject: USB: add USB EHCI support for MPC5121 SoC +Date: Thu, 22 Jul 2010 18:25:22 +0200 +Message-Id: <1279815922-27198-4-git-send-email-agust@denx.de> + +Extends FSL EHCI platform driver glue layer to support +MPC5121 USB controllers. MPC5121 Rev 2.0 silicon EHCI +registers are in big endian format. The appropriate flags +are set using the information in the platform data structure. +MPC83xx system interface registers are not available on +MPC512x, so the access to these registers is isolated in +MPC512x case. Furthermore the USB controller clocks +must be enabled before 512x register accesses which is +done by providing platform specific init callback. + +The MPC512x internal USB PHY doesn't provide supply voltage. +For boards using different power switches allow specifying +DRVVBUS and PWR_FAULT signal polarity of the MPC5121 internal +PHY using "fsl,invert-drvvbus" and "fsl,invert-pwr-fault" +properties in the device tree USB nodes. Adds documentation +for this new device tree bindings. + +Signed-off-by: Anatolij Gustschin <agust@denx.de> +Cc: Grant Likely <grant.likely@secretlab.ca> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/powerpc/dts-bindings/fsl/usb.txt | 22 +++++ + drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 6 - + drivers/usb/host/ehci-fsl.c | 107 ++++++++++++++++++------- + drivers/usb/host/ehci-fsl.h | 19 ++++ + drivers/usb/host/ehci-mem.c | 2 + drivers/usb/host/fsl-mph-dr-of.c | 89 ++++++++++++++++++++ + include/linux/fsl_devices.h | 15 +++ + 8 files changed, 229 insertions(+), 32 deletions(-) + +--- a/Documentation/powerpc/dts-bindings/fsl/usb.txt ++++ b/Documentation/powerpc/dts-bindings/fsl/usb.txt +@@ -8,6 +8,7 @@ and additions : + Required properties : + - compatible : Should be "fsl-usb2-mph" for multi port host USB + controllers, or "fsl-usb2-dr" for dual role USB controllers ++ or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121 + - phy_type : For multi port host USB controllers, should be one of + "ulpi", or "serial". For dual role USB controllers, should be + one of "ulpi", "utmi", "utmi_wide", or "serial". +@@ -33,6 +34,12 @@ Recommended properties : + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + ++Optional properties : ++ - fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the ++ port power polarity of internal PHY signal DRVVBUS is inverted. ++ - fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates ++ the PWR_FAULT signal polarity is inverted. ++ + Example multi port host USB controller device node : + usb@22000 { + compatible = "fsl-usb2-mph"; +@@ -57,3 +64,18 @@ Example dual role USB controller device + dr_mode = "otg"; + phy = "ulpi"; + }; ++ ++Example dual role USB controller device node for MPC5121ADS: ++ ++ usb@4000 { ++ compatible = "fsl,mpc5121-usb2-dr"; ++ reg = <0x4000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupt-parent = < &ipic >; ++ interrupts = <44 0x8>; ++ dr_mode = "otg"; ++ phy_type = "utmi_wide"; ++ fsl,invert-drvvbus; ++ fsl,invert-pwr-fault; ++ }; +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -58,6 +58,7 @@ config USB_ARCH_HAS_OHCI + config USB_ARCH_HAS_EHCI + boolean + default y if PPC_83xx ++ default y if PPC_MPC512x + default y if SOC_AU1200 + default y if ARCH_IXP4XX + default y if ARCH_W90X900 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -93,12 +93,14 @@ config USB_EHCI_TT_NEWSCHED + + config USB_EHCI_BIG_ENDIAN_MMIO + bool +- depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) ++ depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || \ ++ XPS_USB_HCD_XILINX || PPC_MPC512x) + default y + + config USB_EHCI_BIG_ENDIAN_DESC + bool +- depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) ++ depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ ++ PPC_MPC512x) + default y + + config XPS_USB_HCD_XILINX +--- a/drivers/usb/host/ehci-fsl.c ++++ b/drivers/usb/host/ehci-fsl.c +@@ -116,13 +116,39 @@ static int usb_hcd_fsl_probe(const struc + goto err3; + } + +- /* Enable USB controller */ +- temp = in_be32(hcd->regs + 0x500); +- out_be32(hcd->regs + 0x500, temp | 0x4); ++ pdata->regs = hcd->regs; ++ ++ /* ++ * do platform specific init: check the clock, grab/config pins, etc. ++ */ ++ if (pdata->init && pdata->init(pdev)) { ++ retval = -ENODEV; ++ goto err3; ++ } ++ ++ /* ++ * Check if it is MPC5121 SoC, otherwise set pdata->have_sysif_regs ++ * flag for 83xx or 8536 system interface registers. ++ */ ++ if (pdata->big_endian_mmio) ++ temp = in_be32(hcd->regs + FSL_SOC_USB_ID); ++ else ++ temp = in_le32(hcd->regs + FSL_SOC_USB_ID); ++ ++ if ((temp & ID_MSK) != (~((temp & NID_MSK) >> 8) & ID_MSK)) ++ pdata->have_sysif_regs = 1; ++ ++ /* Enable USB controller, 83xx or 8536 */ ++ if (pdata->have_sysif_regs) ++ setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4); + + /* Set to Host mode */ +- temp = in_le32(hcd->regs + 0x1a8); +- out_le32(hcd->regs + 0x1a8, temp | 0x3); ++ if (pdata->big_endian_mmio) { ++ setbits32(hcd->regs + FSL_SOC_USB_USBMODE, USBMODE_CM_HOST); ++ } else { ++ clrsetbits_le32(hcd->regs + FSL_SOC_USB_USBMODE, ++ USBMODE_CM_MASK, USBMODE_CM_HOST); ++ } + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) +@@ -137,6 +163,8 @@ static int usb_hcd_fsl_probe(const struc + usb_put_hcd(hcd); + err1: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); ++ if (pdata->exit) ++ pdata->exit(pdev); + return retval; + } + +@@ -154,17 +182,30 @@ static int usb_hcd_fsl_probe(const struc + static void usb_hcd_fsl_remove(struct usb_hcd *hcd, + struct platform_device *pdev) + { ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ + usb_remove_hcd(hcd); ++ ++ /* ++ * do platform specific un-initialization: ++ * release iomux pins, disable clock, etc. ++ */ ++ if (pdata->exit) ++ pdata->exit(pdev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + } + +-static void mpc83xx_setup_phy(struct ehci_hcd *ehci, +- enum fsl_usb2_phy_modes phy_mode, +- unsigned int port_offset) ++static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, ++ enum fsl_usb2_phy_modes phy_mode, ++ unsigned int port_offset) + { +- u32 portsc = 0; ++ u32 portsc; ++ ++ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); ++ portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); ++ + switch (phy_mode) { + case FSL_USB2_PHY_ULPI: + portsc |= PORT_PTS_ULPI; +@@ -184,20 +225,21 @@ static void mpc83xx_setup_phy(struct ehc + ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); + } + +-static void mpc83xx_usb_setup(struct usb_hcd *hcd) ++static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) + { +- struct ehci_hcd *ehci = hcd_to_ehci(hcd); ++ struct usb_hcd *hcd = ehci_to_hcd(ehci); + struct fsl_usb2_platform_data *pdata; + void __iomem *non_ehci = hcd->regs; + u32 temp; + +- pdata = +- (struct fsl_usb2_platform_data *)hcd->self.controller-> +- platform_data; ++ pdata = hcd->self.controller->platform_data; ++ + /* Enable PHY interface in the control reg. */ +- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); +- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); +- out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); ++ if (pdata->have_sysif_regs) { ++ temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); ++ out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); ++ out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); ++ } + + #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + /* +@@ -214,7 +256,7 @@ static void mpc83xx_usb_setup(struct usb + + if ((pdata->operating_mode == FSL_USB2_DR_HOST) || + (pdata->operating_mode == FSL_USB2_DR_OTG)) +- mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); ++ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); + + if (pdata->operating_mode == FSL_USB2_MPH_HOST) { + unsigned int chip, rev, svr; +@@ -228,27 +270,31 @@ static void mpc83xx_usb_setup(struct usb + ehci->has_fsl_port_bug = 1; + + if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) +- mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); ++ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); + if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) +- mpc83xx_setup_phy(ehci, pdata->phy_mode, 1); ++ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1); + } + + /* put controller in host mode. */ +- ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE); ++ temp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); ++ ehci_writel(ehci, temp, non_ehci + FSL_SOC_USB_USBMODE); ++ ++ if (pdata->have_sysif_regs) { + #ifdef CONFIG_PPC_85xx +- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); +- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); ++ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); ++ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); + #else +- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); +- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); ++ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); ++ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); + #endif +- out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); ++ out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); ++ } + } + + /* called after powerup, by probe or system-pm "wakeup" */ + static int ehci_fsl_reinit(struct ehci_hcd *ehci) + { +- mpc83xx_usb_setup(ehci_to_hcd(ehci)); ++ ehci_fsl_usb_setup(ehci); + ehci_port_power(ehci, 0); + + return 0; +@@ -259,6 +305,11 @@ static int ehci_fsl_setup(struct usb_hcd + { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; ++ struct fsl_usb2_platform_data *pdata; ++ ++ pdata = hcd->self.controller->platform_data; ++ ehci->big_endian_desc = pdata->big_endian_desc; ++ ehci->big_endian_mmio = pdata->big_endian_mmio; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; +@@ -372,7 +423,7 @@ static const struct hc_driver ehci_fsl_h + * generic hardware linkage + */ + .irq = ehci_irq, +- .flags = HCD_USB2, ++ .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations +--- a/drivers/usb/host/ehci-fsl.h ++++ b/drivers/usb/host/ehci-fsl.h +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2005 freescale semiconductor ++/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc. + * Copyright (c) 2005 MontaVista Software + * + * This program is free software; you can redistribute it and/or modify it +@@ -19,6 +19,11 @@ + #define _EHCI_FSL_H + + /* offsets for the non-ehci registers in the FSL SOC USB controller */ ++#define FSL_SOC_USB_ID 0x0 ++#define ID_MSK 0x3f ++#define NID_MSK 0x3f00 ++#define FSL_SOC_USB_SBUSCFG 0x90 ++#define FSL_SOC_USB_BURSTSIZE 0x160 + #define FSL_SOC_USB_ULPIVP 0x170 + #define FSL_SOC_USB_PORTSC1 0x184 + #define PORT_PTS_MSK (3<<30) +@@ -26,8 +31,20 @@ + #define PORT_PTS_ULPI (2<<30) + #define PORT_PTS_SERIAL (3<<30) + #define PORT_PTS_PTW (1<<28) ++#define PORT_PTS_PHCD (1<<23) + #define FSL_SOC_USB_PORTSC2 0x188 + #define FSL_SOC_USB_USBMODE 0x1a8 ++#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ ++#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */ ++#define USBMODE_ES (1 << 2) /* (Big) Endian Select */ ++ ++#define FSL_SOC_USB_USBGENCTRL 0x200 ++#define USBGENCTRL_PPP (1 << 3) ++#define USBGENCTRL_PFP (1 << 2) ++#define FSL_SOC_USB_ISIPHYCTRL 0x204 ++#define ISIPHYCTRL_PXE (1) ++#define ISIPHYCTRL_PHYE (1 << 4) ++ + #define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ + #define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ + #define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +--- a/drivers/usb/host/ehci-mem.c ++++ b/drivers/usb/host/ehci-mem.c +@@ -40,7 +40,7 @@ static inline void ehci_qtd_init(struct + { + memset (qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; +- qtd->hw_token = cpu_to_le32 (QTD_STS_HALT); ++ qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END(ehci); + qtd->hw_alt_next = EHCI_LIST_END(ehci); + INIT_LIST_HEAD (&qtd->qtd_list); +--- a/drivers/usb/host/fsl-mph-dr-of.c ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -14,6 +14,7 @@ + #include <linux/fsl_devices.h> + #include <linux/io.h> + #include <linux/of_platform.h> ++#include <linux/clk.h> + + struct fsl_usb2_dev_data { + char *dr_mode; /* controller mode */ +@@ -146,6 +147,12 @@ static int __devinit fsl_usb2_mph_dr_of_ + + pdata->operating_mode = FSL_USB2_MPH_HOST; + } else { ++ if (of_get_property(np, "fsl,invert-drvvbus", NULL)) ++ pdata->invert_drvvbus = 1; ++ ++ if (of_get_property(np, "fsl,invert-pwr-fault", NULL)) ++ pdata->invert_pwr_fault = 1; ++ + /* setup mode selected in the device tree */ + pdata->operating_mode = dev_data->op_mode; + } +@@ -167,9 +174,91 @@ static int __devinit fsl_usb2_mph_dr_of_ + return 0; + } + ++#ifdef CONFIG_PPC_MPC512x ++ ++#define USBGENCTRL 0x200 /* NOTE: big endian */ ++#define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */ ++#define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/ ++#define GC_PPP (1 << 3) /* Inv. Port Power Polarity */ ++#define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */ ++#define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */ ++#define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */ ++ ++#define ISIPHYCTRL 0x204 /* NOTE: big endian */ ++#define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */ ++#define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */ ++#define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */ ++#define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */ ++#define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */ ++ ++int fsl_usb2_mpc5121_init(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ struct clk *clk; ++ char clk_name[10]; ++ int base, clk_num; ++ ++ base = pdev->resource->start & 0xf000; ++ if (base == 0x3000) ++ clk_num = 1; ++ else if (base == 0x4000) ++ clk_num = 2; ++ else ++ return -ENODEV; ++ ++ snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num); ++ clk = clk_get(&pdev->dev, clk_name); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed to get clk\n"); ++ return PTR_ERR(clk); ++ } ++ ++ clk_enable(clk); ++ pdata->clk = clk; ++ ++ if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) { ++ u32 reg = 0; ++ ++ if (pdata->invert_drvvbus) ++ reg |= GC_PPP; ++ ++ if (pdata->invert_pwr_fault) ++ reg |= GC_PFP; ++ ++ out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE); ++ out_be32(pdata->regs + USBGENCTRL, reg); ++ } ++ return 0; ++} ++ ++static void fsl_usb2_mpc5121_exit(struct platform_device *pdev) ++{ ++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; ++ ++ pdata->regs = NULL; ++ ++ if (pdata->clk) { ++ clk_disable(pdata->clk); ++ clk_put(pdata->clk); ++ } ++} ++ ++struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = { ++ .big_endian_desc = 1, ++ .big_endian_mmio = 1, ++ .es = 1, ++ .le_setup_buf = 1, ++ .init = fsl_usb2_mpc5121_init, ++ .exit = fsl_usb2_mpc5121_exit, ++}; ++#endif /* CONFIG_PPC_MPC512x */ ++ + static const struct of_device_id fsl_usb2_mph_dr_of_match[] = { + { .compatible = "fsl-usb2-mph", }, + { .compatible = "fsl-usb2-dr", }, ++#ifdef CONFIG_PPC_MPC512x ++ { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, }, ++#endif + {}, + }; + +--- a/include/linux/fsl_devices.h ++++ b/include/linux/fsl_devices.h +@@ -58,11 +58,26 @@ enum fsl_usb2_phy_modes { + FSL_USB2_PHY_SERIAL, + }; + ++struct clk; ++struct platform_device; ++ + struct fsl_usb2_platform_data { + /* board specific information */ + enum fsl_usb2_operating_modes operating_mode; + enum fsl_usb2_phy_modes phy_mode; + unsigned int port_enables; ++ ++ int (*init)(struct platform_device *); ++ void (*exit)(struct platform_device *); ++ void __iomem *regs; /* ioremap'd register base */ ++ struct clk *clk; ++ unsigned big_endian_mmio:1; ++ unsigned big_endian_desc:1; ++ unsigned es:1; /* need USBMODE:ES */ ++ unsigned le_setup_buf:1; ++ unsigned have_sysif_regs:1; ++ unsigned invert_drvvbus:1; ++ unsigned invert_pwr_fault:1; + }; + + /* Flags in fsl_usb2_mph_platform_data */ diff --git a/usb/usb-delay-init-quirk-for-logitech-harmony-700-series-devices.patch b/usb/usb-delay-init-quirk-for-logitech-harmony-700-series-devices.patch new file mode 100644 index 00000000000000..699b7c80d8fbb0 --- /dev/null +++ b/usb/usb-delay-init-quirk-for-logitech-harmony-700-series-devices.patch @@ -0,0 +1,76 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 15:58:49 2010 +Message-ID: <4C476F0D.1050707@ipom.com> +Date: Thu, 22 Jul 2010 00:05:01 +0200 +From: Phil Dibowitz <phil@ipom.com> +To: Greg KH <greg@kroah.com> +CC: Stephen Warren <swarren@wwwdotorg.org> +Subject: USB delay init quirk for logitech Harmony 700-series devices + + +The Logitech Harmony 700 series needs an extra delay during +initialization. This patch adds a USB quirk which enables such a delay +and adds the device to the quirks list. + +Signed-off-by: Phil Dibowitz <phil@ipom.com> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/core/hub.c | 6 +++++- + drivers/usb/core/quirks.c | 3 +++ + include/linux/usb/quirks.h | 4 ++++ + 3 files changed, 12 insertions(+), 1 deletion(-) + +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -20,6 +20,7 @@ + #include <linux/usb.h> + #include <linux/usbdevice_fs.h> + #include <linux/usb/hcd.h> ++#include <linux/usb/quirks.h> + #include <linux/kthread.h> + #include <linux/mutex.h> + #include <linux/freezer.h> +@@ -1802,7 +1803,6 @@ int usb_new_device(struct usb_device *ud + pm_runtime_set_active(&udev->dev); + pm_runtime_enable(&udev->dev); + +- usb_detect_quirks(udev); + err = usb_enumerate_device(udev); /* Read descriptors */ + if (err < 0) + goto fail; +@@ -3114,6 +3114,10 @@ static void hub_port_connect_change(stru + if (status < 0) + goto loop; + ++ usb_detect_quirks(udev); ++ if (udev->quirks & USB_QUIRK_DELAY_INIT) ++ msleep(1000); ++ + /* consecutive bus-powered hubs aren't reliable; they can + * violate the voltage drop budget. if the new child has + * a "powered" LED, users should notice we didn't enable it +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -38,6 +38,9 @@ static const struct usb_device_id usb_qu + /* Creative SB Audigy 2 NX */ + { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + ++ /* Logitech Harmony 700-series */ ++ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, ++ + /* Philips PSC805 audio device */ + { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME }, + +--- a/include/linux/usb/quirks.h ++++ b/include/linux/usb/quirks.h +@@ -26,4 +26,8 @@ + and can't handle talking to these interfaces */ + #define USB_QUIRK_HONOR_BNUMINTERFACES 0x00000020 + ++/* device needs a pause during initialization, after we read the device ++ descriptor */ ++#define USB_QUIRK_DELAY_INIT 0x00000040 ++ + #endif /* __LINUX_USB_QUIRKS_H */ diff --git a/usb/usb-ehci-add-missing-frame-microframe-conversion.patch b/usb/usb-ehci-add-missing-frame-microframe-conversion.patch new file mode 100644 index 00000000000000..aca6f30fd2eb88 --- /dev/null +++ b/usb/usb-ehci-add-missing-frame-microframe-conversion.patch @@ -0,0 +1,98 @@ +From stern+4c290172@rowland.harvard.edu Thu Jul 22 15:51:56 2010 +Date: Wed, 14 Jul 2010 11:03:46 -0400 (EDT) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: David Brownell <david-b@pacbell.net>, USB list <linux-usb@vger.kernel.org> +Subject: USB: EHCI: add missing frame -> microframe conversion +Message-ID: <Pine.LNX.4.44L0.1007141056230.1634-100000@iolanthe.rowland.org> + +This patch (as1407) fixes a bug in ehci-hcd's isochronous scheduler. +All its calculations should be done in terms of microframes, but for +full-speed devices, sched->span is stored in frames. It needs to be +converted. + +This fix is liable to expose problems in other drivers. The old code +would accept URBs that should not have been accepted, so drivers have +had no reason to avoid submitting URBs that exceeded the maximum +schedule length. In an attempt to partially compensate for this, the +patch also adjusts the schedule length from a minimum of 256 frames up +to a minimum of 512 frames. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: David Brownell <david-b@pacbell.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/host/ehci-hcd.c | 8 +++++++- + drivers/usb/host/ehci-sched.c | 21 ++++++++++++--------- + 2 files changed, 19 insertions(+), 10 deletions(-) + +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -79,7 +79,13 @@ static const char hcd_name [] = "ehci_hc + #define EHCI_TUNE_RL_TT 0 + #define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ + #define EHCI_TUNE_MULT_TT 1 +-#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ ++/* ++ * Some drivers think it's safe to schedule isochronous transfers more than ++ * 256 ms into the future (partly as a result of an old bug in the scheduling ++ * code). In an attempt to avoid trouble, we will use a minimum scheduling ++ * length of 512 frames instead of 256. ++ */ ++#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ + + #define EHCI_IAA_MSECS 10 /* arbitrary */ + #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -1395,28 +1395,31 @@ iso_stream_schedule ( + struct ehci_iso_stream *stream + ) + { +- u32 now, next, start, period; ++ u32 now, next, start, period, span; + int status; + unsigned mod = ehci->periodic_size << 3; + struct ehci_iso_sched *sched = urb->hcpriv; + +- if (sched->span > (mod - SCHEDULE_SLOP)) { ++ period = urb->interval; ++ span = sched->span; ++ if (!stream->highspeed) { ++ period <<= 3; ++ span <<= 3; ++ } ++ ++ if (span > mod - SCHEDULE_SLOP) { + ehci_dbg (ehci, "iso request %p too long\n", urb); + status = -EFBIG; + goto fail; + } + +- if ((stream->depth + sched->span) > mod) { ++ if (stream->depth + span > mod) { + ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n", +- urb, stream->depth, sched->span, mod); ++ urb, stream->depth, span, mod); + status = -EFBIG; + goto fail; + } + +- period = urb->interval; +- if (!stream->highspeed) +- period <<= 3; +- + now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + + /* Typical case: reuse current schedule, stream is still active. +@@ -1445,7 +1448,7 @@ iso_stream_schedule ( + period); + + /* Tried to schedule too far into the future? */ +- if (unlikely(((start - now) & (mod - 1)) + sched->span ++ if (unlikely(((start - now) & (mod - 1)) + span + >= mod - 2 * SCHEDULE_SLOP)) { + status = -EFBIG; + goto fail; diff --git a/usb/usb-ehci-remove-dead-code-in-the-periodic-scheduler.patch b/usb/usb-ehci-remove-dead-code-in-the-periodic-scheduler.patch new file mode 100644 index 00000000000000..daafb7c2e95162 --- /dev/null +++ b/usb/usb-ehci-remove-dead-code-in-the-periodic-scheduler.patch @@ -0,0 +1,108 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 15:52:31 2010 +Date: Wed, 14 Jul 2010 11:03:57 -0400 (EDT) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: David Brownell <david-b@pacbell.net>, + USB list <linux-usb@vger.kernel.org> +Subject: USB: EHCI: remove dead code in the periodic scheduler +Message-ID: <Pine.LNX.4.44L0.1007141059480.1634-100000@iolanthe.rowland.org> + +This patch (as1409) removes some dead code from the ehci-hcd +scheduler. Thanks to the previous patch in this series, stream->depth +is no longer used. And stream->start and stream->rescheduled +apparently have not been used for quite a while, except in some +statistics-reporting code that never gets invoked. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: David Brownell <david-b@pacbell.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-sched.c | 15 --------------- + drivers/usb/host/ehci.h | 3 --- + 2 files changed, 18 deletions(-) + +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -1074,15 +1074,6 @@ iso_stream_put(struct ehci_hcd *ehci, st + if (stream->ep) + stream->ep->hcpriv = NULL; + +- if (stream->rescheduled) { +- ehci_info (ehci, "ep%d%s-iso rescheduled " +- "%lu times in %lu seconds\n", +- stream->bEndpointAddress, is_in ? "in" : "out", +- stream->rescheduled, +- ((jiffies - stream->start)/HZ) +- ); +- } +- + kfree(stream); + } + } +@@ -1617,7 +1608,6 @@ itd_link_urb ( + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + urb->interval, + next_uframe >> 3, next_uframe & 0x7); +- stream->start = jiffies; + } + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; + +@@ -1643,7 +1633,6 @@ itd_link_urb ( + itd_patch(ehci, itd, iso_sched, packet, uframe); + + next_uframe += stream->interval; +- stream->depth += stream->interval; + next_uframe &= mod - 1; + packet++; + +@@ -1699,7 +1688,6 @@ itd_complete ( + + t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]); + itd->hw_transaction [uframe] = 0; +- stream->depth -= stream->interval; + + /* report transfer status */ + if (unlikely (t & ISO_ERRS)) { +@@ -2029,7 +2017,6 @@ sitd_link_urb ( + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + (next_uframe >> 3) & (ehci->periodic_size - 1), + stream->interval, hc32_to_cpu(ehci, stream->splits)); +- stream->start = jiffies; + } + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; + +@@ -2054,7 +2041,6 @@ sitd_link_urb ( + sitd); + + next_uframe += stream->interval << 3; +- stream->depth += stream->interval << 3; + } + stream->next_uframe = next_uframe & (mod - 1); + +@@ -2114,7 +2100,6 @@ sitd_complete ( + desc->actual_length = desc->length - SITD_LENGTH(t); + urb->actual_length += desc->actual_length; + } +- stream->depth -= stream->interval << 3; + + /* handle completion now? */ + if ((urb_index + 1) != urb->number_of_packets) +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -404,15 +404,12 @@ struct ehci_iso_stream { + u32 refcount; + u8 bEndpointAddress; + u8 highspeed; +- u16 depth; /* depth in uframes */ + struct list_head td_list; /* queued itds/sitds */ + struct list_head free_list; /* list of unused itds/sitds */ + struct usb_device *udev; + struct usb_host_endpoint *ep; + + /* output of (re)scheduling */ +- unsigned long start; /* jiffies */ +- unsigned long rescheduled; + int next_uframe; + __hc32 splits; + diff --git a/usb/usb-ehci-remove-pci-assumption.patch b/usb/usb-ehci-remove-pci-assumption.patch new file mode 100644 index 00000000000000..1c955f1bb89f19 --- /dev/null +++ b/usb/usb-ehci-remove-pci-assumption.patch @@ -0,0 +1,78 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 15:50:30 2010 +Date: Wed, 14 Jul 2010 11:03:23 -0400 (EDT) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: Sarah Sharp <sarah.a.sharp@linux.intel.com>, + David Brownell <david-b@pacbell.net>, + USB list <linux-usb@vger.kernel.org> +Subject: USB: EHCI: remove PCI assumption +Message-ID: <Pine.LNX.4.44L0.1007141053530.1634-100000@iolanthe.rowland.org> + +This patch (as1405) fixes a small bug in ehci-hcd's isochronous +scheduler. Not all EHCI controllers are PCI, and the code shouldn't +assume that they are. Instead, introduce a special flag for +controllers which need to delay iso scheduling for full-speed devices +beyond the scheduling threshold. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: Sarah Sharp <sarah.a.sharp@linux.intel.com> +CC: David Brownell <david-b@pacbell.net> +CC: stable <stable@kernel.org> +Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> + +--- + drivers/usb/host/ehci-pci.c | 1 + + drivers/usb/host/ehci-sched.c | 10 ++++------ + drivers/usb/host/ehci.h | 1 + + 3 files changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/usb/host/ehci-pci.c ++++ b/drivers/usb/host/ehci-pci.c +@@ -114,6 +114,7 @@ static int ehci_pci_setup(struct usb_hcd + break; + case PCI_VENDOR_ID_INTEL: + ehci->need_io_watchdog = 0; ++ ehci->fs_i_thresh = 1; + if (pdev->device == 0x27cc) { + ehci->broken_periodic = 1; + ehci_info(ehci, "using broken periodic workaround\n"); +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -1399,7 +1399,6 @@ iso_stream_schedule ( + int status; + unsigned mod = ehci->periodic_size << 3; + struct ehci_iso_sched *sched = urb->hcpriv; +- struct pci_dev *pdev; + + if (sched->span > (mod - SCHEDULE_SLOP)) { + ehci_dbg (ehci, "iso request %p too long\n", urb); +@@ -1426,15 +1425,14 @@ iso_stream_schedule ( + * slot in the schedule, implicitly assuming URB_ISO_ASAP. + */ + if (likely (!list_empty (&stream->td_list))) { +- pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); + start = stream->next_uframe; + + /* For high speed devices, allow scheduling within the +- * isochronous scheduling threshold. For full speed devices, +- * don't. (Work around for Intel ICH9 bug.) ++ * isochronous scheduling threshold. For full speed devices ++ * and Intel PCI-based controllers, don't (work around for ++ * Intel ICH9 bug). + */ +- if (!stream->highspeed && +- pdev->vendor == PCI_VENDOR_ID_INTEL) ++ if (!stream->highspeed && ehci->fs_i_thresh) + next = now + ehci->i_thresh; + else + next = now; +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controlle + unsigned has_amcc_usb23:1; + unsigned need_io_watchdog:1; + unsigned broken_periodic:1; ++ unsigned fs_i_thresh:1; /* Intel iso scheduling */ + + /* required for usb32 quirk */ + #define OHCI_CTRL_HCFS (3 << 6) diff --git a/usb/usb-ehci-reorganize-isochronous-scheduler-routine.patch b/usb/usb-ehci-reorganize-isochronous-scheduler-routine.patch new file mode 100644 index 00000000000000..3df776d50eea63 --- /dev/null +++ b/usb/usb-ehci-reorganize-isochronous-scheduler-routine.patch @@ -0,0 +1,188 @@ +From stern+4c290172@rowland.harvard.edu Thu Jul 22 15:52:14 2010 +Date: Wed, 14 Jul 2010 11:03:53 -0400 (EDT) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: David Brownell <david-b@pacbell.net>, USB list <linux-usb@vger.kernel.org> +Subject: USB: EHCI: reorganize isochronous scheduler routine +Message-ID: <Pine.LNX.4.44L0.1007141058150.1634-100000@iolanthe.rowland.org> + +This patch (as1408) rearranges the scheduling code in ehci-hcd, partly +to improve its structure, but mainly to change the way it works. +Whether or not a transfer exceeds the hardware schedule length will +now be determined by looking at the last frame the transfer would use, +instead of the first available frame following the end of the transfer. + +The benefit of this change is that it allows the driver to accept +valid URBs which would otherwise be rejected. For example, suppose +the schedule length is 1024 frames, the endpoint period is 256 frames, +and a four-packet URB is submitted. The four transfers would occupy +slots that are 0, 256, 512, and 768 frames past the current frame +(plus an extra slop factor). These don't exceed the 1024-frame limit, +so the URB should be accepted. But the current code notices that the +next available slot would be 1024 frames (plus slop) in the future, +which is beyond the limit, and so the URB is rejected unnecessarily. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: David Brownell <david-b@pacbell.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/host/ehci-sched.c | 110 +++++++++++++++++++++--------------------- + 1 file changed, 57 insertions(+), 53 deletions(-) + +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -1413,13 +1413,6 @@ iso_stream_schedule ( + goto fail; + } + +- if (stream->depth + span > mod) { +- ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n", +- urb, stream->depth, span, mod); +- status = -EFBIG; +- goto fail; +- } +- + now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + + /* Typical case: reuse current schedule, stream is still active. +@@ -1428,7 +1421,7 @@ iso_stream_schedule ( + * slot in the schedule, implicitly assuming URB_ISO_ASAP. + */ + if (likely (!list_empty (&stream->td_list))) { +- start = stream->next_uframe; ++ u32 excess; + + /* For high speed devices, allow scheduling within the + * isochronous scheduling threshold. For full speed devices +@@ -1440,21 +1433,23 @@ iso_stream_schedule ( + else + next = now; + +- /* Fell behind (by up to twice the slop amount)? */ +- if (((start - next) & (mod - 1)) >= +- mod - 2 * SCHEDULE_SLOP) +- start += period * DIV_ROUND_UP( +- (next - start) & (mod - 1), +- period); +- +- /* Tried to schedule too far into the future? */ +- if (unlikely(((start - now) & (mod - 1)) + span +- >= mod - 2 * SCHEDULE_SLOP)) { ++ /* Fell behind (by up to twice the slop amount)? ++ * We decide based on the time of the last currently-scheduled ++ * slot, not the time of the next available slot. ++ */ ++ excess = (stream->next_uframe - period - next) & (mod - 1); ++ if (excess >= mod - 2 * SCHEDULE_SLOP) ++ start = next + excess - mod + period * ++ DIV_ROUND_UP(mod - excess, period); ++ else ++ start = next + excess + period; ++ if (start - now >= mod) { ++ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now - period, period, ++ mod); + status = -EFBIG; + goto fail; + } +- stream->next_uframe = start; +- goto ready; + } + + /* need to schedule; when's the next (u)frame we could start? +@@ -1463,51 +1458,60 @@ iso_stream_schedule ( + * can also help high bandwidth if the dma and irq loads don't + * jump until after the queue is primed. + */ +- start = SCHEDULE_SLOP + (now & ~0x07); +- start &= mod - 1; +- stream->next_uframe = start; +- +- /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ +- +- /* find a uframe slot with enough bandwidth */ +- for (; start < (stream->next_uframe + period); start++) { +- int enough_space; +- +- /* check schedule: enough space? */ +- if (stream->highspeed) +- enough_space = itd_slot_ok (ehci, mod, start, +- stream->usecs, period); +- else { +- if ((start % 8) >= 6) +- continue; +- enough_space = sitd_slot_ok (ehci, mod, stream, +- start, sched, period); ++ else { ++ start = SCHEDULE_SLOP + (now & ~0x07); ++ ++ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ ++ ++ /* find a uframe slot with enough bandwidth */ ++ next = start + period; ++ for (; start < next; start++) { ++ ++ /* check schedule: enough space? */ ++ if (stream->highspeed) { ++ if (itd_slot_ok(ehci, mod, start, ++ stream->usecs, period)) ++ break; ++ } else { ++ if ((start % 8) >= 6) ++ continue; ++ if (sitd_slot_ok(ehci, mod, stream, ++ start, sched, period)) ++ break; ++ } + } + +- /* schedule it here if there's enough bandwidth */ +- if (enough_space) { +- stream->next_uframe = start & (mod - 1); +- goto ready; ++ /* no room in the schedule */ ++ if (start == next) { ++ ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n", ++ urb, now, now + mod); ++ status = -ENOSPC; ++ goto fail; + } + } + +- /* no room in the schedule */ +- ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", +- list_empty (&stream->td_list) ? "" : "re", +- urb, now, now + mod); +- status = -ENOSPC; ++ /* Tried to schedule too far into the future? */ ++ if (unlikely(start - now + span - period ++ >= mod - 2 * SCHEDULE_SLOP)) { ++ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n", ++ urb, start - now, span - period, ++ mod - 2 * SCHEDULE_SLOP); ++ status = -EFBIG; ++ goto fail; ++ } + +-fail: +- iso_sched_free (stream, sched); +- urb->hcpriv = NULL; +- return status; ++ stream->next_uframe = start & (mod - 1); + +-ready: + /* report high speed start in uframes; full speed, in frames */ + urb->start_frame = stream->next_uframe; + if (!stream->highspeed) + urb->start_frame >>= 3; + return 0; ++ ++ fail: ++ iso_sched_free(stream, sched); ++ urb->hcpriv = NULL; ++ return status; + } + + /*-------------------------------------------------------------------------*/ diff --git a/usb/usb-ehci-simplify-remainder-computations.patch b/usb/usb-ehci-simplify-remainder-computations.patch new file mode 100644 index 00000000000000..d49e13986c755d --- /dev/null +++ b/usb/usb-ehci-simplify-remainder-computations.patch @@ -0,0 +1,148 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 15:51:42 2010 +Date: Wed, 14 Jul 2010 11:03:36 -0400 (EDT) +From: Alan Stern <stern@rowland.harvard.edu> +To: Greg KH <greg@kroah.com> +cc: David Brownell <david-b@pacbell.net>, + USB list <linux-usb@vger.kernel.org> +Subject: USB: EHCI: simplify remainder computations +Message-ID: <Pine.LNX.4.44L0.1007141055270.1634-100000@iolanthe.rowland.org> + +This patch (as1406) adds a micro-optimization to ehci-hcd's scheduling +code. Instead of computing remainders with respect to the schedule +length, use bitwise-and (which is quicker). We know that the schedule +length will always be a power of two, but the compiler doesn't have +this information. + +Signed-off-by: Alan Stern <stern@rowland.harvard.edu> +CC: David Brownell <david-b@pacbell.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-sched.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -1417,7 +1417,7 @@ iso_stream_schedule ( + if (!stream->highspeed) + period <<= 3; + +- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; ++ now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + + /* Typical case: reuse current schedule, stream is still active. + * Hopefully there are no gaps from the host falling behind +@@ -1461,7 +1461,7 @@ iso_stream_schedule ( + * jump until after the queue is primed. + */ + start = SCHEDULE_SLOP + (now & ~0x07); +- start %= mod; ++ start &= mod - 1; + stream->next_uframe = start; + + /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ +@@ -1483,7 +1483,7 @@ iso_stream_schedule ( + + /* schedule it here if there's enough bandwidth */ + if (enough_space) { +- stream->next_uframe = start % mod; ++ stream->next_uframe = start & (mod - 1); + goto ready; + } + } +@@ -1599,7 +1599,7 @@ itd_link_urb ( + struct ehci_iso_sched *iso_sched = urb->hcpriv; + struct ehci_itd *itd; + +- next_uframe = stream->next_uframe % mod; ++ next_uframe = stream->next_uframe & (mod - 1); + + if (unlikely (list_empty(&stream->td_list))) { + ehci_to_hcd(ehci)->self.bandwidth_allocated +@@ -1637,13 +1637,13 @@ itd_link_urb ( + + next_uframe += stream->interval; + stream->depth += stream->interval; +- next_uframe %= mod; ++ next_uframe &= mod - 1; + packet++; + + /* link completed itds into the schedule */ + if (((next_uframe >> 3) != frame) + || packet == urb->number_of_packets) { +- itd_link (ehci, frame % ehci->periodic_size, itd); ++ itd_link(ehci, frame & (ehci->periodic_size - 1), itd); + itd = NULL; + } + } +@@ -2020,7 +2020,7 @@ sitd_link_urb ( + "sched devp %s ep%d%s-iso [%d] %dms/%04x\n", + urb->dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", +- (next_uframe >> 3) % ehci->periodic_size, ++ (next_uframe >> 3) & (ehci->periodic_size - 1), + stream->interval, hc32_to_cpu(ehci, stream->splits)); + stream->start = jiffies; + } +@@ -2043,13 +2043,13 @@ sitd_link_urb ( + sitd->urb = urb; + + sitd_patch(ehci, stream, sitd, sched, packet); +- sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, ++ sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1), + sitd); + + next_uframe += stream->interval << 3; + stream->depth += stream->interval << 3; + } +- stream->next_uframe = next_uframe % mod; ++ stream->next_uframe = next_uframe & (mod - 1); + + /* don't need that schedule data any more */ + iso_sched_free (stream, sched); +@@ -2258,7 +2258,7 @@ scan_periodic (struct ehci_hcd *ehci) + now_uframe = ehci->next_uframe; + if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + clock = ehci_readl(ehci, &ehci->regs->frame_index); +- clock_frame = (clock >> 3) % ehci->periodic_size; ++ clock_frame = (clock >> 3) & (ehci->periodic_size - 1); + } else { + clock = now_uframe + mod - 1; + clock_frame = -1; +@@ -2267,7 +2267,7 @@ scan_periodic (struct ehci_hcd *ehci) + free_cached_lists(ehci); + ehci->clock_frame = clock_frame; + } +- clock %= mod; ++ clock &= mod - 1; + clock_frame = clock >> 3; + + for (;;) { +@@ -2356,7 +2356,7 @@ restart: + * frame is current. + */ + if (((frame == clock_frame) || +- (((frame + 1) % ehci->periodic_size) ++ (((frame + 1) & (ehci->periodic_size - 1)) + == clock_frame)) + && live + && (q.sitd->hw_results & +@@ -2423,7 +2423,8 @@ restart: + || ehci->periodic_sched == 0) + break; + ehci->next_uframe = now_uframe; +- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; ++ now = ehci_readl(ehci, &ehci->regs->frame_index) & ++ (mod - 1); + if (now_uframe == now) + break; + +@@ -2436,7 +2437,7 @@ restart: + } + } else { + now_uframe++; +- now_uframe %= mod; ++ now_uframe &= mod - 1; + } + } + } diff --git a/usb/usb-gadget-functionfs-stale-makefile-entry-removed.patch b/usb/usb-gadget-functionfs-stale-makefile-entry-removed.patch new file mode 100644 index 00000000000000..e4db7eac165a22 --- /dev/null +++ b/usb/usb-gadget-functionfs-stale-makefile-entry-removed.patch @@ -0,0 +1,31 @@ +From m.nazarewicz@samsung.com Thu Jul 22 15:54:07 2010 +Date: Mon, 19 Jul 2010 11:47:04 +0200 +From: Michal Nazarewicz <m.nazarewicz@samsung.com> +Subject: USB: gadget: functionfs: stale Makefile entry removed +To: linux-usb@vger.kernel.org +Cc: Michal Nazarewicz <m.nazarewicz@samsung.com>, Greg KH <greg@kroah.com>, + "Robert P. J. Day" <rpjday@crashcourse.ca>, linux-kernel@vger.kernel.org +Message-id: <56bb448ef3f882f8fdc6a80613a841a729755cc9.1279532379.git.m.nazarewicz@samsung.com> + +Removed entry referencing g_eth_ffs.c file from Makefile. +The file never existed and the line was a leftover from a +developing process. + +Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> +Reported-by: Robert P. J. Day <rpjday@crashcourse.ca> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -53,7 +53,6 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o + obj-$(CONFIG_USB_ETH) += g_ether.o + obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o + obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o +-obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o + obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o + obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o + obj-$(CONFIG_USB_G_SERIAL) += g_serial.o diff --git a/usb/usb-gadget-storage-optional-scsi-write-fua-bit.patch b/usb/usb-gadget-storage-optional-scsi-write-fua-bit.patch new file mode 100644 index 00000000000000..3755e1ba7294dd --- /dev/null +++ b/usb/usb-gadget-storage-optional-scsi-write-fua-bit.patch @@ -0,0 +1,217 @@ +From andy.shevchenko@gmail.com Thu Jul 22 16:47:18 2010 +From: Andy Shevchenko <andy.shevchenko@gmail.com> +To: linux-kernel@vger.kernel.org +Cc: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>, + Denis Karpov <ext-denis.2.karpov@nokia.com>, + Adrian Hunter <adrian.hunter@nokia.com>, + Alan Stern <stern@rowland.harvard.edu>, + David Brownell <dbrownell@users.sourceforge.net>, + Greg Kroah-Hartman <gregkh@suse.de>, linux-usb@vger.kernel.org +Subject: USB: gadget: storage: optional SCSI WRITE FUA bit +Date: Thu, 22 Jul 2010 17:53:56 +0300 +Message-Id: <1279810436-15678-1-git-send-email-ext-andriy.shevchenko@nokia.com> + +MS Windows mounts removable storage in "Removal optimized mode" by +default. All the writes to the media are synchronous which is achieved +by setting FUA (Force Unit Access) bit in SCSI WRITE(10,12) commands. +This prevents I/O requests aggregation in block layer dramatically +decreasing performance. + +This patch brings an option to accept or ignore mentioned bit + a) via specifying module parameter "nofua", or + b) through sysfs entry + /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua + (_UDC_ is the name of the USB Device Controller driver) + +Patch is based on the work that was done by Denis Karpov for Maemo 5 +platform. + +Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> +Acked-by: Alan Stern <stern@rowland.harvard.edu> +Cc: Denis Karpov <ext-denis.2.karpov@nokia.com> +Cc: Adrian Hunter <adrian.hunter@nokia.com> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget | 12 +++ + drivers/usb/gadget/file_storage.c | 31 +++++++--- + drivers/usb/gadget/storage_common.c | 28 +++++++++ + 3 files changed, 64 insertions(+), 7 deletions(-) + +--- a/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget ++++ b/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget +@@ -7,3 +7,15 @@ Description: + 0 -> resumed + + (_UDC_ is the name of the USB Device Controller driver) ++ ++What: /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua ++Date: July 2010 ++Contact: Andy Shevchenko <andy.shevchenko@gmail.com> ++Description: ++ Show or set the reaction on the FUA (Force Unit Access) bit in ++ the SCSI WRITE(10,12) commands when a gadget in USB Mass ++ Storage mode. ++ ++ Possible values are: ++ 1 -> ignore the FUA flag ++ 0 -> obey the FUA flag +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -93,6 +93,8 @@ + * removable Default false, boolean for removable media + * luns=N Default N = number of filenames, number of + * LUNs to support ++ * nofua=b[,b...] Default false, booleans for ignore FUA flag ++ * in SCSI WRITE(10,12) commands + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt +@@ -112,12 +114,12 @@ + * PAGE_CACHE_SIZE) + * + * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", +- * "removable", "luns", "stall", and "cdrom" options are available; default +- * values are used for everything else. ++ * "removable", "luns", "nofua", "stall", and "cdrom" options are available; ++ * default values are used for everything else. + * + * The pathnames of the backing files and the ro settings are available in +- * the attribute files "file" and "ro" in the lun<n> subdirectory of the +- * gadget's sysfs directory. If the "removable" option is set, writing to ++ * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of ++ * the gadget's sysfs directory. If the "removable" option is set, writing to + * these files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the ro + * setting are not allowed when the medium is loaded or if CD-ROM emulation +@@ -304,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL"); + static struct { + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; ++ int nofua[FSG_MAX_LUNS]; + unsigned int num_filenames; + unsigned int num_ros; ++ unsigned int num_nofuas; + unsigned int nluns; + + int removable; +@@ -345,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing + module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); + MODULE_PARM_DESC(ro, "true to force read-only"); + ++module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas, ++ S_IRUGO); ++MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit"); ++ + module_param_named(luns, mod_data.nluns, uint, S_IRUGO); + MODULE_PARM_DESC(luns, "number of LUNs"); + +@@ -1279,7 +1287,8 @@ static int do_write(struct fsg_dev *fsg) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } +- if (fsg->cmnd[1] & 0x08) { // FUA ++ /* FUA */ ++ if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) { + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_DSYNC; + spin_unlock(&curlun->filp->f_lock); +@@ -3133,6 +3142,7 @@ static int fsg_main_thread(void *fsg_) + + /* The write permissions and store_xxx pointers are set in fsg_bind() */ + static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); ++static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL); + static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); + + +@@ -3362,6 +3372,10 @@ static int __ref fsg_bind(struct usb_gad + } + } + ++ /* Only for removable media? */ ++ dev_attr_nofua.attr.mode = 0644; ++ dev_attr_nofua.store = fsg_store_nofua; ++ + /* Find out how many LUNs there should be */ + i = mod_data.nluns; + if (i == 0) +@@ -3387,6 +3401,7 @@ static int __ref fsg_bind(struct usb_gad + curlun->ro = mod_data.cdrom || mod_data.ro[i]; + curlun->initially_ro = curlun->ro; + curlun->removable = mod_data.removable; ++ curlun->nofua = mod_data.nofua[i]; + curlun->dev.release = lun_release; + curlun->dev.parent = &gadget->dev; + curlun->dev.driver = &fsg_driver.driver; +@@ -3401,6 +3416,8 @@ static int __ref fsg_bind(struct usb_gad + if ((rc = device_create_file(&curlun->dev, + &dev_attr_ro)) != 0 || + (rc = device_create_file(&curlun->dev, ++ &dev_attr_nofua)) != 0 || ++ (rc = device_create_file(&curlun->dev, + &dev_attr_file)) != 0) { + device_unregister(&curlun->dev); + goto out; +@@ -3525,8 +3542,8 @@ static int __ref fsg_bind(struct usb_gad + if (IS_ERR(p)) + p = NULL; + } +- LINFO(curlun, "ro=%d, file: %s\n", +- curlun->ro, (p ? p : "(error)")); ++ LINFO(curlun, "ro=%d, nofua=%d, file: %s\n", ++ curlun->ro, curlun->nofua, (p ? p : "(error)")); + } + } + kfree(pathbuf); +--- a/drivers/usb/gadget/storage_common.c ++++ b/drivers/usb/gadget/storage_common.c +@@ -284,6 +284,7 @@ struct fsg_lun { + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; ++ unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; +@@ -714,6 +715,14 @@ static ssize_t fsg_show_ro(struct device + : curlun->initially_ro); + } + ++static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct fsg_lun *curlun = fsg_lun_from_dev(dev); ++ ++ return sprintf(buf, "%u\n", curlun->nofua); ++} ++ + static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, + char *buf) + { +@@ -770,6 +779,25 @@ static ssize_t fsg_store_ro(struct devic + return rc; + } + ++static ssize_t fsg_store_nofua(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fsg_lun *curlun = fsg_lun_from_dev(dev); ++ unsigned long nofua; ++ ++ if (strict_strtoul(buf, 2, &nofua)) ++ return -EINVAL; ++ ++ /* Sync data when switching from async mode to sync */ ++ if (!nofua && curlun->nofua) ++ fsg_lun_fsync_sub(curlun); ++ ++ curlun->nofua = nofua; ++ ++ return count; ++} ++ + static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) + { diff --git a/usb/usb-gadget-storage-strict-coversion-of-ro-parameter.patch b/usb/usb-gadget-storage-strict-coversion-of-ro-parameter.patch new file mode 100644 index 00000000000000..4bc783cc215c4b --- /dev/null +++ b/usb/usb-gadget-storage-strict-coversion-of-ro-parameter.patch @@ -0,0 +1,54 @@ +From andy.shevchenko@gmail.com Thu Jul 22 16:46:41 2010 +From: Andy Shevchenko <andy.shevchenko@gmail.com> +To: linux-kernel@vger.kernel.org +Cc: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>, + Alan Stern <stern@rowland.harvard.edu>, + David Brownell <dbrownell@users.sourceforge.net>, + Greg Kroah-Hartman <gregkh@suse.de>, linux-usb@vger.kernel.org +Subject: usb: gadget: storage: strict coversion of 'ro' parameter +Date: Thu, 22 Jul 2010 11:58:47 +0300 +Message-Id: <1279789128-11399-1-git-send-email-ext-andriy.shevchenko@nokia.com> + +Bring a strict way to get the 'ro' parameter from the user. + +The patch followed by this one adds another boolean parameter. To be consistent +Michał Nazarewicz proposed to use simple_strtol() in both cases (correspondend +discussion in LKML [1]). Due to simple_strtol() doesn't return error in a good +way and we have a boolean parameter the strict_strtoul() is used. + +[1] http://lkml.org/lkml/2010/7/14/169 + +Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com> +Acked-by: Alan Stern <stern@rowland.harvard.edu> +Cc: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/storage_common.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/usb/gadget/storage_common.c ++++ b/drivers/usb/gadget/storage_common.c +@@ -748,9 +748,9 @@ static ssize_t fsg_store_ro(struct devic + ssize_t rc = count; + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); +- int i; ++ unsigned long ro; + +- if (sscanf(buf, "%d", &i) != 1) ++ if (strict_strtoul(buf, 2, &ro)) + return -EINVAL; + + /* +@@ -762,8 +762,8 @@ static ssize_t fsg_store_ro(struct devic + LDBG(curlun, "read-only status change prevented\n"); + rc = -EBUSY; + } else { +- curlun->ro = !!i; +- curlun->initially_ro = !!i; ++ curlun->ro = ro; ++ curlun->initially_ro = ro; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + } + up_read(filesem); diff --git a/usb/usb-host-sl811-hcd-check-kzalloc-result.patch b/usb/usb-host-sl811-hcd-check-kzalloc-result.patch new file mode 100644 index 00000000000000..cdcfa5c1c10558 --- /dev/null +++ b/usb/usb-host-sl811-hcd-check-kzalloc-result.patch @@ -0,0 +1,37 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 16:48:01 2010 +From: Kulikov Vasiliy <segooon@gmail.com> +To: kernel-janitors@vger.kernel.org +Cc: Greg Kroah-Hartman <gregkh@suse.de>, + Mike Frysinger <vapier@gentoo.org>, + Alan Stern <stern@rowland.harvard.edu>, + Michael Hennerich <michael.hennerich@analog.com>, + Bryan Wu <cooloney@kernel.org>, linux-usb@vger.kernel.org, + linux-kernel@vger.kernel.org +Subject: usb: host: sl811-hcd: check kzalloc() result +Date: Fri, 16 Jul 2010 20:15:06 +0400 +Message-Id: <1279296906-25107-1-git-send-email-segooon@gmail.com> + +If kzalloc() fails exit with -ENOMEM. + +Signed-off-by: Kulikov Vasiliy <segooon@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/sl811-hcd.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/sl811-hcd.c ++++ b/drivers/usb/host/sl811-hcd.c +@@ -813,8 +813,11 @@ static int sl811h_urb_enqueue( + #endif + + /* avoid all allocations within spinlocks */ +- if (!hep->hcpriv) ++ if (!hep->hcpriv) { + ep = kzalloc(sizeof *ep, mem_flags); ++ if (ep == NULL) ++ return -ENOMEM; ++ } + + spin_lock_irqsave(&sl811->lock, flags); + diff --git a/usb/usb-option-huawei-ets-1220-support-added.patch b/usb/usb-option-huawei-ets-1220-support-added.patch new file mode 100644 index 00000000000000..645fec434fcb5a --- /dev/null +++ b/usb/usb-option-huawei-ets-1220-support-added.patch @@ -0,0 +1,38 @@ +From p.i.kazlou@gmail.com Thu Jul 22 15:59:39 2010 +Date: Thu, 22 Jul 2010 03:22:20 +0300 +From: Pavel Kazlou <p.i.kazlou@gmail.com> +To: Greg Kroah-Hartman <greg@kroah.com> +Cc: linux-usb@vger.kernel.org +Subject: USB: option: Huawei ETS 1220 support added +Message-ID: <20100722032220.647ef563@gmail.com> + +From: Pavel Kazlou <p.i.kazlou@gmail.com> + +The patch adds Huawei ETS 1220 product id into the list of supported +devices in 'option' usb serial driver. + +Signed-off-by: Pavel Kazlou <p.i.kazlou@gmail.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/serial/option.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -146,6 +146,7 @@ static void option_instat_callback(struc + #define HUAWEI_PRODUCT_E143E 0x143E + #define HUAWEI_PRODUCT_E143F 0x143F + #define HUAWEI_PRODUCT_E14AC 0x14AC ++#define HUAWEI_PRODUCT_ETS1220 0x1803 + + #define QUANTA_VENDOR_ID 0x0408 + #define QUANTA_PRODUCT_Q101 0xEA02 +@@ -479,6 +480,7 @@ static const struct usb_device_id option + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, + { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ diff --git a/usb/usb-powerpc-fsl_soc.c-remove-fsl-usb-platform-code.patch b/usb/usb-powerpc-fsl_soc.c-remove-fsl-usb-platform-code.patch new file mode 100644 index 00000000000000..a8fd3e68c83ae2 --- /dev/null +++ b/usb/usb-powerpc-fsl_soc.c-remove-fsl-usb-platform-code.patch @@ -0,0 +1,198 @@ +From agust@denx.de Thu Jul 22 16:53:55 2010 +From: Anatolij Gustschin <agust@denx.de> +To: linux-usb@vger.kernel.org +Cc: linuxppc-dev@ozlabs.org, Greg Kroah-Hartman <gregkh@suse.de>, + David Brownell <dbrownell@users.sourceforge.net>, + Grant Likely <grant.likely@secretlab.ca>, + Detlev Zundel <dzu@denx.de>, Wolfgang Denk <wd@denx.de>, + Anatolij Gustschin <agust@denx.de>, + Kumar Gala <galak@kernel.crashing.org> +Subject: USB: powerpc/fsl_soc.c: remove FSL USB platform code +Date: Thu, 22 Jul 2010 18:25:20 +0200 +Message-Id: <1279815922-27198-2-git-send-email-agust@denx.de> + +This removed code will be replaced by simple of_platform +driver for creation of FSL USB platform devices which is +added by next patch of the series. + +Signed-off-by: Anatolij Gustschin <agust@denx.de> +Cc: Kumar Gala <galak@kernel.crashing.org> +Cc: Grant Likely <grant.likely@secretlab.ca> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/powerpc/sysdev/fsl_soc.c | 163 ------------------------------------------ + 1 file changed, 163 deletions(-) + +--- a/arch/powerpc/sysdev/fsl_soc.c ++++ b/arch/powerpc/sysdev/fsl_soc.c +@@ -209,169 +209,6 @@ static int __init of_add_fixed_phys(void + arch_initcall(of_add_fixed_phys); + #endif /* CONFIG_FIXED_PHY */ + +-static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type) +-{ +- if (!phy_type) +- return FSL_USB2_PHY_NONE; +- if (!strcasecmp(phy_type, "ulpi")) +- return FSL_USB2_PHY_ULPI; +- if (!strcasecmp(phy_type, "utmi")) +- return FSL_USB2_PHY_UTMI; +- if (!strcasecmp(phy_type, "utmi_wide")) +- return FSL_USB2_PHY_UTMI_WIDE; +- if (!strcasecmp(phy_type, "serial")) +- return FSL_USB2_PHY_SERIAL; +- +- return FSL_USB2_PHY_NONE; +-} +- +-static int __init fsl_usb_of_init(void) +-{ +- struct device_node *np; +- unsigned int i = 0; +- struct platform_device *usb_dev_mph = NULL, *usb_dev_dr_host = NULL, +- *usb_dev_dr_client = NULL; +- int ret; +- +- for_each_compatible_node(np, NULL, "fsl-usb2-mph") { +- struct resource r[2]; +- struct fsl_usb2_platform_data usb_data; +- const unsigned char *prop = NULL; +- +- memset(&r, 0, sizeof(r)); +- memset(&usb_data, 0, sizeof(usb_data)); +- +- ret = of_address_to_resource(np, 0, &r[0]); +- if (ret) +- goto err; +- +- of_irq_to_resource(np, 0, &r[1]); +- +- usb_dev_mph = +- platform_device_register_simple("fsl-ehci", i, r, 2); +- if (IS_ERR(usb_dev_mph)) { +- ret = PTR_ERR(usb_dev_mph); +- goto err; +- } +- +- usb_dev_mph->dev.coherent_dma_mask = 0xffffffffUL; +- usb_dev_mph->dev.dma_mask = &usb_dev_mph->dev.coherent_dma_mask; +- +- usb_data.operating_mode = FSL_USB2_MPH_HOST; +- +- prop = of_get_property(np, "port0", NULL); +- if (prop) +- usb_data.port_enables |= FSL_USB2_PORT0_ENABLED; +- +- prop = of_get_property(np, "port1", NULL); +- if (prop) +- usb_data.port_enables |= FSL_USB2_PORT1_ENABLED; +- +- prop = of_get_property(np, "phy_type", NULL); +- usb_data.phy_mode = determine_usb_phy(prop); +- +- ret = +- platform_device_add_data(usb_dev_mph, &usb_data, +- sizeof(struct +- fsl_usb2_platform_data)); +- if (ret) +- goto unreg_mph; +- i++; +- } +- +- for_each_compatible_node(np, NULL, "fsl-usb2-dr") { +- struct resource r[2]; +- struct fsl_usb2_platform_data usb_data; +- const unsigned char *prop = NULL; +- +- if (!of_device_is_available(np)) +- continue; +- +- memset(&r, 0, sizeof(r)); +- memset(&usb_data, 0, sizeof(usb_data)); +- +- ret = of_address_to_resource(np, 0, &r[0]); +- if (ret) +- goto unreg_mph; +- +- of_irq_to_resource(np, 0, &r[1]); +- +- prop = of_get_property(np, "dr_mode", NULL); +- +- if (!prop || !strcmp(prop, "host")) { +- usb_data.operating_mode = FSL_USB2_DR_HOST; +- usb_dev_dr_host = platform_device_register_simple( +- "fsl-ehci", i, r, 2); +- if (IS_ERR(usb_dev_dr_host)) { +- ret = PTR_ERR(usb_dev_dr_host); +- goto err; +- } +- } else if (prop && !strcmp(prop, "peripheral")) { +- usb_data.operating_mode = FSL_USB2_DR_DEVICE; +- usb_dev_dr_client = platform_device_register_simple( +- "fsl-usb2-udc", i, r, 2); +- if (IS_ERR(usb_dev_dr_client)) { +- ret = PTR_ERR(usb_dev_dr_client); +- goto err; +- } +- } else if (prop && !strcmp(prop, "otg")) { +- usb_data.operating_mode = FSL_USB2_DR_OTG; +- usb_dev_dr_host = platform_device_register_simple( +- "fsl-ehci", i, r, 2); +- if (IS_ERR(usb_dev_dr_host)) { +- ret = PTR_ERR(usb_dev_dr_host); +- goto err; +- } +- usb_dev_dr_client = platform_device_register_simple( +- "fsl-usb2-udc", i, r, 2); +- if (IS_ERR(usb_dev_dr_client)) { +- ret = PTR_ERR(usb_dev_dr_client); +- goto err; +- } +- } else { +- ret = -EINVAL; +- goto err; +- } +- +- prop = of_get_property(np, "phy_type", NULL); +- usb_data.phy_mode = determine_usb_phy(prop); +- +- if (usb_dev_dr_host) { +- usb_dev_dr_host->dev.coherent_dma_mask = 0xffffffffUL; +- usb_dev_dr_host->dev.dma_mask = &usb_dev_dr_host-> +- dev.coherent_dma_mask; +- if ((ret = platform_device_add_data(usb_dev_dr_host, +- &usb_data, sizeof(struct +- fsl_usb2_platform_data)))) +- goto unreg_dr; +- } +- if (usb_dev_dr_client) { +- usb_dev_dr_client->dev.coherent_dma_mask = 0xffffffffUL; +- usb_dev_dr_client->dev.dma_mask = &usb_dev_dr_client-> +- dev.coherent_dma_mask; +- if ((ret = platform_device_add_data(usb_dev_dr_client, +- &usb_data, sizeof(struct +- fsl_usb2_platform_data)))) +- goto unreg_dr; +- } +- i++; +- } +- return 0; +- +-unreg_dr: +- if (usb_dev_dr_host) +- platform_device_unregister(usb_dev_dr_host); +- if (usb_dev_dr_client) +- platform_device_unregister(usb_dev_dr_client); +-unreg_mph: +- if (usb_dev_mph) +- platform_device_unregister(usb_dev_mph); +-err: +- return ret; +-} +- +-arch_initcall(fsl_usb_of_init); +- + #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) + static __be32 __iomem *rstcr; + diff --git a/usb/usb-resizing-usbmon-binary-interface-buffer-causes-protection-faults.patch b/usb/usb-resizing-usbmon-binary-interface-buffer-causes-protection-faults.patch new file mode 100644 index 00000000000000..cd5eb6fb2b117d --- /dev/null +++ b/usb/usb-resizing-usbmon-binary-interface-buffer-causes-protection-faults.patch @@ -0,0 +1,35 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 15:57:03 2010 +Message-ID: <4C475AD4.8090000@strobe.cc> +Date: Wed, 21 Jul 2010 16:38:44 -0400 +From: Steven Robertson <steven@strobe.cc> +To: linux-usb@vger.kernel.org, greg@kroah.com +Subject: USB: resizing usbmon binary interface buffer causes protection faults + +Enlarging the buffer size via the MON_IOCT_RING_SIZE ioctl causes +general protection faults. It appears the culprit is an incorrect +argument to mon_free_buff: instead of passing the size of the current +buffer being freed, the size of the new buffer is passed. + +Use the correct size argument to mon_free_buff when changing the size of +the buffer. + +Signed-off-by: Steven Robertson <steven@strobe.cc> +Acked-by: Pete Zaitcev <zaitcev@redhat.com> +Cc: stable <stable@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/mon/mon_bin.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/mon/mon_bin.c ++++ b/drivers/usb/mon/mon_bin.c +@@ -1004,7 +1004,7 @@ static long mon_bin_ioctl(struct file *f + + mutex_lock(&rp->fetch_lock); + spin_lock_irqsave(&rp->b_lock, flags); +- mon_free_buff(rp->b_vec, size/CHUNK_SIZE); ++ mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); + kfree(rp->b_vec); + rp->b_vec = vec; + rp->b_size = size; diff --git a/usb/usb-s3c-hsotg-add-initial-detection-and-setup-for-dedicated-fifo-mode.patch b/usb/usb-s3c-hsotg-add-initial-detection-and-setup-for-dedicated-fifo-mode.patch new file mode 100644 index 00000000000000..979cc20a480e2a --- /dev/null +++ b/usb/usb-s3c-hsotg-add-initial-detection-and-setup-for-dedicated-fifo-mode.patch @@ -0,0 +1,166 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:25 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Add initial detection and setup for dedicated FIFO mode +Date: Mon, 19 Jul 2010 09:40:44 +0100 +Message-Id: <1279528850-28245-6-git-send-email-ben-linux@fluff.org> + +Add support for the dedicated FIFO mode on newer SoCs such as the S5PV210 +partly to improve support and to fix the bug where any non-EP0 IN endpoint +requires its own FIFO allocation. + +To fix this, we ensure that any non-zero IN endpoint is given a TXFIFO +using the same allocation method as the periodic case (all our current +hardware has enough FIFOs and FIFO memory for a 1:1 mapping) and ensure +that the necessary transmission done interrupt is enabled. + +The default settings from reset for the core point all EPs at FIFO0, +used for the control endpoint. However, the controller documentation +states that all IN endpoints _must_ have a unique FIFO to avoid any +contention during transmission. + +Note, this leaves us with a large IN FIFO for EP0 (which re-uses the +old NPTXFIFO) for an endpoint which cannot shift more than a pair of +packets at a time... this is a waste, but it looks like we cannot +re-allocate space to the individual IN FIFOs as they are already +maxed out (to be confirmed). + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h | 2 + + drivers/usb/gadget/s3c-hsotg.c | 40 +++++++++++++++++++- + 2 files changed, 40 insertions(+), 2 deletions(-) + +--- a/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h ++++ b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h +@@ -226,6 +226,7 @@ + + #define S3C_DIEPMSK S3C_HSOTG_REG(0x810) + ++#define S3C_DIEPMSK_TxFIFOEmpty (1 << 7) + #define S3C_DIEPMSK_INEPNakEffMsk (1 << 6) + #define S3C_DIEPMSK_INTknEPMisMsk (1 << 5) + #define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4) +@@ -371,6 +372,7 @@ + + #define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20)) + #define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20)) ++#define S3C_DTXFSTS(_a) S3C_HSOTG_REG(0x918 + ((_a) * 0x20)) + + #define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -12,6 +12,8 @@ + * published by the Free Software Foundation. + */ + ++#define DEBUG ++ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/spinlock.h> +@@ -130,6 +132,7 @@ struct s3c_hsotg_ep { + * @regs: The memory area mapped for accessing registers. + * @regs_res: The resource that was allocated when claiming register space. + * @irq: The IRQ number we are using ++ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. + * @debug_root: root directrory for debugfs. + * @debug_file: main status file for debugfs. + * @debug_fifo: FIFO status file for debugfs. +@@ -148,6 +151,8 @@ struct s3c_hsotg { + struct resource *regs_res; + int irq; + ++ unsigned int dedicated_fifos:1; ++ + struct dentry *debug_root; + struct dentry *debug_file; + struct dentry *debug_fifo; +@@ -466,7 +471,7 @@ static int s3c_hsotg_write_fifo(struct s + if (to_write == 0) + return 0; + +- if (periodic) { ++ if (periodic && !hsotg->dedicated_fifos) { + u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index)); + int size_left; + int size_done; +@@ -504,6 +509,11 @@ static int s3c_hsotg_write_fifo(struct s + s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp); + return -ENOSPC; + } ++ } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { ++ can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index)); ++ ++ can_write &= 0xffff; ++ can_write *= 4; + } else { + if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) { + dev_dbg(hsotg->dev, +@@ -1829,6 +1839,15 @@ static void s3c_hsotg_epint(struct s3c_h + __func__, idx); + clear |= S3C_DIEPMSK_INTknEPMisMsk; + } ++ ++ /* FIFO has space or is empty (see GAHBCFG) */ ++ if (hsotg->dedicated_fifos && ++ ints & S3C_DIEPMSK_TxFIFOEmpty) { ++ dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", ++ __func__, idx); ++ s3c_hsotg_trytx(hsotg, hs_ep); ++ clear |= S3C_DIEPMSK_TxFIFOEmpty; ++ } + } + + writel(clear, hsotg->regs + epint_reg); +@@ -2280,6 +2299,12 @@ static int s3c_hsotg_ep_enable(struct us + break; + } + ++ /* if the hardware has dedicated fifos, we must give each IN EP ++ * a unique tx-fifo even if it is non-periodic. ++ */ ++ if (dir_in && hsotg->dedicated_fifos) ++ epctrl |= S3C_DxEPCTL_TxFNum(index); ++ + /* for non control endpoints, set PID to D0 */ + if (index) + epctrl |= S3C_DxEPCTL_SetD0PID; +@@ -2569,7 +2594,8 @@ int usb_gadget_register_driver(struct us + + writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk | + S3C_DIEPMSK_INTknEPMisMsk | +- S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk, ++ S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk | ++ ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0), + hsotg->regs + S3C_DIEPMSK); + + /* don't need XferCompl, we get that from RXFIFO in slave mode. In +@@ -2778,6 +2804,8 @@ static void s3c_hsotg_otgreset(struct s3 + + static void s3c_hsotg_init(struct s3c_hsotg *hsotg) + { ++ u32 cfg4; ++ + /* unmask subset of endpoint interrupts */ + + writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk | +@@ -2813,6 +2841,14 @@ static void s3c_hsotg_init(struct s3c_hs + + writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0, + hsotg->regs + S3C_GAHBCFG); ++ ++ /* check hardware configuration */ ++ ++ cfg4 = readl(hsotg->regs + 0x50); ++ hsotg->dedicated_fifos = (cfg4 >> 25) & 1; ++ ++ dev_info(hsotg->dev, "%s fifos\n", ++ hsotg->dedicated_fifos ? "dedicated" : "shared"); + } + + static void s3c_hsotg_dump(struct s3c_hsotg *hsotg) diff --git a/usb/usb-s3c-hsotg-add-support-for-clock-gating.patch b/usb/usb-s3c-hsotg-add-support-for-clock-gating.patch new file mode 100644 index 00000000000000..546f52dc54e33e --- /dev/null +++ b/usb/usb-s3c-hsotg-add-support-for-clock-gating.patch @@ -0,0 +1,91 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 16:53:11 2010 +Date: Mon, 19 Jul 2010 16:01:42 +0200 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Subject: USB: s3c-hsotg: add support for clock gating +To: linux-usb@vger.kernel.org, linux-samsung-soc@vger.kernel.org +Cc: m.szyprowski@samsung.com, kyungmin.park@samsung.com, + ben-linux@fluff.org, greg@kroah.com +Message-id: <1279548102-32390-4-git-send-email-m.szyprowski@samsung.com> + +This patch adds support for clock gating of the HS/OTG block. On S5PV210 +otg gating clock is initally disabled so the driver needs to get and +enable it before it can access its registers. + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -152,6 +152,7 @@ struct s3c_hsotg { + void __iomem *regs; + struct resource *regs_res; + int irq; ++ struct clk *clk; + + unsigned int dedicated_fifos:1; + +@@ -3258,13 +3259,20 @@ static int __devinit s3c_hsotg_probe(str + hsotg->dev = dev; + hsotg->plat = plat; + ++ hsotg->clk = clk_get(&pdev->dev, "otg"); ++ if (IS_ERR(hsotg->clk)) { ++ dev_err(dev, "cannot get otg clock\n"); ++ ret = -EINVAL; ++ goto err_mem; ++ } ++ + platform_set_drvdata(pdev, hsotg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "cannot find register resource 0\n"); + ret = -EINVAL; +- goto err_mem; ++ goto err_clk; + } + + hsotg->regs_res = request_mem_region(res->start, resource_size(res), +@@ -3272,7 +3280,7 @@ static int __devinit s3c_hsotg_probe(str + if (!hsotg->regs_res) { + dev_err(dev, "cannot reserve registers\n"); + ret = -ENOENT; +- goto err_mem; ++ goto err_clk; + } + + hsotg->regs = ioremap(res->start, resource_size(res)); +@@ -3325,6 +3333,8 @@ static int __devinit s3c_hsotg_probe(str + + /* reset the system */ + ++ clk_enable(hsotg->clk); ++ + s3c_hsotg_gate(pdev, true); + + s3c_hsotg_otgreset(hsotg); +@@ -3348,7 +3358,8 @@ err_regs: + err_regs_res: + release_resource(hsotg->regs_res); + kfree(hsotg->regs_res); +- ++err_clk: ++ clk_put(hsotg->clk); + err_mem: + kfree(hsotg); + return ret; +@@ -3370,6 +3381,9 @@ static int __devexit s3c_hsotg_remove(st + + s3c_hsotg_gate(pdev, false); + ++ clk_disable(hsotg->clk); ++ clk_put(hsotg->clk); ++ + kfree(hsotg); + return 0; + } diff --git a/usb/usb-s3c-hsotg-add-support-for-external-usb-clock.patch b/usb/usb-s3c-hsotg-add-support-for-external-usb-clock.patch new file mode 100644 index 00000000000000..3ed1117000d43d --- /dev/null +++ b/usb/usb-s3c-hsotg-add-support-for-external-usb-clock.patch @@ -0,0 +1,68 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:50:30 2010 +From: Maurus Cuelenaere <mcuelenaere@gmail.com> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Maurus Cuelenaere <mcuelenaere@gmail.com>, + Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Add support for external USB clock +Date: Mon, 19 Jul 2010 09:40:50 +0100 +Message-Id: <1279528850-28245-12-git-send-email-ben-linux@fluff.org> + +From: Maurus Cuelenaere <mcuelenaere@gmail.com> + +The PLL that drives the USB clock supports 3 input clocks: 12, 24 and 48Mhz. +This patch adds support to the USB driver for setting the correct register bit +according to the given clock. + +This depends on the following patch: +[PATCH] ARM: S3C64XX: Add USB external clock definition + +Signed-off-by: Maurus Cuelenaere <mcuelenaere@gmail.com> +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -25,6 +25,7 @@ + #include <linux/delay.h> + #include <linux/io.h> + #include <linux/slab.h> ++#include <linux/clk.h> + + #include <linux/usb/ch9.h> + #include <linux/usb/gadget.h> +@@ -2798,6 +2799,7 @@ static void __devinit s3c_hsotg_initep(s + */ + static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) + { ++ struct clk *xusbxti; + u32 osc; + + writel(0, S3C_PHYPWR); +@@ -2805,6 +2807,23 @@ static void s3c_hsotg_otgreset(struct s3 + + osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0; + ++ xusbxti = clk_get(hsotg->dev, "xusbxti"); ++ if (xusbxti && !IS_ERR(xusbxti)) { ++ switch (clk_get_rate(xusbxti)) { ++ case 12*MHZ: ++ osc |= S3C_PHYCLK_CLKSEL_12M; ++ break; ++ case 24*MHZ: ++ osc |= S3C_PHYCLK_CLKSEL_24M; ++ break; ++ default: ++ case 48*MHZ: ++ /* default reference clock */ ++ break; ++ } ++ clk_put(xusbxti); ++ } ++ + writel(osc | 0x10, S3C_PHYCLK); + + /* issue a full set of resets to the otg and core */ diff --git a/usb/usb-s3c-hsotg-avoid-overwriting-contents-of-perodic-in-fifo.patch b/usb/usb-s3c-hsotg-avoid-overwriting-contents-of-perodic-in-fifo.patch new file mode 100644 index 00000000000000..699fd2f9f8df31 --- /dev/null +++ b/usb/usb-s3c-hsotg-avoid-overwriting-contents-of-perodic-in-fifo.patch @@ -0,0 +1,51 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:05 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Avoid overwriting contents of perodic in 'fifo' +Date: Mon, 19 Jul 2010 09:40:42 +0100 +Message-Id: <1279528850-28245-4-git-send-email-ben-linux@fluff.org> + +In shared fifo mode (used on older SoCs) the periodic in fifo beahves +much more like a packet buffer, discarding old data when writing new +data. Avoid this by ensuring that we do not load new transactions in +when there is data sitting already in the FIFO. + +Note, this may not be an observed bug, we are fixing the case that this +may happen. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -91,7 +91,9 @@ struct s3c_hsotg_req; + * For periodic IN endpoints, we have fifo_size and fifo_load to try + * and keep track of the amount of data in the periodic FIFO for each + * of these as we don't have a status register that tells us how much +- * is in each of them. ++ * is in each of them. (note, this may actually be useless information ++ * as in shared-fifo mode periodic in acts like a single-frame packet ++ * buffer than a fifo) + */ + struct s3c_hsotg_ep { + struct usb_ep ep; +@@ -474,6 +476,14 @@ static int s3c_hsotg_write_fifo(struct s + + size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); + ++ /* if shared fifo, we cannot write anything until the ++ * previous data has been completely sent. ++ */ ++ if (hs_ep->fifo_load != 0) { ++ s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp); ++ return -ENOSPC; ++ } ++ + dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n", + __func__, size_left, + hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size); diff --git a/usb/usb-s3c-hsotg-check-for-new-request-before-enqueing-new-setup.patch b/usb/usb-s3c-hsotg-check-for-new-request-before-enqueing-new-setup.patch new file mode 100644 index 00000000000000..d9d2fbad74db99 --- /dev/null +++ b/usb/usb-s3c-hsotg-check-for-new-request-before-enqueing-new-setup.patch @@ -0,0 +1,34 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:46 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Check for new request before enqueing new setup +Date: Mon, 19 Jul 2010 09:40:46 +0100 +Message-Id: <1279528850-28245-8-git-send-email-ben-linux@fluff.org> + +Before trying a new setup transaction after getting an EP0 in complete +interrupt, check that the driver did not try and send more EP0 IN data +before enqueing a new setup transaction. + +This fixes a bug where we cannot send all of the IN data in one go +so split the transfer, but then fail to send all the data as we start +waiting for a new OUT transaction + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -1790,7 +1790,7 @@ static void s3c_hsotg_epint(struct s3c_h + if (dir_in) { + s3c_hsotg_complete_in(hsotg, hs_ep); + +- if (idx == 0) ++ if (idx == 0 && !hs_ep->req) + s3c_hsotg_enqueue_setup(hsotg); + } else if (using_dma(hsotg)) { + /* We're using DMA, we need to fire an OutDone here diff --git a/usb/usb-s3c-hsotg-fix-compilation-problem.patch b/usb/usb-s3c-hsotg-fix-compilation-problem.patch new file mode 100644 index 00000000000000..2384ed103b4985 --- /dev/null +++ b/usb/usb-s3c-hsotg-fix-compilation-problem.patch @@ -0,0 +1,32 @@ +From linux-usb-owner@vger.kernel.org Thu Jul 22 16:52:36 2010 +Date: Mon, 19 Jul 2010 16:01:40 +0200 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Subject: USB: s3c-hsotg: fix compilation problem +To: linux-usb@vger.kernel.org, linux-samsung-soc@vger.kernel.org +Cc: m.szyprowski@samsung.com, kyungmin.park@samsung.com, + ben-linux@fluff.org, greg@kroah.com +Message-id: <1279548102-32390-2-git-send-email-m.szyprowski@samsung.com> + +drivers/usb/gadget/s3c-hsotg.c: In function ‘s3c_hsotg_otgreset’: +drivers/usb/gadget/s3c-hsotg.c:2816: error: ‘MHZ’ undeclared (first use in this function) +drivers/usb/gadget/s3c-hsotg.c:2816: error: (Each undeclared identifier is reported only once +drivers/usb/gadget/s3c-hsotg.c:2816: error: for each function it appears in.) + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -36,6 +36,7 @@ + #include <plat/regs-usb-hsotg.h> + #include <mach/regs-sys.h> + #include <plat/udc-hs.h> ++#include <plat/cpu.h> + + #define DMA_ADDR_INVALID (~((dma_addr_t)0)) + diff --git a/usb/usb-s3c-hsotg-fix-max-ep0-in-request-length.patch b/usb/usb-s3c-hsotg-fix-max-ep0-in-request-length.patch new file mode 100644 index 00000000000000..dd52da35379845 --- /dev/null +++ b/usb/usb-s3c-hsotg-fix-max-ep0-in-request-length.patch @@ -0,0 +1,30 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:57 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Fix max EP0 IN request length +Date: Mon, 19 Jul 2010 09:40:47 +0100 +Message-Id: <1279528850-28245-9-git-send-email-ben-linux@fluff.org> + +The maximum length for any EP0 IN request on EP0 is 127 bytes, not 128 +as the driver currently has it. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -612,8 +612,7 @@ static unsigned get_ep_limit(struct s3c_ + maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; + } else { + if (hs_ep->dir_in) { +- /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */ +- maxsize = 64+64+1; ++ maxsize = 64+64; + maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; + } else { + maxsize = 0x3f; diff --git a/usb/usb-s3c-hsotg-fix-out-packet-request-retry.patch b/usb/usb-s3c-hsotg-fix-out-packet-request-retry.patch new file mode 100644 index 00000000000000..396ff6c8f161fd --- /dev/null +++ b/usb/usb-s3c-hsotg-fix-out-packet-request-retry.patch @@ -0,0 +1,89 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:50:21 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Fix OUT packet request retry +Date: Mon, 19 Jul 2010 09:40:49 +0100 +Message-Id: <1279528850-28245-11-git-send-email-ben-linux@fluff.org> + +If there is more data in the request than we could fit into a single +hardware request, then check when the OutDone event is received if +we have more data, and if so, schedule the new data instead of trying +to complete the request (and in the case of EP0, sending a 0 packet +in the middle of a transfer). + +Also, move the debug message about the current transfer state before +the warning about a bad transfer. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -1383,6 +1383,9 @@ static void s3c_hsotg_rx_data(struct s3c + read_ptr = hs_req->req.actual; + max_req = hs_req->req.length - read_ptr; + ++ dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", ++ __func__, to_read, max_req, read_ptr, hs_req->req.length); ++ + if (to_read > max_req) { + /* more data appeared than we where willing + * to deal with in this request. +@@ -1392,9 +1395,6 @@ static void s3c_hsotg_rx_data(struct s3c + WARN_ON_ONCE(1); + } + +- dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", +- __func__, to_read, max_req, read_ptr, hs_req->req.length); +- + hs_ep->total_data += to_read; + hs_req->req.actual += to_read; + to_read = DIV_ROUND_UP(to_read, 4); +@@ -1463,9 +1463,11 @@ static void s3c_hsotg_send_zlp(struct s3 + static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, + int epnum, bool was_setup) + { ++ u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum)); + struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum]; + struct s3c_hsotg_req *hs_req = hs_ep->req; + struct usb_request *req = &hs_req->req; ++ unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); + int result = 0; + + if (!hs_req) { +@@ -1474,9 +1476,7 @@ static void s3c_hsotg_handle_outdone(str + } + + if (using_dma(hsotg)) { +- u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum)); + unsigned size_done; +- unsigned size_left; + + /* Calculate the size of the transfer by checking how much + * is left in the endpoint size register and then working it +@@ -1486,14 +1486,18 @@ static void s3c_hsotg_handle_outdone(str + * so may overshoot/undershoot the transfer. + */ + +- size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); +- + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + req->actual = size_done; + } + ++ /* if there is more request to do, schedule new transfer */ ++ if (req->actual < req->length && size_left == 0) { ++ s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); ++ return; ++ } ++ + if (req->actual < req->length && req->short_not_ok) { + dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n", + __func__, req->actual, req->length); diff --git a/usb/usb-s3c-hsotg-fix-the-out-ep0-limit.patch b/usb/usb-s3c-hsotg-fix-the-out-ep0-limit.patch new file mode 100644 index 00000000000000..7d3bb7b4c9d887 --- /dev/null +++ b/usb/usb-s3c-hsotg-fix-the-out-ep0-limit.patch @@ -0,0 +1,32 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:50:10 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Fix the OUT EP0 limit +Date: Mon, 19 Jul 2010 09:40:48 +0100 +Message-Id: <1279528850-28245-10-git-send-email-ben-linux@fluff.org> + +The EP0 out limit is the same as the IN limit, so make them the same. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -611,11 +611,10 @@ static unsigned get_ep_limit(struct s3c_ + maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1; + maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; + } else { ++ maxsize = 64+64; + if (hs_ep->dir_in) { +- maxsize = 64+64; + maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; + } else { +- maxsize = 0x3f; + maxpkt = 2; + } + } diff --git a/usb/usb-s3c-hsotg-increase-tx-fifo-limit.patch b/usb/usb-s3c-hsotg-increase-tx-fifo-limit.patch new file mode 100644 index 00000000000000..3858476cc2c4fd --- /dev/null +++ b/usb/usb-s3c-hsotg-increase-tx-fifo-limit.patch @@ -0,0 +1,45 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:48:45 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Increase TX fifo limit +Date: Mon, 19 Jul 2010 09:40:40 +0100 +Message-Id: <1279528850-28245-2-git-send-email-ben-linux@fluff.org> + +Up the FIFO size for the TX to 1024 entries, as this now seems to work +with all the cores. This fixes a problem when using large packets on +a core with MPS set to 512 can hang due to insufficient space for the +writes. + +The hang arises due to getting the non-periodic FIFO empty IRQ but +not being able to satisfy any requests since there is never enough +space to write 512 bytes into the buffer. This means we end up with +a stream of interrupt requests. + +It is easier to up the TX FIFO to fill the space we left for it +than to try and fix the positions in the code where we should have +limited the max-packet size to < TXFIFOSIZE, since the TXFIFOSIZE +depends on how the TX FIFOs have been setup. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/gadget/s3c-hsotg.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -310,11 +310,11 @@ static void s3c_hsotg_init_fifo(struct s + hsotg->regs + S3C_GNPTXFSIZ); + */ + +- /* set FIFO sizes to 2048/0x1C0 */ ++ /* set FIFO sizes to 2048/1024 */ + + writel(2048, hsotg->regs + S3C_GRXFSIZ); + writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) | +- S3C_GNPTXFSIZ_NPTxFDep(0x1C0), ++ S3C_GNPTXFSIZ_NPTxFDep(1024), + hsotg->regs + S3C_GNPTXFSIZ); + + /* arange all the rest of the TX FIFOs, as some versions of this diff --git a/usb/usb-s3c-hsotg-modify-only-selected-bits-in-s3c_phypwr-register.patch b/usb/usb-s3c-hsotg-modify-only-selected-bits-in-s3c_phypwr-register.patch new file mode 100644 index 00000000000000..20dc6e133c112e --- /dev/null +++ b/usb/usb-s3c-hsotg-modify-only-selected-bits-in-s3c_phypwr-register.patch @@ -0,0 +1,37 @@ +From m.szyprowski@samsung.com Thu Jul 22 16:52:55 2010 +Date: Mon, 19 Jul 2010 16:01:41 +0200 +From: Marek Szyprowski <m.szyprowski@samsung.com> +Subject: USB: s3c-hsotg: modify only selected bits in S3C_PHYPWR register +To: linux-usb@vger.kernel.org, linux-samsung-soc@vger.kernel.org +Cc: m.szyprowski@samsung.com, kyungmin.park@samsung.com, ben-linux@fluff.org, + greg@kroah.com +Message-id: <1279548102-32390-3-git-send-email-m.szyprowski@samsung.com> + +S5PV210 SoCs has 2 USB PHY interfaces, both enabled by writing zero to +S3C_PHYPWR register. HS/OTG driver uses only PHY0, so do not touch bits +related to PHY1. + +Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> +Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -2801,9 +2801,11 @@ static void __devinit s3c_hsotg_initep(s + static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) + { + struct clk *xusbxti; +- u32 osc; ++ u32 pwr, osc; + +- writel(0, S3C_PHYPWR); ++ pwr = readl(S3C_PHYPWR); ++ pwr &= ~0x19; ++ writel(pwr, S3C_PHYPWR); + mdelay(1); + + osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0; diff --git a/usb/usb-s3c-hsotg-only-load-packet-per-fifo-write.patch b/usb/usb-s3c-hsotg-only-load-packet-per-fifo-write.patch new file mode 100644 index 00000000000000..f60c81a3ea074f --- /dev/null +++ b/usb/usb-s3c-hsotg-only-load-packet-per-fifo-write.patch @@ -0,0 +1,39 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:36 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Only load packet per fifo write +Date: Mon, 19 Jul 2010 09:40:45 +0100 +Message-Id: <1279528850-28245-7-git-send-email-ben-linux@fluff.org> + +Limit the IN FIFO write to a single packet per attempt at writing, +as per the specifications and ensure that we don't return fifo-full +so that we can continue writing packets if we have the space. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -538,6 +538,17 @@ static int s3c_hsotg_write_fifo(struct s + if (can_write > 512) + can_write = 512; + ++ /* limit the write to one max-packet size worth of data, but allow ++ * the transfer to return that it did not run out of fifo space ++ * doing it. */ ++ if (to_write > hs_ep->ep.maxpacket) { ++ to_write = hs_ep->ep.maxpacket; ++ ++ s3c_hsotg_en_gsint(hsotg, ++ periodic ? S3C_GINTSTS_PTxFEmp : ++ S3C_GINTSTS_NPTxFEmp); ++ } ++ + /* see if we can write data */ + + if (to_write > can_write) { diff --git a/usb/usb-s3c-hsotg-re-initialise-all-fifos-on-usb-bus-reset.patch b/usb/usb-s3c-hsotg-re-initialise-all-fifos-on-usb-bus-reset.patch new file mode 100644 index 00000000000000..46dd1ac9060dd8 --- /dev/null +++ b/usb/usb-s3c-hsotg-re-initialise-all-fifos-on-usb-bus-reset.patch @@ -0,0 +1,44 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:49:16 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: Re-initialise all FIFOs on USB bus reset +Date: Mon, 19 Jul 2010 09:40:43 +0100 +Message-Id: <1279528850-28245-5-git-send-email-ben-linux@fluff.org> + +The USB documentation suggest that the FIFOs should be reset when a +bus reset event happens. Use the s3c_hsotg_init_fifo() to ensure that +the FIFO layout is correct and that the FIFOs are flushed before +acknowledging the reset. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -2082,17 +2082,12 @@ irq_retry: + kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true); + + /* it seems after a reset we can end up with a situation +- * where the TXFIFO still has data in it... try flushing +- * it to remove anything that may still be in it. ++ * where the TXFIFO still has data in it... the docs ++ * suggest resetting all the fifos, so use the init_fifo ++ * code to relayout and flush the fifos. + */ + +- if (1) { +- writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh, +- hsotg->regs + S3C_GRSTCTL); +- +- dev_info(hsotg->dev, "GNPTXSTS=%08x\n", +- readl(hsotg->regs + S3C_GNPTXSTS)); +- } ++ s3c_hsotg_init_fifo(hsotg); + + s3c_hsotg_enqueue_setup(hsotg); + diff --git a/usb/usb-s3c-hsotg-the-nptx-ptx-fifo-sizes-are-in-words-not-bytes.patch b/usb/usb-s3c-hsotg-the-nptx-ptx-fifo-sizes-are-in-words-not-bytes.patch new file mode 100644 index 00000000000000..cf7fa79899e9e5 --- /dev/null +++ b/usb/usb-s3c-hsotg-the-nptx-ptx-fifo-sizes-are-in-words-not-bytes.patch @@ -0,0 +1,43 @@ +From ben@ben-laptop.fluff.org Thu Jul 22 16:48:54 2010 +From: Ben Dooks <ben-linux@fluff.org> +To: linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org +Cc: gregkh@suse.de, Ben Dooks <ben-linux@fluff.org> +Subject: USB: s3c-hsotg: The NPTX/PTX FIFO sizes are in words, not bytes +Date: Mon, 19 Jul 2010 09:40:41 +0100 +Message-Id: <1279528850-28245-3-git-send-email-ben-linux@fluff.org> + +Fix a problem where we have been underestimating the space available in +the IN PTX/NPTX FIFOs by assuming that they where simply word aligned +instead of in number-of-words. This means all length calculations need +to be multiplied-by-4. + +Note, we do not change the information about fifo size or start addresses +available to userspace as we assume the user can multiply by four easily +and is already knows these values are in words. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/s3c-hsotg.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/s3c-hsotg.c ++++ b/drivers/usb/gadget/s3c-hsotg.c +@@ -505,6 +505,7 @@ static int s3c_hsotg_write_fifo(struct s + } + + can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts); ++ can_write *= 4; /* fifo size is in 32bit quantities. */ + } + + dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", +@@ -2732,7 +2733,7 @@ static void __devinit s3c_hsotg_initep(s + */ + + ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum)); +- hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo); ++ hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4; + + /* if we're using dma, we need to set the next-endpoint pointer + * to be something valid. diff --git a/usb/usb-xhci-adds-new-cases-to-trb_comp_code-switch.patch b/usb/usb-xhci-adds-new-cases-to-trb_comp_code-switch.patch new file mode 100644 index 00000000000000..561a7a8c7238bb --- /dev/null +++ b/usb/usb-xhci-adds-new-cases-to-trb_comp_code-switch.patch @@ -0,0 +1,128 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:16:08 2010 +Date: Thu, 22 Jul 2010 15:23:20 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: adds new cases to trb_comp_code switch +Message-ID: <20100722222320.GA21388@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch adds new cases to trb_comp_code switch, and moves +the switch judgment ahead of fetching td. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 76 +++++++++++++++++++++++++++++-------------- + 1 file changed, 52 insertions(+), 24 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1605,36 +1605,16 @@ static int handle_tx_event(struct xhci_h + ep = &xdev->eps[ep_index]; + ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); +- if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { ++ if (!ep_ring || ++ (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { + xhci_err(xhci, "ERROR Transfer event for disabled endpoint " + "or incorrect stream ring\n"); + return -ENODEV; + } + + event_dma = event->buffer; +- /* This TRB should be in the TD at the head of this ring's TD list */ +- if (list_empty(&ep_ring->td_list)) { +- xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", +- TRB_TO_SLOT_ID(event->flags), ep_index); +- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", +- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); +- xhci_print_trb_offsets(xhci, (union xhci_trb *) event); +- goto cleanup; +- } +- td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); +- +- /* Is this a TRB in the currently executing TD? */ +- event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, +- td->last_trb, event_dma); +- if (!event_seg) { +- /* HC is busted, give up! */ +- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); +- return -ESHUTDOWN; +- } +- event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; +- +- /* Look for common error cases */ + trb_comp_code = GET_COMP_CODE(event->transfer_len); ++ /* Look for common error cases */ + switch (trb_comp_code) { + /* Skip codes that require special handling depending on + * transfer type +@@ -1670,14 +1650,62 @@ static int handle_tx_event(struct xhci_h + xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n"); + status = -ENOSR; + break; ++ case COMP_BW_OVER: ++ xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n"); ++ break; ++ case COMP_BUFF_OVER: ++ xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n"); ++ break; ++ case COMP_UNDERRUN: ++ /* ++ * When the Isoch ring is empty, the xHC will generate ++ * a Ring Overrun Event for IN Isoch endpoint or Ring ++ * Underrun Event for OUT Isoch endpoint. ++ */ ++ xhci_dbg(xhci, "underrun event on endpoint\n"); ++ if (!list_empty(&ep_ring->td_list)) ++ xhci_dbg(xhci, "Underrun Event for slot %d ep %d " ++ "still with TDs queued?\n", ++ TRB_TO_SLOT_ID(event->flags), ep_index); ++ goto cleanup; ++ case COMP_OVERRUN: ++ xhci_dbg(xhci, "overrun event on endpoint\n"); ++ if (!list_empty(&ep_ring->td_list)) ++ xhci_dbg(xhci, "Overrun Event for slot %d ep %d " ++ "still with TDs queued?\n", ++ TRB_TO_SLOT_ID(event->flags), ep_index); ++ goto cleanup; + default: + if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { + status = 0; + break; + } +- xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); ++ xhci_warn(xhci, "ERROR Unknown event condition, HC probably " ++ "busted\n"); ++ goto cleanup; ++ } ++ ++ /* This TRB should be in the TD at the head of this ring's TD list */ ++ if (list_empty(&ep_ring->td_list)) { ++ xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", ++ TRB_TO_SLOT_ID(event->flags), ep_index); ++ xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", ++ (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); ++ xhci_print_trb_offsets(xhci, (union xhci_trb *) event); + goto cleanup; + } ++ td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); ++ ++ /* Is this a TRB in the currently executing TD? */ ++ event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, ++ td->last_trb, event_dma); ++ if (!event_seg) { ++ /* HC is busted, give up! */ ++ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); ++ return -ESHUTDOWN; ++ } ++ event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; ++ + /* Now update the urb's actual_length and give back to the core */ + /* Was this a control transfer? */ + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) diff --git a/usb/usb-xhci-allocate-bigger-ring-for-isochronous-endpoint.patch b/usb/usb-xhci-allocate-bigger-ring-for-isochronous-endpoint.patch new file mode 100644 index 00000000000000..feb224e065b619 --- /dev/null +++ b/usb/usb-xhci-allocate-bigger-ring-for-isochronous-endpoint.patch @@ -0,0 +1,51 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:18:20 2010 +Date: Thu, 22 Jul 2010 15:23:47 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: allocate bigger ring for isochronous endpoint +Message-ID: <20100722222347.GA21656@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +Isochronous endpoint needs a bigger size of transfer ring. Isochronous URB +consists of multiple packets, each packet needs a isoc td to carry, and +there will be multiple trbs inserted to the ring at one time. One segment +is too small for isochronous endpoints, and it will result in +room_on_ring() check failure and the URB is failed to enqueue. + +Allocate bigger ring for isochronous endpoint. 8 segments should be enough. +This will be replaced with dynamic ring expansion in the future. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-mem.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -1112,8 +1112,18 @@ int xhci_endpoint_init(struct xhci_hcd * + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); + + /* Set up the endpoint ring */ +- virt_dev->eps[ep_index].new_ring = +- xhci_ring_alloc(xhci, 1, true, mem_flags); ++ /* ++ * Isochronous endpoint ring needs bigger size because one isoc URB ++ * carries multiple packets and it will insert multiple tds to the ++ * ring. ++ * This should be replaced with dynamic ring resizing in the future. ++ */ ++ if (usb_endpoint_xfer_isoc(&ep->desc)) ++ virt_dev->eps[ep_index].new_ring = ++ xhci_ring_alloc(xhci, 8, true, mem_flags); ++ else ++ virt_dev->eps[ep_index].new_ring = ++ xhci_ring_alloc(xhci, 1, true, mem_flags); + if (!virt_dev->eps[ep_index].new_ring) { + /* Attempt to use the ring cache */ + if (virt_dev->num_rings_cached == 0) diff --git a/usb/usb-xhci-handle_tx_event-refactor-finish_td.patch b/usb/usb-xhci-handle_tx_event-refactor-finish_td.patch new file mode 100644 index 00000000000000..5bc924c9eb78fe --- /dev/null +++ b/usb/usb-xhci-handle_tx_event-refactor-finish_td.patch @@ -0,0 +1,270 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:14:29 2010 +Date: Thu, 22 Jul 2010 15:22:55 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: handle_tx_event() refactor: finish_td +Message-ID: <20100722222255.GA21215@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch moves the td universal processing part in handle_tx_event() +into a separate function finish_td(). + +if finish_td() returns 1, it indicates the urb can be given back. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/host/xhci-ring.c | 185 ++++++++++++++++++++++++++----------------- + 1 file changed, 112 insertions(+), 73 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1258,6 +1258,104 @@ int xhci_is_vendor_info_code(struct xhci + } + + /* ++ * Finish the td processing, remove the td from td list; ++ * Return 1 if the urb can be given back. ++ */ ++static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, ++ union xhci_trb *event_trb, struct xhci_transfer_event *event, ++ struct xhci_virt_ep *ep, int *status, bool skip) ++{ ++ struct xhci_virt_device *xdev; ++ struct xhci_ring *ep_ring; ++ unsigned int slot_id; ++ int ep_index; ++ struct urb *urb = NULL; ++ struct xhci_ep_ctx *ep_ctx; ++ int ret = 0; ++ u32 trb_comp_code; ++ ++ slot_id = TRB_TO_SLOT_ID(event->flags); ++ xdev = xhci->devs[slot_id]; ++ ep_index = TRB_TO_EP_ID(event->flags) - 1; ++ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ++ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); ++ trb_comp_code = GET_COMP_CODE(event->transfer_len); ++ ++ if (skip) ++ goto td_cleanup; ++ ++ if (trb_comp_code == COMP_STOP_INVAL || ++ trb_comp_code == COMP_STOP) { ++ /* The Endpoint Stop Command completion will take care of any ++ * stopped TDs. A stopped TD may be restarted, so don't update ++ * the ring dequeue pointer or take this TD off any lists yet. ++ */ ++ ep->stopped_td = td; ++ ep->stopped_trb = event_trb; ++ return 0; ++ } else { ++ if (trb_comp_code == COMP_STALL) { ++ /* The transfer is completed from the driver's ++ * perspective, but we need to issue a set dequeue ++ * command for this stalled endpoint to move the dequeue ++ * pointer past the TD. We can't do that here because ++ * the halt condition must be cleared first. Let the ++ * USB class driver clear the stall later. ++ */ ++ ep->stopped_td = td; ++ ep->stopped_trb = event_trb; ++ ep->stopped_stream = ep_ring->stream_id; ++ } else if (xhci_requires_manual_halt_cleanup(xhci, ++ ep_ctx, trb_comp_code)) { ++ /* Other types of errors halt the endpoint, but the ++ * class driver doesn't call usb_reset_endpoint() unless ++ * the error is -EPIPE. Clear the halted status in the ++ * xHCI hardware manually. ++ */ ++ xhci_cleanup_halted_endpoint(xhci, ++ slot_id, ep_index, ep_ring->stream_id, ++ td, event_trb); ++ } else { ++ /* Update ring dequeue pointer */ ++ while (ep_ring->dequeue != td->last_trb) ++ inc_deq(xhci, ep_ring, false); ++ inc_deq(xhci, ep_ring, false); ++ } ++ ++td_cleanup: ++ /* Clean up the endpoint's TD list */ ++ urb = td->urb; ++ ++ /* Do one last check of the actual transfer length. ++ * If the host controller said we transferred more data than ++ * the buffer length, urb->actual_length will be a very big ++ * number (since it's unsigned). Play it safe and say we didn't ++ * transfer anything. ++ */ ++ if (urb->actual_length > urb->transfer_buffer_length) { ++ xhci_warn(xhci, "URB transfer length is wrong, " ++ "xHC issue? req. len = %u, " ++ "act. len = %u\n", ++ urb->transfer_buffer_length, ++ urb->actual_length); ++ urb->actual_length = 0; ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ *status = -EREMOTEIO; ++ else ++ *status = 0; ++ } ++ list_del(&td->td_list); ++ /* Was this TD slated to be cancelled but completed anyway? */ ++ if (!list_empty(&td->cancelled_td_list)) ++ list_del(&td->cancelled_td_list); ++ ++ ret = 1; ++ } ++ ++ return ret; ++} ++ ++/* + * If this function returns an error condition, it means it got a Transfer + * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. + * At this point, the host controller is probably hosed and should be reset. +@@ -1278,6 +1376,7 @@ static int handle_tx_event(struct xhci_h + int status = -EINPROGRESS; + struct xhci_ep_ctx *ep_ctx; + u32 trb_comp_code; ++ int ret = 0; + + xhci_dbg(xhci, "In %s\n", __func__); + slot_id = TRB_TO_SLOT_ID(event->flags); +@@ -1308,7 +1407,6 @@ static int handle_tx_event(struct xhci_h + xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", + (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); + xhci_print_trb_offsets(xhci, (union xhci_trb *) event); +- urb = NULL; + goto cleanup; + } + xhci_dbg(xhci, "%s - getting list entry\n", __func__); +@@ -1379,7 +1477,6 @@ static int handle_tx_event(struct xhci_h + break; + } + xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); +- urb = NULL; + goto cleanup; + } + /* Now update the urb's actual_length and give back to the core */ +@@ -1427,7 +1524,10 @@ static int handle_tx_event(struct xhci_h + + xhci_cleanup_halted_endpoint(xhci, + slot_id, ep_index, 0, td, event_trb); +- goto td_cleanup; ++ ++ ret = finish_td(xhci, td, event_trb, event, ep, ++ &status, true); ++ goto cleanup; + } + /* + * Did we transfer any data, despite the errors that might have +@@ -1456,7 +1556,6 @@ static int handle_tx_event(struct xhci_h + td->urb->transfer_buffer_length - + TRB_LEN(event->transfer_len); + xhci_dbg(xhci, "Waiting for status stage event\n"); +- urb = NULL; + goto cleanup; + } + } +@@ -1558,68 +1657,16 @@ static int handle_tx_event(struct xhci_h + TRB_LEN(event->transfer_len); + } + } +- if (trb_comp_code == COMP_STOP_INVAL || +- trb_comp_code == COMP_STOP) { +- /* The Endpoint Stop Command completion will take care of any +- * stopped TDs. A stopped TD may be restarted, so don't update +- * the ring dequeue pointer or take this TD off any lists yet. +- */ +- ep->stopped_td = td; +- ep->stopped_trb = event_trb; +- } else { +- if (trb_comp_code == COMP_STALL) { +- /* The transfer is completed from the driver's +- * perspective, but we need to issue a set dequeue +- * command for this stalled endpoint to move the dequeue +- * pointer past the TD. We can't do that here because +- * the halt condition must be cleared first. Let the +- * USB class driver clear the stall later. +- */ +- ep->stopped_td = td; +- ep->stopped_trb = event_trb; +- ep->stopped_stream = ep_ring->stream_id; +- } else if (xhci_requires_manual_halt_cleanup(xhci, +- ep_ctx, trb_comp_code)) { +- /* Other types of errors halt the endpoint, but the +- * class driver doesn't call usb_reset_endpoint() unless +- * the error is -EPIPE. Clear the halted status in the +- * xHCI hardware manually. +- */ +- xhci_cleanup_halted_endpoint(xhci, +- slot_id, ep_index, ep_ring->stream_id, td, event_trb); +- } else { +- /* Update ring dequeue pointer */ +- while (ep_ring->dequeue != td->last_trb) +- inc_deq(xhci, ep_ring, false); +- inc_deq(xhci, ep_ring, false); +- } + +-td_cleanup: +- /* Clean up the endpoint's TD list */ +- urb = td->urb; +- /* Do one last check of the actual transfer length. +- * If the host controller said we transferred more data than +- * the buffer length, urb->actual_length will be a very big +- * number (since it's unsigned). Play it safe and say we didn't +- * transfer anything. +- */ +- if (urb->actual_length > urb->transfer_buffer_length) { +- xhci_warn(xhci, "URB transfer length is wrong, " +- "xHC issue? req. len = %u, " +- "act. len = %u\n", +- urb->transfer_buffer_length, +- urb->actual_length); +- urb->actual_length = 0; +- if (td->urb->transfer_flags & URB_SHORT_NOT_OK) +- status = -EREMOTEIO; +- else +- status = 0; +- } +- list_del(&td->td_list); +- /* Was this TD slated to be cancelled but completed anyway? */ +- if (!list_empty(&td->cancelled_td_list)) +- list_del(&td->cancelled_td_list); ++ ret = finish_td(xhci, td, event_trb, event, ep, &status, false); + ++cleanup: ++ inc_deq(xhci, xhci->event_ring, true); ++ xhci_set_hc_event_deq(xhci); ++ ++ /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ ++ if (ret) { ++ urb = td->urb; + /* Leave the TD around for the reset endpoint function to use + * (but only if it's not a control endpoint, since we already + * queued the Set TR dequeue pointer command for stalled +@@ -1627,17 +1674,9 @@ td_cleanup: + */ + if (usb_endpoint_xfer_control(&urb->ep->desc) || + (trb_comp_code != COMP_STALL && +- trb_comp_code != COMP_BABBLE)) { ++ trb_comp_code != COMP_BABBLE)) + kfree(td); +- } +- urb->hcpriv = NULL; +- } +-cleanup: +- inc_deq(xhci, xhci->event_ring, true); +- xhci_set_hc_event_deq(xhci); + +- /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ +- if (urb) { + usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); + xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", + urb, urb->actual_length, status); diff --git a/usb/usb-xhci-handle_tx_event-refactor-process_bulk_intr_td.patch b/usb/usb-xhci-handle_tx_event-refactor-process_bulk_intr_td.patch new file mode 100644 index 00000000000000..6447ef537dda65 --- /dev/null +++ b/usb/usb-xhci-handle_tx_event-refactor-process_bulk_intr_td.patch @@ -0,0 +1,255 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:15:37 2010 +Date: Thu, 22 Jul 2010 15:23:08 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB xHCI: handle_tx_event() refactor: process_bulk_intr_td +Message-ID: <20100722222308.GA21286@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch moves the bulk and interrupt td processing part in +handle_tx_event() into a separate function process_bulk_intr_td(). + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/host/xhci-ring.c | 216 ++++++++++++++++++++++--------------------- + 1 file changed, 115 insertions(+), 101 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1459,6 +1459,117 @@ static int process_ctrl_td(struct xhci_h + } + + /* ++ * Process bulk and interrupt tds, update urb status and actual_length. ++ */ ++static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, ++ union xhci_trb *event_trb, struct xhci_transfer_event *event, ++ struct xhci_virt_ep *ep, int *status) ++{ ++ struct xhci_ring *ep_ring; ++ union xhci_trb *cur_trb; ++ struct xhci_segment *cur_seg; ++ u32 trb_comp_code; ++ ++ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ++ trb_comp_code = GET_COMP_CODE(event->transfer_len); ++ ++ switch (trb_comp_code) { ++ case COMP_SUCCESS: ++ /* Double check that the HW transferred everything. */ ++ if (event_trb != td->last_trb) { ++ xhci_warn(xhci, "WARN Successful completion " ++ "on short TX\n"); ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ *status = -EREMOTEIO; ++ else ++ *status = 0; ++ } else { ++ if (usb_endpoint_xfer_bulk(&td->urb->ep->desc)) ++ xhci_dbg(xhci, "Successful bulk " ++ "transfer!\n"); ++ else ++ xhci_dbg(xhci, "Successful interrupt " ++ "transfer!\n"); ++ *status = 0; ++ } ++ break; ++ case COMP_SHORT_TX: ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ *status = -EREMOTEIO; ++ else ++ *status = 0; ++ break; ++ default: ++ /* Others already handled above */ ++ break; ++ } ++ dev_dbg(&td->urb->dev->dev, ++ "ep %#x - asked for %d bytes, " ++ "%d bytes untransferred\n", ++ td->urb->ep->desc.bEndpointAddress, ++ td->urb->transfer_buffer_length, ++ TRB_LEN(event->transfer_len)); ++ /* Fast path - was this the last TRB in the TD for this URB? */ ++ if (event_trb == td->last_trb) { ++ if (TRB_LEN(event->transfer_len) != 0) { ++ td->urb->actual_length = ++ td->urb->transfer_buffer_length - ++ TRB_LEN(event->transfer_len); ++ if (td->urb->transfer_buffer_length < ++ td->urb->actual_length) { ++ xhci_warn(xhci, "HC gave bad length " ++ "of %d bytes left\n", ++ TRB_LEN(event->transfer_len)); ++ td->urb->actual_length = 0; ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ *status = -EREMOTEIO; ++ else ++ *status = 0; ++ } ++ /* Don't overwrite a previously set error code */ ++ if (*status == -EINPROGRESS) { ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ *status = -EREMOTEIO; ++ else ++ *status = 0; ++ } ++ } else { ++ td->urb->actual_length = ++ td->urb->transfer_buffer_length; ++ /* Ignore a short packet completion if the ++ * untransferred length was zero. ++ */ ++ if (*status == -EREMOTEIO) ++ *status = 0; ++ } ++ } else { ++ /* Slow path - walk the list, starting from the dequeue ++ * pointer, to get the actual length transferred. ++ */ ++ td->urb->actual_length = 0; ++ for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; ++ cur_trb != event_trb; ++ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { ++ if ((cur_trb->generic.field[3] & ++ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && ++ (cur_trb->generic.field[3] & ++ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) ++ td->urb->actual_length += ++ TRB_LEN(cur_trb->generic.field[2]); ++ } ++ /* If the ring didn't stop on a Link or No-op TRB, add ++ * in the actual bytes transferred from the Normal TRB ++ */ ++ if (trb_comp_code != COMP_STOP_INVAL) ++ td->urb->actual_length += ++ TRB_LEN(cur_trb->generic.field[2]) - ++ TRB_LEN(event->transfer_len); ++ } ++ ++ return finish_td(xhci, td, event_trb, event, ep, status, false); ++} ++ ++/* + * If this function returns an error condition, it means it got a Transfer + * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. + * At this point, the host controller is probably hosed and should be reset. +@@ -1584,109 +1695,12 @@ static int handle_tx_event(struct xhci_h + } + /* Now update the urb's actual_length and give back to the core */ + /* Was this a control transfer? */ +- if (usb_endpoint_xfer_control(&td->urb->ep->desc)) { ++ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) + ret = process_ctrl_td(xhci, td, event_trb, event, ep, + &status); +- goto cleanup; +- } else { +- switch (trb_comp_code) { +- case COMP_SUCCESS: +- /* Double check that the HW transferred everything. */ +- if (event_trb != td->last_trb) { +- xhci_warn(xhci, "WARN Successful completion " +- "on short TX\n"); +- if (td->urb->transfer_flags & URB_SHORT_NOT_OK) +- status = -EREMOTEIO; +- else +- status = 0; +- } else { +- if (usb_endpoint_xfer_bulk(&td->urb->ep->desc)) +- xhci_dbg(xhci, "Successful bulk " +- "transfer!\n"); +- else +- xhci_dbg(xhci, "Successful interrupt " +- "transfer!\n"); +- status = 0; +- } +- break; +- case COMP_SHORT_TX: +- if (td->urb->transfer_flags & URB_SHORT_NOT_OK) +- status = -EREMOTEIO; +- else +- status = 0; +- break; +- default: +- /* Others already handled above */ +- break; +- } +- dev_dbg(&td->urb->dev->dev, +- "ep %#x - asked for %d bytes, " +- "%d bytes untransferred\n", +- td->urb->ep->desc.bEndpointAddress, +- td->urb->transfer_buffer_length, +- TRB_LEN(event->transfer_len)); +- /* Fast path - was this the last TRB in the TD for this URB? */ +- if (event_trb == td->last_trb) { +- if (TRB_LEN(event->transfer_len) != 0) { +- td->urb->actual_length = +- td->urb->transfer_buffer_length - +- TRB_LEN(event->transfer_len); +- if (td->urb->transfer_buffer_length < +- td->urb->actual_length) { +- xhci_warn(xhci, "HC gave bad length " +- "of %d bytes left\n", +- TRB_LEN(event->transfer_len)); +- td->urb->actual_length = 0; +- if (td->urb->transfer_flags & +- URB_SHORT_NOT_OK) +- status = -EREMOTEIO; +- else +- status = 0; +- } +- /* Don't overwrite a previously set error code */ +- if (status == -EINPROGRESS) { +- if (td->urb->transfer_flags & URB_SHORT_NOT_OK) +- status = -EREMOTEIO; +- else +- status = 0; +- } +- } else { +- td->urb->actual_length = td->urb->transfer_buffer_length; +- /* Ignore a short packet completion if the +- * untransferred length was zero. +- */ +- if (status == -EREMOTEIO) +- status = 0; +- } +- } else { +- /* Slow path - walk the list, starting from the dequeue +- * pointer, to get the actual length transferred. +- */ +- union xhci_trb *cur_trb; +- struct xhci_segment *cur_seg; +- +- td->urb->actual_length = 0; +- for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; +- cur_trb != event_trb; +- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { +- if ((cur_trb->generic.field[3] & +- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && +- (cur_trb->generic.field[3] & +- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) +- td->urb->actual_length += +- TRB_LEN(cur_trb->generic.field[2]); +- } +- /* If the ring didn't stop on a Link or No-op TRB, add +- * in the actual bytes transferred from the Normal TRB +- */ +- if (trb_comp_code != COMP_STOP_INVAL) +- td->urb->actual_length += +- TRB_LEN(cur_trb->generic.field[2]) - +- TRB_LEN(event->transfer_len); +- } +- } +- +- ret = finish_td(xhci, td, event_trb, event, ep, &status, false); ++ else ++ ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, ++ &status); + + cleanup: + inc_deq(xhci, xhci->event_ring, true); diff --git a/usb/usb-xhci-handle_tx_event-refactor-process_ctrl_td.patch b/usb/usb-xhci-handle_tx_event-refactor-process_ctrl_td.patch new file mode 100644 index 00000000000000..ff71ed728f396c --- /dev/null +++ b/usb/usb-xhci-handle_tx_event-refactor-process_ctrl_td.patch @@ -0,0 +1,222 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:14:54 2010 +Date: Thu, 22 Jul 2010 15:23:03 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: handle_tx_event() refactor: process_ctrl_td +Message-ID: <20100722222303.GA21251@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch moves the ctrl td processing part in handle_tx_event() +into a separate function process_ctrl_td(). + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 184 ++++++++++++++++++++++++------------------- + 1 file changed, 106 insertions(+), 78 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1356,6 +1356,109 @@ td_cleanup: + } + + /* ++ * Process control tds, update urb status and actual_length. ++ */ ++static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ++ union xhci_trb *event_trb, struct xhci_transfer_event *event, ++ struct xhci_virt_ep *ep, int *status) ++{ ++ struct xhci_virt_device *xdev; ++ struct xhci_ring *ep_ring; ++ unsigned int slot_id; ++ int ep_index; ++ struct xhci_ep_ctx *ep_ctx; ++ u32 trb_comp_code; ++ ++ slot_id = TRB_TO_SLOT_ID(event->flags); ++ xdev = xhci->devs[slot_id]; ++ ep_index = TRB_TO_EP_ID(event->flags) - 1; ++ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ++ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); ++ trb_comp_code = GET_COMP_CODE(event->transfer_len); ++ ++ xhci_debug_trb(xhci, xhci->event_ring->dequeue); ++ switch (trb_comp_code) { ++ case COMP_SUCCESS: ++ if (event_trb == ep_ring->dequeue) { ++ xhci_warn(xhci, "WARN: Success on ctrl setup TRB " ++ "without IOC set??\n"); ++ *status = -ESHUTDOWN; ++ } else if (event_trb != td->last_trb) { ++ xhci_warn(xhci, "WARN: Success on ctrl data TRB " ++ "without IOC set??\n"); ++ *status = -ESHUTDOWN; ++ } else { ++ xhci_dbg(xhci, "Successful control transfer!\n"); ++ *status = 0; ++ } ++ 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 ++ *status = 0; ++ break; ++ default: ++ if (!xhci_requires_manual_halt_cleanup(xhci, ++ ep_ctx, trb_comp_code)) ++ break; ++ xhci_dbg(xhci, "TRB error code %u, " ++ "halted endpoint index = %u\n", ++ trb_comp_code, ep_index); ++ /* else fall through */ ++ case COMP_STALL: ++ /* Did we transfer part of the data (middle) phase? */ ++ if (event_trb != ep_ring->dequeue && ++ event_trb != td->last_trb) ++ td->urb->actual_length = ++ td->urb->transfer_buffer_length ++ - TRB_LEN(event->transfer_len); ++ else ++ td->urb->actual_length = 0; ++ ++ xhci_cleanup_halted_endpoint(xhci, ++ slot_id, ep_index, 0, td, event_trb); ++ return finish_td(xhci, td, event_trb, event, ep, status, true); ++ } ++ /* ++ * Did we transfer any data, despite the errors that might have ++ * happened? I.e. did we get past the setup stage? ++ */ ++ if (event_trb != ep_ring->dequeue) { ++ /* The event was for the status stage */ ++ if (event_trb == td->last_trb) { ++ if (td->urb->actual_length != 0) { ++ /* Don't overwrite a previously set error code ++ */ ++ if ((*status == -EINPROGRESS || *status == 0) && ++ (td->urb->transfer_flags ++ & URB_SHORT_NOT_OK)) ++ /* Did we already see a short data ++ * stage? */ ++ *status = -EREMOTEIO; ++ } else { ++ td->urb->actual_length = ++ td->urb->transfer_buffer_length; ++ } ++ } else { ++ /* Maybe the event was for the data stage? */ ++ if (trb_comp_code != COMP_STOP_INVAL) { ++ /* We didn't stop on a link TRB in the middle */ ++ td->urb->actual_length = ++ td->urb->transfer_buffer_length - ++ TRB_LEN(event->transfer_len); ++ xhci_dbg(xhci, "Waiting for status " ++ "stage event\n"); ++ return 0; ++ } ++ } ++ } ++ ++ return finish_td(xhci, td, event_trb, event, ep, status, false); ++} ++ ++/* + * If this function returns an error condition, it means it got a Transfer + * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. + * At this point, the host controller is probably hosed and should be reset. +@@ -1482,84 +1585,9 @@ static int handle_tx_event(struct xhci_h + /* Now update the urb's actual_length and give back to the core */ + /* Was this a control transfer? */ + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) { +- xhci_debug_trb(xhci, xhci->event_ring->dequeue); +- switch (trb_comp_code) { +- case COMP_SUCCESS: +- if (event_trb == ep_ring->dequeue) { +- xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n"); +- status = -ESHUTDOWN; +- } else if (event_trb != td->last_trb) { +- xhci_warn(xhci, "WARN: Success on ctrl data TRB without IOC set??\n"); +- status = -ESHUTDOWN; +- } else { +- xhci_dbg(xhci, "Successful control transfer!\n"); +- status = 0; +- } +- 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 +- status = 0; +- break; +- +- default: +- if (!xhci_requires_manual_halt_cleanup(xhci, +- ep_ctx, trb_comp_code)) +- break; +- xhci_dbg(xhci, "TRB error code %u, " +- "halted endpoint index = %u\n", +- trb_comp_code, ep_index); +- /* else fall through */ +- case COMP_STALL: +- /* Did we transfer part of the data (middle) phase? */ +- if (event_trb != ep_ring->dequeue && +- event_trb != td->last_trb) +- td->urb->actual_length = +- td->urb->transfer_buffer_length +- - TRB_LEN(event->transfer_len); +- else +- td->urb->actual_length = 0; +- +- xhci_cleanup_halted_endpoint(xhci, +- slot_id, ep_index, 0, td, event_trb); +- +- ret = finish_td(xhci, td, event_trb, event, ep, +- &status, true); +- goto cleanup; +- } +- /* +- * Did we transfer any data, despite the errors that might have +- * happened? I.e. did we get past the setup stage? +- */ +- if (event_trb != ep_ring->dequeue) { +- /* The event was for the status stage */ +- if (event_trb == td->last_trb) { +- if (td->urb->actual_length != 0) { +- /* Don't overwrite a previously set error code */ +- if ((status == -EINPROGRESS || +- status == 0) && +- (td->urb->transfer_flags +- & URB_SHORT_NOT_OK)) +- /* Did we already see a short data stage? */ +- status = -EREMOTEIO; +- } else { +- td->urb->actual_length = +- td->urb->transfer_buffer_length; +- } +- } else { +- /* Maybe the event was for the data stage? */ +- if (trb_comp_code != COMP_STOP_INVAL) { +- /* We didn't stop on a link TRB in the middle */ +- td->urb->actual_length = +- td->urb->transfer_buffer_length - +- TRB_LEN(event->transfer_len); +- xhci_dbg(xhci, "Waiting for status stage event\n"); +- goto cleanup; +- } +- } +- } ++ ret = process_ctrl_td(xhci, td, event_trb, event, ep, ++ &status); ++ goto cleanup; + } else { + switch (trb_comp_code) { + case COMP_SUCCESS: diff --git a/usb/usb-xhci-introduce-urb_priv-structure.patch b/usb/usb-xhci-introduce-urb_priv-structure.patch new file mode 100644 index 00000000000000..c46c9dc796911e --- /dev/null +++ b/usb/usb-xhci-introduce-urb_priv-structure.patch @@ -0,0 +1,397 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:17:16 2010 +Date: Thu, 22 Jul 2010 15:23:31 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: Introduce urb_priv structure +Message-ID: <20100722222331.GA21527@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +Add urb_priv data structure to xHCI driver. This structure allows multiple +xhci TDs to be linked to one urb, which is essential for isochronous +transfer. For non-isochronous urb, only one TD is needed for one urb; +for isochronous urb, the TD number for the urb is equal to +urb->number_of_packets. + +The length field of urb_priv indicates the number of TDs in the urb. +The td_cnt field indicates the number of TDs already processed by xHC. +When td_cnt matches length, the urb can be given back to usbcore. + +When an urb is dequeued or cancelled, add all the unprocessed TDs to the +endpoint's cancelled_td_list. When process a cancelled TD, increase +td_cnt field. When td_cnt equals urb_priv->length, giveback the +cancelled urb. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + drivers/usb/host/xhci-mem.c | 16 +++++++ + drivers/usb/host/xhci-ring.c | 91 +++++++++++++++++++++++++++++-------------- + drivers/usb/host/xhci.c | 45 ++++++++++++++++++--- + drivers/usb/host/xhci.h | 7 +++ + 4 files changed, 125 insertions(+), 34 deletions(-) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -1390,6 +1390,22 @@ struct xhci_command *xhci_alloc_command( + return command; + } + ++void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv) ++{ ++ int last; ++ ++ if (!urb_priv) ++ return; ++ ++ last = urb_priv->length - 1; ++ if (last >= 0) { ++ int i; ++ for (i = 0; i <= last; i++) ++ kfree(urb_priv->td[i]); ++ } ++ kfree(urb_priv); ++} ++ + void xhci_free_command(struct xhci_hcd *xhci, + struct xhci_command *command) + { +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -578,16 +578,24 @@ static void xhci_giveback_urb_in_irq(str + struct xhci_td *cur_td, int status, char *adjective) + { + struct usb_hcd *hcd = xhci_to_hcd(xhci); ++ struct urb *urb; ++ struct urb_priv *urb_priv; + +- cur_td->urb->hcpriv = NULL; +- usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb); +- xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb); ++ urb = cur_td->urb; ++ urb_priv = urb->hcpriv; ++ urb_priv->td_cnt++; ++ ++ /* Only giveback urb when this is the last td in urb */ ++ if (urb_priv->td_cnt == urb_priv->length) { ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++ xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb); + +- spin_unlock(&xhci->lock); +- usb_hcd_giveback_urb(hcd, cur_td->urb, status); +- kfree(cur_td); +- spin_lock(&xhci->lock); +- xhci_dbg(xhci, "%s URB given back\n", adjective); ++ spin_unlock(&xhci->lock); ++ usb_hcd_giveback_urb(hcd, urb, status); ++ xhci_urb_free_priv(xhci, urb_priv); ++ spin_lock(&xhci->lock); ++ xhci_dbg(xhci, "%s URB given back\n", adjective); ++ } + } + + /* +@@ -1272,6 +1280,7 @@ static int finish_td(struct xhci_hcd *xh + struct urb *urb = NULL; + struct xhci_ep_ctx *ep_ctx; + int ret = 0; ++ struct urb_priv *urb_priv; + u32 trb_comp_code; + + slot_id = TRB_TO_SLOT_ID(event->flags); +@@ -1325,6 +1334,7 @@ static int finish_td(struct xhci_hcd *xh + td_cleanup: + /* Clean up the endpoint's TD list */ + urb = td->urb; ++ urb_priv = urb->hcpriv; + + /* Do one last check of the actual transfer length. + * If the host controller said we transferred more data than +@@ -1349,7 +1359,10 @@ td_cleanup: + if (!list_empty(&td->cancelled_td_list)) + list_del(&td->cancelled_td_list); + +- ret = 1; ++ urb_priv->td_cnt++; ++ /* Giveback the urb when all the tds are completed */ ++ if (urb_priv->td_cnt == urb_priv->length) ++ ret = 1; + } + + return ret; +@@ -1588,6 +1601,7 @@ static int handle_tx_event(struct xhci_h + union xhci_trb *event_trb; + struct urb *urb = NULL; + int status = -EINPROGRESS; ++ struct urb_priv *urb_priv; + struct xhci_ep_ctx *ep_ctx; + u32 trb_comp_code; + int ret = 0; +@@ -1770,6 +1784,7 @@ cleanup: + + if (ret) { + urb = td->urb; ++ urb_priv = urb->hcpriv; + /* Leave the TD around for the reset endpoint function + * to use(but only if it's not a control endpoint, + * since we already queued the Set TR dequeue pointer +@@ -1778,7 +1793,7 @@ cleanup: + if (usb_endpoint_xfer_control(&urb->ep->desc) || + (trb_comp_code != COMP_STALL && + trb_comp_code != COMP_BABBLE)) +- kfree(td); ++ xhci_urb_free_priv(xhci, urb_priv); + + usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); + xhci_dbg(xhci, "Giveback URB %p, len = %d, " +@@ -1979,10 +1994,12 @@ static int prepare_transfer(struct xhci_ + unsigned int stream_id, + unsigned int num_trbs, + struct urb *urb, +- struct xhci_td **td, ++ unsigned int td_index, + gfp_t mem_flags) + { + int ret; ++ struct urb_priv *urb_priv; ++ struct xhci_td *td; + struct xhci_ring *ep_ring; + struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + +@@ -1998,24 +2015,29 @@ static int prepare_transfer(struct xhci_ + num_trbs, mem_flags); + if (ret) + return ret; +- *td = kzalloc(sizeof(struct xhci_td), mem_flags); +- if (!*td) +- return -ENOMEM; +- INIT_LIST_HEAD(&(*td)->td_list); +- INIT_LIST_HEAD(&(*td)->cancelled_td_list); + +- ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb); +- if (unlikely(ret)) { +- kfree(*td); +- return ret; ++ urb_priv = urb->hcpriv; ++ td = urb_priv->td[td_index]; ++ ++ INIT_LIST_HEAD(&td->td_list); ++ INIT_LIST_HEAD(&td->cancelled_td_list); ++ ++ if (td_index == 0) { ++ ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb); ++ if (unlikely(ret)) { ++ xhci_urb_free_priv(xhci, urb_priv); ++ urb->hcpriv = NULL; ++ return ret; ++ } + } + +- (*td)->urb = urb; +- urb->hcpriv = (void *) (*td); ++ td->urb = urb; + /* Add this TD to the tail of the endpoint ring's TD list */ +- list_add_tail(&(*td)->td_list, &ep_ring->td_list); +- (*td)->start_seg = ep_ring->enq_seg; +- (*td)->first_trb = ep_ring->enqueue; ++ list_add_tail(&td->td_list, &ep_ring->td_list); ++ td->start_seg = ep_ring->enq_seg; ++ td->first_trb = ep_ring->enqueue; ++ ++ urb_priv->td[td_index] = td; + + return 0; + } +@@ -2154,6 +2176,7 @@ static int queue_bulk_sg_tx(struct xhci_ + { + struct xhci_ring *ep_ring; + unsigned int num_trbs; ++ struct urb_priv *urb_priv; + struct xhci_td *td; + struct scatterlist *sg; + int num_sgs; +@@ -2174,9 +2197,13 @@ static int queue_bulk_sg_tx(struct xhci_ + + trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, +- num_trbs, urb, &td, mem_flags); ++ num_trbs, urb, 0, mem_flags); + if (trb_buff_len < 0) + return trb_buff_len; ++ ++ urb_priv = urb->hcpriv; ++ td = urb_priv->td[0]; ++ + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle +@@ -2297,6 +2324,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + struct urb *urb, int slot_id, unsigned int ep_index) + { + struct xhci_ring *ep_ring; ++ struct urb_priv *urb_priv; + struct xhci_td *td; + int num_trbs; + struct xhci_generic_trb *start_trb; +@@ -2342,10 +2370,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, +- num_trbs, urb, &td, mem_flags); ++ num_trbs, urb, 0, mem_flags); + if (ret < 0) + return ret; + ++ urb_priv = urb->hcpriv; ++ td = urb_priv->td[0]; ++ + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle +@@ -2431,6 +2462,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + struct xhci_generic_trb *start_trb; + int start_cycle; + u32 field, length_field; ++ struct urb_priv *urb_priv; + struct xhci_td *td; + + ep_ring = xhci_urb_to_transfer_ring(xhci, urb); +@@ -2458,10 +2490,13 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + num_trbs++; + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, +- num_trbs, urb, &td, mem_flags); ++ num_trbs, urb, 0, mem_flags); + if (ret < 0) + return ret; + ++ urb_priv = urb->hcpriv; ++ td = urb_priv->td[0]; ++ + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -804,7 +804,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd + unsigned long flags; + int ret = 0; + unsigned int slot_id, ep_index; +- ++ struct urb_priv *urb_priv; ++ int size, i; + + if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0) + return -EINVAL; +@@ -824,6 +825,30 @@ int xhci_urb_enqueue(struct usb_hcd *hcd + ret = -ESHUTDOWN; + goto exit; + } ++ ++ if (usb_endpoint_xfer_isoc(&urb->ep->desc)) ++ size = urb->number_of_packets; ++ else ++ size = 1; ++ ++ urb_priv = kzalloc(sizeof(struct urb_priv) + ++ size * sizeof(struct xhci_td *), mem_flags); ++ if (!urb_priv) ++ return -ENOMEM; ++ ++ for (i = 0; i < size; i++) { ++ urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags); ++ if (!urb_priv->td[i]) { ++ urb_priv->length = i; ++ xhci_urb_free_priv(xhci, urb_priv); ++ return -ENOMEM; ++ } ++ } ++ ++ urb_priv->length = size; ++ urb_priv->td_cnt = 0; ++ urb->hcpriv = urb_priv; ++ + if (usb_endpoint_xfer_control(&urb->ep->desc)) { + /* Check to see if the max packet size for the default control + * endpoint changed during FS device enumeration +@@ -877,6 +902,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd + exit: + return ret; + dying: ++ xhci_urb_free_priv(xhci, urb_priv); ++ urb->hcpriv = NULL; + xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for " + "non-responsive xHCI host.\n", + urb->ep->desc.bEndpointAddress, urb); +@@ -918,9 +945,10 @@ dying: + int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) + { + unsigned long flags; +- int ret; ++ int ret, i; + u32 temp; + struct xhci_hcd *xhci; ++ struct urb_priv *urb_priv; + struct xhci_td *td; + unsigned int ep_index; + struct xhci_ring *ep_ring; +@@ -935,12 +963,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd + temp = xhci_readl(xhci, &xhci->op_regs->status); + if (temp == 0xffffffff) { + xhci_dbg(xhci, "HW died, freeing TD.\n"); +- td = (struct xhci_td *) urb->hcpriv; ++ urb_priv = urb->hcpriv; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&xhci->lock, flags); + usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN); +- kfree(td); ++ xhci_urb_free_priv(xhci, urb_priv); + return ret; + } + if (xhci->xhc_state & XHCI_STATE_DYING) { +@@ -968,9 +996,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd + + xhci_dbg(xhci, "Endpoint ring:\n"); + xhci_debug_ring(xhci, ep_ring); +- td = (struct xhci_td *) urb->hcpriv; + +- list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); ++ urb_priv = urb->hcpriv; ++ ++ for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { ++ td = urb_priv->td[i]; ++ list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); ++ } ++ + /* Queue a stop endpoint command, but only if this is + * the first cancellation to be handled. + */ +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1090,6 +1090,12 @@ struct xhci_scratchpad { + dma_addr_t *sp_dma_buffers; + }; + ++struct urb_priv { ++ int length; ++ int td_cnt; ++ struct xhci_td *td[0]; ++}; ++ + /* + * Each segment table entry is 4*32bits long. 1K seems like an ok size: + * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, +@@ -1347,6 +1353,7 @@ struct xhci_ring *xhci_stream_id_to_ring + struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_in_ctx, bool allocate_completion, + gfp_t mem_flags); ++void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv); + void xhci_free_command(struct xhci_hcd *xhci, + struct xhci_command *command); + diff --git a/usb/usb-xhci-isoc-urb-enqueue.patch b/usb/usb-xhci-isoc-urb-enqueue.patch new file mode 100644 index 00000000000000..7db38ba9ba6f81 --- /dev/null +++ b/usb/usb-xhci-isoc-urb-enqueue.patch @@ -0,0 +1,37 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:18:44 2010 +Date: Thu, 22 Jul 2010 15:23:52 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: Isoc urb enqueue +Message-ID: <20100722222352.GA21718@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +Enable isochronous urb enqueue. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -897,7 +897,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd + slot_id, ep_index); + spin_unlock_irqrestore(&xhci->lock, flags); + } else { +- ret = -EINVAL; ++ spin_lock_irqsave(&xhci->lock, flags); ++ if (xhci->xhc_state & XHCI_STATE_DYING) ++ goto dying; ++ ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb, ++ slot_id, ep_index); ++ spin_unlock_irqrestore(&xhci->lock, flags); + } + exit: + return ret; diff --git a/usb/usb-xhci-isochronous-transfer-implementation.patch b/usb/usb-xhci-isochronous-transfer-implementation.patch new file mode 100644 index 00000000000000..dc05da22983e2c --- /dev/null +++ b/usb/usb-xhci-isochronous-transfer-implementation.patch @@ -0,0 +1,408 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:17:49 2010 +Date: Thu, 22 Jul 2010 15:23:39 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: Isochronous transfer implementation +Message-ID: <20100722222339.GA21591@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch implements isochronous urb enqueue and interrupt handler part. + +When an isochronous urb is passed to xHCI driver, first check the transfer +ring to guarantee there is enough room for the whole urb. Then update the +start_frame and interval field of the urb. Always assume URB_ISO_ASAP +is set, and never use urb->start_frame as input. + +The number of isoc TDs is equal to urb->number_of_packets. One isoc TD is +consumed every Interval. Each isoc TD consists of an Isoch TRB chained to +zero or more Normal TRBs. + +Call prepare_transfer for each TD to do initialization; then calculate the +number of TRBs needed for each TD. If the data required by an isoc TD is +physically contiguous (not crosses a page boundary), then only one isoc TRB +is needed; otherwise one or more additional normal TRB shall be chained to +the isoc TRB by the host. + +Set TRB_IOC to the last TRB of each isoc TD. Do not ring endpoint doorbell +to start xHC procession until all the TDs are inserted to the endpoint +transer ring. + +In irq handler, update urb status and actual_length, increase +urb_priv->td_cnt. When all the TDs are completed(td_cnt is equal to +urb_priv->length), giveback the urb to usbcore. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 319 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/host/xhci.h | 5 + 2 files changed, 324 insertions(+) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1472,6 +1472,104 @@ static int process_ctrl_td(struct xhci_h + } + + /* ++ * Process isochronous tds, update urb packet status and actual_length. ++ */ ++static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, ++ union xhci_trb *event_trb, struct xhci_transfer_event *event, ++ struct xhci_virt_ep *ep, int *status) ++{ ++ struct xhci_ring *ep_ring; ++ struct urb_priv *urb_priv; ++ int idx; ++ int len = 0; ++ int skip_td = 0; ++ union xhci_trb *cur_trb; ++ struct xhci_segment *cur_seg; ++ u32 trb_comp_code; ++ ++ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ++ trb_comp_code = GET_COMP_CODE(event->transfer_len); ++ urb_priv = td->urb->hcpriv; ++ idx = urb_priv->td_cnt; ++ ++ if (ep->skip) { ++ /* The transfer is partly done */ ++ *status = -EXDEV; ++ td->urb->iso_frame_desc[idx].status = -EXDEV; ++ } else { ++ /* handle completion code */ ++ switch (trb_comp_code) { ++ case COMP_SUCCESS: ++ td->urb->iso_frame_desc[idx].status = 0; ++ xhci_dbg(xhci, "Successful isoc transfer!\n"); ++ break; ++ case COMP_SHORT_TX: ++ if (td->urb->transfer_flags & URB_SHORT_NOT_OK) ++ td->urb->iso_frame_desc[idx].status = ++ -EREMOTEIO; ++ else ++ td->urb->iso_frame_desc[idx].status = 0; ++ break; ++ case COMP_BW_OVER: ++ td->urb->iso_frame_desc[idx].status = -ECOMM; ++ skip_td = 1; ++ break; ++ case COMP_BUFF_OVER: ++ case COMP_BABBLE: ++ td->urb->iso_frame_desc[idx].status = -EOVERFLOW; ++ skip_td = 1; ++ break; ++ case COMP_STALL: ++ td->urb->iso_frame_desc[idx].status = -EPROTO; ++ skip_td = 1; ++ break; ++ case COMP_STOP: ++ case COMP_STOP_INVAL: ++ break; ++ default: ++ td->urb->iso_frame_desc[idx].status = -1; ++ break; ++ } ++ } ++ ++ /* calc actual length */ ++ if (ep->skip) { ++ td->urb->iso_frame_desc[idx].actual_length = 0; ++ return finish_td(xhci, td, event_trb, event, ep, status, true); ++ } ++ ++ if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { ++ td->urb->iso_frame_desc[idx].actual_length = ++ td->urb->iso_frame_desc[idx].length; ++ td->urb->actual_length += ++ td->urb->iso_frame_desc[idx].length; ++ } else { ++ for (cur_trb = ep_ring->dequeue, ++ cur_seg = ep_ring->deq_seg; cur_trb != event_trb; ++ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { ++ if ((cur_trb->generic.field[3] & ++ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && ++ (cur_trb->generic.field[3] & ++ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) ++ len += ++ TRB_LEN(cur_trb->generic.field[2]); ++ } ++ len += TRB_LEN(cur_trb->generic.field[2]) - ++ TRB_LEN(event->transfer_len); ++ ++ if (trb_comp_code != COMP_STOP_INVAL) { ++ td->urb->iso_frame_desc[idx].actual_length = len; ++ td->urb->actual_length += len; ++ } ++ } ++ ++ if ((idx == urb_priv->length - 1) && *status == -EINPROGRESS) ++ *status = 0; ++ ++ return finish_td(xhci, td, event_trb, event, ep, status, false); ++} ++ ++/* + * Process bulk and interrupt tds, update urb status and actual_length. + */ + static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, +@@ -1768,6 +1866,9 @@ static int handle_tx_event(struct xhci_h + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) + ret = process_ctrl_td(xhci, td, event_trb, event, ep, + &status); ++ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc)) ++ ret = process_isoc_td(xhci, td, event_trb, event, ep, ++ &status); + else + ret = process_bulk_intr_td(xhci, td, event_trb, event, + ep, &status); +@@ -2553,6 +2654,224 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + return 0; + } + ++static int count_isoc_trbs_needed(struct xhci_hcd *xhci, ++ struct urb *urb, int i) ++{ ++ int num_trbs = 0; ++ u64 addr, td_len, running_total; ++ ++ addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset); ++ td_len = urb->iso_frame_desc[i].length; ++ ++ running_total = TRB_MAX_BUFF_SIZE - ++ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); ++ if (running_total != 0) ++ num_trbs++; ++ ++ while (running_total < td_len) { ++ num_trbs++; ++ running_total += TRB_MAX_BUFF_SIZE; ++ } ++ ++ return num_trbs; ++} ++ ++/* This is for isoc transfer */ ++static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ++ struct urb *urb, int slot_id, unsigned int ep_index) ++{ ++ struct xhci_ring *ep_ring; ++ struct urb_priv *urb_priv; ++ struct xhci_td *td; ++ int num_tds, trbs_per_td; ++ struct xhci_generic_trb *start_trb; ++ bool first_trb; ++ int start_cycle; ++ u32 field, length_field; ++ int running_total, trb_buff_len, td_len, td_remain_len, ret; ++ u64 start_addr, addr; ++ int i, j; ++ ++ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ++ ++ num_tds = urb->number_of_packets; ++ if (num_tds < 1) { ++ xhci_dbg(xhci, "Isoc URB with zero packets?\n"); ++ return -EINVAL; ++ } ++ ++ if (!in_interrupt()) ++ dev_dbg(&urb->dev->dev, "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; ++ ++ /* Queue the first TRB, even if it's zero-length */ ++ for (i = 0; i < num_tds; i++) { ++ first_trb = true; ++ ++ running_total = 0; ++ addr = start_addr + urb->iso_frame_desc[i].offset; ++ td_len = urb->iso_frame_desc[i].length; ++ td_remain_len = td_len; ++ ++ trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); ++ ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, ++ urb->stream_id, trbs_per_td, urb, i, mem_flags); ++ if (ret < 0) ++ return ret; ++ ++ urb_priv = urb->hcpriv; ++ td = urb_priv->td[i]; ++ ++ for (j = 0; j < trbs_per_td; j++) { ++ u32 remainder = 0; ++ field = 0; ++ ++ if (first_trb) { ++ /* Queue the isoc TRB */ ++ field |= TRB_TYPE(TRB_ISOC); ++ /* Assume URB_ISO_ASAP is set */ ++ field |= TRB_SIA; ++ if (i > 0) ++ field |= ep_ring->cycle_state; ++ first_trb = false; ++ } else { ++ /* Queue other normal TRBs */ ++ field |= TRB_TYPE(TRB_NORMAL); ++ field |= ep_ring->cycle_state; ++ } ++ ++ /* Chain all the TRBs together; clear the chain bit in ++ * the last TRB to indicate it's the last TRB in the ++ * chain. ++ */ ++ if (j < trbs_per_td - 1) { ++ field |= TRB_CHAIN; ++ } else { ++ td->last_trb = ep_ring->enqueue; ++ field |= TRB_IOC; ++ } ++ ++ /* Calculate TRB length */ ++ trb_buff_len = TRB_MAX_BUFF_SIZE - ++ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); ++ if (trb_buff_len > td_remain_len) ++ trb_buff_len = td_remain_len; ++ ++ remainder = xhci_td_remainder(td_len - running_total); ++ length_field = TRB_LEN(trb_buff_len) | ++ remainder | ++ TRB_INTR_TARGET(0); ++ queue_trb(xhci, ep_ring, false, false, ++ lower_32_bits(addr), ++ upper_32_bits(addr), ++ length_field, ++ /* We always want to know if the TRB was short, ++ * or we won't get an event when it completes. ++ * (Unless we use event data TRBs, which are a ++ * waste of space and HC resources.) ++ */ ++ field | TRB_ISP); ++ running_total += trb_buff_len; ++ ++ addr += trb_buff_len; ++ td_remain_len -= trb_buff_len; ++ } ++ ++ /* Check TD length */ ++ if (running_total != td_len) { ++ xhci_err(xhci, "ISOC TD length unmatch\n"); ++ return -EINVAL; ++ } ++ } ++ ++ wmb(); ++ start_trb->field[3] |= start_cycle; ++ ++ ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id); ++ return 0; ++} ++ ++/* ++ * Check transfer ring to guarantee there is enough room for the urb. ++ * Update ISO URB start_frame and interval. ++ * Update interval as xhci_queue_intr_tx does. Just use xhci frame_index to ++ * update the urb->start_frame by now. ++ * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input. ++ */ ++int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, ++ struct urb *urb, int slot_id, unsigned int ep_index) ++{ ++ struct xhci_virt_device *xdev; ++ struct xhci_ring *ep_ring; ++ struct xhci_ep_ctx *ep_ctx; ++ int start_frame; ++ int xhci_interval; ++ int ep_interval; ++ int num_tds, num_trbs, i; ++ int ret; ++ ++ xdev = xhci->devs[slot_id]; ++ ep_ring = xdev->eps[ep_index].ring; ++ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); ++ ++ num_trbs = 0; ++ num_tds = urb->number_of_packets; ++ for (i = 0; i < num_tds; i++) ++ num_trbs += count_isoc_trbs_needed(xhci, urb, i); ++ ++ /* Check the ring to guarantee there is enough room for the whole urb. ++ * Do not insert any td of the urb to the ring if the check failed. ++ */ ++ ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK, ++ num_trbs, mem_flags); ++ if (ret) ++ return ret; ++ ++ start_frame = xhci_readl(xhci, &xhci->run_regs->microframe_index); ++ start_frame &= 0x3fff; ++ ++ urb->start_frame = start_frame; ++ if (urb->dev->speed == USB_SPEED_LOW || ++ urb->dev->speed == USB_SPEED_FULL) ++ urb->start_frame >>= 3; ++ ++ xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); ++ ep_interval = urb->interval; ++ /* Convert to microframes */ ++ if (urb->dev->speed == USB_SPEED_LOW || ++ urb->dev->speed == USB_SPEED_FULL) ++ ep_interval *= 8; ++ /* FIXME change this to a warning and a suggestion to use the new API ++ * to set the polling interval (once the API is added). ++ */ ++ if (xhci_interval != ep_interval) { ++ if (!printk_ratelimit()) ++ dev_dbg(&urb->dev->dev, "Driver uses different interval" ++ " (%d microframe%s) than xHCI " ++ "(%d microframe%s)\n", ++ ep_interval, ++ ep_interval == 1 ? "" : "s", ++ xhci_interval, ++ xhci_interval == 1 ? "" : "s"); ++ urb->interval = xhci_interval; ++ /* Convert back to frames for LS/FS devices */ ++ if (urb->dev->speed == USB_SPEED_LOW || ++ urb->dev->speed == USB_SPEED_FULL) ++ urb->interval /= 8; ++ } ++ return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); ++} ++ + /**** Command Ring Operations ****/ + + /* Generic function for queueing a command TRB on the command ring. +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -919,6 +919,9 @@ struct xhci_event_cmd { + /* Control transfer TRB specific fields */ + #define TRB_DIR_IN (1<<16) + ++/* Isochronous TRB specific fields */ ++#define TRB_SIA (1<<31) ++ + struct xhci_generic_trb { + u32 field[4]; + }; +@@ -1416,6 +1419,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd * + int slot_id, unsigned int ep_index); + int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, + int slot_id, unsigned int ep_index); ++int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, ++ struct urb *urb, int slot_id, unsigned int ep_index); + int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, + u32 slot_id, bool command_must_succeed); + int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, diff --git a/usb/usb-xhci-missed-service-error-event-process.patch b/usb/usb-xhci-missed-service-error-event-process.patch new file mode 100644 index 00000000000000..47b9dd5c864acb --- /dev/null +++ b/usb/usb-xhci-missed-service-error-event-process.patch @@ -0,0 +1,232 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:16:58 2010 +Date: Thu, 22 Jul 2010 15:23:25 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: Missed Service Error Event process +Message-ID: <20100722222325.GA21452@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +This patch adds mechanism to process Missed Service Error Event. +Sometimes the xHC is unable to process the isoc TDs in time, it will +generate Missed Service Error Event. In this case some TDs on the ring are +not processed and missed. When encounter a Missed Servce Error Event, set +the skip flag of the ep, and process the missed TDs until reach the next +processed TD, then clear the skip flag. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci-ring.c | 156 +++++++++++++++++++++++++++++-------------- + drivers/usb/host/xhci.h | 8 ++ + 3 files changed, 116 insertions(+), 49 deletions(-) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -1124,6 +1124,7 @@ int xhci_endpoint_init(struct xhci_hcd * + virt_dev->num_rings_cached--; + xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); + } ++ virt_dev->eps[ep_index].skip = false; + ep_ring = virt_dev->eps[ep_index].new_ring; + ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1675,6 +1675,16 @@ static int handle_tx_event(struct xhci_h + "still with TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); + goto cleanup; ++ case COMP_MISSED_INT: ++ /* ++ * When encounter missed service error, one or more isoc tds ++ * may be missed by xHC. ++ * Set skip flag of the ep_ring; Complete the missed tds as ++ * short transfer when process the ep_ring next time. ++ */ ++ ep->skip = true; ++ xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); ++ goto cleanup; + default: + if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { + status = 0; +@@ -1685,60 +1695,108 @@ static int handle_tx_event(struct xhci_h + goto cleanup; + } + +- /* This TRB should be in the TD at the head of this ring's TD list */ +- if (list_empty(&ep_ring->td_list)) { +- xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", +- TRB_TO_SLOT_ID(event->flags), ep_index); +- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", +- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); +- xhci_print_trb_offsets(xhci, (union xhci_trb *) event); +- goto cleanup; +- } +- td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); ++ do { ++ /* This TRB should be in the TD at the head of this ring's ++ * TD list. ++ */ ++ if (list_empty(&ep_ring->td_list)) { ++ xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " ++ "with no TDs queued?\n", ++ TRB_TO_SLOT_ID(event->flags), ep_index); ++ xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", ++ (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); ++ xhci_print_trb_offsets(xhci, (union xhci_trb *) event); ++ if (ep->skip) { ++ ep->skip = false; ++ xhci_dbg(xhci, "td_list is empty while skip " ++ "flag set. Clear skip flag.\n"); ++ } ++ ret = 0; ++ goto cleanup; ++ } + +- /* Is this a TRB in the currently executing TD? */ +- event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, +- td->last_trb, event_dma); +- if (!event_seg) { +- /* HC is busted, give up! */ +- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); +- return -ESHUTDOWN; +- } +- event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; ++ td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); ++ /* Is this a TRB in the currently executing TD? */ ++ event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, ++ td->last_trb, event_dma); ++ if (event_seg && ep->skip) { ++ xhci_dbg(xhci, "Found td. Clear skip flag.\n"); ++ ep->skip = false; ++ } ++ if (!event_seg && ++ (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) { ++ /* HC is busted, give up! */ ++ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not " ++ "part of current TD\n"); ++ return -ESHUTDOWN; ++ } + +- /* Now update the urb's actual_length and give back to the core */ +- /* Was this a control transfer? */ +- if (usb_endpoint_xfer_control(&td->urb->ep->desc)) +- ret = process_ctrl_td(xhci, td, event_trb, event, ep, +- &status); +- else +- ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, +- &status); ++ if (event_seg) { ++ event_trb = &event_seg->trbs[(event_dma - ++ event_seg->dma) / sizeof(*event_trb)]; ++ /* ++ * No-op TRB should not trigger interrupts. ++ * If event_trb is a no-op TRB, it means the ++ * corresponding TD has been cancelled. Just ignore ++ * the TD. ++ */ ++ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) ++ == TRB_TYPE(TRB_TR_NOOP)) { ++ xhci_dbg(xhci, "event_trb is a no-op TRB. " ++ "Skip it\n"); ++ goto cleanup; ++ } ++ } + +-cleanup: +- inc_deq(xhci, xhci->event_ring, true); +- xhci_set_hc_event_deq(xhci); ++ /* Now update the urb's actual_length and give back to ++ * the core ++ */ ++ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) ++ ret = process_ctrl_td(xhci, td, event_trb, event, ep, ++ &status); ++ else ++ ret = process_bulk_intr_td(xhci, td, event_trb, event, ++ ep, &status); + +- /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ +- if (ret) { +- urb = td->urb; +- /* Leave the TD around for the reset endpoint function to use +- * (but only if it's not a control endpoint, since we already +- * queued the Set TR dequeue pointer command for stalled +- * control endpoints). ++cleanup: ++ /* ++ * Do not update event ring dequeue pointer if ep->skip is set. ++ * Will roll back to continue process missed tds. + */ +- if (usb_endpoint_xfer_control(&urb->ep->desc) || +- (trb_comp_code != COMP_STALL && +- trb_comp_code != COMP_BABBLE)) +- kfree(td); +- +- usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); +- xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", +- urb, urb->actual_length, status); +- spin_unlock(&xhci->lock); +- usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); +- spin_lock(&xhci->lock); +- } ++ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { ++ inc_deq(xhci, xhci->event_ring, true); ++ xhci_set_hc_event_deq(xhci); ++ } ++ ++ if (ret) { ++ urb = td->urb; ++ /* Leave the TD around for the reset endpoint function ++ * to use(but only if it's not a control endpoint, ++ * since we already queued the Set TR dequeue pointer ++ * command for stalled control endpoints). ++ */ ++ if (usb_endpoint_xfer_control(&urb->ep->desc) || ++ (trb_comp_code != COMP_STALL && ++ trb_comp_code != COMP_BABBLE)) ++ kfree(td); ++ ++ usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); ++ xhci_dbg(xhci, "Giveback URB %p, len = %d, " ++ "status = %d\n", ++ urb, urb->actual_length, status); ++ spin_unlock(&xhci->lock); ++ usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); ++ spin_lock(&xhci->lock); ++ } ++ ++ /* ++ * If ep->skip is set, it means there are missed tds on the ++ * endpoint ring need to take care of. ++ * Process them as short transfer until reach the td pointed by ++ * the event. ++ */ ++ } while (ep->skip && trb_comp_code != COMP_MISSED_INT); ++ + return 0; + } + +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -720,6 +720,14 @@ struct xhci_virt_ep { + struct timer_list stop_cmd_timer; + int stop_cmds_pending; + struct xhci_hcd *xhci; ++ /* ++ * Sometimes the xHC can not process isochronous endpoint ring quickly ++ * enough, and it will miss some isoc tds on the ring and generate ++ * a Missed Service Error Event. ++ * Set skip flag when receive a Missed Service Error Event and ++ * process the missed tds on the endpoint ring. ++ */ ++ bool skip; + }; + + struct xhci_virt_device { diff --git a/usb/usb-xhci-remove-redundant-print-messages.patch b/usb/usb-xhci-remove-redundant-print-messages.patch new file mode 100644 index 00000000000000..224e660583fc3c --- /dev/null +++ b/usb/usb-xhci-remove-redundant-print-messages.patch @@ -0,0 +1,70 @@ +From sarah.a.sharp@linux.intel.com Thu Jul 22 16:16:27 2010 +Date: Thu, 22 Jul 2010 15:23:15 -0700 +From: Andiry Xu <andiry.xu@amd.com> +To: Greg KH <gregkh@suse.de> +Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com> +Subject: USB: xHCI: remove redundant print messages +Message-ID: <20100722222315.GA21323@xanatos> +Content-Disposition: inline + +From: Andiry Xu <andiry.xu@amd.com> + +Remove redundant print messages in the interrupt context. + +Signed-off-by: Andiry Xu <andiry.xu@amd.com> +Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/xhci-ring.c | 15 --------------- + 1 file changed, 15 deletions(-) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1592,7 +1592,6 @@ static int handle_tx_event(struct xhci_h + u32 trb_comp_code; + int ret = 0; + +- xhci_dbg(xhci, "In %s\n", __func__); + slot_id = TRB_TO_SLOT_ID(event->flags); + xdev = xhci->devs[slot_id]; + if (!xdev) { +@@ -1614,7 +1613,6 @@ static int handle_tx_event(struct xhci_h + + event_dma = event->buffer; + /* This TRB should be in the TD at the head of this ring's TD list */ +- xhci_dbg(xhci, "%s - checking for list empty\n", __func__); + if (list_empty(&ep_ring->td_list)) { + xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); +@@ -1623,30 +1621,17 @@ static int handle_tx_event(struct xhci_h + xhci_print_trb_offsets(xhci, (union xhci_trb *) event); + goto cleanup; + } +- xhci_dbg(xhci, "%s - getting list entry\n", __func__); + td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); + + /* Is this a TRB in the currently executing TD? */ +- xhci_dbg(xhci, "%s - looking for TD\n", __func__); + event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, + td->last_trb, event_dma); +- xhci_dbg(xhci, "%s - found event_seg = %p\n", __func__, event_seg); + if (!event_seg) { + /* HC is busted, give up! */ + xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); + return -ESHUTDOWN; + } + event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; +- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", +- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); +- xhci_dbg(xhci, "Offset 0x00 (buffer lo) = 0x%x\n", +- lower_32_bits(event->buffer)); +- xhci_dbg(xhci, "Offset 0x04 (buffer hi) = 0x%x\n", +- upper_32_bits(event->buffer)); +- xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n", +- (unsigned int) event->transfer_len); +- xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n", +- (unsigned int) event->flags); + + /* Look for common error cases */ + trb_comp_code = GET_COMP_CODE(event->transfer_len); |
