From 31c6bf70955dda6ef92ab40624f289576cff97d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Sat, 11 Jan 2014 02:04:00 +0100 Subject: usb: core: let dynamic ids override static ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This modifies the probing order so that any matching dynamic entry always will be used, even if the driver has a matching static entry. It is sometimes useful to dynamically update existing device entries. With the new ability to set the dynamic entry driver_info field, this can be used to test new additions to class driver exception lists or proposed changes to existing static per-device driver_info entries. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 5d01558cef66..9cd218135087 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -302,9 +302,9 @@ static int usb_probe_interface(struct device *dev) return error; } - id = usb_match_id(intf, driver->id_table); + id = usb_match_dynamic_id(intf, driver); if (!id) - id = usb_match_dynamic_id(intf, driver); + id = usb_match_id(intf, driver->id_table); if (!id) return error; -- cgit v1.2.1 From ca52a17ba975dbf47e87c9bc63086aca0cf92806 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:40 +0100 Subject: ohci-platform: Add support for devicetree instantiation Add support for ohci-platform instantiation from devicetree, including optionally getting clks and a phy from devicetree, and enabling / disabling those on power_on / off. This should allow using ohci-platform from devicetree in various cases. Specifically after this commit it can be used for the ohci controller found on Allwinner sunxi SoCs. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 162 +++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 68f674cd095f..49304ddde422 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Michael Buesch * Copyright 2011-2012 Hauke Mehrtens + * Copyright 2014 Hans de Goede * * Derived from the OCHI-SSB driver * Derived from the OHCI-PCI driver @@ -14,11 +15,14 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include +#include #include #include #include #include #include +#include #include #include #include @@ -27,6 +31,13 @@ #include "ohci.h" #define DRIVER_DESC "OHCI generic platform driver" +#define OHCI_MAX_CLKS 3 +#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) + +struct ohci_platform_priv { + struct clk *clks[OHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ohci-platform"; @@ -48,11 +59,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd) return ohci_setup(hcd); } +static int ohci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ohci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ohci_platform_hc_driver; static const struct ohci_driver_overrides platform_overrides __initconst = { - .product_desc = "Generic Platform OHCI controller", - .reset = ohci_platform_reset, + .product_desc = "Generic Platform OHCI controller", + .reset = ohci_platform_reset, + .extra_priv_size = sizeof(struct ohci_platform_priv), +}; + +static struct usb_ohci_pdata ohci_platform_defaults = { + .power_on = ohci_platform_power_on, + .power_suspend = ohci_platform_power_off, + .power_off = ohci_platform_power_off, }; static int ohci_platform_probe(struct platform_device *dev) @@ -60,17 +127,23 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); - int irq; - int err = -ENOMEM; - - if (!pdata) { - WARN_ON(1); - return -ENODEV; - } + struct ohci_platform_priv *priv; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; + /* + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. + */ + if (!pdata) + pdata = &ohci_platform_defaults; + + err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); + if (err) + return err; + irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -83,17 +156,40 @@ static int ohci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ohci_priv(hcd); + + if (pdata == &ohci_platform_defaults && dev->dev.of_node) { + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -102,11 +198,11 @@ static int ohci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); @@ -114,11 +210,17 @@ static int ohci_platform_probe(struct platform_device *dev) return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -127,13 +229,22 @@ static int ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + + if (pdata == &ohci_platform_defaults) + dev->dev.platform_data = NULL; + return 0; } @@ -180,6 +291,12 @@ static int ohci_platform_resume(struct device *dev) #define ohci_platform_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id ohci_platform_ids[] = { + { .compatible = "usb-ohci", }, + { } +}; +MODULE_DEVICE_TABLE(of, ohci_platform_ids); + static const struct platform_device_id ohci_platform_table[] = { { "ohci-platform", 0 }, { } @@ -200,6 +317,7 @@ static struct platform_driver ohci_platform_driver = { .owner = THIS_MODULE, .name = "ohci-platform", .pm = &ohci_platform_pm_ops, + .of_match_table = ohci_platform_ids, } }; -- cgit v1.2.1 From a4aeb2117571292f4e002c54b3f91e138722bf7a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:41 +0100 Subject: ehci-platform: Add support for clks and phy passed through devicetree Currently ehci-platform is only used in combination with devicetree when used with some Via socs. By extending it to (optionally) get clks and a phy from devicetree, and enabling / disabling those on power_on / off, it can be used more generically. Specifically after this commit it can be used for the ehci controller on Allwinner sunxi SoCs. Since ehci-platform is intended to handle any generic enough non pci ehci device, add a "usb-ehci" compatibility string. There already is a usb-ehci device-tree bindings document, update this with clks and phy bindings info. Although actually quite generic so far the via,vt8500 compatibilty string had its own bindings document. Somehow we even ended up with 2 of them. Since these provide no extra information over the generic usb-ehci documentation, this patch removes them. The ehci-ppc-of.c driver also claims the usb-ehci compatibility string, even though it mostly is ibm,usb-ehci-440epx specific. ehci-platform.c is not needed on ppc platforms, so add a !PPC_OF dependency to it to avoid 2 drivers claiming the same compatibility string getting build on ppc. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 1 + drivers/usb/host/ehci-platform.c | 147 ++++++++++++++++++++++++++++++++------- 2 files changed, 124 insertions(+), 24 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a9707da7da0b..e28cbe0e1f57 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -255,6 +255,7 @@ config USB_EHCI_ATH79 config USB_EHCI_HCD_PLATFORM tristate "Generic EHCI driver for a platform device" + depends on !PPC_OF default n ---help--- Adds an EHCI host driver for a generic platform device, which diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 01536cfd361d..5ebd0b75b58e 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -3,6 +3,7 @@ * * Copyright 2007 Steven Brown * Copyright 2010-2012 Hauke Mehrtens + * Copyright 2014 Hans de Goede * * Derived from the ohci-ssb driver * Copyright 2007 Michael Buesch @@ -18,6 +19,7 @@ * * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +36,13 @@ #include "ehci.h" #define DRIVER_DESC "EHCI generic platform driver" +#define EHCI_MAX_CLKS 3 +#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) + +struct ehci_platform_priv { + struct clk *clks[EHCI_MAX_CLKS]; + struct phy *phy; +}; static const char hcd_name[] = "ehci-platform"; @@ -64,38 +74,90 @@ static int ehci_platform_reset(struct usb_hcd *hcd) return 0; } +static int ehci_platform_power_on(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk, ret; + + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); + if (ret) + goto err_disable_clks; + } + + if (priv->phy) { + ret = phy_init(priv->phy); + if (ret) + goto err_disable_clks; + + ret = phy_power_on(priv->phy); + if (ret) + goto err_exit_phy; + } + + return 0; + +err_exit_phy: + phy_exit(priv->phy); +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); + + return ret; +} + +static void ehci_platform_power_off(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; + + if (priv->phy) { + phy_power_off(priv->phy); + phy_exit(priv->phy); + } + + for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) + if (priv->clks[clk]) + clk_disable_unprepare(priv->clks[clk]); +} + static struct hc_driver __read_mostly ehci_platform_hc_driver; static const struct ehci_driver_overrides platform_overrides __initconst = { - .reset = ehci_platform_reset, + .reset = ehci_platform_reset, + .extra_priv_size = sizeof(struct ehci_platform_priv), }; -static struct usb_ehci_pdata ehci_platform_defaults; +static struct usb_ehci_pdata ehci_platform_defaults = { + .power_on = ehci_platform_power_on, + .power_suspend = ehci_platform_power_off, + .power_off = ehci_platform_power_off, +}; static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct resource *res_mem; - struct usb_ehci_pdata *pdata; - int irq; - int err; + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; /* - * use reasonable defaults so platforms don't have to provide these. - * with DT probing on ARM, none of these are set. + * Use reasonable defaults so platforms don't have to provide these + * with DT probing on ARM. */ - if (!dev_get_platdata(&dev->dev)) - dev->dev.platform_data = &ehci_platform_defaults; + if (!pdata) + pdata = &ehci_platform_defaults; err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); if (err) return err; - pdata = dev_get_platdata(&dev->dev); - irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -107,17 +169,40 @@ static int ehci_platform_probe(struct platform_device *dev) return -ENXIO; } + hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + platform_set_drvdata(dev, hcd); + dev->dev.platform_data = pdata; + priv = hcd_to_ehci_priv(hcd); + + if (pdata == &ehci_platform_defaults && dev->dev.of_node) { + priv->phy = devm_phy_get(&dev->dev, "usb"); + if (IS_ERR(priv->phy)) { + err = PTR_ERR(priv->phy); + if (err == -EPROBE_DEFER) + goto err_put_hcd; + priv->phy = NULL; + } + + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); + if (IS_ERR(priv->clks[clk])) { + err = PTR_ERR(priv->clks[clk]); + if (err == -EPROBE_DEFER) + goto err_put_clks; + priv->clks[clk] = NULL; + break; + } + } + } + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) - return err; - } - - hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, - dev_name(&dev->dev)); - if (!hcd) { - err = -ENOMEM; - goto err_power; + goto err_put_clks; } hcd->rsrc_start = res_mem->start; @@ -126,22 +211,28 @@ static int ehci_platform_probe(struct platform_device *dev) hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); if (IS_ERR(hcd->regs)) { err = PTR_ERR(hcd->regs); - goto err_put_hcd; + goto err_power; } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err_put_hcd; + goto err_power; device_wakeup_enable(hcd->self.controller); platform_set_drvdata(dev, hcd); return err; -err_put_hcd: - usb_put_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); +err_put_clks: + while (--clk >= 0) + clk_put(priv->clks[clk]); +err_put_hcd: + if (pdata == &ehci_platform_defaults) + dev->dev.platform_data = NULL; + + usb_put_hcd(hcd); return err; } @@ -150,13 +241,19 @@ static int ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int clk; usb_remove_hcd(hcd); - usb_put_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) + clk_put(priv->clks[clk]); + + usb_put_hcd(hcd); + if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; @@ -207,8 +304,10 @@ static int ehci_platform_resume(struct device *dev) static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "via,vt8500-ehci", }, { .compatible = "wm,prizm-ehci", }, + { .compatible = "usb-ehci", }, {} }; +MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); static const struct platform_device_id ehci_platform_table[] = { { "ehci-platform", 0 }, -- cgit v1.2.1 From b1034412570f38d32b88dfd5da5cb2f86b5c321c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:42 +0100 Subject: ohci-platform: Add support for controllers with big-endian regs / descriptors Note this commit uses the same devicetree booleans for this as the ones already existing in the usb-ehci bindings, see: Documentation/devicetree/bindings/usb/usb-ehci.txt Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 49304ddde422..e2c28fd03484 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -128,6 +128,7 @@ static int ohci_platform_probe(struct platform_device *dev) struct resource *res_mem; struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; + struct ohci_hcd *ohci; int err, irq, clk = 0; if (usb_disabled()) @@ -164,8 +165,34 @@ static int ohci_platform_probe(struct platform_device *dev) platform_set_drvdata(dev, hcd); dev->dev.platform_data = pdata; priv = hcd_to_ohci_priv(hcd); + ohci = hcd_to_ohci(hcd); if (pdata == &ohci_platform_defaults && dev->dev.of_node) { + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) + ohci->flags |= OHCI_QUIRK_BE_DESC; + + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + if (ohci->flags & OHCI_QUIRK_BE_MMIO) { + dev_err(&dev->dev, + "Error big-endian-regs not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC + if (ohci->flags & OHCI_QUIRK_BE_DESC) { + dev_err(&dev->dev, + "Error big-endian-desc not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); -- cgit v1.2.1 From ad3db5dabad169bfffcd36b94a2f65ae88a3405a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:36:43 +0100 Subject: ehci-platform: Add support for controllers with big-endian regs / descriptors This uses the already documented devicetree booleans for this, see: Documentation/devicetree/bindings/usb/usb-ehci.txt Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 5ebd0b75b58e..8fde6490234f 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -55,8 +55,10 @@ static int ehci_platform_reset(struct usb_hcd *hcd) hcd->has_tt = pdata->has_tt; ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; - ehci->big_endian_desc = pdata->big_endian_desc; - ehci->big_endian_mmio = pdata->big_endian_mmio; + if (pdata->big_endian_desc) + ehci->big_endian_desc = 1; + if (pdata->big_endian_mmio) + ehci->big_endian_mmio = 1; if (pdata->pre_setup) { retval = pdata->pre_setup(hcd); @@ -142,6 +144,7 @@ static int ehci_platform_probe(struct platform_device *dev) struct resource *res_mem; struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct ehci_platform_priv *priv; + struct ehci_hcd *ehci; int err, irq, clk = 0; if (usb_disabled()) @@ -177,8 +180,34 @@ static int ehci_platform_probe(struct platform_device *dev) platform_set_drvdata(dev, hcd); dev->dev.platform_data = pdata; priv = hcd_to_ehci_priv(hcd); + ehci = hcd_to_ehci(hcd); if (pdata == &ehci_platform_defaults && dev->dev.of_node) { + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ehci->big_endian_mmio = 1; + + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) + ehci->big_endian_desc = 1; + + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ehci->big_endian_mmio = ehci->big_endian_desc = 1; + +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + if (ehci->big_endian_mmio) { + dev_err(&dev->dev, + "Error big-endian-regs not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC + if (ehci->big_endian_desc) { + dev_err(&dev->dev, + "Error big-endian-desc not compiled in\n"); + err = -EINVAL; + goto err_put_hcd; + } +#endif priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); -- cgit v1.2.1 From 93571adbb132c47d4d3a8e42d47276862f9635a2 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 4 Feb 2014 15:19:40 -0800 Subject: usb: dwc2: handle the Host Port Interrupt when it occurs in device mode According to the spec for the DWC2 controller, when the PRTINT interrupt fires, the application must clear the appropriate status bit in the Host Port Control and Status register to clear this bit. When disconnecting an A-cable when the dwc2 host driver, the PRTINT fires, but only the GINTSTS_PRTINT status is cleared, no action is done with the HPRT0 register. The HPRT0_ENACHG bit in the HPRT0 must also be poked to correctly clear the GINTSTS_PRTINT interrupt. I am seeing this behavoir on v2.93 of the DWC2 IP. When I disconnect an OTG A-cable adapter, the PRTINT interrupt fires when the DWC2 is in device mode and is never cleared. This patch adds the function to read the HPRT0 register when the PRTINT fires and the dwc2 IP has already transitioned to device mode. This function is only clearing the HPRT0_ENACHG bit for now, but can be modified to handle more. Signed-off-by: Dinh Nguyen [ paulz: modified patch to preserve HPRT0_ENA bit ] Signed-off-by: Paul Zimmerman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/core_intr.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 8205799e6db3..c93918b70d03 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -71,6 +71,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) } } +/** + * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts. + * When the PRTINT interrupt fires, there are certain status bits in the Host + * Port that needs to get cleared. + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg) +{ + u32 hprt0 = readl(hsotg->regs + HPRT0); + + if (hprt0 & HPRT0_ENACHG) { + hprt0 &= ~HPRT0_ENA; + writel(hprt0, hsotg->regs + HPRT0); + } + + /* Clear interrupt */ + writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS); +} + /** * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message * @@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, " --Port interrupt received in Device mode--\n"); - gintsts = GINTSTS_PRTINT; - writel(gintsts, hsotg->regs + GINTSTS); - retval = 1; + dwc2_handle_usb_port_intr(hsotg); + retval = IRQ_HANDLED; } } -- cgit v1.2.1 From 5c73e74034820ca2a85101a52622f1c92237edc0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 21 Jan 2014 09:50:51 +0300 Subject: usb: phy: msm: tiny leak on error in probe() Free "motog" on error. This is more to appease the static checkers than a real worry. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-msm-usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 8546c8dccd51..64c9d14ea77d 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1429,7 +1429,8 @@ static int __init msm_otg_probe(struct platform_device *pdev) motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); if (!motg->phy.otg) { dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; + ret = -ENOMEM; + goto free_motg; } motg->pdata = dev_get_platdata(&pdev->dev); -- cgit v1.2.1 From 22f6a0f0e3549b73b3319e26e1689f72b1f1284b Mon Sep 17 00:00:00 2001 From: Shaibal Dutta Date: Sat, 1 Feb 2014 19:16:46 -0800 Subject: usb: move hub init and LED blink work to power efficient workqueue Allow the scheduler to select the best CPU to handle hub initalization and LED blinking work. This extends idle residency times on idle CPUs and conserves power. This functionality is enabled when CONFIG_WQ_POWER_EFFICIENT is selected. [zoran.markovic@linaro.org: Rebased to latest kernel. Added commit message. Changed reference from system to power efficient workqueue for LEDs in check_highspeed() and hub_port_connect_change().] Acked-by: Alan Stern Cc: Sarah Sharp Cc: Xenia Ragiadakou Cc: Julius Werner Cc: Krzysztof Mazur Cc: Matthias Beyer Cc: Dan Williams Cc: Mathias Nyman Cc: Thomas Pugliese Signed-off-by: Shaibal Dutta Signed-off-by: Zoran Markovic Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 64ea21971be2..519f2c3594b2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -499,7 +499,8 @@ static void led_work (struct work_struct *work) changed++; } if (changed) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, LED_CYCLE_PERIOD); } /* use a short timeout for hub/port status fetches */ @@ -1041,7 +1042,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) if (type == HUB_INIT) { delay = hub_power_on(hub, false); PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); - schedule_delayed_work(&hub->init_work, + queue_delayed_work(system_power_efficient_wq, + &hub->init_work, msecs_to_jiffies(delay)); /* Suppress autosuspend until init is done */ @@ -1195,7 +1197,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Don't do a long sleep inside a workqueue routine */ if (type == HUB_INIT2) { PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); - schedule_delayed_work(&hub->init_work, + queue_delayed_work(system_power_efficient_wq, + &hub->init_work, msecs_to_jiffies(delay)); return; /* Continues at init3: below */ } else { @@ -1209,7 +1212,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) if (status < 0) dev_err(hub->intfdev, "activate --> %d\n", status); if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, LED_CYCLE_PERIOD); /* Scan all ports that need attention */ kick_khubd(hub); @@ -4311,7 +4315,8 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) /* hub LEDs are probably harder to miss than syslog */ if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_GREEN_BLINK; - schedule_delayed_work (&hub->leds, 0); + queue_delayed_work(system_power_efficient_wq, + &hub->leds, 0); } } kfree(qual); @@ -4540,7 +4545,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (hub->has_indicators) { hub->indicator[port1-1] = INDICATOR_AMBER_BLINK; - schedule_delayed_work (&hub->leds, 0); + queue_delayed_work( + system_power_efficient_wq, + &hub->leds, 0); } status = -ENOTCONN; /* Don't retry */ goto loop_disable; -- cgit v1.2.1 From ce149c30b9f89d0c9addd1d71ccdb57c1051553b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Feb 2014 17:35:28 +0100 Subject: ohci-platform: Change compatible string from usb-ohci to generic-ohci The initial versions of the devicetree enablement patches for ohci-platform used "ohci-platform" as compatible string. However this was disliked by various reviewers because the platform bus is a Linux invention and devicetree is supposed to be OS agnostic. After much discussion I gave up and went with the generic usb-ohci as requested. In retro-spect I should have chosen something different, the dts files for many existing boards already claim to be compatible with "usb-ohci", ie they have: compatible = "ti,ohci-omap3", "usb-ohci"; In theory this should not be a problem since the "ti,ohci-omap3" entry takes presedence, but in practice using a conflicting compatible string is an issue, because it makes which driver gets used depend on driver registration order. This patch changes the compatible string claimed by ohci-platform to "generic-ohci", avoiding the driver registration / module loading ordering problems. Signed-off-by: Hans de Goede Tested-by: Kevin Hilman Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index e2c28fd03484..b6ca0b25259a 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -319,7 +319,7 @@ static int ohci_platform_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct of_device_id ohci_platform_ids[] = { - { .compatible = "usb-ohci", }, + { .compatible = "generic-ohci", }, { } }; MODULE_DEVICE_TABLE(of, ohci_platform_ids); -- cgit v1.2.1 From 915974c34ee056be918b7ea287a870766a0db6ba Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Feb 2014 17:35:29 +0100 Subject: ehci-platform: Change compatible string from usb-ehci to generic-ehci The initial versions of the devicetree enablement patches for ehci-platform used "ehci-platform" as compatible string. However this was disliked by various reviewers because the platform bus is a Linux invention and devicetree is supposed to be OS agnostic. After much discussion I gave up, added a: "depends on !PPC_OF" to Kconfig to avoid a known conflict with PPC-OF platforms and went with the generic usb-ehci as requested. In retro-spect I should have chosen something different, the dts files for many existing boards already claim to be compatible with "usb-ehci", ie they have: compatible = "ti,ehci-omap", "usb-ehci"; In theory this should not be a problem since the "ti,ehci-omap" entry takes presedence, but in practice using a conflicting compatible string is an issue, because it makes which driver gets used depend on driver registration order. This patch changes the compatible string claimed by ehci-platform to "generic-ehci", avoiding the driver registration / module loading ordering problems, and removes the "depends on !PPC_OF" workaround. Signed-off-by: Hans de Goede Acked-by: Alan Stern Tested-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 1 - drivers/usb/host/ehci-platform.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index e28cbe0e1f57..a9707da7da0b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -255,7 +255,6 @@ config USB_EHCI_ATH79 config USB_EHCI_HCD_PLATFORM tristate "Generic EHCI driver for a platform device" - depends on !PPC_OF default n ---help--- Adds an EHCI host driver for a generic platform device, which diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 8fde6490234f..117873033d00 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -333,7 +333,7 @@ static int ehci_platform_resume(struct device *dev) static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "via,vt8500-ehci", }, { .compatible = "wm,prizm-ehci", }, - { .compatible = "usb-ehci", }, + { .compatible = "generic-ehci", }, {} }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); -- cgit v1.2.1 From 843d5e036419bddb4aaf21d60c7ffe437e963166 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 11 Feb 2014 11:26:10 -0500 Subject: USB: ehci-platform: check for platform data misconfiguration The ehci-platform driver checks for misconfigurations in cases where the Device Tree data specifies big-endian registers or descriptors but the corresponding driver config settings have not been enabled. As Jonas Gorski suggested, we may as well apply the same check to general platform data too. This requires moving the code that sets the big-endian quirk flags from the ehci_platform_reset() routine into ehci_platform_probe(), and moving the checks out of the DT-specific "if" statement clause. The patch also changes the text of the error messages in an attempt to make the nature of the error more clear. Signed-off-by: Alan Stern Reported-by: Jonas Gorski Acked-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 42 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 117873033d00..b3a0e11073aa 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -55,10 +55,6 @@ static int ehci_platform_reset(struct usb_hcd *hcd) hcd->has_tt = pdata->has_tt; ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; - if (pdata->big_endian_desc) - ehci->big_endian_desc = 1; - if (pdata->big_endian_mmio) - ehci->big_endian_mmio = 1; if (pdata->pre_setup) { retval = pdata->pre_setup(hcd); @@ -192,22 +188,6 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_property_read_bool(dev->dev.of_node, "big-endian")) ehci->big_endian_mmio = ehci->big_endian_desc = 1; -#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO - if (ehci->big_endian_mmio) { - dev_err(&dev->dev, - "Error big-endian-regs not compiled in\n"); - err = -EINVAL; - goto err_put_hcd; - } -#endif -#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC - if (ehci->big_endian_desc) { - dev_err(&dev->dev, - "Error big-endian-desc not compiled in\n"); - err = -EINVAL; - goto err_put_hcd; - } -#endif priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); @@ -228,6 +208,28 @@ static int ehci_platform_probe(struct platform_device *dev) } } + if (pdata->big_endian_desc) + ehci->big_endian_desc = 1; + if (pdata->big_endian_mmio) + ehci->big_endian_mmio = 1; + +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + if (ehci->big_endian_mmio) { + dev_err(&dev->dev, + "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif +#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC + if (ehci->big_endian_desc) { + dev_err(&dev->dev, + "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) -- cgit v1.2.1 From adff52952ef52c4dbfb930727f6f8cfe14d9967c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 11 Feb 2014 11:26:00 -0500 Subject: USB: ohci-platform: check for platform data misconfiguration The ohci-platform driver checks for misconfigurations in cases where the Device Tree data specifies big-endian registers or descriptors but the corresponding driver config settings have not been enabled. As Jonas Gorski suggested, we may as well apply the same check to general platform data too. This requires moving the code that sets the big-endian quirk flags from the ohci_platform_reset() routine into ohci_platform_probe(), and moving the checks out of the DT-specific "if" statement clause. The patch also changes the text of the error messages in an attempt to make the nature of the error more clear. Signed-off-by: Alan Stern Reported-by: Jonas Gorski Acked-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 42 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index b6ca0b25259a..b6002c951c5c 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -47,10 +47,6 @@ static int ohci_platform_reset(struct usb_hcd *hcd) struct usb_ohci_pdata *pdata = dev_get_platdata(&pdev->dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - if (pdata->big_endian_desc) - ohci->flags |= OHCI_QUIRK_BE_DESC; - if (pdata->big_endian_mmio) - ohci->flags |= OHCI_QUIRK_BE_MMIO; if (pdata->no_big_frame_no) ohci->flags |= OHCI_QUIRK_FRAME_NO; if (pdata->num_ports) @@ -177,22 +173,6 @@ static int ohci_platform_probe(struct platform_device *dev) if (of_property_read_bool(dev->dev.of_node, "big-endian")) ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; -#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO - if (ohci->flags & OHCI_QUIRK_BE_MMIO) { - dev_err(&dev->dev, - "Error big-endian-regs not compiled in\n"); - err = -EINVAL; - goto err_put_hcd; - } -#endif -#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC - if (ohci->flags & OHCI_QUIRK_BE_DESC) { - dev_err(&dev->dev, - "Error big-endian-desc not compiled in\n"); - err = -EINVAL; - goto err_put_hcd; - } -#endif priv->phy = devm_phy_get(&dev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); @@ -213,6 +193,28 @@ static int ohci_platform_probe(struct platform_device *dev) } } + if (pdata->big_endian_desc) + ohci->flags |= OHCI_QUIRK_BE_DESC; + if (pdata->big_endian_mmio) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + if (ohci->flags & OHCI_QUIRK_BE_MMIO) { + dev_err(&dev->dev, + "Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif +#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC + if (ohci->flags & OHCI_QUIRK_BE_DESC) { + dev_err(&dev->dev, + "Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n"); + err = -EINVAL; + goto err_put_clks; + } +#endif + if (pdata->power_on) { err = pdata->power_on(dev); if (err < 0) -- cgit v1.2.1 From 275200b38d7f996c7a658e62ca38b0a788dfc489 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 9 Feb 2014 19:48:01 +0100 Subject: Remove MACH_OMAP_H4_OTG The symbol is an orphan, get rid of it. Signed-off-by: Richard Weinberger Acked-by: Aaro Koskinen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8154165aa601..42f017afd366 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -226,7 +226,7 @@ config USB_GR_UDC config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 - select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 -- cgit v1.2.1 From ea17c7c6ad3176fede61b07d5dc6a9abe665fa1a Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 11 Feb 2014 13:23:07 +0100 Subject: USB: ELAN: Remove useless "default M" lines The Kconfig entries for USB_U132_HCD and USB_FTDI_ELAN default to (uppercase) "M". But in Kconfig (lowercase) "m" is a magic symbol. "M" is an ordinary symbol. As "M" is never set these Kconfig symbols will also not be set by default. Since I'm not aware of a reason why these driver should be set by default, let's just drop these lines (that basically do nothing). Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 1 - drivers/usb/misc/Kconfig | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a9707da7da0b..e22b82660831 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -584,7 +584,6 @@ config FHCI_DEBUG config USB_U132_HCD tristate "Elan U132 Adapter Host Controller" depends on USB_FTDI_ELAN - default M help The U132 adapter is a USB to CardBus adapter specifically designed for PC cards that contain an OHCI host controller. Typical PC cards diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index ba5f70f92888..1bca274dc3b5 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -128,7 +128,6 @@ config USB_IDMOUSE config USB_FTDI_ELAN tristate "Elan PCMCIA CardBus Adapter USB Client" - default M help ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters. Currently only the U132 adapter is available. -- cgit v1.2.1 From e8fcbb61405997f03b9e127806db620c7cfb9909 Mon Sep 17 00:00:00 2001 From: Christian Vogel Date: Mon, 10 Feb 2014 18:49:43 +0100 Subject: usb/misc/usbled: Add Riso Kagaku Webmail Notifier Add support for the "Webmail Notifier" (USB powered LED for signaling new emails) made by Riso Kagaku Corp. which displays 7 distinct colors. USB Protocol initially reverse engineered by https://code.google.com/p/usbmailnotifier/. Signed-off-by: Christian Vogel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbled.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 78eb4ff33269..bdef0d6eb91d 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -22,8 +22,27 @@ enum led_type { DELCOM_VISUAL_SIGNAL_INDICATOR, DREAM_CHEEKY_WEBMAIL_NOTIFIER, + RISO_KAGAKU_LED }; +/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index + internally, we want to keep the red+green+blue sysfs api, so we decode + from 1-bit RGB to the riso kagaku color index according to this table... */ + +static unsigned const char riso_kagaku_tbl[] = { +/* R+2G+4B -> riso kagaku color index */ + [0] = 0, /* black */ + [1] = 2, /* red */ + [2] = 1, /* green */ + [3] = 5, /* yellow */ + [4] = 3, /* blue */ + [5] = 6, /* magenta */ + [6] = 4, /* cyan */ + [7] = 7 /* white */ +}; + +#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)] + /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0fc5, 0x1223), @@ -32,6 +51,8 @@ static const struct usb_device_id id_table[] = { .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { USB_DEVICE(0x1d34, 0x000a), .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, + { USB_DEVICE(0x1294, 0x1320), + .driver_info = RISO_KAGAKU_LED }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); @@ -48,6 +69,7 @@ static void change_color(struct usb_led *led) { int retval = 0; unsigned char *buffer; + int actlength; buffer = kmalloc(8, GFP_KERNEL); if (!buffer) { @@ -104,6 +126,18 @@ static void change_color(struct usb_led *led) 2000); break; + case RISO_KAGAKU_LED: + buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue); + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + buffer[4] = 0; + + retval = usb_interrupt_msg(led->udev, + usb_sndctrlpipe(led->udev, 2), + buffer, 5, &actlength, 1000 /*ms timeout*/); + break; + default: dev_err(&led->udev->dev, "unknown device type %d\n", led->type); } -- cgit v1.2.1 From e16fa44b39189cf31baaa8e880bc1c23a458c669 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Feb 2014 17:54:45 +0100 Subject: uhci-platform: Change compatible string from platform-uhci to generic-uhci This brings the uhci-platform bindings in sync with what we've done for the ohci- and ehci-platform drivers. As discussed there using platform as a prefix is a bit weird as the platform bus is a Linux specific thing and the bindings are supposed to be OS agnostic. Note that the old platform-uhci compatible string is kept around for, well, compatibility reasons. While at it rename the bindings txt file to match the name of all the other ?hci-platform bindings docs. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 44e6c9da8892..01833ab2b5c3 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -148,6 +148,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) } static const struct of_device_id platform_uhci_ids[] = { + { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, {} }; -- cgit v1.2.1 From 0f94388b27c599015b74eedf1a32126a3f5fc0f9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Feb 2014 17:54:46 +0100 Subject: xhci-platform: Change compatible string from xhci-platform to generic-xhci This brings the xhci-platform bindings in sync with what we've done for the ohci- and ehci-platform drivers. As discussed there using platform as a postfix is a bit weird as the platform bus is a Linux specific thing and the bindings are supposed to be OS agnostic. Note that the old xhci-platform compatible string is kept around for, well, compatibility reasons. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8abda5c73ca1..8affef910782 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -226,6 +226,7 @@ static const struct dev_pm_ops xhci_plat_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id usb_xhci_of_match[] = { + { .compatible = "generic-xhci" }, { .compatible = "xhci-platform" }, { }, }; -- cgit v1.2.1 From 2b54fa6bbef4dbc0beabcc989625204b608d062d Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Wed, 12 Feb 2014 17:44:35 -0800 Subject: usb: dwc2: fix dereference before NULL check In a couple of places, we were checking qtd->urb for NULL after we had already dereferenced it. Fix this by moving the check to before the dereference. Signed-off-by: Paul Zimmerman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd_intr.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 012f17ec1a37..47b9eb5389b4 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd) { struct dwc2_hcd_urb *urb = qtd->urb; - int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE; + int pipe_type; int urb_xfer_done; if (dbg_hc(chan)) @@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, "--Host Channel %d Interrupt: Transfer Complete--\n", chnum); + if (!urb) + goto handle_xfercomp_done; + + pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status); if (pipe_type == USB_ENDPOINT_XFER_ISOC) @@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, } } - if (!urb) - goto handle_xfercomp_done; - /* Update the QTD and URB states */ switch (pipe_type) { case USB_ENDPOINT_XFER_CONTROL: @@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd) { struct dwc2_hcd_urb *urb = qtd->urb; - int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + int pipe_type; dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n", chnum); @@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, if (!urb) goto handle_stall_halt; + pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + if (pipe_type == USB_ENDPOINT_XFER_CONTROL) dwc2_host_complete(hsotg, qtd, -EPIPE); -- cgit v1.2.1 From c57c41d2c6a1fdb3cfecb5edcee386d5db67ac02 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Mon, 27 Jan 2014 15:07:24 +0530 Subject: usb: musb: musb_host: Enable ISOCH IN handling for AM335x host Enable the isochrounous IN handling for AM335x HOST. Reprogram CPPI to receive consecutive ISOCH frames in the same URB. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index ed455724017b..79b75106f273 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1691,7 +1691,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) if (usb_pipeisoc(pipe)) { struct usb_iso_packet_descriptor *d; @@ -1704,10 +1705,30 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (d->status != -EILSEQ && d->status != -EOVERFLOW) d->status = 0; - if (++qh->iso_idx >= urb->number_of_packets) + if (++qh->iso_idx >= urb->number_of_packets) { done = true; - else + } else { +#if defined(CONFIG_USB_TI_CPPI41_DMA) + struct dma_controller *c; + dma_addr_t *buf; + u32 length, ret; + + c = musb->dma_controller; + buf = (void *) + urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = + urb->iso_frame_desc[qh->iso_idx].length; + + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + ret = c->channel_program(dma, qh->maxpacket, + 0, (u32) buf, length); +#endif done = false; + } } else { /* done if urb buffer is full or short packet is recd */ @@ -1747,7 +1768,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) if (dma) { struct dma_controller *c; u16 rx_count; -- cgit v1.2.1 From f82503f549c70c15e283511270e6713a912fef37 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Mon, 27 Jan 2014 15:07:25 +0530 Subject: usb: musb: musb_cppi41: Make CPPI aware of high bandwidth transfers Enable CPPI to handle high bandwidth transfers, especially to support webcam captures. Use a single bd to get the whole of the data in case of high bandwidth transfers. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index f88929609bac..39ee516c8cbf 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -448,12 +448,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel, dma_addr_t dma_addr, u32 len) { int ret; + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + int hb_mult = 0; BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || channel->status == MUSB_DMA_STATUS_BUSY); + if (is_host_active(cppi41_channel->controller->musb)) { + if (cppi41_channel->is_tx) + hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult; + else + hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult; + } + channel->status = MUSB_DMA_STATUS_BUSY; channel->actual_len = 0; + + if (hb_mult) + packet_sz = hb_mult * (packet_sz & 0x7FF); + ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len); if (!ret) channel->status = MUSB_DMA_STATUS_FREE; -- cgit v1.2.1 From 1af54b7a40ca9bbd549e626be01870caa3f0299d Mon Sep 17 00:00:00 2001 From: George Cherian Date: Mon, 27 Jan 2014 15:07:26 +0530 Subject: usb: musb: musb_cppi41: Handle ISOCH differently and not use the hrtimer. In case of ISOCH transfers the hrtimer workaround for the hardware issue is not very reliable. Instead of checking musb_is_tx_fifo_empty() in hrtimer routine, schedule a completion work and check the same in completion work. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 39ee516c8cbf..5b0f2a58f8ce 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,6 +39,7 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; + struct work_struct dma_completion; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) return true; } +static bool is_isoc(struct musb_hw_ep *hw_ep, bool in) +{ + if (in && hw_ep->in_qh) { + if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } else if (hw_ep->out_qh) { + if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC) + return true; + } + return false; +} + static void cppi41_dma_callback(void *private_data); static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) @@ -165,6 +178,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) } } +static void cppi_trans_done_work(struct work_struct *work) +{ + unsigned long flags; + struct cppi41_dma_channel *cppi41_channel = + container_of(work, struct cppi41_dma_channel, dma_completion); + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + bool empty; + + if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + empty = musb_is_tx_fifo_empty(hw_ep); + if (empty) { + spin_lock_irqsave(&musb->lock, flags); + cppi41_trans_done(cppi41_channel); + spin_unlock_irqrestore(&musb->lock, flags); + } else { + schedule_work(&cppi41_channel->dma_completion); + } + } +} + static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) { struct cppi41_dma_controller *controller; @@ -228,6 +267,14 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; + if (!cppi41_channel->is_tx) { + if (is_isoc(hw_ep, 1)) + schedule_work(&cppi41_channel->dma_completion); + else + cppi41_trans_done(cppi41_channel); + goto out; + } + empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); @@ -264,6 +311,10 @@ static void cppi41_dma_callback(void *private_data) goto out; } } + if (is_isoc(hw_ep, 0)) { + schedule_work(&cppi41_channel->dma_completion); + goto out; + } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); if (!hrtimer_active(&controller->early_tx)) { @@ -620,6 +671,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) cppi41_channel->port_num = port; cppi41_channel->is_tx = is_tx; INIT_LIST_HEAD(&cppi41_channel->tx_check); + INIT_WORK(&cppi41_channel->dma_completion, + cppi_trans_done_work); musb_dma = &cppi41_channel->channel; musb_dma->private_data = cppi41_channel; -- cgit v1.2.1 From 1a7ed5bec407478b9ce5e3267708110277851614 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 3 Feb 2014 10:29:09 -0500 Subject: usb: gadget: s3c-hsotg: fix build on x86 and other architectures The readsl and writesl I/O accessors are only defined on some architectures. The driver currently depends on CONFIG_ARM because the build breaks on x86, in particular. Switch to use of ioread32_rep and iowrite32_rep to fix build on all architectures and remove the CONFIG_ARM dependency. Also update printk formatting to handle a long long dma_addr_t to avoid warnings on !32-bit architectures. Signed-off-by: Matt Porter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 - drivers/usb/gadget/s3c-hsotg.c | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 42f017afd366..3557c7e5040d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -301,7 +301,6 @@ config USB_PXA27X gadget drivers to also be dynamically linked. config USB_S3C_HSOTG - depends on ARM tristate "Designware/S3C HS/OtG USB Device controller" help The Designware USB2.0 high-speed gadget controller diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 1172eaeddd85..0449b768ac08 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, to_write = DIV_ROUND_UP(to_write, 4); data = hs_req->req.buf + buf_pos; - writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); + iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); return (to_write >= can_write) ? -ENOSPC : 0; } @@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, ureq->length, ureq->actual); if (0) dev_dbg(hsotg->dev, - "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n", - ureq->buf, length, ureq->dma, + "REQ buf %p len %d dma 0x%08llx noi=%d zp=%d snok=%d\n", + ureq->buf, length, (unsigned long long)ureq->dma, ureq->no_interrupt, ureq->zero, ureq->short_not_ok); maxreq = get_ep_limit(hs_ep); @@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); writel(ureq->dma, hsotg->regs + dma_reg); - dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n", - __func__, ureq->dma, dma_reg); + dev_dbg(hsotg->dev, "%s: 0x%08llx => 0x%08x\n", + __func__, (unsigned long long)ureq->dma, dma_reg); } ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */ @@ -1488,7 +1488,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) * note, we might over-write the buffer end by 3 bytes depending on * alignment of the data. */ - readsl(fifo, hs_req->req.buf + read_ptr, to_read); + ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read); } /** -- cgit v1.2.1 From 8b3bc14f2d841eaabaaad6aba05b7657dd0b5c7e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 4 Feb 2014 14:25:29 +0900 Subject: usb: gadget: s3c-hsotg: use %pad for dma_addr_t Use %pad for dma_addr_t to avoid the following build warnings in printks. drivers/usb/gadget/s3c-hsotg.c: In function 's3c_hsotg_start_req' drivers/usb/gadget/s3c-hsotg.c:722:3: warning: format '%x' expects argument of type 'unsigned int' but argument 6 has type 'dma_addr_t' [-Wformat] drivers/usb/gadget/s3c-hsotg.c:792:3: warning: format '%x' expects argument of type 'unsigned int' but argument 5 has type 'dma_addr_t' [-Wformat] Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 0449b768ac08..930c490dd83b 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, ureq->length, ureq->actual); if (0) dev_dbg(hsotg->dev, - "REQ buf %p len %d dma 0x%08llx noi=%d zp=%d snok=%d\n", - ureq->buf, length, (unsigned long long)ureq->dma, + "REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n", + ureq->buf, length, &ureq->dma, ureq->no_interrupt, ureq->zero, ureq->short_not_ok); maxreq = get_ep_limit(hs_ep); @@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); writel(ureq->dma, hsotg->regs + dma_reg); - dev_dbg(hsotg->dev, "%s: 0x%08llx => 0x%08x\n", - __func__, (unsigned long long)ureq->dma, dma_reg); + dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n", + __func__, &ureq->dma, dma_reg); } ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */ -- cgit v1.2.1 From abcdcc29c6aed12c137c8c06ee914d08605bf521 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 29 Dec 2013 23:47:25 +0100 Subject: usb: gadget: fix error return code Set the return variable to an error code as done elsewhere in the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/gadget/printer.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index bf7a56b6d48a..92b55bdda566 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1133,6 +1133,7 @@ static int __init printer_bind_config(struct usb_configuration *c) NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); + status = PTR_ERR(dev->pdev); goto fail; } -- cgit v1.2.1 From e362115a07eaf3509b12853c2047d19720501fd9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 Jan 2014 10:58:59 +0530 Subject: usb: gadget: s3c2410_udc: Fix build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass value instead of address as expected by 'usb_ep_set_maxpacket_limit'. Fixes the following compilation error introduced by commit e117e742d310 ("usb: gadget: add "maxpacket_limit" field to struct usb_ep"): drivers/usb/gadget/s3c2410_udc.c: In function ‘s3c2410_udc_reinit’: drivers/usb/gadget/s3c2410_udc.c:1632:3: error: cannot take address of bit-field ‘maxpacket’ usb_ep_set_maxpacket_limit(&ep->ep, &ep->ep.maxpacket); Signed-off-by: Sachin Kamat Reviewed-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c2410_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index f04b2c3154de..dd9678f85c58 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1629,7 +1629,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev) ep->ep.desc = NULL; ep->halted = 0; INIT_LIST_HEAD(&ep->queue); - usb_ep_set_maxpacket_limit(&ep->ep, &ep->ep.maxpacket); + usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); } } -- cgit v1.2.1 From 236064c253581367a62d67083a620318670683d0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 Jan 2014 10:59:00 +0530 Subject: usb: gadget: s3c-hsudc: remove unused label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following compilation warning: drivers/usb/gadget/s3c-hsudc.c: In function ‘s3c_hsudc_probe’: drivers/usb/gadget/s3c-hsudc.c:1347:1: warning: label ‘err_add_device’ defined but not used [-Wunused-label] Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsudc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index ea4bbfe72ec0..10c6a128250c 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) return 0; err_add_udc: -err_add_device: clk_disable(hsudc->uclk); err_res: if (!IS_ERR_OR_NULL(hsudc->transceiver)) -- cgit v1.2.1 From 2c2b04253bfc7ac67a2ab71a6e771cd42e9418fe Mon Sep 17 00:00:00 2001 From: "wenlin.kang" Date: Wed, 8 Jan 2014 14:22:11 +0800 Subject: usb: gadget: printer: fix possible deadlock The problem occurs in follow path. printer_read() | +---setup_rx_reqs() | +---usb_ep_queue() | +---... | +---rx_complete() Although it is clear from code, we can't get it normally. only when we enable some spin_lock debug config option, we can find it. eg: BUG: spinlock lockup on CPU#0, g_printer_test_/584 lock: bf05e158, .magic: dead4ead, .owner: g_printer_test_/584, .owner_cpu: 0 [] (unwind_backtrace+0x0/0x104) from [] (dump_stack+0x20/0x24) [] (dump_stack+0x20/0x24) from [] (spin_dump+0x8c/0x94) [] (spin_dump+0x8c/0x94) from [] (do_raw_spin_lock+0x128/0x154) [] (do_raw_spin_lock+0x128/0x154) from [] (_raw_spin_lock_irqsave+0x64/0x70) [] (_raw_spin_lock_irqsave+0x64/0x70) from [] (rx_complete+0x54/0x10c [g_printer]) [] (rx_complete+0x54/0x10c [g_printer]) from [] (musb_g_giveback+0x78/0x88) [] (musb_g_giveback+0x78/0x88) from [] (rxstate+0xa0/0x10c) [] (rxstate+0xa0/0x10c) from [] (musb_ep_restart+0x44/0x70) [] (musb_ep_restart+0x44/0x70) from [] (musb_gadget_queue+0xe8/0xf8) [] (musb_gadget_queue+0xe8/0xf8) from [] (setup_rx_reqs+0xa4/0x178 [g_printer]) [] (setup_rx_reqs+0xa4/0x178 [g_printer]) from [] (printer_read+0x9c/0x3f4 [g_printer]) [] (printer_read+0x9c/0x3f4 [g_printer]) from [] (vfs_read+0xb4/0x144) [] (vfs_read+0xb4/0x144) from [] (sys_read+0x50/0x124) [] (sys_read+0x50/0x124) from [] (ret_fast_syscall+0x0/0x3c) The root cause is that we use the same lock two time in a path, so to avoid the deadlock, we need to unlock in setup_rx_reqs(), and only unlock. Signed-off-by: wenlin.kang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/printer.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 92b55bdda566..96ffdac330b5 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -427,7 +427,10 @@ setup_rx_reqs(struct printer_dev *dev) req->length = USB_BUFSIZE; req->complete = rx_complete; + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); if (error) { DBG(dev, "rx submit --> %d\n", error); list_add(&req->list, &dev->rx_reqs); -- cgit v1.2.1 From 7e98f60003df98026edd66916f282501eee075c4 Mon Sep 17 00:00:00 2001 From: "wenlin.kang" Date: Wed, 8 Jan 2014 14:22:12 +0800 Subject: usb: gadget: printer: fix memory leak When read data from g_printer, we see a Segmentation fault. eg: Unable to handle kernel paging request at virtual address bf048000 pgd = cf038000 [bf048000] *pgd=8e8cf811, *pte=00000000, *ppte=00000000 Internal error: Oops: 7 [#1] PREEMPT ARM Modules linked in: bluetooth rfcomm g_printer CPU: 0 Not tainted (3.4.43-WR5.0.1.9_standard #1) PC is at __copy_to_user_std+0x310/0x3a8 LR is at 0x4c808010 pc : [] lr : [<4c808010>] psr: 20000013 sp : cf883ea8 ip : 80801018 fp : cf883f24 r10: bf04706c r9 : 18a21205 r8 : 21953888 r7 : 201588aa r6 : 5109aa16 r5 : 0705aaa2 r4 : 5140aa8a r3 : 0000004c r2 : 00000fdc r1 : bf048000 r0 : bef5fc3c Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 10c5387d Table: 8f038019 DAC: 00000015 Process g_printer_test. (pid: 661, stack limit = 0xcf8822e8) Stack: (0xcf883ea8 to 0xcf884000) 3ea0: bf047068 00001fff bef5ecb9 cf882000 00001fff bef5ecb9 3ec0: 00001fff 00000000 cf2e8724 bf044d3c 80000013 80000013 00000001 bf04706c 3ee0: cf883f24 cf883ef0 c012e5ac c0324388 c007c8ac c0046298 00008180 cf29b900 3f00: 00002000 bef5ecb8 cf883f68 00000003 cf882000 cf29b900 cf883f54 cf883f28 3f20: c012ea08 bf044b0c c000eb88 00000000 cf883f7c 00000000 00000000 00002000 3f40: bef5ecb8 00000003 cf883fa4 cf883f58 c012eae8 c012e960 00000001 bef60cb8 3f60: 000000a8 c000eb88 00000000 00000000 cf883fa4 00000000 c014329c 00000000 3f80: 000000d4 41af63f0 00000003 c000eb88 cf882000 00000000 00000000 cf883fa8 3fa0: c000e920 c012eaa4 00000000 000000d4 00000003 bef5ecb8 00002000 bef5ecb8 3fc0: 00000000 000000d4 41af63f0 00000003 b6f534c0 00000000 419f9000 00000000 3fe0: 00000000 bef5ecac 000086d9 41a986bc 60000010 00000003 0109608a 0088828a Code: f5d1f07c e8b100f0 e1a03c2e e2522020 (e8b15300) ---[ end trace 97e2618e250e3377 ]--- Segmentation fault The root cause is the dev->rx_buffers list has been broken. When we call printer_read(), the following call tree is triggered: printer_read() | +---setup_rx_reqs(req) | | | +---usb_ep_queue(req) | | | | | +---... | | | | | +---rx_complete(req). | | | +---add the req to dev->rx_reqs_active | +---while(!list_empty(&dev->rx_buffers))) The route happens when we don't use DMA or fail to start DMA in USB driver. We can see: in the case, in rx_complete() it will add the req to dev->rx_buffers. meanwhile we see that we will also add the req to dev->rx_reqs_active after usb_ep_queue() return, so this adding will break the dev->rx_buffers out. After, when we call list_empty() to check dev->rx_buffers in while(), due to can't check correctly dev->rx_buffers, so the Segmentation fault occurs when copy_to_user() is called. Signed-off-by: wenlin.kang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/printer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 96ffdac330b5..0fd459e724be 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -435,7 +435,9 @@ setup_rx_reqs(struct printer_dev *dev) DBG(dev, "rx submit --> %d\n", error); list_add(&req->list, &dev->rx_reqs); break; - } else { + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) { list_add(&req->list, &dev->rx_reqs_active); } } -- cgit v1.2.1 From c9f721b2f3168a0a3b1cc29e92ad1f6f3d62e376 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 14 Jan 2014 08:36:00 +0100 Subject: usb: gadget: s3c-hsotg: stall ep0 in set_halt function When s3c_hsotg_ep_sethalt() function is called for ep0 it should be stalled in the same way that it is in s3c_hsotg_process_control() function, because SET_HALT for ep0 is delayed response for setup request. Endpoint 0, if halted, it doesn't need CLEAR_HALT because it clears "stalled" state automatically when next setup request is received. For this reason this patch moves code setting ep0 to "stalled" state to new function named s3c_hsotg_stall_ep0() which is called in s3c_hsotg_process_control() function as an immediate response for setup request, and in s3c_hsotg_ep_sethalt() function as a delayed response for setup request. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 78 +++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 930c490dd83b..99c8e3ca6a3c 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1185,6 +1185,41 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); +/** + * s3c_hsotg_stall_ep0 - stall ep0 + * @hsotg: The device state + * + * Set stall for ep0 as response for setup request. + */ +static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { + struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + u32 reg; + u32 ctrl; + + dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); + reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; + + /* + * DxEPCTL_Stall will be cleared by EP once it has + * taken effect, so no need to clear later. + */ + + ctrl = readl(hsotg->regs + reg); + ctrl |= DxEPCTL_Stall; + ctrl |= DxEPCTL_CNAK; + writel(ctrl, hsotg->regs + reg); + + dev_dbg(hsotg->dev, + "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n", + ctrl, reg, readl(hsotg->regs + reg)); + + /* + * complete won't be called, so we enqueue + * setup request here + */ + s3c_hsotg_enqueue_setup(hsotg); +} + /** * s3c_hsotg_process_control - process a control request * @hsotg: The device state @@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, * so respond with a STALL for the status stage to indicate failure. */ - if (ret < 0) { - u32 reg; - u32 ctrl; - - dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); - reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; - - /* - * DxEPCTL_Stall will be cleared by EP once it has - * taken effect, so no need to clear later. - */ - - ctrl = readl(hsotg->regs + reg); - ctrl |= DxEPCTL_Stall; - ctrl |= DxEPCTL_CNAK; - writel(ctrl, hsotg->regs + reg); - - dev_dbg(hsotg->dev, - "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n", - ctrl, reg, readl(hsotg->regs + reg)); - - /* - * don't believe we need to anything more to get the EP - * to reply with a STALL packet - */ - - /* - * complete won't be called, so we enqueue - * setup request here - */ - s3c_hsotg_enqueue_setup(hsotg); - } + if (ret < 0) + s3c_hsotg_stall_ep0(hsotg); } /** @@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); + if (index == 0) { + if (value) + s3c_hsotg_stall_ep0(hs); + else + dev_warn(hs->dev, + "%s: can't clear halt on ep0\n", __func__); + return 0; + } + /* write both IN and OUT control registers */ epreg = DIEPCTL(index); -- cgit v1.2.1 From 30bbae9fad15ee25686e62f97d734624b86d3405 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 14 Jan 2014 12:58:56 +0100 Subject: usb: dwc3: omap: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index b269dbd47fc4..80b3357034b7 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -424,11 +424,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory base resource\n"); - return -EINVAL; - } - base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) return PTR_ERR(base); -- cgit v1.2.1 From de9db572fe5135bb2cf5bb2d8520f99a56b21d9b Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 17 Jan 2014 10:22:36 +0100 Subject: usb: musb: dsps, use devm_kzalloc Replace kzalloc by devm_kzalloc and remove the kfree() calls. Signed-off-by: Markus Pargmann Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 7a109eae9b9a..68c14e1eac66 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -616,7 +616,7 @@ static int dsps_probe(struct platform_device *pdev) wrp = match->data; /* allocate glue */ - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "unable to allocate glue memory\n"); return -ENOMEM; @@ -644,7 +644,6 @@ err3: pm_runtime_put(&pdev->dev); err2: pm_runtime_disable(&pdev->dev); - kfree(glue); return ret; } @@ -657,7 +656,6 @@ static int dsps_remove(struct platform_device *pdev) /* disable usbss clocks */ pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - kfree(glue); return 0; } -- cgit v1.2.1 From ea365922f9eb2fb6fc0486f521748f19bfd5c502 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 13 Jan 2014 16:49:35 +0100 Subject: usb: gadget: FunctionFS: dereference ffs_dev conditionally ffs_dev->ffs_release_dev_callback should be accessed only if ffs_dev is not NULL. Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 306a2b52125c..78333f033e90 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -2590,11 +2590,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data) ffs_dev_lock(); ffs_dev = ffs_data->private_data; - if (ffs_dev) + if (ffs_dev) { ffs_dev->mounted = false; - - if (ffs_dev->ffs_release_dev_callback) - ffs_dev->ffs_release_dev_callback(ffs_dev); + + if (ffs_dev->ffs_release_dev_callback) + ffs_dev->ffs_release_dev_callback(ffs_dev); + } ffs_dev_unlock(); } -- cgit v1.2.1 From ab13cb0c0da6d728d87924339db4feaa598483ad Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 13 Jan 2014 16:49:36 +0100 Subject: usb: gadget: code cleanup Remove trailing whitespace Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 78333f033e90..f0c657d2c77e 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -2509,7 +2509,7 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name) existing = _ffs_find_dev(name); if (existing) return -EBUSY; - + dev->name = name; return 0; -- cgit v1.2.1 From 10b69ce08ca7b9cf1c3c73122c0d489427fab5fd Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 13 Jan 2014 16:49:37 +0100 Subject: usb: gadget: FunctionFS: staticize functions used only in f_fs.c ffs_alloc_dev and ffs_free_dev are used only in f_fs.c, so make them static. Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 6 ++++-- drivers/usb/gadget/u_fs.h | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index f0c657d2c77e..96bea089ef20 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -162,7 +162,9 @@ DEFINE_MUTEX(ffs_lock); EXPORT_SYMBOL(ffs_lock); static struct ffs_dev *ffs_find_dev(const char *name); +static struct ffs_dev *ffs_alloc_dev(void); static int _ffs_name_dev(struct ffs_dev *dev, const char *name); +static void ffs_free_dev(struct ffs_dev *dev); static void *ffs_acquire_dev(const char *dev_name); static void ffs_release_dev(struct ffs_data *ffs_data); static int ffs_ready(struct ffs_data *ffs); @@ -2473,7 +2475,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) /* * ffs_lock must be taken by the caller of this function */ -struct ffs_dev *ffs_alloc_dev(void) +static struct ffs_dev *ffs_alloc_dev(void) { struct ffs_dev *dev; int ret; @@ -2550,7 +2552,7 @@ EXPORT_SYMBOL(ffs_single_dev); /* * ffs_lock must be taken by the caller of this function */ -void ffs_free_dev(struct ffs_dev *dev) +static void ffs_free_dev(struct ffs_dev *dev) { list_del(&dev->entry); if (dev->name_allocated) diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index bc2d3718219b..f418c25f8511 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void) mutex_unlock(&ffs_lock); } -struct ffs_dev *ffs_alloc_dev(void); int ffs_name_dev(struct ffs_dev *dev, const char *name); int ffs_single_dev(struct ffs_dev *dev); -void ffs_free_dev(struct ffs_dev *dev); struct ffs_epfile; struct ffs_function; -- cgit v1.2.1 From da13a7738e87a5adecbd8741191ceabfc056767b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 13 Jan 2014 16:49:38 +0100 Subject: usb: gadget: FunctionFS: use consistent naming with regard to ffs_lock Consistently prefix function name with underscore if the function has to be called with ffs_lock taken. Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 96bea089ef20..fffda6113b0f 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -161,10 +161,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, DEFINE_MUTEX(ffs_lock); EXPORT_SYMBOL(ffs_lock); -static struct ffs_dev *ffs_find_dev(const char *name); -static struct ffs_dev *ffs_alloc_dev(void); +static struct ffs_dev *_ffs_find_dev(const char *name); +static struct ffs_dev *_ffs_alloc_dev(void); static int _ffs_name_dev(struct ffs_dev *dev, const char *name); -static void ffs_free_dev(struct ffs_dev *dev); +static void _ffs_free_dev(struct ffs_dev *dev); static void *ffs_acquire_dev(const char *dev_name); static void ffs_release_dev(struct ffs_data *ffs_data); static int ffs_ready(struct ffs_data *ffs); @@ -2255,7 +2255,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) static LIST_HEAD(ffs_devices); -static struct ffs_dev *_ffs_find_dev(const char *name) +static struct ffs_dev *_ffs_do_find_dev(const char *name) { struct ffs_dev *dev; @@ -2272,7 +2272,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name) /* * ffs_lock must be taken by the caller of this function */ -static struct ffs_dev *ffs_get_single_dev(void) +static struct ffs_dev *_ffs_get_single_dev(void) { struct ffs_dev *dev; @@ -2288,15 +2288,15 @@ static struct ffs_dev *ffs_get_single_dev(void) /* * ffs_lock must be taken by the caller of this function */ -static struct ffs_dev *ffs_find_dev(const char *name) +static struct ffs_dev *_ffs_find_dev(const char *name) { struct ffs_dev *dev; - dev = ffs_get_single_dev(); + dev = _ffs_get_single_dev(); if (dev) return dev; - return _ffs_find_dev(name); + return _ffs_do_find_dev(name); } /* Configfs support *********************************************************/ @@ -2332,7 +2332,7 @@ static void ffs_free_inst(struct usb_function_instance *f) opts = to_f_fs_opts(f); ffs_dev_lock(); - ffs_free_dev(opts->dev); + _ffs_free_dev(opts->dev); ffs_dev_unlock(); kfree(opts); } @@ -2387,7 +2387,7 @@ static struct usb_function_instance *ffs_alloc_inst(void) opts->func_inst.set_inst_name = ffs_set_inst_name; opts->func_inst.free_func_inst = ffs_free_inst; ffs_dev_lock(); - dev = ffs_alloc_dev(); + dev = _ffs_alloc_dev(); ffs_dev_unlock(); if (IS_ERR(dev)) { kfree(opts); @@ -2475,12 +2475,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) /* * ffs_lock must be taken by the caller of this function */ -static struct ffs_dev *ffs_alloc_dev(void) +static struct ffs_dev *_ffs_alloc_dev(void) { struct ffs_dev *dev; int ret; - if (ffs_get_single_dev()) + if (_ffs_get_single_dev()) return ERR_PTR(-EBUSY); dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -2508,7 +2508,7 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name) { struct ffs_dev *existing; - existing = _ffs_find_dev(name); + existing = _ffs_do_find_dev(name); if (existing) return -EBUSY; @@ -2552,7 +2552,7 @@ EXPORT_SYMBOL(ffs_single_dev); /* * ffs_lock must be taken by the caller of this function */ -static void ffs_free_dev(struct ffs_dev *dev) +static void _ffs_free_dev(struct ffs_dev *dev) { list_del(&dev->entry); if (dev->name_allocated) @@ -2569,7 +2569,7 @@ static void *ffs_acquire_dev(const char *dev_name) ENTER(); ffs_dev_lock(); - ffs_dev = ffs_find_dev(dev_name); + ffs_dev = _ffs_find_dev(dev_name); if (!ffs_dev) ffs_dev = ERR_PTR(-ENODEV); else if (ffs_dev->mounted) -- cgit v1.2.1 From e46318a00091e3e009363a516acc44a4a80e2ebb Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 10 Feb 2014 10:42:40 +0100 Subject: usb: gadget: functionfs: fix typo in the enum variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since “cancelled” is spelled with two “l”s, rename FFS_SETUP_CANCELED to FFS_SETUP_CANCELLED. Signed-off-by: Michal Nazarewicz Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 16 ++++++++-------- drivers/usb/gadget/u_fs.h | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index fffda6113b0f..eb5cb2b4b90d 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -246,7 +246,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -313,7 +313,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, */ spin_lock_irq(&ffs->ev.waitq.lock); switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + case FFS_SETUP_CANCELLED: ret = -EIDRM; goto done_spin; @@ -348,7 +348,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, /* * We are guaranteed to be still in FFS_ACTIVE state * but the state of setup could have changed from - * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need + * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need * to check for that. If that happened we copied data * from user space in vain but it's unlikely. * @@ -357,7 +357,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * transition can be performed and it's protected by * mutex. */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) { ret = -EIDRM; done_spin: spin_unlock_irq(&ffs->ev.waitq.lock); @@ -423,7 +423,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -444,7 +444,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_lock_irq(&ffs->ev.waitq.lock); switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + case FFS_SETUP_CANCELLED: ret = -EIDRM; break; @@ -491,7 +491,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_lock_irq(&ffs->ev.waitq.lock); /* See ffs_ep0_write() */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) { ret = -EIDRM; break; } @@ -1786,7 +1786,7 @@ static void __ffs_event_add(struct ffs_data *ffs, * the source does nothing. */ if (ffs->setup_state == FFS_SETUP_PENDING) - ffs->setup_state = FFS_SETUP_CANCELED; + ffs->setup_state = FFS_SETUP_CANCELLED; switch (type) { case FUNCTIONFS_RESUME: diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index f418c25f8511..38012bcfe9ec 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -123,7 +123,7 @@ enum ffs_setup_state { * setup. If this state is set read/write on ep0 return * -EIDRM. This state is only set when adding event. */ - FFS_SETUP_CANCELED + FFS_SETUP_CANCELLED }; struct ffs_data { @@ -166,18 +166,18 @@ struct ffs_data { /* * Possible transitions: - * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock * happens only in ep0 read which is P: mutex - * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock * happens only in ep0 i/o which is P: mutex - * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock - * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg */ enum ffs_setup_state setup_state; #define FFS_SETUP_STATE(ffs) \ ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELED, FFS_NO_SETUP)) + FFS_SETUP_CANCELLED, FFS_NO_SETUP)) /* Events & such. */ struct { -- cgit v1.2.1 From a7ecf0544f0fc710ba6e2ff751d328a4190c4a1f Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 10 Feb 2014 10:42:41 +0100 Subject: usb: gadget: functionfs: replace FFS_SETUP_STATUS with an inline function The FFS_SETUP_STATUS macro could be trivialy replaced with an static inline function but more importantly its name was tad confusing. The name suggested it was a simple accessor macro but it actually did change the state of the ffs_data structure perfomring a FFS_SETUP_CANCELLED -> FFS_NO_SETUP transition. The name of the function -- ffs_setup_state_clear_cancelled -- should better describe what the function actually does. Signed-off-by: Michal Nazarewicz Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 22 ++++++++++++++++------ drivers/usb/gadget/u_fs.h | 7 +++---- 2 files changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index eb5cb2b4b90d..5bfacf8b9e6e 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -99,6 +99,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) } +static inline enum ffs_setup_state +ffs_setup_state_clear_cancelled(struct ffs_data *ffs) +{ + return (enum ffs_setup_state) + cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP); +} + + static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); @@ -246,7 +254,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -312,7 +320,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * rather then _irqsave */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { + switch (ffs_setup_state_clear_cancelled(ffs)) { case FFS_SETUP_CANCELLED: ret = -EIDRM; goto done_spin; @@ -357,7 +365,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * transition can be performed and it's protected by * mutex. */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; done_spin: spin_unlock_irq(&ffs->ev.waitq.lock); @@ -423,7 +432,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -443,7 +452,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { + switch (ffs_setup_state_clear_cancelled(ffs)) { case FFS_SETUP_CANCELLED: ret = -EIDRM; break; @@ -491,7 +500,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_lock_irq(&ffs->ev.waitq.lock); /* See ffs_ep0_write() */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELLED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; break; } diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 38012bcfe9ec..78263cc36df8 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -172,13 +172,12 @@ struct ffs_data { * happens only in ep0 i/o which is P: mutex * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg + * + * This field should never be accessed directly and instead + * ffs_setup_state_clear_cancelled function should be used. */ enum ffs_setup_state setup_state; -#define FFS_SETUP_STATE(ffs) \ - ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELLED, FFS_NO_SETUP)) - /* Events & such. */ struct { u8 types[4]; -- cgit v1.2.1 From 0a7b1f8a70a59fd8215a937f77da20e003b9fc49 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 10 Feb 2014 10:42:42 +0100 Subject: usb: gadget: f_fs: fix setup request handling This patch fixes __ffs_ep0_queue_wait() function, which now returns number of bytes transferred in USB request or error code in case of failure. This is needed by ffs_ep0_read() function, when read data is copied to userspace. It also cleans up code by removing usused variable ep0req_status. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 2 +- drivers/usb/gadget/u_fs.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 5bfacf8b9e6e..b65a5029deed 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -228,7 +228,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) } ffs->setup_state = FFS_NO_SETUP; - return ffs->ep0req_status; + return req->status ? req->status : req->actual; } static int __ffs_ep0_stall(struct ffs_data *ffs) diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 78263cc36df8..c39e805025b9 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -154,7 +154,6 @@ struct ffs_data { */ struct usb_request *ep0req; /* P: mutex */ struct completion ep0req_completion; /* P: mutex */ - int ep0req_status; /* P: mutex */ /* reference counter */ atomic_t ref; -- cgit v1.2.1 From 23de91e970a410a30b5927b9a749ed1dfd914140 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 10 Feb 2014 10:42:43 +0100 Subject: usb: gadget: f_fs: add poll for endpoint 0 This patch adds poll function for file representing ep0. Ability of read from or write to ep0 file is related with actual state of ffs: - When desctiptors or strings are not written yet, POLLOUT flag is set. - If there is any event to read, POLLIN flag is set. - If setup request was read, POLLIN and POLLOUT flag is set, to allow send response (by performing I/O operation consistent with setup request direction) or set stall (by performing I/O operation opposite setup request direction). Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index b65a5029deed..20321631f0f4 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -28,6 +28,8 @@ #include #include +#include + #include "u_fs.h" #include "configfs.h" @@ -570,6 +572,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) return ret; } +static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait) +{ + struct ffs_data *ffs = file->private_data; + unsigned int mask = POLLWRNORM; + int ret; + + poll_wait(file, &ffs->ev.waitq, wait); + + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return mask; + + switch (ffs->state) { + case FFS_READ_DESCRIPTORS: + case FFS_READ_STRINGS: + mask |= POLLOUT; + break; + + case FFS_ACTIVE: + switch (ffs->setup_state) { + case FFS_NO_SETUP: + if (ffs->ev.count) + mask |= POLLIN; + break; + + case FFS_SETUP_PENDING: + case FFS_SETUP_CANCELLED: + mask |= (POLLIN | POLLOUT); + break; + } + case FFS_CLOSING: + break; + } + + mutex_unlock(&ffs->mutex); + + return mask; +} + static const struct file_operations ffs_ep0_operations = { .llseek = no_llseek, @@ -578,6 +619,7 @@ static const struct file_operations ffs_ep0_operations = { .read = ffs_ep0_read, .release = ffs_ep0_release, .unlocked_ioctl = ffs_ep0_ioctl, + .poll = ffs_ep0_poll, }; -- cgit v1.2.1 From 2e4c7553cd6f9c68bb741582dcb614edcbeca70f Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 10 Feb 2014 10:42:44 +0100 Subject: usb: gadget: f_fs: add aio support This patch adds asynchronous I/O support for FunctionFS endpoint files. It adds ffs_epfile_aio_write() and ffs_epfile_aio_read() functions responsible for preparing AIO operations. It also modifies ffs_epfile_io() function, adding aio handling code. Instead of extending list of parameters of this function, there is new struct ffs_io_data which contains all information needed to perform I/O operation. Pointer to this struct replaces "buf" and "len" parameters of ffs_epfile_io() function. Allocated buffer is freed immediately only after sync operation, because in async IO it's freed in complete funcion. For each async operation an USB request is allocated, because it allows to have more than one request queued on single endpoint. According to changes in ffs_epfile_io() function, functions ffs_epfile_write() and ffs_epfile_read() are updated to use new API. For asynchronous I/O operations there is new request complete function named ffs_epfile_async_io_complete(), which completes AIO operation, and frees used memory. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 265 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 239 insertions(+), 26 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 20321631f0f4..1ae741fdace0 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include "u_fs.h" @@ -158,6 +160,25 @@ struct ffs_epfile { unsigned char _pad; }; +/* ffs_io_data structure ***************************************************/ + +struct ffs_io_data { + bool aio; + bool read; + + struct kiocb *kiocb; + const struct iovec *iovec; + unsigned long nr_segs; + char __user *buf; + size_t len; + + struct mm_struct *mm; + struct work_struct work; + + struct usb_ep *ep; + struct usb_request *req; +}; + static int __must_check ffs_epfiles_create(struct ffs_data *ffs); static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); @@ -635,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) } } -static ssize_t ffs_epfile_io(struct file *file, - char __user *buf, size_t len, int read) +static void ffs_user_copy_worker(struct work_struct *work) +{ + struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, + work); + int ret = io_data->req->status ? io_data->req->status : + io_data->req->actual; + + if (io_data->read && ret > 0) { + int i; + size_t pos = 0; + use_mm(io_data->mm); + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_to_user(io_data->iovec[i].iov_base, + &io_data->buf[pos], + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + break; + } + pos += io_data->iovec[i].iov_len; + } + unuse_mm(io_data->mm); + } + + aio_complete(io_data->kiocb, ret, ret); + + usb_ep_free_request(io_data->ep, io_data->req); + + io_data->kiocb->private = NULL; + if (io_data->read) + kfree(io_data->iovec); + kfree(io_data->buf); + kfree(io_data); +} + +static void ffs_epfile_async_io_complete(struct usb_ep *_ep, + struct usb_request *req) +{ + struct ffs_io_data *io_data = req->context; + + ENTER(); + + INIT_WORK(&io_data->work, ffs_user_copy_worker); + schedule_work(&io_data->work); +} + +static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; struct usb_gadget *gadget = epfile->ffs->gadget; @@ -667,7 +732,7 @@ static ssize_t ffs_epfile_io(struct file *file, } /* Do we halt? */ - halt = !read == !epfile->in; + halt = (!io_data->read == !epfile->in); if (halt && epfile->isoc) { ret = -EINVAL; goto error; @@ -679,15 +744,32 @@ static ssize_t ffs_epfile_io(struct file *file, * Controller may require buffer size to be aligned to * maxpacketsize of an out endpoint. */ - data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len; + data_len = io_data->read ? + usb_ep_align_maybe(gadget, ep->ep, io_data->len) : + io_data->len; data = kmalloc(data_len, GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; - - if (!read && unlikely(copy_from_user(data, buf, len))) { - ret = -EFAULT; - goto error; + if (io_data->aio && !io_data->read) { + int i; + size_t pos = 0; + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_from_user(&data[pos], + io_data->iovec[i].iov_base, + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + goto error; + } + pos += io_data->iovec[i].iov_len; + } + } else { + if (!io_data->read && + unlikely(__copy_from_user(data, io_data->buf, + io_data->len))) { + ret = -EFAULT; + goto error; + } } } @@ -710,24 +792,52 @@ static ssize_t ffs_epfile_io(struct file *file, ret = -EBADMSG; } else { /* Fire the request */ - DECLARE_COMPLETION_ONSTACK(done); + struct usb_request *req; - struct usb_request *req = ep->req; - req->context = &done; - req->complete = ffs_epfile_io_complete; - req->buf = data; - req->length = data_len; + if (io_data->aio) { + req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + if (unlikely(!req)) + goto error; - ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + req->buf = data; + req->length = io_data->len; - spin_unlock_irq(&epfile->ffs->eps_lock); + io_data->buf = data; + io_data->ep = ep->ep; + io_data->req = req; - if (unlikely(ret < 0)) { - /* nop */ - } else if (unlikely(wait_for_completion_interruptible(&done))) { - ret = -EINTR; - usb_ep_dequeue(ep->ep, req); + req->context = io_data; + req->complete = ffs_epfile_async_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + if (unlikely(ret)) { + usb_ep_free_request(ep->ep, req); + goto error; + } + ret = -EIOCBQUEUED; + + spin_unlock_irq(&epfile->ffs->eps_lock); } else { + DECLARE_COMPLETION_ONSTACK(done); + + req = ep->req; + req->buf = data; + req->length = io_data->len; + + req->context = &done; + req->complete = ffs_epfile_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + + spin_unlock_irq(&epfile->ffs->eps_lock); + + if (unlikely(ret < 0)) { + /* nop */ + } else if (unlikely( + wait_for_completion_interruptible(&done))) { + ret = -EINTR; + usb_ep_dequeue(ep->ep, req); + } else { /* * XXX We may end up silently droping data here. * Since data_len (i.e. req->length) may be bigger @@ -736,14 +846,18 @@ static ssize_t ffs_epfile_io(struct file *file, * space for. */ ret = ep->status; - if (read && ret > 0 && - unlikely(copy_to_user(buf, data, - min_t(size_t, ret, len)))) + if (io_data->read && ret > 0 && + unlikely(copy_to_user(io_data->buf, data, + min_t(size_t, ret, + io_data->len)))) ret = -EFAULT; + } + kfree(data); } } mutex_unlock(&epfile->mutex); + return ret; error: kfree(data); return ret; @@ -753,17 +867,31 @@ static ssize_t ffs_epfile_write(struct file *file, const char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, (char __user *)buf, len, 0); + io_data.aio = false; + io_data.read = false; + io_data.buf = (char * __user)buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static ssize_t ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, buf, len, 1); + io_data.aio = false; + io_data.read = true; + io_data.buf = buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static int @@ -782,6 +910,89 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } +static int ffs_aio_cancel(struct kiocb *kiocb) +{ + struct ffs_io_data *io_data = kiocb->private; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + int value; + + ENTER(); + + spin_lock_irq(&epfile->ffs->eps_lock); + + if (likely(io_data && io_data->ep && io_data->req)) + value = usb_ep_dequeue(io_data->ep, io_data->req); + else + value = -EINVAL; + + spin_unlock_irq(&epfile->ffs->eps_lock); + + return value; +} + +static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + + ENTER(); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) + return -ENOMEM; + + io_data->aio = true; + io_data->read = false; + io_data->kiocb = kiocb; + io_data->iovec = iovec; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + +static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + struct iovec *iovec_copy; + + ENTER(); + + iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL); + if (unlikely(!iovec_copy)) + return -ENOMEM; + + memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) { + kfree(iovec_copy); + return -ENOMEM; + } + + io_data->aio = true; + io_data->read = true; + io_data->kiocb = kiocb; + io_data->iovec = iovec_copy; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + static int ffs_epfile_release(struct inode *inode, struct file *file) { @@ -838,6 +1049,8 @@ static const struct file_operations ffs_epfile_operations = { .open = ffs_epfile_open, .write = ffs_epfile_write, .read = ffs_epfile_read, + .aio_write = ffs_epfile_aio_write, + .aio_read = ffs_epfile_aio_read, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, }; -- cgit v1.2.1 From b797b76fb464ed6939ce71386bee7fdda4b68632 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 16 Feb 2014 20:45:41 +0100 Subject: usb: host: remove USB_ARCH_HAS_?HCI USB_ARCH_HAS_EHCI, USB_ARCH_HAS_OHCI, and USB_ARCH_HAS_XHCI were made obsolete in v3.11. They have not been used ever since. Setting them has no effect. They can safely be removed. Signed-off-by: Paul Bolle Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2e6b832e004b..e0cad4418085 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -2,10 +2,6 @@ # USB device configuration # -# These are unused now, remove them once they are no longer selected -config USB_ARCH_HAS_OHCI - bool - config USB_OHCI_BIG_ENDIAN_DESC bool @@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN default n if STB03xxx || PPC_MPC52xx default y -config USB_ARCH_HAS_EHCI - bool - config USB_EHCI_BIG_ENDIAN_MMIO bool config USB_EHCI_BIG_ENDIAN_DESC bool -config USB_ARCH_HAS_XHCI - bool - menuconfig USB_SUPPORT bool "USB support" depends on HAS_IOMEM -- cgit v1.2.1 From aa7be0f8f7fbd446c7cd5352480d0d10d78f742b Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 19 Feb 2014 10:07:43 +0800 Subject: usb: gadget: at91: fix the number of endpoint parameter In sama5d3 SoC, there are 16 endpoints, which is different with earlier SoCs (only have 7 endpoints). The USBA_NR_ENDPOINTS macro is not suitable for sama5d3. So, get the endpoints number through the udc->num_ep, which get from platform data for non-dt kernel, or parse from dt node. Acked-by: Nicolas Ferre Signed-off-by: Bo Shen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 52771d4c44bc..53eea030fc15 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (ep_status) { int i; - for (i = 0; i < USBA_NR_ENDPOINTS; i++) + for (i = 0; i < udc->num_ep; i++) if (ep_status & (1 << i)) { if (ep_is_control(&udc->usba_ep[i])) usba_control_irq(udc, &udc->usba_ep[i]); -- cgit v1.2.1 From 3fcba0d87bfb5f880d34868090e05571dca80f78 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 19 Feb 2014 10:07:44 +0800 Subject: usb: gadget: at91: using USBA_NR_DMAS for DMA channels The SoCs earlier than sama5d3, they have the same number endpoints and DMA channels. In driver code, they use the same definition USBA_NR_ENDPOINTS for both endpoints and dma channels. However, in sama5d3, it has different number for endpoints and DMA channels. So, define a new macro USBA_NR_DMAs for DMA channels. And the USBA_NR_ENDPOINS is not used anymore, remove it at the same time. Acked-by: Nicolas Ferre Signed-off-by: Bo Shen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 2 +- drivers/usb/gadget/atmel_usba_udc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 53eea030fc15..18a44919ae9f 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; - for (i = 1; i < USBA_NR_ENDPOINTS; i++) + for (i = 1; i < USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); } diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h index 2922db50befe..a70706e8cb02 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -210,7 +210,7 @@ #define USBA_FIFO_BASE(x) ((x) << 16) /* Synth parameters */ -#define USBA_NR_ENDPOINTS 7 +#define USBA_NR_DMAS 7 #define EP0_FIFO_SIZE 64 #define EP0_EPT_SIZE USBA_EPT_SIZE_64 -- cgit v1.2.1 From 798a2468e026ab6a922f2c6c70ca4406b8e8dbc1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 17 Jan 2014 16:34:11 +0300 Subject: usb: gadget: gr_udc: remove some unneeded error handling Debugfs function return an ERR_PTR if they compiled out. We don't need to test for that here because if the debugfs file are compiled out then it is ok to pass an ERR_PTR to debugfs_create_file() since it will just be a no-op stub. Debugfs return NULLs on error, but we don't need to test for that either because debugfs_create_file() will accept NULL pointers. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/gr_udc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c index 914cbd84ee40..f984ee75324d 100644 --- a/drivers/usb/gadget/gr_udc.c +++ b/drivers/usb/gadget/gr_udc.c @@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev) const char *name = "gr_udc_state"; dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); - if (IS_ERR(dev->dfs_root)) { - dev_err(dev->dev, "Failed to create debugfs directory\n"); - return; - } - dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, - dev, &gr_dfs_fops); - if (IS_ERR(dev->dfs_state)) - dev_err(dev->dev, "Failed to create debugfs file %s\n", name); + dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev, + &gr_dfs_fops); } static void gr_dfs_delete(struct gr_udc *dev) -- cgit v1.2.1 From 06f9b6e59661cee510b04513b13ea7927727d758 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 7 Jan 2014 17:45:50 +0800 Subject: usb: dwc3: fix wrong bit mask in dwc3_event_devt Around DWC USB3 2.30a release another bit has been added to the Device-Specific Event (DEVT) Event Information (EvtInfo) bitfield. Because of that, what used to be 8 bits long, has become 9 bits long. Per dwc3 2.30a+ spec in the Device-Specific Event (DEVT), the field of Event Information Bits(EvtInfo) uses [24:16] bits, and it has 9 bits not 8 bits. And the following reserved field uses [31:25] bits not [31:24] bits, and it has 7 bits. So in dwc3_event_devt, the bit mask should be: event_info [24:16] 9 bits reserved31_25 [31:25] 7 bits This patch makes sure that newer core releases will work fine with Linux and that we will decode the event information properly on new core releases. [ balbi@ti.com : improve commit log a bit ] Cc: Signed-off-by: Huang Rui Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f8af8d44af85..69c4583933d1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -815,15 +815,15 @@ struct dwc3_event_depevt { * 12 - VndrDevTstRcved * @reserved15_12: Reserved, not used * @event_info: Information about this event - * @reserved31_24: Reserved, not used + * @reserved31_25: Reserved, not used */ struct dwc3_event_devt { u32 one_bit:1; u32 device_event:7; u32 type:4; u32 reserved15_12:4; - u32 event_info:8; - u32 reserved31_24:8; + u32 event_info:9; + u32 reserved31_25:7; } __packed; /** -- cgit v1.2.1 From 40f099e32c2a06bad7d75683421e30fcc74924cd Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 17 Jan 2014 10:22:35 +0100 Subject: usb: musb: dsps, debugfs files debugfs files to show the contents of important dsps registers. Signed-off-by: Markus Pargmann Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 68c14e1eac66..3372ded5def7 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -45,6 +45,8 @@ #include #include +#include + #include "musb_core.h" static const struct of_device_id musb_dsps_of_match[]; @@ -136,6 +138,26 @@ struct dsps_glue { unsigned long last_timer; /* last timer data for each instance */ struct dsps_context context; + struct debugfs_regset32 regset; + struct dentry *dbgfs_root; +}; + +static const struct debugfs_reg32 dsps_musb_regs[] = { + { "revision", 0x00 }, + { "control", 0x14 }, + { "status", 0x18 }, + { "eoi", 0x24 }, + { "intr0_stat", 0x30 }, + { "intr1_stat", 0x34 }, + { "intr0_set", 0x38 }, + { "intr1_set", 0x3c }, + { "txmode", 0x70 }, + { "rxmode", 0x74 }, + { "autoreq", 0xd0 }, + { "srpfixtime", 0xd4 }, + { "tdown", 0xd8 }, + { "phy_utmi", 0xe0 }, + { "mode", 0xe8 }, }; static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) @@ -368,6 +390,30 @@ out: return ret; } +static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue) +{ + struct dentry *root; + struct dentry *file; + char buf[128]; + + sprintf(buf, "%s.dsps", dev_name(musb->controller)); + root = debugfs_create_dir(buf, NULL); + if (!root) + return -ENOMEM; + glue->dbgfs_root = root; + + glue->regset.regs = dsps_musb_regs; + glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs); + glue->regset.base = musb->ctrl_base; + + file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset); + if (!file) { + debugfs_remove_recursive(root); + return -ENOMEM; + } + return 0; +} + static int dsps_musb_init(struct musb *musb) { struct device *dev = musb->controller; @@ -377,6 +423,7 @@ static int dsps_musb_init(struct musb *musb) void __iomem *reg_base; struct resource *r; u32 rev, val; + int ret; r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control"); if (!r) @@ -410,6 +457,10 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + ret = dsps_musb_dbg_init(musb, glue); + if (ret) + return ret; + return 0; } @@ -656,6 +707,9 @@ static int dsps_remove(struct platform_device *pdev) /* disable usbss clocks */ pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); + + debugfs_remove_recursive(glue->dbgfs_root); + return 0; } -- cgit v1.2.1 From c859aa65a7ec40c02f435f14fa71de2a87c64513 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 19 Feb 2014 13:41:40 +0800 Subject: usb: chipidea: refine PHY operation - Delete global_phy due to we can get the phy from phy layer now - using devm_usb_get_phy to instead of usb_get_phy - delete the otg_set_peripheral, which should be handled by otg layer Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 2 -- drivers/usb/chipidea/core.c | 70 +++++++++++++++++---------------------------- drivers/usb/chipidea/udc.c | 6 ---- 3 files changed, 26 insertions(+), 52 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 88b80f7728e4..e206406ae1d9 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -196,8 +196,6 @@ struct ci_hdrc { struct ci_hdrc_platform_data *platdata; int vbus_active; - /* FIXME: some day, we'll not use global phy */ - bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; struct dentry *debugfs; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 33f22bc6ad7f..75aaa9c3cc4a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -496,33 +496,6 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) } } -static int ci_usb_phy_init(struct ci_hdrc *ci) -{ - if (ci->platdata->phy) { - ci->transceiver = ci->platdata->phy; - return usb_phy_init(ci->transceiver); - } else { - ci->global_phy = true; - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (IS_ERR(ci->transceiver)) - ci->transceiver = NULL; - - return 0; - } -} - -static void ci_usb_phy_destroy(struct ci_hdrc *ci) -{ - if (!ci->transceiver) - return; - - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - else - usb_phy_shutdown(ci->transceiver); -} - static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -561,7 +534,26 @@ static int ci_hdrc_probe(struct platform_device *pdev) hw_phymode_configure(ci); - ret = ci_usb_phy_init(ci); + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + + if (IS_ERR(ci->transceiver)) { + ret = PTR_ERR(ci->transceiver); + /* + * if -ENXIO is returned, it means PHY layer wasn't + * enabled, so it makes no sense to return -EPROBE_DEFER + * in that case, since no PHY driver will ever probe. + */ + if (ret == -ENXIO) + return ret; + + dev_err(dev, "no usb2 phy configured\n"); + return -EPROBE_DEFER; + } + + ret = usb_phy_init(ci->transceiver); if (ret) { dev_err(dev, "unable to init phy: %d\n", ret); return ret; @@ -573,7 +565,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ci->irq < 0) { dev_err(dev, "missing IRQ\n"); ret = -ENODEV; - goto destroy_phy; + goto deinit_phy; } ci_get_otg_capable(ci); @@ -590,23 +582,12 @@ static int ci_hdrc_probe(struct platform_device *pdev) ret = ci_hdrc_gadget_init(ci); if (ret) dev_info(dev, "doesn't support gadget\n"); - if (!ret && ci->transceiver) { - ret = otg_set_peripheral(ci->transceiver->otg, - &ci->gadget); - /* - * If we implement all USB functions using chipidea drivers, - * it doesn't need to call above API, meanwhile, if we only - * use gadget function, calling above API is useless. - */ - if (ret && ret != -ENOTSUPP) - goto destroy_phy; - } } if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { dev_err(dev, "no supported roles\n"); ret = -ENODEV; - goto destroy_phy; + goto deinit_phy; } if (ci->is_otg) { @@ -663,8 +644,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) free_irq(ci->irq, ci); stop: ci_role_destroy(ci); -destroy_phy: - ci_usb_phy_destroy(ci); +deinit_phy: + usb_phy_shutdown(ci->transceiver); return ret; } @@ -677,7 +658,8 @@ static int ci_hdrc_remove(struct platform_device *pdev) free_irq(ci->irq, ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); - ci_usb_phy_destroy(ci); + usb_phy_shutdown(ci->transceiver); + kfree(ci->hw_bank.regmap); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4ab2cb62dfce..0a61c6688bc1 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1837,12 +1837,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); - - if (ci->transceiver) { - otg_set_peripheral(ci->transceiver->otg, NULL); - if (ci->global_phy) - usb_put_phy(ci->transceiver); - } } static int udc_id_switch_for_device(struct ci_hdrc *ci) -- cgit v1.2.1 From 64fc06c40e01aa8129f5bc4c51567b5e205126b0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 19 Feb 2014 13:41:41 +0800 Subject: usb: chipidea: udc: refine ep operation at isr_tr_complete_handler - delete the warning message at interrupt handler, and adds judgement at ep_enable, if non-ep0 requests ctrl transfer, it will indicate an error. - delete hw_test_and_clear_setup_status which is a broken code - Tested with g_mass_storage, g_ncm, g_ether Cc: matthieu.castet@parrot.com Reported-by: Michael Grzeschik Acked-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0a61c6688bc1..0dc56aebb807 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -177,19 +177,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; } -/** - * hw_test_and_clear_setup_status: test & clear setup status (execute without - * interruption) - * @n: endpoint number - * - * This function returns setup status - */ -static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n) -{ - n = ep_to_bit(ci, n); - return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); -} - /** * hw_ep_prime: primes endpoint (execute without interruption) * @num: endpoint number @@ -997,14 +984,10 @@ __acquires(ci->lock) } } - if (hwep->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(ci, i)) - continue; - - if (i != 0) { - dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); + /* Only handle setup packet below */ + if (i != 0 || + !hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) continue; - } /* * Flush data and handshake transactions of previous @@ -1193,6 +1176,11 @@ static int ep_enable(struct usb_ep *ep, hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ + if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) { + dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n"); + retval = -EINVAL; + } + /* * Enable endpoints in the HW other than ep0 as ep0 * is always enabled -- cgit v1.2.1 From fad56745a641750745d85f59d5cc640a1a4c1719 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 19 Feb 2014 13:41:42 +0800 Subject: usb: chipidea: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. This is a cosmetic change to make the code simpler and enhance the readability. Signed-off-by: Peter Chen Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 75aaa9c3cc4a..47b4bd860b65 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -505,7 +505,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) int ret; enum usb_dr_mode dr_mode; - if (!dev->platform_data) { + if (!dev_get_platdata(dev)) { dev_err(dev, "platform data missing\n"); return -ENODEV; } @@ -522,7 +522,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } ci->dev = dev; - ci->platdata = dev->platform_data; + ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & CI_HDRC_IMX28_WRITE_FIX); -- cgit v1.2.1 From 4f6743d5ca97ac66831add302cc5467db4ee3809 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Wed, 19 Feb 2014 13:41:43 +0800 Subject: usb: chipidea: udc: add maximum-speed = full-speed option This patch makes it possible to set the chipidea udc into full-speed only mode. It is set by the oftree property "maximum-speed = full-speed". Signed-off-by: Peter Chen Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/bits.h | 2 ++ drivers/usb/chipidea/core.c | 11 +++++++++++ 2 files changed, 13 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index a85713165688..83d06c1455b7 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -50,12 +50,14 @@ #define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) /* PTS and PTW for non lpm version only */ +#define PORTSC_PFSC BIT(24) #define PORTSC_PTS(d) \ (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0)) #define PORTSC_PTW BIT(28) #define PORTSC_STS BIT(29) /* DEVLC */ +#define DEVLC_PFSC BIT(23) #define DEVLC_PSPD (0x03UL << 25) #define DEVLC_PSPD_HS (0x02UL << 25) #define DEVLC_PTW BIT(27) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 47b4bd860b65..65aeaacda6cd 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { + if (ci->hw_bank.lpm) + hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); + else + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + } + /* USBMODE should be configured step by step */ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, mode); @@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev, } } + if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) + platdata->flags |= CI_HDRC_FORCE_FULLSPEED; + return 0; } -- cgit v1.2.1 From 42d182124801573e06284200d81c3963962e753d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 19 Feb 2014 13:41:44 +0800 Subject: usb: chipidea: Propagate the real error code on platform_get_irq() failure No need to return a 'fake' return value on platform_get_irq() failure. Just return the error code itself instead. Signed-off-by: Peter Chen Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 65aeaacda6cd..ca6831c5b763 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -575,7 +575,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->irq = platform_get_irq(pdev, 0); if (ci->irq < 0) { dev_err(dev, "missing IRQ\n"); - ret = -ENODEV; + ret = ci->irq; goto deinit_phy; } -- cgit v1.2.1 From f080a51bef2caa9b0f647dc430bc608d5723ac29 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 20 Feb 2014 10:49:30 -0500 Subject: USB: complain if userspace resets an active endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is an error for a driver to call usb_clear_halt() or usb_reset_endpoint() while there are URBs queued for the endpoint, because the end result is not well defined. At the time the endpoint gets reset, it may or may not be actively running. As far as I know, no kernel drivers do this. But some userspace drivers do, and it seems like a good idea to bring this error to their attention. This patch adds a warning to the kernel log whenever a program invokes the USBDEVFS_CLEAR_HALT or USBDEVFS_RESETEP ioctls at an inappropriate time, and includes the name of the program. This will make it clear that any subsequent errors are not due to the misbehavior of a kernel driver. Signed-off-by: Alan Stern Suggested-by: Bjørn Mork CC: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 90e18f6fa2bb..f3ba2e076ee3 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1043,6 +1043,20 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return ret; } +static void check_reset_of_active_ep(struct usb_device *udev, + unsigned int epnum, char *ioctl_name) +{ + struct usb_host_endpoint **eps; + struct usb_host_endpoint *ep; + + eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out; + ep = eps[epnum & 0x0f]; + if (ep && !list_empty(&ep->urb_list)) + dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n", + task_pid_nr(current), current->comm, + ioctl_name, epnum); +} + static int proc_resetep(struct dev_state *ps, void __user *arg) { unsigned int ep; @@ -1056,6 +1070,7 @@ static int proc_resetep(struct dev_state *ps, void __user *arg) ret = checkintf(ps, ret); if (ret) return ret; + check_reset_of_active_ep(ps->dev, ep, "RESETEP"); usb_reset_endpoint(ps->dev, ep); return 0; } @@ -1074,6 +1089,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg) ret = checkintf(ps, ret); if (ret) return ret; + check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT"); if (ep & USB_DIR_IN) pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f); else -- cgit v1.2.1 From 618836cc3478aec3dc9a60488bfd43ca93a322bd Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 14:31:55 -0600 Subject: usb: wusbcore: fix kernel panic on HWA unplug This patch adds ref counting to sections of code that operate on struct wa_xfer objects that were missing it. Specifically, error handling cases need to be protected from freeing the xfer while it is still in use elsewhere. This fixes a kernel panic that can occur when pulling the HWA dongle while data is being transferred to a wireless device. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3cd96e936d77..3ca055555105 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -733,6 +733,8 @@ static void wa_seg_dto_cb(struct urb *urb) seg->isoc_frame_offset + seg->isoc_frame_index); /* resubmit the URB with the next isoc frame. */ + /* take a ref on resubmit. */ + wa_xfer_get(xfer); result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "xfer 0x%08X#%u: DTO submit failed: %d\n", @@ -760,9 +762,13 @@ static void wa_seg_dto_cb(struct urb *urb) goto error_default; } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); return; error_dto_submit: + /* taken on resubmit attempt. */ + wa_xfer_put(xfer); error_default: spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; @@ -788,7 +794,8 @@ error_default: wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); - + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -855,6 +862,8 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -931,6 +940,8 @@ static void wa_seg_tr_cb(struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); } + /* taken when this URB was submitted. */ + wa_xfer_put(xfer); } /* @@ -1318,30 +1329,41 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, /* default to done unless we encounter a multi-frame isoc segment. */ *dto_done = 1; + /* + * Take a ref for each segment urb so the xfer cannot disappear until + * all of the callbacks run. + */ + wa_xfer_get(xfer); /* submit the transfer request. */ + seg->status = WA_SEG_SUBMITTED; result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC); if (result < 0) { pr_err("%s: xfer %p#%u: REQ submit failed: %d\n", __func__, xfer, seg->index, result); - goto error_seg_submit; + wa_xfer_put(xfer); + goto error_tr_submit; } /* submit the isoc packet descriptor if present. */ if (seg->isoc_pack_desc_urb) { + wa_xfer_get(xfer); result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); seg->isoc_frame_index = 0; if (result < 0) { pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n", __func__, xfer, seg->index, result); + wa_xfer_put(xfer); goto error_iso_pack_desc_submit; } } /* submit the out data if this is an out request. */ if (seg->dto_urb) { struct wahc *wa = xfer->wa; + wa_xfer_get(xfer); result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); if (result < 0) { pr_err("%s: xfer %p#%u: DTO submit failed: %d\n", __func__, xfer, seg->index, result); + wa_xfer_put(xfer); goto error_dto_submit; } /* @@ -1353,7 +1375,6 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, && (seg->isoc_frame_count > 1)) *dto_done = 0; } - seg->status = WA_SEG_SUBMITTED; rpipe_avail_dec(rpipe); return 0; @@ -1361,7 +1382,7 @@ error_dto_submit: usb_unlink_urb(seg->isoc_pack_desc_urb); error_iso_pack_desc_submit: usb_unlink_urb(&seg->tr_urb); -error_seg_submit: +error_tr_submit: seg->status = WA_SEG_ERROR; seg->result = result; *dto_done = 1; @@ -1393,6 +1414,12 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) list_node); list_del(&seg->list_node); xfer = seg->xfer; + /* + * Get a reference to the xfer in case the callbacks for the + * URBs submitted by __wa_seg_submit attempt to complete + * the xfer before this function completes. + */ + wa_xfer_get(xfer); result = __wa_seg_submit(rpipe, xfer, seg, &dto_done); /* release the dto resource if this RPIPE is done with it. */ if (dto_done) @@ -1404,10 +1431,15 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) spin_unlock_irqrestore(&rpipe->seg_lock, flags); spin_lock_irqsave(&xfer->lock, flags); __wa_xfer_abort(xfer); + /* + * This seg was marked as submitted when it was put on + * the RPIPE seg_list. Mark it done. + */ xfer->segs_done++; spin_unlock_irqrestore(&xfer->lock, flags); spin_lock_irqsave(&rpipe->seg_lock, flags); } + wa_xfer_put(xfer); } /* * Mark this RPIPE as waiting if dto was not acquired, there are @@ -1592,12 +1624,19 @@ static int wa_urb_enqueue_b(struct wa_xfer *xfer) dev_err(&(urb->dev->dev), "%s: error_xfer_setup\n", __func__); goto error_xfer_setup; } + /* + * Get a xfer reference since __wa_xfer_submit starts asynchronous + * operations that may try to complete the xfer before this function + * exits. + */ + wa_xfer_get(xfer); result = __wa_xfer_submit(xfer); if (result < 0) { dev_err(&(urb->dev->dev), "%s: error_xfer_submit\n", __func__); goto error_xfer_submit; } spin_unlock_irqrestore(&xfer->lock, flags); + wa_xfer_put(xfer); return 0; /* @@ -1623,6 +1662,7 @@ error_xfer_submit: spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); + wa_xfer_put(xfer); /* return success since the completion routine will run. */ return 0; } -- cgit v1.2.1 From acfadcea2adaa52048c6b3c8a3c75105a5540707 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 14:31:56 -0600 Subject: usb: wusbcore: fix stranded URB after HWA unplug This patch adds error checking to the abort request callback to forcibly clean up the dequeued transfers if the abort request failed. The wa_complete_remaining_xfer_segs was modified so that it could be used in this situation as well. This fixes a stranded URB/PNP hang when the HWA is unplugged while playing audio to a wireless audio device. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 56 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3ca055555105..cb061915f051 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -167,6 +167,8 @@ struct wa_xfer { static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, struct wa_seg *seg, int curr_iso_frame); +static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, + int starting_index, enum wa_seg_status status); static inline void wa_xfer_init(struct wa_xfer *xfer) { @@ -416,12 +418,51 @@ out: struct wa_xfer_abort_buffer { struct urb urb; + struct wahc *wa; struct wa_xfer_abort cmd; }; static void __wa_xfer_abort_cb(struct urb *urb) { struct wa_xfer_abort_buffer *b = urb->context; + struct wahc *wa = b->wa; + + /* + * If the abort request URB failed, then the HWA did not get the abort + * command. Forcibly clean up the xfer without waiting for a Transfer + * Result from the HWA. + */ + if (urb->status < 0) { + struct wa_xfer *xfer; + struct device *dev = &wa->usb_iface->dev; + + xfer = wa_xfer_get_by_id(wa, le32_to_cpu(b->cmd.dwTransferID)); + dev_err(dev, "%s: Transfer Abort request failed. result: %d\n", + __func__, urb->status); + if (xfer) { + unsigned long flags; + int done; + struct wa_rpipe *rpipe = xfer->ep->hcpriv; + + dev_err(dev, "%s: cleaning up xfer %p ID 0x%08X.\n", + __func__, xfer, wa_xfer_id(xfer)); + spin_lock_irqsave(&xfer->lock, flags); + /* mark all segs as aborted. */ + wa_complete_remaining_xfer_segs(xfer, 0, + WA_SEG_ABORTED); + done = __wa_xfer_is_done(xfer); + spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); + wa_xfer_delayed_run(rpipe); + wa_xfer_put(xfer); + } else { + dev_err(dev, "%s: xfer ID 0x%08X already gone.\n", + __func__, le32_to_cpu(b->cmd.dwTransferID)); + } + } + + wa_put(wa); /* taken in __wa_xfer_abort */ usb_put_urb(&b->urb); } @@ -449,6 +490,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer) b->cmd.bRequestType = WA_XFER_ABORT; b->cmd.wRPipe = rpipe->descr.wRPipeIndex; b->cmd.dwTransferID = wa_xfer_id_le32(xfer); + b->wa = wa_get(xfer->wa); usb_init_urb(&b->urb); usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev, @@ -462,6 +504,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer) error_submit: + wa_put(xfer->wa); if (printk_ratelimit()) dev_err(dev, "xfer %p: Can't submit abort request: %d\n", xfer, result); @@ -2036,15 +2079,17 @@ static int wa_xfer_status_to_errno(u8 status) * no other segment transfer results will be returned from the device. * Mark the remaining submitted or pending xfers as completed so that * the xfer will complete cleanly. + * + * xfer->lock must be held + * */ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, - struct wa_seg *incoming_seg, enum wa_seg_status status) + int starting_index, enum wa_seg_status status) { int index; struct wa_rpipe *rpipe = xfer->ep->hcpriv; - for (index = incoming_seg->index + 1; index < xfer->segs_submitted; - index++) { + for (index = starting_index; index < xfer->segs_submitted; index++) { struct wa_seg *current_seg = xfer->seg[index]; BUG_ON(current_seg == NULL); @@ -2202,7 +2247,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, * transfers with data or below for no data, the xfer will complete. */ if (xfer_result->bTransferSegment & 0x80) - wa_complete_remaining_xfer_segs(xfer, seg, WA_SEG_DONE); + wa_complete_remaining_xfer_segs(xfer, seg->index + 1, + WA_SEG_DONE); if (usb_pipeisoc(xfer->urb->pipe) && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) { /* set up WA state to read the isoc packet status next. */ @@ -2253,7 +2299,7 @@ error_buf_in_populate: error_complete: xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - wa_complete_remaining_xfer_segs(xfer, seg, seg->status); + wa_complete_remaining_xfer_segs(xfer, seg->index + 1, seg->status); done = __wa_xfer_is_done(xfer); /* * queue work item to clear STALL for control endpoints. -- cgit v1.2.1 From 5da43afc2b73795e82c4bc3e53a4a177a02637d0 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 14:31:57 -0600 Subject: usb: wusbcore: prevent urb dequeue and giveback race This patch takes a reference to the wa_xfer object in wa_urb_dequeue to prevent the urb giveback code from completing the xfer and freeing it while wa_urb_dequeue is executing. It also checks for done at the start to avoid a double completion scenario. Adding the check for done in urb_dequeue means that any other place where a submitted transfer segment is marked as done must complete the transfer if it is done. __wa_xfer_delayed_run was not checking this case so that check was added as well. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index cb061915f051..5e5343e69915 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1471,6 +1471,8 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) xfer, wa_xfer_id(xfer), seg->index, atomic_read(&rpipe->segs_available), result); if (unlikely(result < 0)) { + int done; + spin_unlock_irqrestore(&rpipe->seg_lock, flags); spin_lock_irqsave(&xfer->lock, flags); __wa_xfer_abort(xfer); @@ -1479,7 +1481,10 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) * the RPIPE seg_list. Mark it done. */ xfer->segs_done++; + done = __wa_xfer_is_done(xfer); spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); spin_lock_irqsave(&rpipe->seg_lock, flags); } wa_xfer_put(xfer); @@ -1915,20 +1920,20 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) /* check if it is safe to unlink. */ spin_lock_irqsave(&wa->xfer_list_lock, flags); result = usb_hcd_check_unlink_urb(&(wa->wusb->usb_hcd), urb, status); + if ((result == 0) && urb->hcpriv) { + /* + * Get a xfer ref to prevent a race with wa_xfer_giveback + * cleaning up the xfer while we are working with it. + */ + wa_xfer_get(urb->hcpriv); + } spin_unlock_irqrestore(&wa->xfer_list_lock, flags); if (result) return result; xfer = urb->hcpriv; - if (xfer == NULL) { - /* - * Nothing setup yet enqueue will see urb->status != - * -EINPROGRESS (by hcd layer) and bail out with - * error, no need to do completion - */ - BUG_ON(urb->status == -EINPROGRESS); - goto out; - } + if (xfer == NULL) + return -ENOENT; spin_lock_irqsave(&xfer->lock, flags); pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer)); rpipe = xfer->ep->hcpriv; @@ -1939,6 +1944,16 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) result = -ENOENT; goto out_unlock; } + /* + * Check for done to avoid racing with wa_xfer_giveback and completing + * twice. + */ + if (__wa_xfer_is_done(xfer)) { + pr_debug("%s: xfer %p id 0x%08X already done.\n", __func__, + xfer, wa_xfer_id(xfer)); + result = -ENOENT; + goto out_unlock; + } /* Check the delayed list -> if there, release and complete */ spin_lock_irqsave(&wa->xfer_list_lock, flags2); if (!list_empty(&xfer->list_node) && xfer->seg == NULL) @@ -2007,11 +2022,12 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); + wa_xfer_put(xfer); return result; out_unlock: spin_unlock_irqrestore(&xfer->lock, flags); -out: + wa_xfer_put(xfer); return result; dequeue_delayed: @@ -2020,6 +2036,7 @@ dequeue_delayed: xfer->result = urb->status; spin_unlock_irqrestore(&xfer->lock, flags); wa_xfer_giveback(xfer); + wa_xfer_put(xfer); usb_put_urb(urb); /* we got a ref in enqueue() */ return 0; } -- cgit v1.2.1 From e500d526f968f184462912334b74b80dc905fca0 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 14:31:58 -0600 Subject: usb: wusbcore: add a convenience function for completing a transfer segment This patch adds a convenience function for the commonly performed task of marking a transfer segment as done. It combines the 3 steps of setting the segment status, incrementing the segs_done field of the transfer and checking if the completed segment results in the transfer also being done. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 48 ++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 5e5343e69915..3d6b30d8520e 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -391,6 +391,24 @@ out: return result; } +/* + * Mark the given segment as done. Return true if this completes the xfer. + * This should only be called for segs that have been submitted to an RPIPE. + * Delayed segs are not marked as submitted so they do not need to be marked + * as done when cleaning up. + * + * xfer->lock has to be locked + */ +static unsigned __wa_xfer_mark_seg_as_done(struct wa_xfer *xfer, + struct wa_seg *seg, enum wa_seg_status status) +{ + seg->status = status; + xfer->segs_done++; + + /* check for done. */ + return __wa_xfer_is_done(xfer); +} + /* * Search for a transfer list ID on the HCD's URB list * @@ -821,12 +839,10 @@ error_default: wa_reset_all(wa); } if (seg->status != WA_SEG_ERROR) { - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); } spin_unlock_irqrestore(&xfer->lock, flags); if (holding_dto) { @@ -892,12 +908,11 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb) } if (seg->status != WA_SEG_ERROR) { usb_unlink_urb(seg->dto_urb); - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_ERROR); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -971,12 +986,10 @@ static void wa_seg_tr_cb(struct urb *urb) } usb_unlink_urb(seg->isoc_pack_desc_urb); usb_unlink_urb(seg->dto_urb); - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; __wa_xfer_abort(xfer); rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); @@ -2285,11 +2298,9 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, goto error_submit_buf_in; } else { /* OUT data phase or no data, complete it -- */ - seg->status = WA_SEG_DONE; seg->result = bytes_transferred; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -2453,10 +2464,8 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) dti_busy = 1; } else { /* OUT transfer or no more IN data, complete it -- */ - seg->status = WA_SEG_DONE; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; @@ -2547,12 +2556,11 @@ static void wa_buf_in_cb(struct urb *urb) } } else { rpipe = xfer->ep->hcpriv; - seg->status = WA_SEG_DONE; dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n", xfer, seg->index, seg->result); - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); if (done) @@ -2575,12 +2583,10 @@ static void wa_buf_in_cb(struct urb *urb) "exceeded, resetting device\n"); wa_reset_all(wa); } - seg->status = WA_SEG_ERROR; seg->result = urb->status; - xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); __wa_xfer_abort(xfer); - done = __wa_xfer_is_done(xfer); + done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); -- cgit v1.2.1 From 4659a2452baa7d89324fda097158d7f8fe71e0cb Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 15:15:19 -0600 Subject: usb: wusbcore: adjust iterator correctly when searching for ep comp descriptor If the endpoint companion descriptor is not the first descriptor in the extra descriptor buffer of a usb_host_endpoint, the loop in rpipe_epc_find will get its buffer pointer and remaining size values out of sync. The buffer ptr 'itr' is advanced by the descriptor's bLength field but the remaining size value 'itr_size' is decremented by the bDescriptorType field which is incorrect. This patch fixes the loop to decrement itr_size by bLength as it should. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-rpipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index 6ca80a4efc1b..6d6da127f6de 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -298,7 +298,7 @@ static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find( break; } itr += hdr->bLength; - itr_size -= hdr->bDescriptorType; + itr_size -= hdr->bLength; } out: return epcd; -- cgit v1.2.1 From ecf3701cede840476b012ac4796f77c6dd9ee623 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 15:10:26 -0600 Subject: usb: wusbcore: read actual_length bytes isoc in segments Use the iso_frame_desc.actual_length field instead of length when reading isoc in data segments from the HWA. This fixes a case where the isoc in read URB would never complete because it expected the HWA to send more data than it actually did. When this happened the URB would be stuck in the driver preventing module unload and clean shutdown. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3d6b30d8520e..ff1de5e396a4 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2159,7 +2159,7 @@ static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + xfer->urb->iso_frame_desc[curr_iso_frame].offset; wa->buf_in_urb->transfer_buffer_length = - xfer->urb->iso_frame_desc[curr_iso_frame].length; + xfer->urb->iso_frame_desc[curr_iso_frame].actual_length; wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; wa->buf_in_urb->transfer_buffer = NULL; wa->buf_in_urb->sg = NULL; -- cgit v1.2.1 From d1c5dd6f8edf16d2ed5a9a3d023b6f3f091cc42d Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 15:06:51 -0600 Subject: usb: wusbcore: add info to HWA debug prints This patch adds a debug print in the transfer dequeue case where a transfer result arrives for a transfer that has already been cleaned up. It also adds the transfer ID to some debug prints and prints error codes as signed integers in a couple of others. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index ff1de5e396a4..0636206f75ab 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -369,13 +369,13 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer) break; case WA_SEG_ERROR: xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n", + dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zi(0x%08zX)\n", xfer, wa_xfer_id(xfer), seg->index, seg->result, seg->result); goto out; case WA_SEG_ABORTED: xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zu(0x%08zX)\n", + dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zi(0x%08zX)\n", xfer, wa_xfer_id(xfer), seg->index, seg->result, seg->result); goto out; @@ -2262,7 +2262,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, } if (usb_status & 0x80) { seg->result = wa_xfer_status_to_errno(usb_status); - dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n", + dev_err(dev, "DTI: xfer %p 0x%08X:#%u failed (0x%02x)\n", xfer, xfer->id, seg->index, usb_status); seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ? WA_SEG_ABORTED : WA_SEG_ERROR; @@ -2556,8 +2556,10 @@ static void wa_buf_in_cb(struct urb *urb) } } else { rpipe = xfer->ep->hcpriv; - dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n", - xfer, seg->index, seg->result); + dev_dbg(dev, + "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", + xfer, wa_xfer_id(xfer), seg->index, + seg->result); rpipe_ready = rpipe_avail_inc(rpipe); done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); @@ -2575,8 +2577,9 @@ static void wa_buf_in_cb(struct urb *urb) spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) - dev_err(dev, "xfer %p#%u: data in error %d\n", - xfer, seg->index, urb->status); + dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n", + xfer, wa_xfer_id(xfer), seg->index, + urb->status); if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)){ dev_err(dev, "DTO: URB max acceptable errors " @@ -2670,11 +2673,15 @@ static void wa_dti_cb(struct urb *urb) xfer_result->hdr.bNotifyType); break; } + xfer_id = le32_to_cpu(xfer_result->dwTransferID); usb_status = xfer_result->bTransferStatus & 0x3f; - if (usb_status == WA_XFER_STATUS_NOT_FOUND) + if (usb_status == WA_XFER_STATUS_NOT_FOUND) { /* taken care of already */ + dev_dbg(dev, "%s: xfer 0x%08X#%u not found.\n", + __func__, xfer_id, + xfer_result->bTransferSegment & 0x7f); break; - xfer_id = le32_to_cpu(xfer_result->dwTransferID); + } xfer = wa_xfer_get_by_id(wa, xfer_id); if (xfer == NULL) { /* FIXME: transaction not found. */ -- cgit v1.2.1 From 938569eb75b329fee9f2634900ad7e12caf13fd2 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 27 Feb 2014 10:57:10 +0100 Subject: hub: debug message for failing to enable device This error case isn't reported during enumeration. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 519f2c3594b2..69687de9de67 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4111,8 +4111,12 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, did_new_scheme = true; retval = hub_enable_device(udev); - if (retval < 0) + if (retval < 0) { + dev_err(&udev->dev, + "hub failed to enable device, error %d\n", + retval); goto fail; + } #define GET_DESCRIPTOR_BUFSIZE 64 buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); -- cgit v1.2.1 From 23c058201fe36adf1a225281739b3ec31ec4e858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 27 Feb 2014 14:23:55 +0100 Subject: usb: hub: usb_ext_cap_descriptor.bmAttributes is le32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better check the correct bit on big endian systems too. Shuts up the following sparse __CHECK_ENDIAN__ warning: .../hub.c:3965:32: warning: restricted __le32 degrades to integer Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 69687de9de67..f6c6b6f7cc9c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3962,7 +3962,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) connect_type = usb_get_hub_port_connect_type(udev->parent, udev->portnum); - if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) || + if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) || connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { udev->usb2_hw_lpm_allowed = 1; usb_set_usb2_hardware_lpm(udev, 1); -- cgit v1.2.1 From 927c4dac34cf465089b8d3ff570147ae3ce1a984 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 28 Feb 2014 21:54:37 -0600 Subject: usb: wusbcore: fix compile warnings Fix "pointer targets differ in signedness" and "variable set but not used" warnings Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/devconnect.c | 4 +--- drivers/usb/wusbcore/wa-xfer.c | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index 3b959e83b28e..0677139c6065 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -284,7 +284,7 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, struct device *dev = wusbhc->dev; struct wusb_dev *wusb_dev; struct wusb_port *port; - unsigned idx, devnum; + unsigned idx; mutex_lock(&wusbhc->mutex); @@ -312,8 +312,6 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, goto error_unlock; } - devnum = idx + 2; - /* Make sure we are using no crypto on that "virtual port" */ wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0); diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 0636206f75ab..404ce81b286c 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1007,7 +1007,7 @@ static void wa_seg_tr_cb(struct urb *urb) */ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg, const unsigned int bytes_transferred, - const unsigned int bytes_to_transfer, unsigned int *out_num_sgs) + const unsigned int bytes_to_transfer, int *out_num_sgs) { struct scatterlist *out_sg; unsigned int bytes_processed = 0, offset_into_current_page_data = 0, @@ -1161,14 +1161,13 @@ static int __wa_populate_dto_urb(struct wa_xfer *xfer, */ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) { - int result, cnt, iso_frame_offset; + int result, cnt, isoc_frame_offset = 0; size_t alloc_size = sizeof(*xfer->seg[0]) - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size; struct usb_device *usb_dev = xfer->wa->usb_dev; const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd; struct wa_seg *seg; size_t buf_itr, buf_size, buf_itr_size; - int isoc_frame_offset = 0; result = -ENOMEM; xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC); @@ -1176,7 +1175,6 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) goto error_segs_kzalloc; buf_itr = 0; buf_size = xfer->urb->transfer_buffer_length; - iso_frame_offset = 0; for (cnt = 0; cnt < xfer->segs; cnt++) { size_t iso_pkt_descr_size = 0; int seg_isoc_frame_count = 0, seg_isoc_size = 0; -- cgit v1.2.1 From 5d188d6df3f461788edb86d8ba8f6e6b05203012 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Feb 2014 17:09:53 +0100 Subject: USB: EHCI: tegra: Drop unused defines Since commit 2d22b42db02f "usb: phy: registering Tegra USB PHY as platform driver" the driver no longer relies on the hard-coded physical addresses to determine the association between PHY and EHCI port, so these defines can be dropped. Signed-off-by: Thierry Reding Reviewed-by: Stephen Warren Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index af28b748e87a..27ac6ad53c3d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -38,10 +38,6 @@ #include "ehci.h" -#define TEGRA_USB_BASE 0xC5000000 -#define TEGRA_USB2_BASE 0xC5004000 -#define TEGRA_USB3_BASE 0xC5008000 - #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) #define TEGRA_USB_DMA_ALIGN 32 -- cgit v1.2.1 From 3c1b2c3ecd3122fe6dded7b012f74021144d95b2 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 24 Feb 2014 10:01:16 -0800 Subject: USB: sisusb: Use static const, fix typo Convert 1 char * array to 2 char arrays to reduce size. Use static const to avoid array reloads on function entry. Fix asymmetric typo. $ size drivers/usb/misc/sisusbvga/sisusb.o* text data bss dec hex filename 29971 4841 9180 43992 abd8 drivers/usb/misc/sisusbvga/sisusb.o.new 30083 4841 9180 44104 ac48 drivers/usb/misc/sisusbvga/sisusb.o.old Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/sisusbvga/sisusb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index de98906f786d..06b5d77cd9ad 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2123,8 +2123,8 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) u8 tmp8, tmp82, ramtype; int bw = 0; char *ramtypetext1 = NULL; - const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM", - "DDR SDRAM", "DDR SGRAM" }; + static const char ram_datarate[4] = {'S', 'S', 'D', 'D'}; + static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'}; static const int busSDR[4] = {64, 64, 128, 128}; static const int busDDR[4] = {32, 32, 64, 64}; static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2}; @@ -2156,8 +2156,10 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) break; } - dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1, - ramtypetext2[ramtype], bw); + + dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n", + sisusb->vramsize >> 20, ramtypetext1, + ram_datarate[ramtype], ram_dynamictype[ramtype], bw); } static int -- cgit v1.2.1 From 25cd2882e2fc8bd8ed7acaee0ec979f11feda6d7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 17 Jan 2014 14:15:44 -0800 Subject: usb/xhci: Change how we indicate a host supports Link PM. The xHCI driver currently uses a USB core internal field, udev->lpm_capable, to indicate the xHCI driver knows how to calculate the LPM timeout values. If this value is set for the host controller udev, it means Link PM can be enabled for child devices under that host. Change the code so the xHCI driver isn't mucking with USB core internal fields. Instead, indicate the xHCI driver doesn't support Link PM on this host by clearing the U1 and U2 exit latencies in the roothub SuperSpeed Extended Capabilities BOS descriptor. The code to check for the roothub setting U1 and U2 exit latencies to zero will also disable LPM for external devices that do that same. This was already effectively done with commit ae8963adb4ad8c5f2a89ca1d99fb7bb721e7599f "usb: Don't enable LPM if the exit latency is zero." Leave that code in place, so that if a device sets one exit latency value to zero, but the other is set to a valid value, LPM is only enabled for the U1 or U2 state that had the valid value. This is the same behavior the code had before. Also, change messages about missing Link PM information from warning level to info level. Only print a warning about the first device that doesn't support LPM, to avoid log spam. Further, cleanup some unnecessary line breaks to help people to grep for the error messages. Signed-off-by: Sarah Sharp Cc: Alan Stern --- drivers/usb/core/hub.c | 24 ++++++++++++++++-------- drivers/usb/host/xhci-hub.c | 8 +++++--- drivers/usb/host/xhci-pci.c | 6 ------ 3 files changed, 21 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f6c6b6f7cc9c..763c3134dd78 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -141,19 +141,27 @@ static int usb_device_supports_lpm(struct usb_device *udev) return 0; } - /* All USB 3.0 must support LPM, but we need their max exit latency - * information from the SuperSpeed Extended Capabilities BOS descriptor. + /* + * According to the USB 3.0 spec, all USB 3.0 devices must support LPM. + * However, there are some that don't, and they set the U1/U2 exit + * latencies to zero. */ if (!udev->bos->ss_cap) { - dev_warn(&udev->dev, "No LPM exit latency info found. " - "Power management will be impacted.\n"); + dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n"); return 0; } - if (udev->parent->lpm_capable) - return 1; - dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. " - "Power management will be impacted.\n"); + if (udev->bos->ss_cap->bU1devExitLat == 0 && + udev->bos->ss_cap->bU2DevExitLat == 0) { + if (udev->parent) + dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n"); + else + dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n"); + return 0; + } + + if (!udev->parent || udev->parent->lpm_capable) + return 1; return 0; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 9992fbfec85f..1ad6bc1951c7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -732,9 +732,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Set the U1 and U2 exit latencies. */ memcpy(buf, &usb_bos_descriptor, USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); - temp = readl(&xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + temp = readl(&xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + } /* Indicate whether the host has LTM support. */ temp = readl(&xhci->cap_regs->hcc_params); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 04f986d9234f..6c03584ac15f 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -222,12 +222,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ - /* We know the LPM timeout algorithms for this host, let the USB core - * enable and disable LPM for devices under the USB 3.0 roothub. - */ - if (xhci->quirks & XHCI_LPM_SUPPORT) - hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1; - return 0; put_usb3_hcd: -- cgit v1.2.1 From e587b8b270d3706147c806d42cc4ac78232caac7 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 8 Jan 2014 17:13:11 +0100 Subject: xhci: make warnings greppable This changes debug messages and warnings in xhci-ring.c to be on a single line so grep can find them. grep must have precedence over the 80 column limit. [Sarah fixed two checkpatch.pl issues with split lines introduced by this commit.] Signed-off-by: Oliver Neukum Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0ed64eb68e48..3ec1d8fe06fa 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1082,8 +1082,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); if (!ep_ring) { - xhci_warn(xhci, "WARN Set TR deq ptr command for " - "freed stream ID %u\n", + xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n", stream_id); /* XXX: Harmless??? */ dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; @@ -1099,12 +1098,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, switch (cmd_comp_code) { case COMP_TRB_ERR: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " - "of stream ID configuration\n"); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n"); break; case COMP_CTX_STATE: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due " - "to incorrect slot or ep state.\n"); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n"); ep_state = le32_to_cpu(ep_ctx->ep_info); ep_state &= EP_STATE_MASK; slot_state = le32_to_cpu(slot_ctx->dev_state); @@ -1114,13 +1111,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, slot_state, ep_state); break; case COMP_EBADSLT: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because " - "slot %u was not enabled.\n", slot_id); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n", + slot_id); break; default: - xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " - "completion code of %u.\n", - cmd_comp_code); + xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown completion code of %u.\n", + cmd_comp_code); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -1142,8 +1138,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, update_ring_for_set_deq_completion(xhci, dev, ep_ring, ep_index); } else { - xhci_warn(xhci, "Mismatch between completed Set TR Deq " - "Ptr command & xHCI internal state.\n"); + xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", dev->eps[ep_index].queued_deq_seg, dev->eps[ep_index].queued_deq_ptr); -- cgit v1.2.1 From 153413032c6ea624fccc6732aba27a57688a7f91 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 4 Oct 2013 00:29:44 +0200 Subject: xhci: fix usb3 streams xhci maintains a radix tree for each stream endpoint because it must be able to map a trb address to the stream ring. Each ring segment must be added to the ring for this to work. Currently xhci sticks only the first segment of each stream ring into the radix tree. Result is that things work initially, but as soon as the first segment is full xhci can't map the trb address from the completion event to the stream ring any more -> BOOM. You'll find this message in the logs: ERROR Transfer event for disabled endpoint or incorrect stream ring This patch adds a helper function to update the radix tree, and a function to remove ring segments from the tree. Both functions loop over the segment list and handles all segments instead of just the first. [Note: Sarah changed this patch to add radix_tree_maybe_preload() and radix_tree_preload_end() calls around the radix tree insert, since we can now insert entries in interrupt context. There are now two helper functions to make the code cleaner, and those functions are moved to make them static.] Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 132 +++++++++++++++++++++++++++++--------------- drivers/usb/host/xhci.h | 1 + 2 files changed, 90 insertions(+), 43 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bce4391a0e7d..39f9a99a503b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -149,14 +149,95 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, } } +/* + * We need a radix tree for mapping physical addresses of TRBs to which stream + * ID they belong to. We need to do this because the host controller won't tell + * us which stream ring the TRB came from. We could store the stream ID in an + * event data TRB, but that doesn't help us for the cancellation case, since the + * endpoint may stop before it reaches that event data TRB. + * + * The radix tree maps the upper portion of the TRB DMA address to a ring + * segment that has the same upper portion of DMA addresses. For example, say I + * have segments of size 1KB, that are always 64-byte aligned. A segment may + * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the + * key to the stream ID is 0x43244. I can use the DMA address of the TRB to + * pass the radix tree a key to get the right stream ID: + * + * 0x10c90fff >> 10 = 0x43243 + * 0x10c912c0 >> 10 = 0x43244 + * 0x10c91400 >> 10 = 0x43245 + * + * Obviously, only those TRBs with DMA addresses that are within the segment + * will make the radix tree return the stream ID for that ring. + * + * Caveats for the radix tree: + * + * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an + * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be + * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the + * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit + * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit + * extended systems (where the DMA address can be bigger than 32-bits), + * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. + */ +static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) +{ + struct xhci_segment *seg; + unsigned long key; + int ret; + + if (WARN_ON_ONCE(ring->trb_address_map == NULL)) + return 0; + + seg = ring->first_seg; + do { + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + /* Skip any segments that were already added. */ + if (radix_tree_lookup(ring->trb_address_map, key)) + continue; + + ret = radix_tree_maybe_preload(mem_flags); + if (ret) + return ret; + ret = radix_tree_insert(ring->trb_address_map, + key, ring); + radix_tree_preload_end(); + if (ret) + return ret; + seg = seg->next; + } while (seg != ring->first_seg); + + return 0; +} + +static void xhci_remove_stream_mapping(struct xhci_ring *ring) +{ + struct xhci_segment *seg; + unsigned long key; + + if (WARN_ON_ONCE(ring->trb_address_map == NULL)) + return; + + seg = ring->first_seg; + do { + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + if (radix_tree_lookup(ring->trb_address_map, key)) + radix_tree_delete(ring->trb_address_map, key); + seg = seg->next; + } while (seg != ring->first_seg); +} + /* XXX: Do we need the hcd structure in all these functions? */ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { if (!ring) return; - if (ring->first_seg) + if (ring->first_seg) { + if (ring->type == TYPE_STREAM) + xhci_remove_stream_mapping(ring); xhci_free_segments_for_ring(xhci, ring->first_seg); + } kfree(ring); } @@ -354,6 +435,11 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, "ring expansion succeed, now has %d segments", ring->num_segs); + if (ring->type == TYPE_STREAM) { + ret = xhci_update_stream_mapping(ring, flags); + WARN_ON(ret); /* FIXME */ + } + return 0; } @@ -510,36 +596,6 @@ struct xhci_ring *xhci_stream_id_to_ring( * The number of stream contexts in the stream context array may be bigger than * the number of streams the driver wants to use. This is because the number of * stream context array entries must be a power of two. - * - * We need a radix tree for mapping physical addresses of TRBs to which stream - * ID they belong to. We need to do this because the host controller won't tell - * us which stream ring the TRB came from. We could store the stream ID in an - * event data TRB, but that doesn't help us for the cancellation case, since the - * endpoint may stop before it reaches that event data TRB. - * - * The radix tree maps the upper portion of the TRB DMA address to a ring - * segment that has the same upper portion of DMA addresses. For example, say I - * have segments of size 1KB, that are always 64-byte aligned. A segment may - * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the - * key to the stream ID is 0x43244. I can use the DMA address of the TRB to - * pass the radix tree a key to get the right stream ID: - * - * 0x10c90fff >> 10 = 0x43243 - * 0x10c912c0 >> 10 = 0x43244 - * 0x10c91400 >> 10 = 0x43245 - * - * Obviously, only those TRBs with DMA addresses that are within the segment - * will make the radix tree return the stream ID for that ring. - * - * Caveats for the radix tree: - * - * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an - * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be - * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the - * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit - * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit - * extended systems (where the DMA address can be bigger than 32-bits), - * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. */ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, @@ -548,7 +604,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, struct xhci_stream_info *stream_info; u32 cur_stream; struct xhci_ring *cur_ring; - unsigned long key; u64 addr; int ret; @@ -603,6 +658,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, if (!cur_ring) goto cleanup_rings; cur_ring->stream_id = cur_stream; + cur_ring->trb_address_map = &stream_info->trb_address_map; /* Set deq ptr, cycle bit, and stream context type */ addr = cur_ring->first_seg->dma | SCT_FOR_CTX(SCT_PRI_TR) | @@ -612,10 +668,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, (unsigned long long) addr); - key = (unsigned long) - (cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT); - ret = radix_tree_insert(&stream_info->trb_address_map, - key, cur_ring); + ret = xhci_update_stream_mapping(cur_ring, mem_flags); if (ret) { xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; @@ -635,9 +688,6 @@ cleanup_rings: for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { cur_ring = stream_info->stream_rings[cur_stream]; if (cur_ring) { - addr = cur_ring->first_seg->dma; - radix_tree_delete(&stream_info->trb_address_map, - addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } @@ -698,7 +748,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, { int cur_stream; struct xhci_ring *cur_ring; - dma_addr_t addr; if (!stream_info) return; @@ -707,9 +756,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, cur_stream++) { cur_ring = stream_info->stream_rings[cur_stream]; if (cur_ring) { - addr = cur_ring->first_seg->dma; - radix_tree_delete(&stream_info->trb_address_map, - addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 58ed9d088e63..a6aa98f19a1f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1341,6 +1341,7 @@ struct xhci_ring { unsigned int num_trbs_free_temp; enum xhci_ring_type type; bool last_td_was_short; + struct radix_tree_root *trb_address_map; }; struct xhci_erst_entry { -- cgit v1.2.1 From df6138347bcde316e4d4b24ae4d9d0296461c79a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 00:29:45 +0200 Subject: xhci: Free streams when they are still allocated on a set_interface call And warn about this, as that would be a driver bug. Like wise drivers should ensure that streams are properly free-ed before a device is reset. So lets warn about that too. This already causes warnings in the form of: [ 96.982398] xhci_hcd 0000:01:00.0: WARN Can't disable streams for endpoint 0x81 , streams are already disabled! [ 96.982400] xhci_hcd 0000:01:00.0: WARN xhci_free_streams() called with non-streams endpoint But it is better to also warn about the actual cause of this later warnings. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6fe577d46fa2..c3fa32a905ec 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2678,6 +2678,20 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, return ret; } +static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci, + struct xhci_virt_device *vdev, int i) +{ + struct xhci_virt_ep *ep = &vdev->eps[i]; + + if (ep->ep_state & EP_HAS_STREAMS) { + xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n", + xhci_get_endpoint_address(i)); + xhci_free_stream_info(xhci, ep->stream_info); + ep->stream_info = NULL; + ep->ep_state &= ~EP_HAS_STREAMS; + } +} + /* Called after one or more calls to xhci_add_endpoint() or * xhci_drop_endpoint(). If this call fails, the USB core is expected * to call xhci_reset_bandwidth(). @@ -2742,8 +2756,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* Free any rings that were dropped, but not changed. */ for (i = 1; i < 31; ++i) { if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) && - !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) + !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) { xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + xhci_check_bw_drop_ep_streams(xhci, virt_dev, i); + } } xhci_zero_in_ctx(xhci, virt_dev); /* @@ -2759,6 +2775,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) if (virt_dev->eps[i].ring) { xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); } + xhci_check_bw_drop_ep_streams(xhci, virt_dev, i); virt_dev->eps[i].ring = virt_dev->eps[i].new_ring; virt_dev->eps[i].new_ring = NULL; } @@ -3519,6 +3536,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_ep *ep = &virt_dev->eps[i]; if (ep->ep_state & EP_HAS_STREAMS) { + xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n", + xhci_get_endpoint_address(i)); xhci_free_stream_info(xhci, ep->stream_info); ep->stream_info = NULL; ep->ep_state &= ~EP_HAS_STREAMS; -- cgit v1.2.1 From ee4aa54bce2ae27c36998da3d32d9f55cb48ca21 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 00:29:46 +0200 Subject: xhci: Check size rather then number of streams when allocating stream ctxs Before this a device needing ie 32 stream ctxs would end up with an entry from the small_streams_pool which has 256 bytes entries, where as 32 stream ctxs need 512 bytes. Things actually keep running for a surprisingly long time before crashing because of this. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 39f9a99a503b..be7b7b6b5517 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -520,12 +520,12 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, struct xhci_stream_ctx *stream_ctx, dma_addr_t dma) { struct device *dev = xhci_to_hcd(xhci)->self.controller; + size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs; - if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - dma_free_coherent(dev, - sizeof(struct xhci_stream_ctx)*num_stream_ctxs, + if (size > MEDIUM_STREAM_ARRAY_SIZE) + dma_free_coherent(dev, size, stream_ctx, dma); - else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) + else if (size <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_free(xhci->small_streams_pool, stream_ctx, dma); else @@ -548,12 +548,12 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, gfp_t mem_flags) { struct device *dev = xhci_to_hcd(xhci)->self.controller; + size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs; - if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return dma_alloc_coherent(dev, - sizeof(struct xhci_stream_ctx)*num_stream_ctxs, + if (size > MEDIUM_STREAM_ARRAY_SIZE) + return dma_alloc_coherent(dev, size, dma, mem_flags); - else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) + else if (size <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_alloc(xhci->small_streams_pool, mem_flags, dma); else -- cgit v1.2.1 From c4bedb77ec4cb42f37cae4cbfddda8283161f7c8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 00:29:47 +0200 Subject: xhci: For streams the css flag most be read from the stream-ctx on ep stop Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3ec1d8fe06fa..51c5109bbefc 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -546,9 +546,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_dequeue_state *state) { struct xhci_virt_device *dev = xhci->devs[slot_id]; + struct xhci_virt_ep *ep = &dev->eps[ep_index]; struct xhci_ring *ep_ring; struct xhci_generic_trb *trb; - struct xhci_ep_ctx *ep_ctx; dma_addr_t addr; ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, @@ -573,8 +573,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, /* Dig out the cycle state saved by the xHC during the stop ep cmd */ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Finding endpoint context"); - ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); - state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); + /* 4.6.9 the css flag is written to the stream context for streams */ + if (ep->ep_state & EP_HAS_STREAMS) { + struct xhci_stream_ctx *ctx = + &ep->stream_info->stream_ctx_array[stream_id]; + state->new_cycle_state = 0x1 & le64_to_cpu(ctx->stream_ring); + } else { + struct xhci_ep_ctx *ep_ctx + = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); + state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); + } state->new_deq_ptr = cur_td->last_trb; xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, -- cgit v1.2.1 From 95241dbdf8281ece1355b8673b882d6a182f3c7d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 00:29:48 +0200 Subject: xhci: Set SCT field for Set TR dequeue on streams Nec XHCI controllers don't seem to care, but without this Intel XHCI controllers reject Set TR dequeue commands with a COMP_TRB_ERR, leading to the following warning: WARN Set TR Deq Ptr cmd invalid because of stream ID configuration And very shortly after this the system completely freezes. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 5 ++++- drivers/usb/host/xhci.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 51c5109bbefc..f17e5c8c1cb3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -4073,6 +4073,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id); + u32 trb_sct = 0; u32 type = TRB_TYPE(TRB_SET_DEQ); struct xhci_virt_ep *ep; @@ -4091,7 +4092,9 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, } ep->queued_deq_seg = deq_seg; ep->queued_deq_ptr = deq_ptr; - return queue_command(xhci, lower_32_bits(addr) | cycle_state, + if (stream_id) + trb_sct = SCT_FOR_TRB(SCT_PRI_TR); + return queue_command(xhci, lower_32_bits(addr) | trb_sct | cycle_state, upper_32_bits(addr), trb_stream_id, trb_slot_id | trb_ep_index | type, false); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index a6aa98f19a1f..2c77bf7ab9e5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1118,9 +1118,10 @@ enum xhci_setup_dev { #define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) #define LAST_EP_INDEX 30 -/* Set TR Dequeue Pointer command TRB fields */ +/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */ #define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) #define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) +#define SCT_FOR_TRB(p) (((p) << 1) & 0x7) /* Port Status Change Event TRB fields */ -- cgit v1.2.1 From 9aad95e292f58d00aa0f2e30c7f7dafd7fc7491c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 00:29:49 +0200 Subject: xhci: For streams the dequeue ptr must be read from the stream ctx This fixes TR dequeue validation failing on Intel XHCI controllers with the following warning: Mismatch between completed Set TR Deq Ptr command & xHCI internal state. Interestingly enough reading the deq ptr from the ep ctx after a TR Deq Ptr command does work on a Nec XHCI controller, it seems the Nec writes the ptr to both the ep and stream contexts when streams are used. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 22 +++++++++++++++------- drivers/usb/host/xhci.h | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f17e5c8c1cb3..b3d27e6467ea 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1081,12 +1081,14 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, unsigned int stream_id; struct xhci_ring *ep_ring; struct xhci_virt_device *dev; + struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; + ep = &dev->eps[ep_index]; ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); if (!ep_ring) { @@ -1134,12 +1136,19 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, * cancelling URBs, which might not be an error... */ } else { + u64 deq; + /* 4.6.10 deq ptr is written to the stream ctx for streams */ + if (ep->ep_state & EP_HAS_STREAMS) { + struct xhci_stream_ctx *ctx = + &ep->stream_info->stream_ctx_array[stream_id]; + deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + } else { + deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; + } xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Successful Set TR Deq Ptr cmd, deq = @%08llx", - le64_to_cpu(ep_ctx->deq)); - if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr) == - (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) { + "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq); + if (xhci_trb_virt_to_dma(ep->queued_deq_seg, + ep->queued_deq_ptr) == deq) { /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ @@ -1148,8 +1157,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, } else { xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", - dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr); + ep->queued_deq_seg, ep->queued_deq_ptr); } } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2c77bf7ab9e5..d280e9213d08 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -703,6 +703,7 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) +#define SCTX_DEQ_MASK (~0xfL) /** -- cgit v1.2.1 From a3901538611f8d5180df44f5b61293e70903958f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Oct 2013 17:05:55 +0200 Subject: xhci: use usb_ss_max_streams in xhci_check_streams_endpoint The ss_ep_comp bmAttributes filed can contain more info then just the streams, use usb_ss_max_streams to properly get max streams. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c3fa32a905ec..6cb9c2235a0b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2971,7 +2971,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__); if (ret <= 0) return -EINVAL; - if (ep->ss_ep_comp.bmAttributes == 0) { + if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) { xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion" " descriptor for ep 0x%x does not support streams\n", ep->desc.bEndpointAddress); -- cgit v1.2.1 From d57342232db459bf820b90e39496190965d7a775 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 17 Oct 2013 12:44:58 -0700 Subject: xhci: Remove segments from radix tree on failed insert. If we're expanding a stream ring, we want to make sure we can add those ring segments to the radix tree that maps segments to ring pointers. Try the radix tree insert after the new ring segments have been allocated (the last segment in the new ring chunk will point to the first newly allocated segment), but before the new ring segments are linked into the old ring. If insert fails on any one segment, remove each segment from the radix tree, deallocate the new segments, and return. Otherwise, link the new segments into the tree. HdG: Add a check to only update stream mappings in xhci_ring_expansion when the ring is a stream ring. Signed-off-by: Sarah Sharp Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 107 +++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 26 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index be7b7b6b5517..edfb31ad5949 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -180,53 +180,98 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, * extended systems (where the DMA address can be bigger than 32-bits), * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that. */ -static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) +static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map, + struct xhci_ring *ring, + struct xhci_segment *seg, + gfp_t mem_flags) { - struct xhci_segment *seg; unsigned long key; int ret; - if (WARN_ON_ONCE(ring->trb_address_map == NULL)) + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + /* Skip any segments that were already added. */ + if (radix_tree_lookup(trb_address_map, key)) return 0; - seg = ring->first_seg; - do { - key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); - /* Skip any segments that were already added. */ - if (radix_tree_lookup(ring->trb_address_map, key)) - continue; + ret = radix_tree_maybe_preload(mem_flags); + if (ret) + return ret; + ret = radix_tree_insert(trb_address_map, + key, ring); + radix_tree_preload_end(); + return ret; +} - ret = radix_tree_maybe_preload(mem_flags); - if (ret) - return ret; - ret = radix_tree_insert(ring->trb_address_map, - key, ring); - radix_tree_preload_end(); +static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map, + struct xhci_segment *seg) +{ + unsigned long key; + + key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); + if (radix_tree_lookup(trb_address_map, key)) + radix_tree_delete(trb_address_map, key); +} + +static int xhci_update_stream_segment_mapping( + struct radix_tree_root *trb_address_map, + struct xhci_ring *ring, + struct xhci_segment *first_seg, + struct xhci_segment *last_seg, + gfp_t mem_flags) +{ + struct xhci_segment *seg; + struct xhci_segment *failed_seg; + int ret; + + if (WARN_ON_ONCE(trb_address_map == NULL)) + return 0; + + seg = first_seg; + do { + ret = xhci_insert_segment_mapping(trb_address_map, + ring, seg, mem_flags); if (ret) - return ret; + goto remove_streams; + if (seg == last_seg) + return 0; seg = seg->next; - } while (seg != ring->first_seg); + } while (seg != first_seg); return 0; + +remove_streams: + failed_seg = seg; + seg = first_seg; + do { + xhci_remove_segment_mapping(trb_address_map, seg); + if (seg == failed_seg) + return ret; + seg = seg->next; + } while (seg != first_seg); + + return ret; } static void xhci_remove_stream_mapping(struct xhci_ring *ring) { struct xhci_segment *seg; - unsigned long key; if (WARN_ON_ONCE(ring->trb_address_map == NULL)) return; seg = ring->first_seg; do { - key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT); - if (radix_tree_lookup(ring->trb_address_map, key)) - radix_tree_delete(ring->trb_address_map, key); + xhci_remove_segment_mapping(ring->trb_address_map, seg); seg = seg->next; } while (seg != ring->first_seg); } +static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) +{ + return xhci_update_stream_segment_mapping(ring->trb_address_map, ring, + ring->first_seg, ring->last_seg, mem_flags); +} + /* XXX: Do we need the hcd structure in all these functions? */ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { @@ -430,16 +475,26 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, if (ret) return -ENOMEM; + if (ring->type == TYPE_STREAM) + ret = xhci_update_stream_segment_mapping(ring->trb_address_map, + ring, first, last, flags); + if (ret) { + struct xhci_segment *next; + do { + next = first->next; + xhci_segment_free(xhci, first); + if (first == last) + break; + first = next; + } while (true); + return ret; + } + xhci_link_rings(xhci, ring, first, last, num_segs); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", ring->num_segs); - if (ring->type == TYPE_STREAM) { - ret = xhci_update_stream_mapping(ring, flags); - WARN_ON(ret); /* FIXME */ - } - return 0; } -- cgit v1.2.1 From 12d4bbcea727710bbd04de3e1de05957a0675e60 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:23 +0200 Subject: usb-core: Fix usb_free_streams return value documentation Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2518c3250750..9b445f43f825 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2078,8 +2078,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams); * Reverts a group of bulk endpoints back to not using stream IDs. * Can fail if we are given bad arguments, or HCD is broken. * - * Return: On success, the number of allocated streams. On failure, a negative - * error code. + * Return: 0 on success. On failure, a negative error code. */ int usb_free_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, -- cgit v1.2.1 From 8f5d35441ff26b31e3812556ce468c76f1eb216b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:24 +0200 Subject: usb-core: Move USB_MAXENDPOINTS definitions to usb.h So that it can be used in other places too. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/config.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 8d72f0c65937..14ba398d6def 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -10,7 +10,6 @@ #define USB_MAXALTSETTING 128 /* Hard limit */ -#define USB_MAXENDPOINTS 30 /* Hard limit */ #define USB_MAXCONFIG 8 /* Arbitrary limit */ -- cgit v1.2.1 From 8d4f70b2fa52ca80f74faebc2471f74ee374cf61 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:25 +0200 Subject: usb-core: Track if an endpoint has streams This is a preparation patch for adding support for bulk streams to usbfs. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/hcd.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9b445f43f825..9c4e2922b04d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface, { struct usb_hcd *hcd; struct usb_device *dev; - int i; + int i, ret; dev = interface_to_usbdev(interface); hcd = bus_to_hcd(dev->bus); @@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface, if (dev->speed != USB_SPEED_SUPER) return -EINVAL; - /* Streams only apply to bulk endpoints. */ - for (i = 0; i < num_eps; i++) + for (i = 0; i < num_eps; i++) { + /* Streams only apply to bulk endpoints. */ if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) return -EINVAL; + /* Re-alloc is not allowed */ + if (eps[i]->streams) + return -EINVAL; + } - return hcd->driver->alloc_streams(hcd, dev, eps, num_eps, + ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps, num_streams, mem_flags); + if (ret < 0) + return ret; + + for (i = 0; i < num_eps; i++) + eps[i]->streams = ret; + + return ret; } EXPORT_SYMBOL_GPL(usb_alloc_streams); @@ -2086,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface, { struct usb_hcd *hcd; struct usb_device *dev; - int i; + int i, ret; dev = interface_to_usbdev(interface); hcd = bus_to_hcd(dev->bus); if (dev->speed != USB_SPEED_SUPER) return -EINVAL; - /* Streams only apply to bulk endpoints. */ + /* Double-free is not allowed */ for (i = 0; i < num_eps; i++) - if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc)) + if (!eps[i] || !eps[i]->streams) return -EINVAL; - return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); + ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); + if (ret < 0) + return ret; + + for (i = 0; i < num_eps; i++) + eps[i]->streams = 0; + + return ret; } EXPORT_SYMBOL_GPL(usb_free_streams); -- cgit v1.2.1 From 6343e8bf09de16ab4dcae2c6ca1a0e8dbd4dd770 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:26 +0200 Subject: usb-core: Free bulk streams on interface release Documentation/usb/bulk-streams.txt says: All stream IDs will be deallocated when the driver releases the interface, to ensure that drivers that don't support streams will be able to use the endpoint This commit actually implements this. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/driver.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 85e0450a2bc7..08283d40616c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev) { struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); + struct usb_host_endpoint *ep, **eps = NULL; struct usb_device *udev; - int error, r, lpm_disable_error; + int i, j, error, r, lpm_disable_error; intf->condition = USB_INTERFACE_UNBINDING; @@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev) driver->disconnect(intf); usb_cancel_queued_reset(intf); + /* Free streams */ + for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep = &intf->cur_altsetting->endpoint[i]; + if (ep->streams == 0) + continue; + if (j == 0) { + eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *), + GFP_KERNEL); + if (!eps) { + dev_warn(dev, "oom, leaking streams\n"); + break; + } + } + eps[j++] = ep; + } + if (j) { + usb_free_streams(intf, eps, j, GFP_KERNEL); + kfree(eps); + } + /* Reset other interface state. * We cannot do a Set-Interface if the device is suspended or * if it is prepared for a system sleep (since installing a new -- cgit v1.2.1 From 5ec9c1771ce83a1e2b7ec96ed9f29a9f1b25e71e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:27 +0200 Subject: usbfs: Kill urbs on interface before doing a set_interface The usb_set_interface documentation says: * Also, drivers must not change altsettings while urbs are scheduled for * endpoints in that interface; all such urbs must first be completed * (perhaps forced by unlinking). For in kernel drivers we trust the drivers to get this right, but we cannot trust userspace to get this right, so enforce it by killing any urbs still pending on the interface. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/devio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index f3ba2e076ee3..2a95e4e574bb 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1143,6 +1143,9 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) return -EFAULT; if ((ret = checkintf(ps, setintf.interface))) return ret; + + destroy_async_on_interface(ps, setintf.interface); + return usb_set_interface(ps->dev, setintf.interface, setintf.altsetting); } -- cgit v1.2.1 From b2d03eb56e66620a9b27f1a0c2795722087effc9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:28 +0200 Subject: usbfs: proc_do_submiturb use a local variable for number_of_packets This is a preparation patch for adding support for bulk streams. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/devio.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 2a95e4e574bb..c88d8bfaca8d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1208,6 +1208,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; int i, ret, is_in, num_sgs = 0, ifnum = -1; + int number_of_packets = 0; void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | @@ -1261,7 +1262,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, le16_to_cpup(&dr->wIndex)); if (ret) goto error; - uurb->number_of_packets = 0; uurb->buffer_length = le16_to_cpup(&dr->wLength); uurb->buffer += 8; if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) { @@ -1291,7 +1291,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->type = USBDEVFS_URB_TYPE_INTERRUPT; goto interrupt_urb; } - uurb->number_of_packets = 0; num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) num_sgs = 0; @@ -1301,7 +1300,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if (!usb_endpoint_xfer_int(&ep->desc)) return -EINVAL; interrupt_urb: - uurb->number_of_packets = 0; break; case USBDEVFS_URB_TYPE_ISO: @@ -1311,15 +1309,16 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!usb_endpoint_xfer_isoc(&ep->desc)) return -EINVAL; + number_of_packets = uurb->number_of_packets; isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * - uurb->number_of_packets; + number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { ret = -EFAULT; goto error; } - for (totlen = u = 0; u < uurb->number_of_packets; u++) { + for (totlen = u = 0; u < number_of_packets; u++) { /* * arbitrary limit need for USB 3.0 * bMaxBurst (0~15 allowed, 1~16 packets) @@ -1350,7 +1349,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -EFAULT; goto error; } - as = alloc_async(uurb->number_of_packets); + as = alloc_async(number_of_packets); if (!as) { ret = -ENOMEM; goto error; @@ -1444,7 +1443,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->urb->setup_packet = (unsigned char *)dr; dr = NULL; as->urb->start_frame = uurb->start_frame; - as->urb->number_of_packets = uurb->number_of_packets; + as->urb->number_of_packets = number_of_packets; if (uurb->type == USBDEVFS_URB_TYPE_ISO || ps->dev->speed == USB_SPEED_HIGH) as->urb->interval = 1 << min(15, ep->desc.bInterval - 1); @@ -1452,7 +1451,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->urb->interval = ep->desc.bInterval; as->urb->context = as; as->urb->complete = async_completed; - for (totlen = u = 0; u < uurb->number_of_packets; u++) { + for (totlen = u = 0; u < number_of_packets; u++) { as->urb->iso_frame_desc[u].offset = totlen; as->urb->iso_frame_desc[u].length = isopkt[u].length; totlen += isopkt[u].length; -- cgit v1.2.1 From 948cd8c18c466fdcbe707bb2a42a148796bfccdd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:29 +0200 Subject: usbfs: Add support for bulk stream ids This patch makes it possible to specify a bulk stream id when submitting an urb using the async usbfs API. It overloads the number_of_packets usbdevfs_urb field for this. This is not pretty, but given other constraints it is the best we can do. The reasoning leading to this goes as follows: 1) We want to support bulk streams in the usbfs API 2) We do not want to extend the usbdevfs_urb struct with a new member, as that would mean defining new ioctl numbers for all async API ioctls + adding compat versions for the old ones (times 2 for 32 bit support) 3) 1 + 2 means we need to re-use an existing field 4) number_of_packets is only used for isoc urbs, and streams are bulk only so it is the best (and only) candidate for re-using Note that: 1) This patch only uses number_of_packets as stream_id if the app has actually allocated streams on the ep, so that old apps which may have garbage in there (as it was unused until now in the bulk case), will not break 2) This patch does not add support for allocating / freeing bulk-streams, that is done in a follow up patch Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/devio.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index c88d8bfaca8d..d7571a63181d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1209,6 +1209,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, unsigned int u, totlen, isofrmlen; int i, ret, is_in, num_sgs = 0, ifnum = -1; int number_of_packets = 0; + unsigned int stream_id = 0; void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | @@ -1294,6 +1295,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) num_sgs = 0; + if (ep->streams) + stream_id = uurb->stream_id; break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1444,6 +1447,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, dr = NULL; as->urb->start_frame = uurb->start_frame; as->urb->number_of_packets = number_of_packets; + as->urb->stream_id = stream_id; if (uurb->type == USBDEVFS_URB_TYPE_ISO || ps->dev->speed == USB_SPEED_HIGH) as->urb->interval = 1 << min(15, ep->desc.bInterval - 1); -- cgit v1.2.1 From 2fec32b06e374642802f7fb4f5350317cd14732b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:30 +0200 Subject: usbfs: Add ep_to_host_endpoint helper function Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/devio.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d7571a63181d..502974b4deb5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -769,6 +769,15 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, return ret; } +static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev, + unsigned char ep) +{ + if (ep & USB_ENDPOINT_DIR_MASK) + return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK]; + else + return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; +} + static int match_devt(struct device *dev, void *data) { return dev->devt == (dev_t) (unsigned long) data; @@ -1230,15 +1239,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if (ret) return ret; } - if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) { - is_in = 1; - ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; - } else { - is_in = 0; - ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; - } + ep = ep_to_host_endpoint(ps->dev, uurb->endpoint); if (!ep) return -ENOENT; + is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0; u = 0; switch(uurb->type) { -- cgit v1.2.1 From bcf7f6e39335af4f03da8c26a98185fd49754fcc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Oct 2013 17:19:31 +0200 Subject: usbfs: Add support for allocating / freeing streams This allows userspace to use bulk-streams, just like in kernel drivers, see Documentation/usb/bulk-streams.txt for details on the in kernel API. This is exported pretty much one on one to userspace. To use streams an app must first make a USBDEVFS_ALLOC_STREAMS ioctl, on success this will return the number of streams available (which may be less then requested). If there are n streams the app can then submit usbdevfs_urb-s with their stream_id member set to 1-n to use a specific stream. IE if USBDEVFS_ALLOC_STREAMS returns 4 then stream_id 1-4 can be used. When the app is done using streams it should call USBDEVFS_FREE_STREAMS Note applications are advised to use libusb rather then using the usbdevfs api directly. The latest version of libusb has support for streams. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/devio.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 502974b4deb5..12401ee4ba0e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -778,6 +778,79 @@ static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev, return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; } +static int parse_usbdevfs_streams(struct dev_state *ps, + struct usbdevfs_streams __user *streams, + unsigned int *num_streams_ret, + unsigned int *num_eps_ret, + struct usb_host_endpoint ***eps_ret, + struct usb_interface **intf_ret) +{ + unsigned int i, num_streams, num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf = NULL; + unsigned char ep; + int ifnum, ret; + + if (get_user(num_streams, &streams->num_streams) || + get_user(num_eps, &streams->num_eps)) + return -EFAULT; + + if (num_eps < 1 || num_eps > USB_MAXENDPOINTS) + return -EINVAL; + + /* The XHCI controller allows max 2 ^ 16 streams */ + if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) + return -EINVAL; + + eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL); + if (!eps) + return -ENOMEM; + + for (i = 0; i < num_eps; i++) { + if (get_user(ep, &streams->eps[i])) { + ret = -EFAULT; + goto error; + } + eps[i] = ep_to_host_endpoint(ps->dev, ep); + if (!eps[i]) { + ret = -EINVAL; + goto error; + } + + /* usb_alloc/free_streams operate on an usb_interface */ + ifnum = findintfep(ps->dev, ep); + if (ifnum < 0) { + ret = ifnum; + goto error; + } + + if (i == 0) { + ret = checkintf(ps, ifnum); + if (ret < 0) + goto error; + intf = usb_ifnum_to_if(ps->dev, ifnum); + } else { + /* Verify all eps belong to the same interface */ + if (ifnum != intf->altsetting->desc.bInterfaceNumber) { + ret = -EINVAL; + goto error; + } + } + } + + if (num_streams_ret) + *num_streams_ret = num_streams; + *num_eps_ret = num_eps; + *eps_ret = eps; + *intf_ret = intf; + + return 0; + +error: + kfree(eps); + return ret; +} + static int match_devt(struct device *dev, void *data) { return dev->devt == (dev_t) (unsigned long) data; @@ -2009,6 +2082,45 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) return claimintf(ps, dc.interface); } +static int proc_alloc_streams(struct dev_state *ps, void __user *arg) +{ + unsigned num_streams, num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf; + int r; + + r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps, + &eps, &intf); + if (r) + return r; + + destroy_async_on_interface(ps, + intf->altsetting[0].desc.bInterfaceNumber); + + r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL); + kfree(eps); + return r; +} + +static int proc_free_streams(struct dev_state *ps, void __user *arg) +{ + unsigned num_eps; + struct usb_host_endpoint **eps; + struct usb_interface *intf; + int r; + + r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf); + if (r) + return r; + + destroy_async_on_interface(ps, + intf->altsetting[0].desc.bInterfaceNumber); + + r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL); + kfree(eps); + return r; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -2185,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_DISCONNECT_CLAIM: ret = proc_disconnect_claim(ps, p); break; + case USBDEVFS_ALLOC_STREAMS: + ret = proc_alloc_streams(ps, p); + break; + case USBDEVFS_FREE_STREAMS: + ret = proc_free_streams(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) -- cgit v1.2.1 From d89bd835326947e6618b97469159d3266016fe0a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Sep 2013 13:27:11 +0200 Subject: uas: properly reinitialize in uas_eh_bus_reset_handler Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d966b59f7d7b..fc08ee919439 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -85,6 +85,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp); static void uas_do_work(struct work_struct *work); static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); +static void uas_configure_endpoints(struct uas_dev_info *devinfo); +static void uas_free_streams(struct uas_dev_info *devinfo); static DECLARE_WORK(uas_work, uas_do_work); static DEFINE_SPINLOCK(uas_work_lock); @@ -800,7 +802,10 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_free_streams(devinfo); err = usb_reset_device(udev); + if (!err) + uas_configure_endpoints(devinfo); devinfo->resetting = 0; if (err) { -- cgit v1.2.1 From 1bf8198e6b2da3e30960e95f8d215f44572515ce Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Sep 2013 13:27:12 +0200 Subject: uas: make work list per-device Simplifies locking, we'll protect the list with the device spin lock. Also plugs races which can happen when two devices operate on the global list. While being at it rename the list head from "list" to "work", preparing for the addition of a second list. Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 106 +++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 62 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index fc08ee919439..3cf5a5ff3d53 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -51,6 +51,8 @@ struct uas_dev_info { unsigned uas_sense_old:1; struct scsi_cmnd *cmnd; spinlock_t lock; + struct work_struct work; + struct list_head work_list; }; enum { @@ -77,7 +79,7 @@ struct uas_cmd_info { struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; - struct list_head list; + struct list_head work; }; /* I hate forward declarations, but I actually have a loop */ @@ -88,10 +90,6 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); static void uas_configure_endpoints(struct uas_dev_info *devinfo); static void uas_free_streams(struct uas_dev_info *devinfo); -static DECLARE_WORK(uas_work, uas_do_work); -static DEFINE_SPINLOCK(uas_work_lock); -static LIST_HEAD(uas_work_list); - static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, struct uas_cmd_info *cmdinfo) { @@ -118,75 +116,66 @@ static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, static void uas_do_work(struct work_struct *work) { + struct uas_dev_info *devinfo = + container_of(work, struct uas_dev_info, work); struct uas_cmd_info *cmdinfo; struct uas_cmd_info *temp; - struct list_head list; unsigned long flags; int err; - spin_lock_irq(&uas_work_lock); - list_replace_init(&uas_work_list, &list); - spin_unlock_irq(&uas_work_lock); - - list_for_each_entry_safe(cmdinfo, temp, &list, list) { + spin_lock_irqsave(&devinfo->lock, flags); + list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) { struct scsi_pointer *scp = (void *)cmdinfo; - struct scsi_cmnd *cmnd = container_of(scp, - struct scsi_cmnd, SCp); - struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - spin_lock_irqsave(&devinfo->lock, flags); + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, + SCp); err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); - if (!err) + if (!err) { cmdinfo->state &= ~IS_IN_WORK_LIST; - spin_unlock_irqrestore(&devinfo->lock, flags); - if (err) { - list_del(&cmdinfo->list); - spin_lock_irq(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - spin_unlock_irq(&uas_work_lock); - schedule_work(&uas_work); + list_del(&cmdinfo->work); + } else { + schedule_work(&devinfo->work); } } + spin_unlock_irqrestore(&devinfo->lock, flags); } static void uas_abort_work(struct uas_dev_info *devinfo) { struct uas_cmd_info *cmdinfo; struct uas_cmd_info *temp; - struct list_head list; unsigned long flags; - spin_lock_irq(&uas_work_lock); - list_replace_init(&uas_work_list, &list); - spin_unlock_irq(&uas_work_lock); - spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &list, list) { + list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) { struct scsi_pointer *scp = (void *)cmdinfo; - struct scsi_cmnd *cmnd = container_of(scp, - struct scsi_cmnd, SCp); - struct uas_dev_info *di = (void *)cmnd->device->hostdata; - - if (di == devinfo) { - cmdinfo->state |= COMMAND_ABORTED; - cmdinfo->state &= ~IS_IN_WORK_LIST; - if (devinfo->resetting) { - /* uas_stat_cmplt() will not do that - * when a device reset is in - * progress */ - cmdinfo->state &= ~COMMAND_INFLIGHT; - } - uas_try_complete(cmnd, __func__); - } else { - /* not our uas device, relink into list */ - list_del(&cmdinfo->list); - spin_lock_irq(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - spin_unlock_irq(&uas_work_lock); + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, + SCp); + cmdinfo->state |= COMMAND_ABORTED; + cmdinfo->state &= ~IS_IN_WORK_LIST; + if (devinfo->resetting) { + /* uas_stat_cmplt() will not do that + * when a device reset is in + * progress */ + cmdinfo->state &= ~COMMAND_INFLIGHT; } + uas_try_complete(cmnd, __func__); + list_del(&cmdinfo->work); } spin_unlock_irqrestore(&devinfo->lock, flags); } +static void uas_add_work(struct uas_cmd_info *cmdinfo) +{ + struct scsi_pointer *scp = (void *)cmdinfo; + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + struct uas_dev_info *devinfo = cmnd->device->hostdata; + + WARN_ON(!spin_is_locked(&devinfo->lock)); + list_add_tail(&cmdinfo->work, &devinfo->work_list); + cmdinfo->state |= IS_IN_WORK_LIST; + schedule_work(&devinfo->work); +} + static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu *sense_iu = urb->transfer_buffer; @@ -288,11 +277,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, cmdinfo->state |= direction | SUBMIT_STATUS_URB; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { - spin_lock(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - cmdinfo->state |= IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); - schedule_work(&uas_work); + uas_add_work(cmdinfo); } } @@ -694,11 +679,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, spin_unlock_irqrestore(&devinfo->lock, flags); return SCSI_MLQUEUE_DEVICE_BUSY; } - spin_lock(&uas_work_lock); - list_add_tail(&cmdinfo->list, &uas_work_list); - cmdinfo->state |= IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); - schedule_work(&uas_work); + uas_add_work(cmdinfo); } spin_unlock_irqrestore(&devinfo->lock, flags); @@ -764,10 +745,8 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) spin_lock_irqsave(&devinfo->lock, flags); cmdinfo->state |= COMMAND_ABORTED; if (cmdinfo->state & IS_IN_WORK_LIST) { - spin_lock(&uas_work_lock); - list_del(&cmdinfo->list); + list_del(&cmdinfo->work); cmdinfo->state &= ~IS_IN_WORK_LIST; - spin_unlock(&uas_work_lock); } if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); @@ -1007,6 +986,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); spin_lock_init(&devinfo->lock); + INIT_WORK(&devinfo->work, uas_do_work); + INIT_LIST_HEAD(&devinfo->work_list); uas_configure_endpoints(devinfo); result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); @@ -1050,6 +1031,7 @@ static void uas_disconnect(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; devinfo->resetting = 1; + cancel_work_sync(&devinfo->work); uas_abort_work(devinfo); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); -- cgit v1.2.1 From 326349f82461918830ed59913a3ccd8ffa9ac46f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Sep 2013 13:27:13 +0200 Subject: uas: add dead request list This patch adds a new list where all requests which are canceled are added to, so we don't loose them. Then, after killing all inflight urbs on bus reset (and disconnect) we'll walk over the list and clean them up. Without this we can end up with aborted requests lingering around in case of status pipe transfer errors. Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 50 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 3cf5a5ff3d53..f0490382351d 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -53,6 +53,7 @@ struct uas_dev_info { spinlock_t lock; struct work_struct work; struct list_head work_list; + struct list_head dead_list; }; enum { @@ -80,6 +81,7 @@ struct uas_cmd_info { struct urb *data_in_urb; struct urb *data_out_urb; struct list_head work; + struct list_head dead; }; /* I hate forward declarations, but I actually have a loop */ @@ -89,6 +91,7 @@ static void uas_do_work(struct work_struct *work); static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); static void uas_configure_endpoints(struct uas_dev_info *devinfo); static void uas_free_streams(struct uas_dev_info *devinfo); +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller); static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, struct uas_cmd_info *cmdinfo) @@ -150,16 +153,12 @@ static void uas_abort_work(struct uas_dev_info *devinfo) struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + uas_log_cmd_state(cmnd, __func__); + WARN_ON(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state &= ~IS_IN_WORK_LIST; - if (devinfo->resetting) { - /* uas_stat_cmplt() will not do that - * when a device reset is in - * progress */ - cmdinfo->state &= ~COMMAND_INFLIGHT; - } - uas_try_complete(cmnd, __func__); list_del(&cmdinfo->work); + list_add_tail(&cmdinfo->dead, &devinfo->dead_list); } spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -176,6 +175,28 @@ static void uas_add_work(struct uas_cmd_info *cmdinfo) schedule_work(&devinfo->work); } +static void uas_zap_dead(struct uas_dev_info *devinfo) +{ + struct uas_cmd_info *cmdinfo; + struct uas_cmd_info *temp; + unsigned long flags; + + spin_lock_irqsave(&devinfo->lock, flags); + list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, dead) { + struct scsi_pointer *scp = (void *)cmdinfo; + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, + SCp); + uas_log_cmd_state(cmnd, __func__); + WARN_ON(!(cmdinfo->state & COMMAND_ABORTED)); + /* all urbs are killed, clear inflight bits */ + cmdinfo->state &= ~(COMMAND_INFLIGHT | + DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT); + uas_try_complete(cmnd, __func__); + } + spin_unlock_irqrestore(&devinfo->lock, flags); +} + static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu *sense_iu = urb->transfer_buffer; @@ -263,6 +284,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) if (cmdinfo->state & COMMAND_ABORTED) { scmd_printk(KERN_INFO, cmnd, "abort completed\n"); cmnd->result = DID_ABORT << 16; + list_del(&cmdinfo->dead); } cmnd->scsi_done(cmnd); return 0; @@ -292,7 +314,13 @@ static void uas_stat_cmplt(struct urb *urb) u16 tag; if (urb->status) { - dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status); + if (urb->status == -ENOENT) { + dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n", + urb->stream_id); + } else { + dev_err(&urb->dev->dev, "stat urb: status %d\n", + urb->status); + } usb_free_urb(urb); return; } @@ -743,7 +771,9 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) uas_log_cmd_state(cmnd, __func__); spin_lock_irqsave(&devinfo->lock, flags); + WARN_ON(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; + list_add_tail(&cmdinfo->dead, &devinfo->dead_list); if (cmdinfo->state & IS_IN_WORK_LIST) { list_del(&cmdinfo->work); cmdinfo->state &= ~IS_IN_WORK_LIST; @@ -776,11 +806,13 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct usb_device *udev = devinfo->udev; int err; + shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); devinfo->resetting = 1; uas_abort_work(devinfo); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_dead(devinfo); uas_free_streams(devinfo); err = usb_reset_device(udev); if (!err) @@ -988,6 +1020,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) spin_lock_init(&devinfo->lock); INIT_WORK(&devinfo->work, uas_do_work); INIT_LIST_HEAD(&devinfo->work_list); + INIT_LIST_HEAD(&devinfo->dead_list); uas_configure_endpoints(devinfo); result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); @@ -1036,6 +1069,7 @@ static void uas_disconnect(struct usb_interface *intf) usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_dead(devinfo); scsi_remove_host(shost); uas_free_streams(devinfo); kfree(devinfo); -- cgit v1.2.1 From f491ecbb47d5a709d46401da3cd54ff8ee666ca0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Sep 2013 13:27:14 +0200 Subject: uas: replace BUG_ON() + WARN_ON() with WARN_ON_ONCE() Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f0490382351d..046eedfc3828 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -154,7 +154,7 @@ static void uas_abort_work(struct uas_dev_info *devinfo) struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); uas_log_cmd_state(cmnd, __func__); - WARN_ON(cmdinfo->state & COMMAND_ABORTED); + WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state &= ~IS_IN_WORK_LIST; list_del(&cmdinfo->work); @@ -169,7 +169,7 @@ static void uas_add_work(struct uas_cmd_info *cmdinfo) struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); struct uas_dev_info *devinfo = cmnd->device->hostdata; - WARN_ON(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); list_add_tail(&cmdinfo->work, &devinfo->work_list); cmdinfo->state |= IS_IN_WORK_LIST; schedule_work(&devinfo->work); @@ -187,7 +187,7 @@ static void uas_zap_dead(struct uas_dev_info *devinfo) struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); uas_log_cmd_state(cmnd, __func__); - WARN_ON(!(cmdinfo->state & COMMAND_ABORTED)); + WARN_ON_ONCE(!(cmdinfo->state & COMMAND_ABORTED)); /* all urbs are killed, clear inflight bits */ cmdinfo->state &= ~(COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | @@ -271,13 +271,13 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; - WARN_ON(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | DATA_OUT_URB_INFLIGHT | UNLINK_DATA_URBS)) return -EBUSY; - BUG_ON(cmdinfo->state & COMMAND_COMPLETED); + WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED); cmdinfo->state |= COMMAND_COMPLETED; usb_free_urb(cmdinfo->data_in_urb); usb_free_urb(cmdinfo->data_out_urb); @@ -398,8 +398,9 @@ static void uas_data_cmplt(struct urb *urb) sdb = scsi_out(cmnd); cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; } - BUG_ON(sdb == NULL); - if (urb->status) { + if (sdb == NULL) { + WARN_ON_ONCE(1); + } else if (urb->status) { /* error: no data transfered */ sdb->resid = sdb->length; } else { @@ -573,7 +574,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; int err; - WARN_ON(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & SUBMIT_STATUS_URB) { err = uas_submit_sense_urb(cmnd->device->host, gfp, cmdinfo->stream); @@ -771,7 +772,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) uas_log_cmd_state(cmnd, __func__); spin_lock_irqsave(&devinfo->lock, flags); - WARN_ON(cmdinfo->state & COMMAND_ABORTED); + WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; list_add_tail(&cmdinfo->dead, &devinfo->dead_list); if (cmdinfo->state & IS_IN_WORK_LIST) { -- cgit v1.2.1 From d5f808d3f88e90b13a4839e07b3dc6e715e2ba88 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Oct 2013 19:30:26 +0200 Subject: uas: Urbs must be anchored before submitting them Otherwise they may complete before they get anchored and thus never get unanchored (as the unanchoring is done by the usb core on completion). This commit also remove the usb_get_urb / usb_put_urb around cmd submission + anchoring, since if done in the proper order this is not necessary. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 046eedfc3828..059ce62de4b0 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -531,10 +531,12 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, usb_free_urb, NULL); urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &devinfo->cmd_urbs); err = usb_submit_urb(urb, gfp); - if (err) + if (err) { + usb_unanchor_urb(urb); goto err; - usb_anchor_urb(urb, &devinfo->cmd_urbs); + } return 0; @@ -558,13 +560,14 @@ static int uas_submit_sense_urb(struct Scsi_Host *shost, urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); if (!urb) return SCSI_MLQUEUE_DEVICE_BUSY; + usb_anchor_urb(urb, &devinfo->sense_urbs); if (usb_submit_urb(urb, gfp)) { + usb_unanchor_urb(urb); shost_printk(KERN_INFO, shost, "sense urb submission failure\n"); usb_free_urb(urb); return SCSI_MLQUEUE_DEVICE_BUSY; } - usb_anchor_urb(urb, &devinfo->sense_urbs); return 0; } @@ -594,14 +597,15 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & SUBMIT_DATA_IN_URB) { + usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) { + usb_unanchor_urb(cmdinfo->data_in_urb); scmd_printk(KERN_INFO, cmnd, "data in urb submission failure\n"); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; cmdinfo->state |= DATA_IN_URB_INFLIGHT; - usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_DATA_OUT_URB) { @@ -614,14 +618,15 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { + usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) { + usb_unanchor_urb(cmdinfo->data_out_urb); scmd_printk(KERN_INFO, cmnd, "data out urb submission failure\n"); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; cmdinfo->state |= DATA_OUT_URB_INFLIGHT; - usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_CMD_URB) { @@ -633,14 +638,13 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & SUBMIT_CMD_URB) { - usb_get_urb(cmdinfo->cmd_urb); + usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) { + usb_unanchor_urb(cmdinfo->cmd_urb); scmd_printk(KERN_INFO, cmnd, "cmd urb submission failure\n"); return SCSI_MLQUEUE_DEVICE_BUSY; } - usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); - usb_put_urb(cmdinfo->cmd_urb); cmdinfo->cmd_urb = NULL; cmdinfo->state &= ~SUBMIT_CMD_URB; cmdinfo->state |= COMMAND_INFLIGHT; -- cgit v1.2.1 From 6ce8213b3328ae4a1db34339c282144740ac1ec6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Oct 2013 19:00:45 +0200 Subject: uas: Properly set interface to altsetting 0 on probe failure - Rename labels to properly reflect this - Don't skip free-ing the streams when scsi_init_shared_tag_map fails Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 059ce62de4b0..ec1b22d29501 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -993,8 +993,8 @@ static void uas_free_streams(struct uas_dev_info *devinfo) */ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) { - int result; - struct Scsi_Host *shost; + int result = -ENOMEM; + struct Scsi_Host *shost = NULL; struct uas_dev_info *devinfo; struct usb_device *udev = interface_to_usbdev(intf); @@ -1003,12 +1003,11 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL); if (!devinfo) - return -ENOMEM; + goto set_alt0; - result = -ENOMEM; shost = scsi_host_alloc(&uas_host_template, sizeof(void *)); if (!shost) - goto free; + goto set_alt0; shost->max_cmd_len = 16 + 252; shost->max_id = 1; @@ -1030,11 +1029,11 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); if (result) - goto free; + goto free_streams; result = scsi_add_host(shost, &intf->dev); if (result) - goto deconfig_eps; + goto free_streams; shost->hostdata[0] = (unsigned long)devinfo; @@ -1042,9 +1041,10 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, shost); return result; -deconfig_eps: +free_streams: uas_free_streams(devinfo); - free: +set_alt0: + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); kfree(devinfo); if (shost) scsi_host_put(shost); -- cgit v1.2.1 From 7e50e0bec45897caeb978e5aecc9184a2dc00df2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Oct 2013 19:19:04 +0200 Subject: uas: Avoid unnecessary unlock / lock calls around unlink_data_urbs All callers of unlink_data_urbs drop devinfo->lock before calling it, and then immediately take it again after the call. And the first thing unlink_data_urbs does is take the lock again, and the last thing it does is drop it. This commit removes all the unnecessary lock dropping and taking. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index ec1b22d29501..dcaf61197032 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -93,28 +93,26 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo); static void uas_free_streams(struct uas_dev_info *devinfo); static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller); +/* Must be called with devinfo->lock held, will temporary unlock the lock */ static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, - struct uas_cmd_info *cmdinfo) + struct uas_cmd_info *cmdinfo, + unsigned long *lock_flags) { - unsigned long flags; - /* * The UNLINK_DATA_URBS flag makes sure uas_try_complete * (called by urb completion) doesn't release cmdinfo * underneath us. */ - spin_lock_irqsave(&devinfo->lock, flags); cmdinfo->state |= UNLINK_DATA_URBS; - spin_unlock_irqrestore(&devinfo->lock, flags); + spin_unlock_irqrestore(&devinfo->lock, *lock_flags); if (cmdinfo->data_in_urb) usb_unlink_urb(cmdinfo->data_in_urb); if (cmdinfo->data_out_urb) usb_unlink_urb(cmdinfo->data_out_urb); - spin_lock_irqsave(&devinfo->lock, flags); + spin_lock_irqsave(&devinfo->lock, *lock_flags); cmdinfo->state &= ~UNLINK_DATA_URBS; - spin_unlock_irqrestore(&devinfo->lock, flags); } static void uas_do_work(struct work_struct *work) @@ -361,9 +359,7 @@ static void uas_stat_cmplt(struct urb *urb) uas_sense(urb, cmnd); if (cmnd->result != 0) { /* cancel data transfers on error */ - spin_unlock_irqrestore(&devinfo->lock, flags); - uas_unlink_data_urbs(devinfo, cmdinfo); - spin_lock_irqsave(&devinfo->lock, flags); + uas_unlink_data_urbs(devinfo, cmdinfo, &flags); } cmdinfo->state &= ~COMMAND_INFLIGHT; uas_try_complete(cmnd, __func__); @@ -787,9 +783,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) spin_unlock_irqrestore(&devinfo->lock, flags); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); } else { - spin_unlock_irqrestore(&devinfo->lock, flags); - uas_unlink_data_urbs(devinfo, cmdinfo); - spin_lock_irqsave(&devinfo->lock, flags); + uas_unlink_data_urbs(devinfo, cmdinfo, &flags); uas_try_complete(cmnd, __func__); spin_unlock_irqrestore(&devinfo->lock, flags); ret = SUCCESS; -- cgit v1.2.1 From a887cd366347c38607f0d9c28ca2baed40cac8fc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Oct 2013 19:47:28 +0200 Subject: uas: uas_alloc_cmd_urb: drop unused stream_id parameter The cmd endpoint never has streams, so the stream_id parameter is unused. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index dcaf61197032..5eacb8054457 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -454,7 +454,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct scsi_cmnd *cmnd, u16 stream_id) + struct scsi_cmnd *cmnd) { struct usb_device *udev = devinfo->udev; struct scsi_device *sdev = cmnd->device; @@ -626,8 +626,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } if (cmdinfo->state & ALLOC_CMD_URB) { - cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd, - cmdinfo->stream); + cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd); if (!cmdinfo->cmd_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_CMD_URB; -- cgit v1.2.1 From 6c2334e9019039d7952190e239e6a8f0d10101fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Oct 2013 22:20:54 +0200 Subject: uas: Fix uas not working when plugged into an ehci port I thought it would be a good idea to also test uas with usb-2, and it turns out it was, as it did not work. The problem is that the uas driver was passing the bEndpointAddress' direction bit to usb_rcvbulkpipe, the xhci code seems to not care about this, but with the ehci code this causes usb_submit_urb failure. With this fixed the uas code works nicely with an uas device plugged into an ehci port. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 5eacb8054457..6ad5de9639d5 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -948,13 +948,13 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); } else { devinfo->cmd_pipe = usb_sndbulkpipe(udev, - eps[0]->desc.bEndpointAddress); + usb_endpoint_num(&eps[0]->desc)); devinfo->status_pipe = usb_rcvbulkpipe(udev, - eps[1]->desc.bEndpointAddress); + usb_endpoint_num(&eps[1]->desc)); devinfo->data_in_pipe = usb_rcvbulkpipe(udev, - eps[2]->desc.bEndpointAddress); + usb_endpoint_num(&eps[2]->desc)); devinfo->data_out_pipe = usb_sndbulkpipe(udev, - eps[3]->desc.bEndpointAddress); + usb_endpoint_num(&eps[3]->desc)); } devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256, -- cgit v1.2.1 From be326f4c9bdfdff8a85145fb89b0a44c4d20ebc6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 22 Sep 2013 16:27:02 +0200 Subject: uas: Fix reset locking Fix the uas_eh_bus_reset_handler not properly taking the usbdev lock before calling usb_device_reset, the usb-core expects this lock to be taken when usb_device_reset is called. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 6ad5de9639d5..36ef82a34131 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -804,6 +804,13 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct usb_device *udev = devinfo->udev; int err; + err = usb_lock_device_for_reset(udev, devinfo->intf); + if (err) { + shost_printk(KERN_ERR, sdev->host, + "%s FAILED to get lock err %d\n", __func__, err); + return FAILED; + } + shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); devinfo->resetting = 1; uas_abort_work(devinfo); @@ -817,6 +824,8 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) uas_configure_endpoints(devinfo); devinfo->resetting = 0; + usb_unlock_device(udev); + if (err) { shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); return FAILED; -- cgit v1.2.1 From 4de7a3735bdc4219cf57a0d44f92c06d7127a211 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 Oct 2013 16:10:44 +0100 Subject: uas: Fix reset handling for externally triggered reset Handle usb-device resets not triggered from uas_eh_bus_reset_handler(), when this happens, disable cmd queuing during the reset, and wait for existing requests to finish in pre_reset. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 36ef82a34131..0ee5a05c0a7b 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -818,10 +819,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); uas_zap_dead(devinfo); - uas_free_streams(devinfo); err = usb_reset_device(udev); - if (!err) - uas_configure_endpoints(devinfo); devinfo->resetting = 0; usb_unlock_device(udev); @@ -1055,13 +1053,41 @@ set_alt0: static int uas_pre_reset(struct usb_interface *intf) { -/* XXX: Need to return 1 if it's not our device in error handling */ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + unsigned long flags; + + /* Block new requests */ + spin_lock_irqsave(shost->host_lock, flags); + scsi_block_requests(shost); + spin_unlock_irqrestore(shost->host_lock, flags); + + /* Wait for any pending requests to complete */ + flush_work(&devinfo->work); + if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + return 1; + } + + uas_free_streams(devinfo); + return 0; } static int uas_post_reset(struct usb_interface *intf) { -/* XXX: Need to return 1 if it's not our device in error handling */ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + unsigned long flags; + + uas_configure_endpoints(devinfo); + + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + + scsi_unblock_requests(shost); + return 0; } -- cgit v1.2.1 From e52e031498cb51aff4f80a19a56700a127cf2a9a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Oct 2013 14:27:09 +0100 Subject: uas: s/response_ui/response_iu/ Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 0ee5a05c0a7b..33f9dcd68e24 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -46,7 +46,7 @@ struct uas_dev_info { struct usb_anchor sense_urbs; struct usb_anchor data_urbs; int qdepth, resetting; - struct response_ui response; + struct response_iu response; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; -- cgit v1.2.1 From d3f7c1560aee57d0ec293253e0c0e79a84ea3016 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Oct 2013 17:46:17 +0100 Subject: uas: Use all available stream ids If we get ie 16 streams we can use stream-id 1-16, not 1-15. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 33f9dcd68e24..3f021f2fafdf 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -722,7 +722,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, { struct Scsi_Host *shost = cmnd->device->host; struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; - u16 tag = devinfo->qdepth - 1; + u16 tag = devinfo->qdepth; unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); @@ -843,7 +843,7 @@ static int uas_slave_configure(struct scsi_device *sdev) { struct uas_dev_info *devinfo = sdev->hostdata; scsi_set_tag_type(sdev, MSG_ORDERED_TAG); - scsi_activate_tcq(sdev, devinfo->qdepth - 3); + scsi_activate_tcq(sdev, devinfo->qdepth - 2); return 0; } @@ -1027,7 +1027,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) INIT_LIST_HEAD(&devinfo->dead_list); uas_configure_endpoints(devinfo); - result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); + result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2); if (result) goto free_streams; -- cgit v1.2.1 From e1be067b681054e963dfd01346c0d7fc0f8a63aa Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Oct 2013 08:00:58 +0100 Subject: uas: Add a uas_find_uas_alt_setting helper function This is a preparation patch for teaching usb-storage to not bind to uas devices. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 3f021f2fafdf..54db36541b93 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -892,10 +892,10 @@ static int uas_isnt_supported(struct usb_device *udev) return -ENODEV; } -static int uas_switch_interface(struct usb_device *udev, - struct usb_interface *intf) +static int uas_find_uas_alt_setting(struct usb_interface *intf) { int i; + struct usb_device *udev = interface_to_usbdev(intf); int sg_supported = udev->bus->sg_tablesize != 0; for (i = 0; i < intf->num_altsetting; i++) { @@ -904,15 +904,26 @@ static int uas_switch_interface(struct usb_device *udev, if (uas_is_interface(alt)) { if (!sg_supported) return uas_isnt_supported(udev); - return usb_set_interface(udev, - alt->desc.bInterfaceNumber, - alt->desc.bAlternateSetting); + return alt->desc.bAlternateSetting; } } return -ENODEV; } +static int uas_switch_interface(struct usb_device *udev, + struct usb_interface *intf) +{ + int alt; + + alt = uas_find_uas_alt_setting(intf); + if (alt < 0) + return alt; + + return usb_set_interface(udev, + intf->altsetting[0].desc.bInterfaceNumber, alt); +} + static void uas_configure_endpoints(struct uas_dev_info *devinfo) { struct usb_host_endpoint *eps[4] = { }; -- cgit v1.2.1 From 82aa0387d52dd0db2173b844ad52d114807c189b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Oct 2013 08:53:31 +0100 Subject: uas: Move uas detect code to uas-detect.h This is a preparation patch for teaching usb-storage to not bind to uas devices. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas-detect.h | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/storage/uas.c | 40 ++-------------------------------------- 2 files changed, 42 insertions(+), 38 deletions(-) create mode 100644 drivers/usb/storage/uas-detect.h (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h new file mode 100644 index 000000000000..28101c7e6a9f --- /dev/null +++ b/drivers/usb/storage/uas-detect.h @@ -0,0 +1,40 @@ +#include +#include + +static int uas_is_interface(struct usb_host_interface *intf) +{ + return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && + intf->desc.bInterfaceSubClass == USB_SC_SCSI && + intf->desc.bInterfaceProtocol == USB_PR_UAS); +} + +static int uas_isnt_supported(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + dev_warn(&udev->dev, "The driver for the USB controller %s does not " + "support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, "required by the UAS driver. Please try an" + "alternative USB controller if you wish to use UAS.\n"); + return -ENODEV; +} + +static int uas_find_uas_alt_setting(struct usb_interface *intf) +{ + int i; + struct usb_device *udev = interface_to_usbdev(intf); + int sg_supported = udev->bus->sg_tablesize != 0; + + for (i = 0; i < intf->num_altsetting; i++) { + struct usb_host_interface *alt = &intf->altsetting[i]; + + if (uas_is_interface(alt)) { + if (!sg_supported) + return uas_isnt_supported(udev); + return alt->desc.bAlternateSetting; + } + } + + return -ENODEV; +} diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 54db36541b93..6ea892f32f74 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -25,6 +25,8 @@ #include #include +#include "uas-detect.h" + /* * The r00-r01c specs define this version of the SENSE IU data structure. * It's still in use by several different firmware releases. @@ -873,44 +875,6 @@ static struct usb_device_id uas_usb_ids[] = { }; MODULE_DEVICE_TABLE(usb, uas_usb_ids); -static int uas_is_interface(struct usb_host_interface *intf) -{ - return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && - intf->desc.bInterfaceSubClass == USB_SC_SCSI && - intf->desc.bInterfaceProtocol == USB_PR_UAS); -} - -static int uas_isnt_supported(struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - dev_warn(&udev->dev, "The driver for the USB controller %s does not " - "support scatter-gather which is\n", - hcd->driver->description); - dev_warn(&udev->dev, "required by the UAS driver. Please try an" - "alternative USB controller if you wish to use UAS.\n"); - return -ENODEV; -} - -static int uas_find_uas_alt_setting(struct usb_interface *intf) -{ - int i; - struct usb_device *udev = interface_to_usbdev(intf); - int sg_supported = udev->bus->sg_tablesize != 0; - - for (i = 0; i < intf->num_altsetting; i++) { - struct usb_host_interface *alt = &intf->altsetting[i]; - - if (uas_is_interface(alt)) { - if (!sg_supported) - return uas_isnt_supported(udev); - return alt->desc.bAlternateSetting; - } - } - - return -ENODEV; -} - static int uas_switch_interface(struct usb_device *udev, struct usb_interface *intf) { -- cgit v1.2.1 From 127329d76b8534fb58c207db1f172d8468b828ff Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Nov 2013 08:19:45 +0100 Subject: xhci: xhci_mem_cleanup: make sure cmd_ring_reserved_trbs really is 0 cmd_ring_reserved_trbs gets decremented by xhci_free_stream_info(), so set it to 0 after freeing all rings, otherwise it wraps around to a very large value when rings with streams are free-ed. Before this patch the wrap-around could be triggered when xhci_resume calls xhci_mem_cleanup if the controller resume fails. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index edfb31ad5949..60fc6720007f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1812,7 +1812,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); - xhci->cmd_ring_reserved_trbs = 0; if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); xhci->cmd_ring = NULL; @@ -1877,6 +1876,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } no_bw: + xhci->cmd_ring_reserved_trbs = 0; xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; xhci->num_active_eps = 0; -- cgit v1.2.1 From 84c1e40fd794a883431fc7688f653d3faa0265f0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Nov 2013 15:50:03 +0100 Subject: xhci: The trb_address_map radix tree expects 1KB segment memory aligment If we align segment dma pool memory to 64 bytes, then a segment can be located at 0x10000040 - 0x1000043f, and a segment from another ring at 0x10000440 - 0x1000083f. The last trb in the first segment at 0x10000430 will then translate to the same radix tree key as the first trb of the second segment, while they are in different rings! This patches fixes this by changing the alignment of the dma pool to be 1KB rather then 64 bytes. An alternative fix would be to reduce the shift used to calculate the radix tree keys, but that would (slighlty) grow the radix trees so I believe this is the better fix. Note this patch is mostly theoretical since in practice I've not seen the dma_pool actually return not 1KB aligned memory. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-mem.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 60fc6720007f..c089668308ad 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -158,7 +158,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, * * The radix tree maps the upper portion of the TRB DMA address to a ring * segment that has the same upper portion of DMA addresses. For example, say I - * have segments of size 1KB, that are always 64-byte aligned. A segment may + * have segments of size 1KB, that are always 1KB aligned. A segment may * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the * key to the stream ID is 0x43244. I can use the DMA address of the TRB to * pass the radix tree a key to get the right stream ID: @@ -2375,11 +2375,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* * Initialize the ring segment pool. The ring must be a contiguous * structure comprised of TRBs. The TRBs must be 16 byte aligned, - * however, the command ring segment needs 64-byte aligned segments, - * so we pick the greater alignment need. + * however, the command ring segment needs 64-byte aligned segments + * and our use of dma addresses in the trb_address_map radix tree needs + * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, - TRB_SEGMENT_SIZE, 64, xhci->page_size); + TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size); /* See Table 46 and Note on Figure 55 */ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, -- cgit v1.2.1 From f7920884eb640bc642f3b4e56f5237d30a080eda Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 15 Nov 2013 12:14:38 +0100 Subject: xhci: Handle MaxPSASize == 0 Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6cb9c2235a0b..1db3c2794a49 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3138,6 +3138,12 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n", num_streams); + /* MaxPSASize value 0 (2 streams) means streams are not supported */ + if (HCC_MAX_PSA(xhci->hcc_params) < 4) { + xhci_dbg(xhci, "xHCI controller does not support streams.\n"); + return -ENOSYS; + } + config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); if (!config_cmd) { xhci_dbg(xhci, "Could not allocate xHCI command structure.\n"); -- cgit v1.2.1 From 7a7b562d08ad6db98d6c8ec634620a11aaf8921a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 8 Nov 2013 16:37:26 +0100 Subject: usb: Clear host_endpoint->streams when implicitly freeing streams If streams are still allocated on device-reset or set-interface then the hcd code implictly frees the streams. Clear host_endpoint->streams in this case so that if a driver later tries to re-allocate them it won't run afoul of the device already having streams check in usb_alloc_streams(). Note normally streams still being allocated at reset / set-intf would be a driver bug, but this can happen without it being a driver bug on reset-resume. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 5 ++++- drivers/usb/core/message.c | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 763c3134dd78..4f7629d1ba6a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5131,7 +5131,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_device_descriptor descriptor = udev->descriptor; struct usb_host_bos *bos; - int i, ret = 0; + int i, j, ret = 0; int port1 = udev->portnum; if (udev->state == USB_STATE_NOTATTACHED || @@ -5257,6 +5257,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ret); goto re_enumerate; } + /* Resetting also frees any allocated streams */ + for (j = 0; j < intf->cur_altsetting->desc.bNumEndpoints; j++) + intf->cur_altsetting->endpoint[j].streams = 0; } done: diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f829a1aad1c3..964695741031 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) struct usb_interface *iface; struct usb_host_interface *alt; struct usb_hcd *hcd = bus_to_hcd(dev->bus); - int ret; - int manual = 0; + int i, ret, manual = 0; unsigned int epaddr; unsigned int pipe; @@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } + /* Changing alt-setting also frees any allocated streams */ + for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++) + iface->cur_altsetting->endpoint[i].streams = 0; + ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", -- cgit v1.2.1 From a82b76f7fa6154e8ab2d8071842a3e38b9c0d0ff Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 8 Nov 2013 13:41:05 +0100 Subject: usb: Reset USB-3 devices on USB-3 link bounce On disconnect USB3 protocol ports transit from U0 to SS.Inactive to Rx.Detect, on a recoverable error, the port stays in SS.Inactive and we recover from it by doing a warm-reset (through usb_device_reset if we have a udev for the port). If this really is a disconnect we may end up trying the warm-reset anyways, since khubd may run before the SS.Inactive to Rx.Detect transition, or it may get skipped if the transition to Rx.Detect happens before khubd gets run. With a loose connector, or in the case which actually led me to debugging this bad ACPI firmware toggling Vbus off and on in quick succession, the port may transition from Rx.Detect to U0 again before khubd gets run. In this case the device state is unknown really, but khubd happily goes into the resuscitate an existing device path, and the device driver never gets notified about the device state being messed up. If the above scenario happens with a streams using device, as soon as an urb is submitted to an endpoint with streams, the following appears in dmesg: ERROR Transfer event for disabled endpoint or incorrect stream ring @0000000036807420 00000000 00000000 04000000 04078000 Notice how the TRB address is all zeros. I've seen this both on Intel Pantherpoint and Nec xhci hosts. Luckily we can detect the U0 to SS.Inactive to Rx.Detect to U0 all having happened before khubd runs case since the C_LINK_STATE bit gets set in the portchange bits on the U0 -> SS.Inactive change. This bit will also be set on suspend / resume, but then it gets cleared by port_hub_init before khubd runs. So if the C_LINK_STATE bit is set and a warm-reset is not needed, iow the port is not still in SS.Inactive, and the port still has a connection, then the device needs to be reset to put it back in a known state. I've verified that doing the device reset also fixes the transfer event with all zeros address issue. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4f7629d1ba6a..5da5394127e9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4758,6 +4758,8 @@ static void hub_events(void) /* deal with port status changes */ for (i = 1; i <= hdev->maxchild; i++) { + struct usb_device *udev = hub->ports[i - 1]->child; + if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); @@ -4856,8 +4858,6 @@ static void hub_events(void) */ if (hub_port_warm_reset_required(hub, portstatus)) { int status; - struct usb_device *udev = - hub->ports[i - 1]->child; dev_dbg(hub_dev, "warm reset port %d\n", i); if (!udev || @@ -4874,6 +4874,24 @@ static void hub_events(void) usb_unlock_device(udev); connect_change = 0; } + /* + * On disconnect USB3 protocol ports transit from U0 to + * SS.Inactive to Rx.Detect. If this happens a warm- + * reset is not needed, but a (re)connect may happen + * before khubd runs and sees the disconnect, and the + * device may be an unknown state. + * + * If the port went through SS.Inactive without khubd + * seeing it the C_LINK_STATE change flag will be set, + * and we reset the dev to put it in a known state. + */ + } else if (udev && hub_is_superspeed(hub->hdev) && + (portchange & USB_PORT_STAT_C_LINK_STATE) && + (portstatus & USB_PORT_STAT_CONNECTION)) { + usb_lock_device(udev); + usb_reset_device(udev); + usb_unlock_device(udev); + connect_change = 0; } if (connect_change) -- cgit v1.2.1 From 79b4c06112f12c31d03cf22b1ed5ce09423fd887 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 25 Oct 2013 17:04:33 +0100 Subject: uas: Add the posibilty to blacklist uas devices from using the uas driver Once we start supporting uas hardware, and as more and more uas devices become available, we will likely start seeing broken devices. This patch prepares for the inevitable need for blacklisting those devices from using the uas driver (they will use usb-storage instead). Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas-detect.h | 11 +++++++++ drivers/usb/storage/uas.c | 17 ++++++++++--- drivers/usb/storage/unusual_uas.h | 52 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 drivers/usb/storage/unusual_uas.h (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 28101c7e6a9f..02bf5ec957f5 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -38,3 +38,14 @@ static int uas_find_uas_alt_setting(struct usb_interface *intf) return -ENODEV; } + +static int uas_use_uas_driver(struct usb_interface *intf, + const struct usb_device_id *id) +{ + unsigned long flags = id->driver_info; + + if (flags & US_FL_IGNORE_UAS) + return 0; + + return uas_find_uas_alt_setting(intf) >= 0; +} diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 6ea892f32f74..e817e72d8673 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -866,7 +867,14 @@ static struct scsi_host_template uas_host_template = { .ordered_tag = 1, }; +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + static struct usb_device_id uas_usb_ids[] = { +# include "unusual_uas.h" { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) }, /* 0xaa is a prototype device I happen to have access to */ @@ -875,6 +883,8 @@ static struct usb_device_id uas_usb_ids[] = { }; MODULE_DEVICE_TABLE(usb, uas_usb_ids); +#undef UNUSUAL_DEV + static int uas_switch_interface(struct usb_device *udev, struct usb_interface *intf) { @@ -973,6 +983,9 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) struct uas_dev_info *devinfo; struct usb_device *udev = interface_to_usbdev(intf); + if (!uas_use_uas_driver(intf, id)) + return -ENODEV; + if (uas_switch_interface(udev, intf)) return -ENODEV; @@ -1083,10 +1096,6 @@ static void uas_disconnect(struct usb_interface *intf) kfree(devinfo); } -/* - * XXX: Should this plug into libusual so we can auto-upgrade devices from - * Bulk-Only to UAS? - */ static struct usb_driver uas_driver = { .name = "uas", .probe = uas_probe, diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h new file mode 100644 index 000000000000..7244444df8ee --- /dev/null +++ b/drivers/usb/storage/unusual_uas.h @@ -0,0 +1,52 @@ +/* Driver for USB Attached SCSI devices - Unusual Devices File + * + * (c) 2013 Hans de Goede + * + * Based on the same file for the usb-storage driver, which is: + * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * IMPORTANT NOTE: This file must be included in another file which defines + * a UNUSUAL_DEV macro before this file is included. + */ + +/* + * If you edit this file, please try to keep it sorted first by VendorID, + * then by ProductID. + * + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), + * - lsusb -v output for the device + * Send your submission to Hans de Goede + * and don't forget to CC: the USB development list + */ + +/* + * This is an example entry for the US_FL_IGNORE_UAS flag. Once we have an + * actual entry using US_FL_IGNORE_UAS this entry should be removed. + * + * UNUSUAL_DEV( 0xabcd, 0x1234, 0x0100, 0x0100, + * "Example", + * "Storage with broken UAS", + * USB_SC_DEVICE, USB_PR_DEVICE, NULL, + * US_FL_IGNORE_UAS), + */ -- cgit v1.2.1 From 5bfd5b5d8b9cd9e8ba1709f2b9dc35bd4b26c8b1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 21 Oct 2013 09:40:48 +0100 Subject: usb-storage: Don't bind to uas devices if the uas driver is enabled uas devices have 2 alternative settings on their usb-storage interface, one for usb-storage and one for uas. Using the uas driver is preferred, so if the uas driver is enabled, and the device has an uas alt setting, don't bind. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/unusual_devs.h | 5 +++++ drivers/usb/storage/usb.c | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index adbeb255616a..f4a82291894a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2086,6 +2086,11 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001, "Digital MP3 Audio Player", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), +/* Unusual uas devices */ +#if IS_ENABLED(CONFIG_USB_UAS) +#include "unusual_uas.h" +#endif + /* Control/Bulk transport for all SubClass values */ USUAL_DEV(USB_SC_RBC, USB_PR_CB), USUAL_DEV(USB_SC_8020, USB_PR_CB), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 1c0b89f2a138..388f567524d8 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -72,6 +72,10 @@ #include "sierra_ms.h" #include "option_ms.h" +#if IS_ENABLED(CONFIG_USB_UAS) +#include "uas-detect.h" +#endif + /* Some informational data */ MODULE_AUTHOR("Matthew Dharm "); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); @@ -1035,6 +1039,12 @@ static int storage_probe(struct usb_interface *intf, int result; int size; + /* If uas is enabled and this device can do uas then ignore it. */ +#if IS_ENABLED(CONFIG_USB_UAS) + if (uas_use_uas_driver(intf, id)) + return -ENXIO; +#endif + /* * If the device isn't standard (is handled by a subdriver * module) then don't accept it. -- cgit v1.2.1 From d24d481b7d369b08cce734bf80be374eed5a6e58 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 16 Nov 2013 12:09:39 +0100 Subject: usb-storage: Modify and export adjust_quirks so that it can be used by uas Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/usb.c | 16 ++++++++++------ drivers/usb/storage/usb.h | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 388f567524d8..f1c96261a501 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -463,14 +463,14 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) #define TOLOWER(x) ((x) | 0x20) /* Adjust device flags based on the "quirks=" module parameter */ -static void adjust_quirks(struct us_data *us) +void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) { char *p; - u16 vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor); - u16 pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct); + u16 vid = le16_to_cpu(udev->descriptor.idVendor); + u16 pid = le16_to_cpu(udev->descriptor.idProduct); unsigned f = 0; unsigned int mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE | - US_FL_FIX_CAPACITY | + US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS | US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | @@ -541,14 +541,18 @@ static void adjust_quirks(struct us_data *us) case 's': f |= US_FL_SINGLE_LUN; break; + case 'u': + f |= US_FL_IGNORE_UAS; + break; case 'w': f |= US_FL_NO_WP_DETECT; break; /* Ignore unrecognized flag characters */ } } - us->fflags = (us->fflags & ~mask) | f; + *fflags = (*fflags & ~mask) | f; } +EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); /* Get the unusual_devs entries and the string descriptors */ static int get_device_info(struct us_data *us, const struct usb_device_id *id, @@ -568,7 +572,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id, idesc->bInterfaceProtocol : unusual_dev->useTransport; us->fflags = id->driver_info; - adjust_quirks(us); + usb_stor_adjust_quirks(us->pusb_dev, &us->fflags); if (us->fflags & US_FL_IGNORE_DEVICE) { dev_info(pdev, "device ignored\n"); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 75f70f04f37b..307e339a9478 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -201,4 +201,7 @@ extern int usb_stor_probe1(struct us_data **pus, extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); +extern void usb_stor_adjust_quirks(struct usb_device *dev, + unsigned long *fflags); + #endif -- cgit v1.2.1 From 97172a660cfc744996112eb625a77282a4b627b7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 16 Nov 2013 12:19:36 +0100 Subject: uas: Honor no-uas quirk set in usb-storage's quirks module parameter Falling back from uas to usb-storage requires coordination between uas and usb-storage, so use usb-storage's quirks module parameter, rather then requiring the user to pass a param to 2 different modules. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/Kconfig | 2 +- drivers/usb/storage/uas-detect.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 1dd0604d1911..666dcb692e12 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -204,7 +204,7 @@ config USB_STORAGE_ENE_UB6250 config USB_UAS tristate "USB Attached SCSI" - depends on SCSI && BROKEN + depends on SCSI && USB_STORAGE && BROKEN help The USB Attached SCSI protocol is supported by some USB storage devices. It permits higher performance by supporting diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 02bf5ec957f5..082bde1fa74d 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -1,5 +1,6 @@ #include #include +#include "usb.h" static int uas_is_interface(struct usb_host_interface *intf) { @@ -42,8 +43,11 @@ static int uas_find_uas_alt_setting(struct usb_interface *intf) static int uas_use_uas_driver(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(intf); unsigned long flags = id->driver_info; + usb_stor_adjust_quirks(udev, &flags); + if (flags & US_FL_IGNORE_UAS) return 0; -- cgit v1.2.1 From 34f11e59c3d5d025ad9162d8fd126e0b7ca54f40 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 08:54:48 +0100 Subject: uas: Add uas_find_endpoints() helper function This is a preparation patch for adding better descriptor validation. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e817e72d8673..1ac66f290fbf 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -898,16 +898,11 @@ static int uas_switch_interface(struct usb_device *udev, intf->altsetting[0].desc.bInterfaceNumber, alt); } -static void uas_configure_endpoints(struct uas_dev_info *devinfo) +static int uas_find_endpoints(struct usb_host_interface *alt, + struct usb_host_endpoint *eps[]) { - struct usb_host_endpoint *eps[4] = { }; - struct usb_interface *intf = devinfo->intf; - struct usb_device *udev = devinfo->udev; - struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint; - unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints; - - devinfo->uas_sense_old = 0; - devinfo->cmnd = NULL; + struct usb_host_endpoint *endpoint = alt->endpoint; + unsigned i, n_endpoints = alt->desc.bNumEndpoints; for (i = 0; i < n_endpoints; i++) { unsigned char *extra = endpoint[i].extra; @@ -924,12 +919,29 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) } } + if (!eps[0] || !eps[1] || !eps[2] || !eps[3]) + return -ENODEV; + + return 0; +} + +static void uas_configure_endpoints(struct uas_dev_info *devinfo) +{ + struct usb_host_endpoint *eps[4] = { }; + struct usb_device *udev = devinfo->udev; + int r; + + devinfo->uas_sense_old = 0; + devinfo->cmnd = NULL; + + r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); + /* - * Assume that if we didn't find a control pipe descriptor, we're + * Assume that if we didn't find a proper set of descriptors, we're * using a device with old firmware that happens to be set up like * this. */ - if (!eps[0]) { + if (r != 0) { devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1); devinfo->status_pipe = usb_rcvbulkpipe(udev, 1); devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2); -- cgit v1.2.1 From d495c1baa1b3ba277bb5ae24adeab0600151cba4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 09:06:54 +0100 Subject: uas: Fix bounds check in uas_find_endpoints The loop uses up to 3 bytes of the endpoint extra data. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 1ac66f290fbf..7662b3e13c4d 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -907,7 +907,7 @@ static int uas_find_endpoints(struct usb_host_interface *alt, for (i = 0; i < n_endpoints; i++) { unsigned char *extra = endpoint[i].extra; int len = endpoint[i].extralen; - while (len > 1) { + while (len >= 3) { if (extra[1] == USB_DT_PIPE_USAGE) { unsigned pipe_id = extra[2]; if (pipe_id > 0 && pipe_id < 5) -- cgit v1.2.1 From d77adc0284beea5783c52b2af49217a37c2114cd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 10:03:34 +0100 Subject: uas: Move uas_find_endpoints to uas-detect.h No changes, just the move. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas-detect.h | 27 +++++++++++++++++++++++++++ drivers/usb/storage/uas.c | 27 --------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 082bde1fa74d..8de030a0a4a4 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -40,6 +40,33 @@ static int uas_find_uas_alt_setting(struct usb_interface *intf) return -ENODEV; } +static int uas_find_endpoints(struct usb_host_interface *alt, + struct usb_host_endpoint *eps[]) +{ + struct usb_host_endpoint *endpoint = alt->endpoint; + unsigned i, n_endpoints = alt->desc.bNumEndpoints; + + for (i = 0; i < n_endpoints; i++) { + unsigned char *extra = endpoint[i].extra; + int len = endpoint[i].extralen; + while (len >= 3) { + if (extra[1] == USB_DT_PIPE_USAGE) { + unsigned pipe_id = extra[2]; + if (pipe_id > 0 && pipe_id < 5) + eps[pipe_id - 1] = &endpoint[i]; + break; + } + len -= extra[0]; + extra += extra[0]; + } + } + + if (!eps[0] || !eps[1] || !eps[2] || !eps[3]) + return -ENODEV; + + return 0; +} + static int uas_use_uas_driver(struct usb_interface *intf, const struct usb_device_id *id) { diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 7662b3e13c4d..5cedc7f034b6 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -898,33 +898,6 @@ static int uas_switch_interface(struct usb_device *udev, intf->altsetting[0].desc.bInterfaceNumber, alt); } -static int uas_find_endpoints(struct usb_host_interface *alt, - struct usb_host_endpoint *eps[]) -{ - struct usb_host_endpoint *endpoint = alt->endpoint; - unsigned i, n_endpoints = alt->desc.bNumEndpoints; - - for (i = 0; i < n_endpoints; i++) { - unsigned char *extra = endpoint[i].extra; - int len = endpoint[i].extralen; - while (len >= 3) { - if (extra[1] == USB_DT_PIPE_USAGE) { - unsigned pipe_id = extra[2]; - if (pipe_id > 0 && pipe_id < 5) - eps[pipe_id - 1] = &endpoint[i]; - break; - } - len -= extra[0]; - extra += extra[0]; - } - } - - if (!eps[0] || !eps[1] || !eps[2] || !eps[3]) - return -ENODEV; - - return 0; -} - static void uas_configure_endpoints(struct uas_dev_info *devinfo) { struct usb_host_endpoint *eps[4] = { }; -- cgit v1.2.1 From 74d71aec619f33ec1ff5b2090792ab96d840bd3b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 10:10:36 +0100 Subject: uas: Drop fixed endpoint config handling The fixed endpoint config code was only necessary to deal with an early uas prototype which has never been released, so lets drop it and enforce proper uas endpoint descriptors. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 5cedc7f034b6..754468bbbfdc 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -908,31 +908,17 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) devinfo->cmnd = NULL; r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); - - /* - * Assume that if we didn't find a proper set of descriptors, we're - * using a device with old firmware that happens to be set up like - * this. - */ - if (r != 0) { - devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1); - devinfo->status_pipe = usb_rcvbulkpipe(udev, 1); - devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2); - devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2); - - eps[1] = usb_pipe_endpoint(udev, devinfo->status_pipe); - eps[2] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); - eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); - } else { - devinfo->cmd_pipe = usb_sndbulkpipe(udev, - usb_endpoint_num(&eps[0]->desc)); - devinfo->status_pipe = usb_rcvbulkpipe(udev, - usb_endpoint_num(&eps[1]->desc)); - devinfo->data_in_pipe = usb_rcvbulkpipe(udev, - usb_endpoint_num(&eps[2]->desc)); - devinfo->data_out_pipe = usb_sndbulkpipe(udev, - usb_endpoint_num(&eps[3]->desc)); - } + if (r) + return r; + + devinfo->cmd_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[0]->desc)); + devinfo->status_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[1]->desc)); + devinfo->data_in_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[2]->desc)); + devinfo->data_out_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[3]->desc)); devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256, GFP_KERNEL); -- cgit v1.2.1 From 6134041bef0aeb9cb7c8a8daf045b44513cd8396 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 16 Nov 2013 11:37:41 +0100 Subject: uas: Verify endpoint descriptors from uas_use_uas_driver() Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas-detect.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 8de030a0a4a4..b8a02e12a8a2 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -70,13 +70,23 @@ static int uas_find_endpoints(struct usb_host_interface *alt, static int uas_use_uas_driver(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_host_endpoint *eps[4] = { }; struct usb_device *udev = interface_to_usbdev(intf); unsigned long flags = id->driver_info; + int r, alt; usb_stor_adjust_quirks(udev, &flags); if (flags & US_FL_IGNORE_UAS) return 0; - return uas_find_uas_alt_setting(intf) >= 0; + alt = uas_find_uas_alt_setting(intf); + if (alt < 0) + return 0; + + r = uas_find_endpoints(&intf->altsetting[alt], eps); + if (r < 0) + return 0; + + return 1; } -- cgit v1.2.1 From 58d51444cdd066239e9b660d72319d941c758fc3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 10:23:26 +0100 Subject: uas: Not being able to alloc streams when connected through usb-3 is an error Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 754468bbbfdc..d758faef8664 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -93,7 +93,6 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp); static void uas_do_work(struct work_struct *work); static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); -static void uas_configure_endpoints(struct uas_dev_info *devinfo); static void uas_free_streams(struct uas_dev_info *devinfo); static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller); @@ -898,7 +897,7 @@ static int uas_switch_interface(struct usb_device *udev, intf->altsetting[0].desc.bInterfaceNumber, alt); } -static void uas_configure_endpoints(struct uas_dev_info *devinfo) +static int uas_configure_endpoints(struct uas_dev_info *devinfo) { struct usb_host_endpoint *eps[4] = { }; struct usb_device *udev = devinfo->udev; @@ -920,14 +919,18 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) devinfo->data_out_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(&eps[3]->desc)); - devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256, - GFP_KERNEL); - if (devinfo->qdepth < 0) { + if (udev->speed != USB_SPEED_SUPER) { devinfo->qdepth = 256; devinfo->use_streams = 0; } else { + devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, + 3, 256, GFP_KERNEL); + if (devinfo->qdepth < 0) + return devinfo->qdepth; devinfo->use_streams = 1; } + + return 0; } static void uas_free_streams(struct uas_dev_info *devinfo) @@ -984,7 +987,10 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) INIT_WORK(&devinfo->work, uas_do_work); INIT_LIST_HEAD(&devinfo->work_list); INIT_LIST_HEAD(&devinfo->dead_list); - uas_configure_endpoints(devinfo); + + result = uas_configure_endpoints(devinfo); + if (result) + goto set_alt0; result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2); if (result) @@ -1039,7 +1045,11 @@ static int uas_post_reset(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; unsigned long flags; - uas_configure_endpoints(devinfo); + if (uas_configure_endpoints(devinfo) != 0) { + shost_printk(KERN_ERR, shost, + "%s: alloc streams error after reset", __func__); + return 1; + } spin_lock_irqsave(shost->host_lock, flags); scsi_report_bus_reset(shost, 0); -- cgit v1.2.1 From 70cf0fba7625987ef16085f458e3869c6e3043c1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 10:37:23 +0100 Subject: uas: task_mgmt: Kill the sense-urb if we fail to submit the cmd urb Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d758faef8664..9c6f9f9804fd 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -550,39 +550,38 @@ err: * daft to me. */ -static int uas_submit_sense_urb(struct Scsi_Host *shost, - gfp_t gfp, unsigned int stream) +static struct urb *uas_submit_sense_urb(struct Scsi_Host *shost, + gfp_t gfp, unsigned int stream) { struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct urb *urb; urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); if (!urb) - return SCSI_MLQUEUE_DEVICE_BUSY; + return NULL; usb_anchor_urb(urb, &devinfo->sense_urbs); if (usb_submit_urb(urb, gfp)) { usb_unanchor_urb(urb); shost_printk(KERN_INFO, shost, "sense urb submission failure\n"); usb_free_urb(urb); - return SCSI_MLQUEUE_DEVICE_BUSY; + return NULL; } - return 0; + return urb; } static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp) { struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; - int err; + struct urb *urb; WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & SUBMIT_STATUS_URB) { - err = uas_submit_sense_urb(cmnd->device->host, gfp, + urb = uas_submit_sense_urb(cmnd->device->host, gfp, cmdinfo->stream); - if (err) { - return err; - } + if (!urb) + return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~SUBMIT_STATUS_URB; } @@ -726,10 +725,12 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; u16 tag = devinfo->qdepth; unsigned long flags; + struct urb *sense_urb; spin_lock_irqsave(&devinfo->lock, flags); memset(&devinfo->response, 0, sizeof(devinfo->response)); - if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) { + sense_urb = uas_submit_sense_urb(shost, GFP_ATOMIC, tag); + if (!sense_urb) { shost_printk(KERN_INFO, shost, "%s: %s: submit sense urb failed\n", __func__, fname); @@ -741,6 +742,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, "%s: %s: submit task mgmt urb failed\n", __func__, fname); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_kill_urb(sense_urb); return FAILED; } spin_unlock_irqrestore(&devinfo->lock, flags); -- cgit v1.2.1 From b83b86a352280cc8cbbf3760096c703986143b81 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 10:51:00 +0100 Subject: uas: Don't allow more then one task to run at the same time Since we use a fixed tag / stream for tasks we cannot allow more then one to run at the same time. This could happen before this time if a task timed out. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 9c6f9f9804fd..7fc4ad207752 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -53,6 +53,7 @@ struct uas_dev_info { unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; + unsigned running_task:1; struct scsi_cmnd *cmnd; spinlock_t lock; struct work_struct work; @@ -195,6 +196,7 @@ static void uas_zap_dead(struct uas_dev_info *devinfo) DATA_OUT_URB_INFLIGHT); uas_try_complete(cmnd, __func__); } + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -340,6 +342,9 @@ static void uas_stat_cmplt(struct urb *urb) if (!cmnd) { if (iu->iu_id == IU_ID_RESPONSE) { + if (!devinfo->running_task) + dev_warn(&urb->dev->dev, + "stat urb: recv unexpected response iu\n"); /* store results for uas_eh_task_mgmt() */ memcpy(&devinfo->response, iu, sizeof(devinfo->response)); } @@ -726,14 +731,26 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, u16 tag = devinfo->qdepth; unsigned long flags; struct urb *sense_urb; + int result = SUCCESS; spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->running_task) { + shost_printk(KERN_INFO, shost, + "%s: %s: error already running a task\n", + __func__, fname); + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; + } + + devinfo->running_task = 1; memset(&devinfo->response, 0, sizeof(devinfo->response)); sense_urb = uas_submit_sense_urb(shost, GFP_ATOMIC, tag); if (!sense_urb) { shost_printk(KERN_INFO, shost, "%s: %s: submit sense urb failed\n", __func__, fname); + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); return FAILED; } @@ -741,6 +758,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, shost_printk(KERN_INFO, shost, "%s: %s: submit task mgmt urb failed\n", __func__, fname); + devinfo->running_task = 0; spin_unlock_irqrestore(&devinfo->lock, flags); usb_kill_urb(sense_urb); return FAILED; @@ -748,23 +766,33 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, spin_unlock_irqrestore(&devinfo->lock, flags); if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { + /* + * Note we deliberately do not clear running_task here. If we + * allow new tasks to be submitted, there is no way to figure + * out if a received response_iu is for the failed task or for + * the new one. A bus-reset will eventually clear running_task. + */ shost_printk(KERN_INFO, shost, "%s: %s timed out\n", __func__, fname); return FAILED; } + + spin_lock_irqsave(&devinfo->lock, flags); + devinfo->running_task = 0; if (be16_to_cpu(devinfo->response.tag) != tag) { shost_printk(KERN_INFO, shost, "%s: %s failed (wrong tag %d/%d)\n", __func__, fname, be16_to_cpu(devinfo->response.tag), tag); - return FAILED; - } - if (devinfo->response.response_code != RC_TMF_COMPLETE) { + result = FAILED; + } else if (devinfo->response.response_code != RC_TMF_COMPLETE) { shost_printk(KERN_INFO, shost, "%s: %s failed (rc 0x%x)\n", __func__, fname, devinfo->response.response_code); - return FAILED; + result = FAILED; } - return SUCCESS; + spin_unlock_irqrestore(&devinfo->lock, flags); + + return result; } static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) @@ -982,6 +1010,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; devinfo->resetting = 0; + devinfo->running_task = 0; init_usb_anchor(&devinfo->cmd_urbs); init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); -- cgit v1.2.1 From e36e64930cffd94e1c37fdb82f35989384aa946b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Nov 2013 08:35:55 +0100 Subject: uas: Use GFP_NOIO rather then GFP_ATOMIC where possible We can sleep in our own workqueue (which is the whole reason for having it), and scsi error handlers are also always called from a context which may sleep. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 7fc4ad207752..8023944f2501 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -133,7 +133,7 @@ static void uas_do_work(struct work_struct *work) struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); - err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); + err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); if (!err) { cmdinfo->state &= ~IS_IN_WORK_LIST; list_del(&cmdinfo->work); @@ -745,7 +745,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, devinfo->running_task = 1; memset(&devinfo->response, 0, sizeof(devinfo->response)); - sense_urb = uas_submit_sense_urb(shost, GFP_ATOMIC, tag); + sense_urb = uas_submit_sense_urb(shost, GFP_NOIO, tag); if (!sense_urb) { shost_printk(KERN_INFO, shost, "%s: %s: submit sense urb failed\n", @@ -754,7 +754,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, spin_unlock_irqrestore(&devinfo->lock, flags); return FAILED; } - if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) { + if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { shost_printk(KERN_INFO, shost, "%s: %s: submit task mgmt urb failed\n", __func__, fname); -- cgit v1.2.1 From 0df1f663f32e5dc28cba68375b09bba5eaad103f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Nov 2013 08:47:05 +0100 Subject: uas: Add suspend/resume support Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 8023944f2501..7a16ed8e8aac 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1091,6 +1091,45 @@ static int uas_post_reset(struct usb_interface *intf) return 0; } +static int uas_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + + /* Wait for any pending requests to complete */ + flush_work(&devinfo->work); + if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + return -ETIME; + } + + return 0; +} + +static int uas_resume(struct usb_interface *intf) +{ + return 0; +} + +static int uas_reset_resume(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + unsigned long flags; + + if (uas_configure_endpoints(devinfo) != 0) { + shost_printk(KERN_ERR, shost, + "%s: alloc streams error after reset", __func__); + return -EIO; + } + + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + + return 0; +} + static void uas_disconnect(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); @@ -1114,6 +1153,9 @@ static struct usb_driver uas_driver = { .disconnect = uas_disconnect, .pre_reset = uas_pre_reset, .post_reset = uas_post_reset, + .suspend = uas_suspend, + .resume = uas_resume, + .reset_resume = uas_reset_resume, .id_table = uas_usb_ids, }; -- cgit v1.2.1 From da65c2bb99542d05f2d8f67efe6627915f4c5ea4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 11 Nov 2013 11:51:42 +0100 Subject: uas: Reset device on reboot Some BIOS-es will hang on reboot when an uas device is attached and left in uas mode on reboot. This commit adds a shutdown handler which on reboot puts the device back into usb-storage mode, fixing the hang on reboot on these systems. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 7a16ed8e8aac..019f2030ea0c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -54,6 +54,7 @@ struct uas_dev_info { unsigned use_streams:1; unsigned uas_sense_old:1; unsigned running_task:1; + unsigned shutdown:1; struct scsi_cmnd *cmnd; spinlock_t lock; struct work_struct work; @@ -1011,6 +1012,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->udev = udev; devinfo->resetting = 0; devinfo->running_task = 0; + devinfo->shutdown = 0; init_usb_anchor(&devinfo->cmd_urbs); init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); @@ -1053,6 +1055,9 @@ static int uas_pre_reset(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; unsigned long flags; + if (devinfo->shutdown) + return 0; + /* Block new requests */ spin_lock_irqsave(shost->host_lock, flags); scsi_block_requests(shost); @@ -1076,6 +1081,9 @@ static int uas_post_reset(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; unsigned long flags; + if (devinfo->shutdown) + return 0; + if (uas_configure_endpoints(devinfo) != 0) { shost_printk(KERN_ERR, shost, "%s: alloc streams error after reset", __func__); @@ -1147,6 +1155,27 @@ static void uas_disconnect(struct usb_interface *intf) kfree(devinfo); } +/* + * Put the device back in usb-storage mode on shutdown, as some BIOS-es + * hang on reboot when the device is still in uas mode. Note the reset is + * necessary as some devices won't revert to usb-storage mode without it. + */ +static void uas_shutdown(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + + if (system_state != SYSTEM_RESTART) + return; + + devinfo->shutdown = 1; + uas_free_streams(devinfo); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); + usb_reset_device(udev); +} + static struct usb_driver uas_driver = { .name = "uas", .probe = uas_probe, @@ -1156,6 +1185,7 @@ static struct usb_driver uas_driver = { .suspend = uas_suspend, .resume = uas_resume, .reset_resume = uas_reset_resume, + .drvwrap.driver.shutdown = uas_shutdown, .id_table = uas_usb_ids, }; -- cgit v1.2.1 From f323abcda35ea4bae851c9be8f115ee45cc9cf22 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Nov 2013 10:51:33 +0100 Subject: uas: Fix task-management not working when connected over USB-2 For USB-2 connections the stream-id must always be 0. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 019f2030ea0c..10e580e56d4c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -746,7 +746,8 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, devinfo->running_task = 1; memset(&devinfo->response, 0, sizeof(devinfo->response)); - sense_urb = uas_submit_sense_urb(shost, GFP_NOIO, tag); + sense_urb = uas_submit_sense_urb(shost, GFP_NOIO, + devinfo->use_streams ? tag : 0); if (!sense_urb) { shost_printk(KERN_INFO, shost, "%s: %s: submit sense urb failed\n", -- cgit v1.2.1 From c6d4579d4ba24c494d03daf656cd2ff2a9e683c6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Nov 2013 10:53:57 +0100 Subject: uas: uas_alloc_data_urb: Remove unnecessary use_streams check uas_alloc_data_urb always gets called with a stream_id value of 0 when not using streams. Removing the check makes it consistent with uas_alloc_sense_urb. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 10e580e56d4c..e06505c8f6f0 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -429,8 +429,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto out; usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, cmnd); - if (devinfo->use_streams) - urb->stream_id = stream_id; + urb->stream_id = stream_id; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; urb->sg = sdb->table.sgl; out: -- cgit v1.2.1 From 61c09ce510a1eba8595beda6aac194f42571d768 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Nov 2013 13:44:20 +0100 Subject: uas: Properly complete inflight commands on bus-reset or disconnect Before this commit the uas driver would keep track of scsi commands which still need to have some urbs submitted to the device, and complete this with an ABORT result code on bus-reset or disconnect, but in flight scsi commands which have all their urbs submitted, and thus are not part of the work list, would never get their done callback called. The problem is killed sense urbs don't have any tag info, so it is impossible to tell which scsi cmd they belong to, so merely making sure all the urbs have completed one way or the other is not enough. This commit fixes this by changing the work list to an inflight list, which keeps tracks of all inflight scsi cmnds, using the IS_IN_WORK_LIST flag to determine if actual work needs to be done in uas_do_work(), and by moving marking all inflight scsi commands as aborted and moving them to the dead list on bus-reset or disconnect. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e06505c8f6f0..1a188399e090 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -58,7 +58,7 @@ struct uas_dev_info { struct scsi_cmnd *cmnd; spinlock_t lock; struct work_struct work; - struct list_head work_list; + struct list_head inflight_list; struct list_head dead_list; }; @@ -86,7 +86,7 @@ struct uas_cmd_info { struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; - struct list_head work; + struct list_head inflight; struct list_head dead; }; @@ -125,34 +125,36 @@ static void uas_do_work(struct work_struct *work) struct uas_dev_info *devinfo = container_of(work, struct uas_dev_info, work); struct uas_cmd_info *cmdinfo; - struct uas_cmd_info *temp; unsigned long flags; int err; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) { + list_for_each_entry(cmdinfo, &devinfo->inflight_list, inflight) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + + if (!(cmdinfo->state & IS_IN_WORK_LIST)) + continue; + err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); - if (!err) { + if (!err) cmdinfo->state &= ~IS_IN_WORK_LIST; - list_del(&cmdinfo->work); - } else { + else schedule_work(&devinfo->work); - } } spin_unlock_irqrestore(&devinfo->lock, flags); } -static void uas_abort_work(struct uas_dev_info *devinfo) +static void uas_abort_inflight(struct uas_dev_info *devinfo) { struct uas_cmd_info *cmdinfo; struct uas_cmd_info *temp; unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) { + list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, + inflight) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); @@ -160,7 +162,7 @@ static void uas_abort_work(struct uas_dev_info *devinfo) WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state &= ~IS_IN_WORK_LIST; - list_del(&cmdinfo->work); + list_del(&cmdinfo->inflight); list_add_tail(&cmdinfo->dead, &devinfo->dead_list); } spin_unlock_irqrestore(&devinfo->lock, flags); @@ -173,7 +175,6 @@ static void uas_add_work(struct uas_cmd_info *cmdinfo) struct uas_dev_info *devinfo = cmnd->device->hostdata; WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); - list_add_tail(&cmdinfo->work, &devinfo->work_list); cmdinfo->state |= IS_IN_WORK_LIST; schedule_work(&devinfo->work); } @@ -289,7 +290,8 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) scmd_printk(KERN_INFO, cmnd, "abort completed\n"); cmnd->result = DID_ABORT << 16; list_del(&cmdinfo->dead); - } + } else + list_del(&cmdinfo->inflight); cmnd->scsi_done(cmnd); return 0; } @@ -717,6 +719,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, uas_add_work(cmdinfo); } + list_add_tail(&cmdinfo->inflight, &devinfo->inflight_list); spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } @@ -807,11 +810,9 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) spin_lock_irqsave(&devinfo->lock, flags); WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; + cmdinfo->state &= ~IS_IN_WORK_LIST; + list_del(&cmdinfo->inflight); list_add_tail(&cmdinfo->dead, &devinfo->dead_list); - if (cmdinfo->state & IS_IN_WORK_LIST) { - list_del(&cmdinfo->work); - cmdinfo->state &= ~IS_IN_WORK_LIST; - } if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); @@ -847,7 +848,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); devinfo->resetting = 1; - uas_abort_work(devinfo); + uas_abort_inflight(devinfo); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); @@ -1018,7 +1019,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) init_usb_anchor(&devinfo->data_urbs); spin_lock_init(&devinfo->lock); INIT_WORK(&devinfo->work, uas_do_work); - INIT_LIST_HEAD(&devinfo->work_list); + INIT_LIST_HEAD(&devinfo->inflight_list); INIT_LIST_HEAD(&devinfo->dead_list); result = uas_configure_endpoints(devinfo); @@ -1145,7 +1146,7 @@ static void uas_disconnect(struct usb_interface *intf) devinfo->resetting = 1; cancel_work_sync(&devinfo->work); - uas_abort_work(devinfo); + uas_abort_inflight(devinfo); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); -- cgit v1.2.1 From da3033ea08397fb70279f22789002e6001432f3d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Nov 2013 13:57:24 +0100 Subject: uas: add uas_mark_cmd_dead helper function Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 1a188399e090..7810c135a69e 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -146,6 +146,21 @@ static void uas_do_work(struct work_struct *work) spin_unlock_irqrestore(&devinfo->lock, flags); } +static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, + struct uas_cmd_info *cmdinfo, const char *caller) +{ + struct scsi_pointer *scp = (void *)cmdinfo; + struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); + + uas_log_cmd_state(cmnd, caller); + WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); + WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); + cmdinfo->state |= COMMAND_ABORTED; + cmdinfo->state &= ~IS_IN_WORK_LIST; + list_del(&cmdinfo->inflight); + list_add_tail(&cmdinfo->dead, &devinfo->dead_list); +} + static void uas_abort_inflight(struct uas_dev_info *devinfo) { struct uas_cmd_info *cmdinfo; @@ -154,17 +169,8 @@ static void uas_abort_inflight(struct uas_dev_info *devinfo) spin_lock_irqsave(&devinfo->lock, flags); list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, - inflight) { - struct scsi_pointer *scp = (void *)cmdinfo; - struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, - SCp); - uas_log_cmd_state(cmnd, __func__); - WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); - cmdinfo->state |= COMMAND_ABORTED; - cmdinfo->state &= ~IS_IN_WORK_LIST; - list_del(&cmdinfo->inflight); - list_add_tail(&cmdinfo->dead, &devinfo->dead_list); - } + inflight) + uas_mark_cmd_dead(devinfo, cmdinfo, __func__); spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -806,13 +812,8 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) unsigned long flags; int ret; - uas_log_cmd_state(cmnd, __func__); spin_lock_irqsave(&devinfo->lock, flags); - WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); - cmdinfo->state |= COMMAND_ABORTED; - cmdinfo->state &= ~IS_IN_WORK_LIST; - list_del(&cmdinfo->inflight); - list_add_tail(&cmdinfo->dead, &devinfo->dead_list); + uas_mark_cmd_dead(devinfo, cmdinfo, __func__); if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); -- cgit v1.2.1 From 040d1a8f11f390f36a8cd7fc04c0c836639b0b6a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Nov 2013 14:02:12 +0100 Subject: uas: cmdinfo: use only one list head cmds are either on the inflight list or on the dead list, never both, so we only need one list head. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 7810c135a69e..cfe0102fcbae 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -86,8 +86,7 @@ struct uas_cmd_info { struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; - struct list_head inflight; - struct list_head dead; + struct list_head list; }; /* I hate forward declarations, but I actually have a loop */ @@ -129,7 +128,7 @@ static void uas_do_work(struct work_struct *work) int err; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry(cmdinfo, &devinfo->inflight_list, inflight) { + list_for_each_entry(cmdinfo, &devinfo->inflight_list, list) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); @@ -157,8 +156,7 @@ static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state &= ~IS_IN_WORK_LIST; - list_del(&cmdinfo->inflight); - list_add_tail(&cmdinfo->dead, &devinfo->dead_list); + list_move_tail(&cmdinfo->list, &devinfo->dead_list); } static void uas_abort_inflight(struct uas_dev_info *devinfo) @@ -168,8 +166,7 @@ static void uas_abort_inflight(struct uas_dev_info *devinfo) unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, - inflight) + list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, list) uas_mark_cmd_dead(devinfo, cmdinfo, __func__); spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -192,7 +189,7 @@ static void uas_zap_dead(struct uas_dev_info *devinfo) unsigned long flags; spin_lock_irqsave(&devinfo->lock, flags); - list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, dead) { + list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, list) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); @@ -295,9 +292,8 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) if (cmdinfo->state & COMMAND_ABORTED) { scmd_printk(KERN_INFO, cmnd, "abort completed\n"); cmnd->result = DID_ABORT << 16; - list_del(&cmdinfo->dead); - } else - list_del(&cmdinfo->inflight); + } + list_del(&cmdinfo->list); cmnd->scsi_done(cmnd); return 0; } @@ -725,7 +721,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, uas_add_work(cmdinfo); } - list_add_tail(&cmdinfo->inflight, &devinfo->inflight_list); + list_add_tail(&cmdinfo->list, &devinfo->inflight_list); spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } -- cgit v1.2.1 From c6f63207a3ba689025b2120792ea831cf72f9a81 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 13 Nov 2013 09:24:15 +0100 Subject: uas: Fix command / task mgmt submission racing with disconnect Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index cfe0102fcbae..8c685801e267 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -670,13 +670,15 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); + spin_lock_irqsave(&devinfo->lock, flags); + if (devinfo->resetting) { cmnd->result = DID_ERROR << 16; cmnd->scsi_done(cmnd); + spin_unlock_irqrestore(&devinfo->lock, flags); return 0; } - spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->cmnd) { spin_unlock_irqrestore(&devinfo->lock, flags); return SCSI_MLQUEUE_DEVICE_BUSY; @@ -740,6 +742,11 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, spin_lock_irqsave(&devinfo->lock, flags); + if (devinfo->resetting) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; + } + if (devinfo->running_task) { shost_printk(KERN_INFO, shost, "%s: %s: error already running a task\n", @@ -809,6 +816,12 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) int ret; spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return FAILED; + } + uas_mark_cmd_dead(devinfo, cmdinfo, __func__); if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); -- cgit v1.2.1 From 21fc05b680f6fba868b41e2713ade3fdea4049f9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 13 Nov 2013 09:32:22 +0100 Subject: uas: Fix memory management The scsi-host structure is refcounted, scsi_remove_host tears down the scsi-host but does not decrement the refcount, so we need to call scsi_put_host on disconnect to get the underlying memory to be freed. After calling scsi_remove_host, the scsi-core may still hold a reference to the scsi-host, iow we may still get called after uas_disconnect, but we do our own life cycle management of uas_devinfo, freeing it on disconnect, and thus may end up using devinfo after it has been freed. Switch to letting scsi_host_alloc allocate and manage the memory for us. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 8c685801e267..d81d041842f4 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -315,7 +315,7 @@ static void uas_stat_cmplt(struct urb *urb) { struct iu *iu = urb->transfer_buffer; struct Scsi_Host *shost = urb->context; - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; struct scsi_cmnd *cmnd; struct uas_cmd_info *cmdinfo; unsigned long flags; @@ -562,7 +562,7 @@ err: static struct urb *uas_submit_sense_urb(struct Scsi_Host *shost, gfp_t gfp, unsigned int stream) { - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; struct urb *urb; urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); @@ -734,7 +734,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, const char *fname, u8 function) { struct Scsi_Host *shost = cmnd->device->host; - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; u16 tag = devinfo->qdepth; unsigned long flags; struct urb *sense_urb; @@ -879,7 +879,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) static int uas_slave_alloc(struct scsi_device *sdev) { - sdev->hostdata = (void *)sdev->host->hostdata[0]; + sdev->hostdata = (void *)sdev->host->hostdata; return 0; } @@ -1005,11 +1005,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) if (uas_switch_interface(udev, intf)) return -ENODEV; - devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL); - if (!devinfo) - goto set_alt0; - - shost = scsi_host_alloc(&uas_host_template, sizeof(void *)); + shost = scsi_host_alloc(&uas_host_template, + sizeof(struct uas_dev_info)); if (!shost) goto set_alt0; @@ -1019,6 +1016,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->max_channel = 0; shost->sg_tablesize = udev->bus->sg_tablesize; + devinfo = (struct uas_dev_info *)shost->hostdata; devinfo->intf = intf; devinfo->udev = udev; devinfo->resetting = 0; @@ -1044,8 +1042,6 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) if (result) goto free_streams; - shost->hostdata[0] = (unsigned long)devinfo; - scsi_scan_host(shost); usb_set_intfdata(intf, shost); return result; @@ -1054,7 +1050,6 @@ free_streams: uas_free_streams(devinfo); set_alt0: usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); - kfree(devinfo); if (shost) scsi_host_put(shost); return result; @@ -1063,7 +1058,7 @@ set_alt0: static int uas_pre_reset(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; unsigned long flags; if (devinfo->shutdown) @@ -1089,7 +1084,7 @@ static int uas_pre_reset(struct usb_interface *intf) static int uas_post_reset(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; unsigned long flags; if (devinfo->shutdown) @@ -1113,7 +1108,7 @@ static int uas_post_reset(struct usb_interface *intf) static int uas_suspend(struct usb_interface *intf, pm_message_t message) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; /* Wait for any pending requests to complete */ flush_work(&devinfo->work); @@ -1133,7 +1128,7 @@ static int uas_resume(struct usb_interface *intf) static int uas_reset_resume(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; unsigned long flags; if (uas_configure_endpoints(devinfo) != 0) { @@ -1152,7 +1147,7 @@ static int uas_reset_resume(struct usb_interface *intf) static void uas_disconnect(struct usb_interface *intf) { struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; devinfo->resetting = 1; cancel_work_sync(&devinfo->work); @@ -1163,7 +1158,7 @@ static void uas_disconnect(struct usb_interface *intf) uas_zap_dead(devinfo); scsi_remove_host(shost); uas_free_streams(devinfo); - kfree(devinfo); + scsi_host_put(shost); } /* @@ -1176,7 +1171,7 @@ static void uas_shutdown(struct device *dev) struct usb_interface *intf = to_usb_interface(dev); struct usb_device *udev = interface_to_usbdev(intf); struct Scsi_Host *shost = usb_get_intfdata(intf); - struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; if (system_state != SYSTEM_RESTART) return; -- cgit v1.2.1 From 3a4462e0e2fe8f715f54147d36b5433a7ff5a85a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2013 11:06:13 +0100 Subject: uas: Clear cmdinfo on command queue-ing The scsi error handling path re-uses previously queued up (and errored-out) cmds. If such a re-used cmd had a data-phase then cmdinfo will have data_in_urb / data_out_urb still set to the free-ed urbs from the errored-out cmd, and they will get free-ed a second time when the error handling cmd completes, corrupting the kernel heap. Clearing cmdinfo on command queue-ing fixes this, and seems like a good idea in general. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d81d041842f4..fceffccc1be1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -684,6 +684,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, return SCSI_MLQUEUE_DEVICE_BUSY; } + memset(cmdinfo, 0, sizeof(*cmdinfo)); + if (blk_rq_tagged(cmnd->request)) { cmdinfo->stream = cmnd->request->tag + 2; } else { -- cgit v1.2.1 From 673331c87c492898a9152f3754f3174128e1514a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2013 14:27:27 +0100 Subject: uas: Use the right error codes for different kinds of errors Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index fceffccc1be1..6ec48c2daf45 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -146,7 +146,8 @@ static void uas_do_work(struct work_struct *work) } static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, - struct uas_cmd_info *cmdinfo, const char *caller) + struct uas_cmd_info *cmdinfo, + int result, const char *caller) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); @@ -156,10 +157,12 @@ static void uas_mark_cmd_dead(struct uas_dev_info *devinfo, WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED); cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state &= ~IS_IN_WORK_LIST; + cmnd->result = result << 16; list_move_tail(&cmdinfo->list, &devinfo->dead_list); } -static void uas_abort_inflight(struct uas_dev_info *devinfo) +static void uas_abort_inflight(struct uas_dev_info *devinfo, int result, + const char *caller) { struct uas_cmd_info *cmdinfo; struct uas_cmd_info *temp; @@ -167,7 +170,7 @@ static void uas_abort_inflight(struct uas_dev_info *devinfo) spin_lock_irqsave(&devinfo->lock, flags); list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, list) - uas_mark_cmd_dead(devinfo, cmdinfo, __func__); + uas_mark_cmd_dead(devinfo, cmdinfo, result, caller); spin_unlock_irqrestore(&devinfo->lock, flags); } @@ -289,10 +292,8 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) cmdinfo->state |= COMMAND_COMPLETED; usb_free_urb(cmdinfo->data_in_urb); usb_free_urb(cmdinfo->data_out_urb); - if (cmdinfo->state & COMMAND_ABORTED) { + if (cmdinfo->state & COMMAND_ABORTED) scmd_printk(KERN_INFO, cmnd, "abort completed\n"); - cmnd->result = DID_ABORT << 16; - } list_del(&cmdinfo->list); cmnd->scsi_done(cmnd); return 0; @@ -824,7 +825,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) return FAILED; } - uas_mark_cmd_dead(devinfo, cmdinfo, __func__); + uas_mark_cmd_dead(devinfo, cmdinfo, DID_ABORT, __func__); if (cmdinfo->state & COMMAND_INFLIGHT) { spin_unlock_irqrestore(&devinfo->lock, flags); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); @@ -860,7 +861,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); devinfo->resetting = 1; - uas_abort_inflight(devinfo); + uas_abort_inflight(devinfo, DID_RESET, __func__); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); @@ -1153,7 +1154,7 @@ static void uas_disconnect(struct usb_interface *intf) devinfo->resetting = 1; cancel_work_sync(&devinfo->work); - uas_abort_inflight(devinfo); + uas_abort_inflight(devinfo, DID_NO_CONNECT, __func__); usb_kill_anchored_urbs(&devinfo->cmd_urbs); usb_kill_anchored_urbs(&devinfo->sense_urbs); usb_kill_anchored_urbs(&devinfo->data_urbs); -- cgit v1.2.1 From 876285cc9cf418f626375f28bb0fc5d88012f12d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Nov 2013 08:52:42 +0100 Subject: uas: Improve error reporting Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 60 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 6ec48c2daf45..f09205b162e4 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -411,6 +411,12 @@ static void uas_data_cmplt(struct urb *urb) if (sdb == NULL) { WARN_ON_ONCE(1); } else if (urb->status) { + if (urb->status != -ECONNRESET) { + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, + "data cmplt err %d stream %d\n", + urb->status, urb->stream_id); + } /* error: no data transfered */ sdb->resid = sdb->length; } else { @@ -420,6 +426,17 @@ static void uas_data_cmplt(struct urb *urb) spin_unlock_irqrestore(&devinfo->lock, flags); } +static void uas_cmd_cmplt(struct urb *urb) +{ + struct scsi_cmnd *cmnd = urb->context; + + if (urb->status) { + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, "cmd cmplt err %d\n", urb->status); + } + usb_free_urb(urb); +} + static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, unsigned int pipe, u16 stream_id, struct scsi_cmnd *cmnd, @@ -497,7 +514,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, - usb_free_urb, NULL); + uas_cmd_cmplt, cmnd); urb->transfer_flags |= URB_FREE_BUFFER; out: return urb; @@ -537,13 +554,15 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, } usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), - usb_free_urb, NULL); + uas_cmd_cmplt, cmnd); urb->transfer_flags |= URB_FREE_BUFFER; usb_anchor_urb(urb, &devinfo->cmd_urbs); err = usb_submit_urb(urb, gfp); if (err) { usb_unanchor_urb(urb); + uas_log_cmd_state(cmnd, __func__); + scmd_printk(KERN_ERR, cmnd, "task submission err %d\n", err); goto err; } @@ -560,20 +579,25 @@ err: * daft to me. */ -static struct urb *uas_submit_sense_urb(struct Scsi_Host *shost, +static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp, unsigned int stream) { + struct Scsi_Host *shost = cmnd->device->host; struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; struct urb *urb; + int err; urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); if (!urb) return NULL; usb_anchor_urb(urb, &devinfo->sense_urbs); - if (usb_submit_urb(urb, gfp)) { + err = usb_submit_urb(urb, gfp); + if (err) { usb_unanchor_urb(urb); + uas_log_cmd_state(cmnd, __func__); shost_printk(KERN_INFO, shost, - "sense urb submission failure\n"); + "sense urb submission error %d stream %d\n", + err, stream); usb_free_urb(urb); return NULL; } @@ -585,11 +609,11 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, { struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct urb *urb; + int err; WARN_ON_ONCE(!spin_is_locked(&devinfo->lock)); if (cmdinfo->state & SUBMIT_STATUS_URB) { - urb = uas_submit_sense_urb(cmnd->device->host, gfp, - cmdinfo->stream); + urb = uas_submit_sense_urb(cmnd, gfp, cmdinfo->stream); if (!urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~SUBMIT_STATUS_URB; @@ -606,10 +630,13 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & SUBMIT_DATA_IN_URB) { usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); - if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) { + err = usb_submit_urb(cmdinfo->data_in_urb, gfp); + if (err) { usb_unanchor_urb(cmdinfo->data_in_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "data in urb submission failure\n"); + "data in urb submission error %d stream %d\n", + err, cmdinfo->data_in_urb->stream_id); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; @@ -627,10 +654,13 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); - if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) { + err = usb_submit_urb(cmdinfo->data_out_urb, gfp); + if (err) { usb_unanchor_urb(cmdinfo->data_out_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "data out urb submission failure\n"); + "data out urb submission error %d stream %d\n", + err, cmdinfo->data_out_urb->stream_id); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; @@ -646,10 +676,12 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & SUBMIT_CMD_URB) { usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); - if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) { + err = usb_submit_urb(cmdinfo->cmd_urb, gfp); + if (err) { usb_unanchor_urb(cmdinfo->cmd_urb); + uas_log_cmd_state(cmnd, __func__); scmd_printk(KERN_INFO, cmnd, - "cmd urb submission failure\n"); + "cmd urb submission error %d\n", err); return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->cmd_urb = NULL; @@ -760,7 +792,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, devinfo->running_task = 1; memset(&devinfo->response, 0, sizeof(devinfo->response)); - sense_urb = uas_submit_sense_urb(shost, GFP_NOIO, + sense_urb = uas_submit_sense_urb(cmnd, GFP_NOIO, devinfo->use_streams ? tag : 0); if (!sense_urb) { shost_printk(KERN_INFO, shost, -- cgit v1.2.1 From 8e453155d7f8dfa53863ba6f8da6c68f7c17ece4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 15 Nov 2013 10:04:11 +0100 Subject: uas: Add some data in/out ready iu sanity checks Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f09205b162e4..62086829af14 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -379,9 +379,19 @@ static void uas_stat_cmplt(struct urb *urb) uas_try_complete(cmnd, __func__); break; case IU_ID_READ_READY: + if (!cmdinfo->data_in_urb || + (cmdinfo->state & DATA_IN_URB_INFLIGHT)) { + scmd_printk(KERN_ERR, cmnd, "unexpected read rdy\n"); + break; + } uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); break; case IU_ID_WRITE_READY: + if (!cmdinfo->data_out_urb || + (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) { + scmd_printk(KERN_ERR, cmnd, "unexpected write rdy\n"); + break; + } uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; default: -- cgit v1.2.1 From 37599f9603bed3d72becdc1a59c164576df9fbfc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 15 Nov 2013 10:04:31 +0100 Subject: uas: Make sure sg elements are properly aligned Copy the sg alignment trick from the usb-storage driver, without this I'm seeing intermittent errors when using uas devices with an ehci controller. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 62086829af14..ad97615b75b1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -925,6 +925,24 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) static int uas_slave_alloc(struct scsi_device *sdev) { sdev->hostdata = (void *)sdev->host->hostdata; + + /* USB has unusual DMA-alignment requirements: Although the + * starting address of each scatter-gather element doesn't matter, + * the length of each element except the last must be divisible + * by the Bulk maxpacket value. There's currently no way to + * express this by block-layer constraints, so we'll cop out + * and simply require addresses to be aligned at 512-byte + * boundaries. This is okay since most block I/O involves + * hardware sectors that are multiples of 512 bytes in length, + * and since host controllers up through USB 2.0 have maxpacket + * values no larger than 512. + * + * But it doesn't suffice for Wireless USB, where Bulk maxpacket + * values can be as large as 2048. To make that work properly + * will require changes to the block layer. + */ + blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); + return 0; } -- cgit v1.2.1 From dc88608dba784f902b3127fd68d0c4f92a532cd0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Sep 2013 13:27:15 +0200 Subject: uas: remove BROKEN xhci streams support is fixed, unblock usb attached scsi. Signed-off-by: Gerd Hoffmann Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 666dcb692e12..13b5bfbaf951 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -204,7 +204,7 @@ config USB_STORAGE_ENE_UB6250 config USB_UAS tristate "USB Attached SCSI" - depends on SCSI && USB_STORAGE && BROKEN + depends on SCSI && USB_STORAGE help The USB Attached SCSI protocol is supported by some USB storage devices. It permits higher performance by supporting -- cgit v1.2.1 From f50a4968deb7bf38c46f5baf62db9431a099531a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 28 Oct 2013 10:48:04 +0000 Subject: uas: Add Hans de Goede as uas maintainer At the kernel-summit Sarah Sharp asked me if I was willing to become the uas maintainer. I said yes, and here is a patch to make this official. Also remove Matthew Wilcox and Sarah Sharp as maintainers at their request. I've also added myself to the module's author tag, so that if people look there rather then in maintainers they will know they should bug me about uas too. Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index ad97615b75b1..08e9710f193d 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -2,6 +2,7 @@ * USB Attached SCSI * Note that this is not the same as the USB Mass Storage driver * + * Copyright Hans de Goede for Red Hat, Inc. 2013 * Copyright Matthew Wilcox for Intel Corp, 2010 * Copyright Sarah Sharp for Intel Corp, 2010 * @@ -1261,4 +1262,5 @@ static struct usb_driver uas_driver = { module_usb_driver(uas_driver); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matthew Wilcox and Sarah Sharp"); +MODULE_AUTHOR( + "Hans de Goede , Matthew Wilcox and Sarah Sharp"); -- cgit v1.2.1 From 7cace978fba5d0ec6eed50509cda40eea85f8e98 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Oct 2013 11:36:43 +0100 Subject: uas: Remove comment about registering a uas scsi controller for each usb bus Although an interesting concept, I don't think that this is a good idea: -This will result in lots of "virtual" scsi controllers confusing users -If we get a scsi-bus-reset we will now need to do a usb-device-reset of all uas devices on the same usb bus, which is something to avoid if possible Signed-off-by: Hans de Goede Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 08e9710f193d..a7ac97cc5949 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1050,12 +1050,6 @@ static void uas_free_streams(struct uas_dev_info *devinfo) usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL); } -/* - * XXX: What I'd like to do here is register a SCSI host for each USB host in - * the system. Follow usb-storage's design of registering a SCSI host for - * each USB device for the moment. Can implement this by walking up the - * USB hierarchy until we find a USB host. - */ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) { int result = -ENOMEM; -- cgit v1.2.1 From 50e8725e7c429701e530439013f9681e1fa36b5d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 21 Feb 2014 09:27:30 -0800 Subject: xhci: Refactor command watchdog and fix split string. In preparation for fixing this function for streams endpoints, refactor code in the command watchdog timeout function into two new functions. One kills all URBs on a ring (either stream or endpoint), the other kills all URBs associated with an endpoint. Fix a split string while we're at it. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 70 ++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b3d27e6467ea..58cbc06ecdf9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -900,6 +900,43 @@ remove_finished_td: /* Return to the event handler with xhci->lock re-acquired */ } +static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + struct xhci_td *cur_td; + + while (!list_empty(&ring->td_list)) { + cur_td = list_first_entry(&ring->td_list, + struct xhci_td, td_list); + list_del_init(&cur_td->td_list); + if (!list_empty(&cur_td->cancelled_td_list)) + list_del_init(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN); + } +} + +static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, + int slot_id, int ep_index) +{ + struct xhci_td *cur_td; + struct xhci_virt_ep *ep; + struct xhci_ring *ring; + + ep = &xhci->devs[slot_id]->eps[ep_index]; + ring = ep->ring; + if (!ring) + return; + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, ep index %u", + slot_id, ep_index); + xhci_kill_ring_urbs(xhci, ring); + while (!list_empty(&ep->cancelled_td_list)) { + cur_td = list_first_entry(&ep->cancelled_td_list, + struct xhci_td, cancelled_td_list); + list_del_init(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN); + } +} + /* Watchdog timer function for when a stop endpoint command fails to complete. * In this case, we assume the host controller is broken or dying or dead. The * host may still be completing some other events, so we have to be careful to @@ -923,9 +960,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) { struct xhci_hcd *xhci; struct xhci_virt_ep *ep; - struct xhci_virt_ep *temp_ep; - struct xhci_ring *ring; - struct xhci_td *cur_td; int ret, i, j; unsigned long flags; @@ -982,34 +1016,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) for (i = 0; i < MAX_HC_SLOTS; i++) { if (!xhci->devs[i]) continue; - for (j = 0; j < 31; j++) { - temp_ep = &xhci->devs[i]->eps[j]; - ring = temp_ep->ring; - if (!ring) - continue; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Killing URBs for slot ID %u, " - "ep index %u", i, j); - while (!list_empty(&ring->td_list)) { - cur_td = list_first_entry(&ring->td_list, - struct xhci_td, - td_list); - list_del_init(&cur_td->td_list); - if (!list_empty(&cur_td->cancelled_td_list)) - list_del_init(&cur_td->cancelled_td_list); - xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN); - } - while (!list_empty(&temp_ep->cancelled_td_list)) { - cur_td = list_first_entry( - &temp_ep->cancelled_td_list, - struct xhci_td, - cancelled_td_list); - list_del_init(&cur_td->cancelled_td_list); - xhci_giveback_urb_in_irq(xhci, cur_td, - -ESHUTDOWN); - } - } + for (j = 0; j < 31; j++) + xhci_kill_endpoint_urbs(xhci, i, j); } spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, -- cgit v1.2.1 From 21d0e51bfb290349adc02fa8fec716d77f53df51 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 21 Feb 2014 14:29:02 -0800 Subject: xhci: Kill streams URBs when the host dies. If the host controller stops responding to commands, we need to kill all the URBs that were queued to all endpoints. The current code would only kill URBs that had been queued to the endpoint rings. ep->ring is set to NULL if streams has been enabled for the endpoint, which means URBs submitted with a non-zero stream_id would never get killed. Fix this. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-ring.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 58cbc06ecdf9..5f926bea5ab1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -922,13 +922,27 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring; ep = &xhci->devs[slot_id]->eps[ep_index]; - ring = ep->ring; - if (!ring) - return; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Killing URBs for slot ID %u, ep index %u", - slot_id, ep_index); - xhci_kill_ring_urbs(xhci, ring); + if ((ep->ep_state & EP_HAS_STREAMS) || + (ep->ep_state & EP_GETTING_NO_STREAMS)) { + int stream_id; + + for (stream_id = 0; stream_id < ep->stream_info->num_streams; + stream_id++) { + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, ep index %u, stream %u", + slot_id, ep_index, stream_id + 1); + xhci_kill_ring_urbs(xhci, + ep->stream_info->stream_rings[stream_id]); + } + } else { + ring = ep->ring; + if (!ring) + return; + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Killing URBs for slot ID %u, ep index %u", + slot_id, ep_index); + xhci_kill_ring_urbs(xhci, ring); + } while (!list_empty(&ep->cancelled_td_list)) { cur_td = list_first_entry(&ep->cancelled_td_list, struct xhci_td, cancelled_td_list); -- cgit v1.2.1 From 14aec589327a6fc4035f5327d90ac5548f501c4c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 11 Feb 2014 20:36:04 +0100 Subject: storage: accept some UAS devices if streams are unavailable On some older XHCIs streams are not supported and the UAS driver will fail at probe time. For those devices storage should try to bind to UAS devices. This patch adds a flag for stream support to HCDs and evaluates it. [Note: Sarah fixed a bug where the USB 2.0 root hub, not USB 3.0 root hub would get marked as being able to support streams.] Signed-off-by: Oliver Neukum Signed-off-by: Sarah Sharp Acked-by: Hans de Goede --- drivers/usb/host/xhci-pci.c | 3 +++ drivers/usb/host/xhci-plat.c | 3 +++ drivers/usb/storage/uas-detect.h | 4 ++++ 3 files changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 6c03584ac15f..cbf0c5cffc92 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -222,6 +222,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + return 0; put_usb3_hcd: diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8affef910782..151901ce1ba9 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -158,6 +158,9 @@ static int xhci_plat_probe(struct platform_device *pdev) */ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); if (ret) goto put_usb3_hcd; diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index b8a02e12a8a2..bb05b984d5f6 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -72,6 +72,7 @@ static int uas_use_uas_driver(struct usb_interface *intf, { struct usb_host_endpoint *eps[4] = { }; struct usb_device *udev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); unsigned long flags = id->driver_info; int r, alt; @@ -80,6 +81,9 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (flags & US_FL_IGNORE_UAS) return 0; + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) + return 0; + alt = uas_find_uas_alt_setting(intf); if (alt < 0) return 0; -- cgit v1.2.1 From eee3f15d5f1f4f0c283dd4db67dc1b874a2852d1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Feb 2014 10:58:43 -0600 Subject: usb: musb: avoid NULL pointer dereference instead of relying on the otg pointer, which can be NULL in certain cases, we can use the gadget and host pointers we already hold inside struct musb. Cc: Tested-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fc192ad9cc6a..6c1dd42e5894 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -438,7 +438,6 @@ void musb_hnp_stop(struct musb *musb) static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, u8 devctl) { - struct usb_otg *otg = musb->xceiv->otg; irqreturn_t handled = IRQ_NONE; dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl, @@ -653,7 +652,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_B_PERIPHERAL: musb_g_suspend(musb); - musb->is_active = otg->gadget->b_hnp_enable; + musb->is_active = musb->g.b_hnp_enable; if (musb->is_active) { musb->xceiv->state = OTG_STATE_B_WAIT_ACON; dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); @@ -669,7 +668,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_A_HOST: musb->xceiv->state = OTG_STATE_A_SUSPEND; - musb->is_active = otg->host->b_hnp_enable; + musb->is_active = musb->hcd->self.b_hnp_enable; break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ -- cgit v1.2.1 From 7241a21a7178eb327ca2c8f2ffc325b10e1e05bd Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 22 Feb 2014 04:29:15 +0300 Subject: usb: phy: rcar-gen2-usb: always use 'dev' variable in probe() method The probe() method has the 'dev' local variable declared and used but strangely not in all cases where it should be... Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-rcar-gen2-usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c index 551e0a6c0e22..388d89f6b141 100644 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ b/drivers/usb/phy/phy-rcar-gen2-usb.c @@ -177,15 +177,15 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) struct clk *clk; int retval; - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); if (!pdata) { dev_err(dev, "No platform data\n"); return -EINVAL; } - clk = devm_clk_get(&pdev->dev, "usbhs"); + clk = devm_clk_get(dev, "usbhs"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Can't get the clock\n"); + dev_err(dev, "Can't get the clock\n"); return PTR_ERR(clk); } -- cgit v1.2.1 From 3f83e53878b9cb3314d76f957a497562ce508984 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2014 20:49:53 +0100 Subject: usb: musb: USB_MUSB_DUAL_ROLE/USB_MUSB_GADGET should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `txstate': musb_gadget.c:(.text+0x35955a): undefined reference to `dma_unmap_single' musb_gadget.c:(.text+0x35957e): undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `musb_g_giveback': (.text+0x359672): undefined reference to `dma_mapping_error' drivers/built-in.o: In function `musb_g_giveback': (.text+0x3596ba): undefined reference to `dma_unmap_single' drivers/built-in.o: In function `musb_g_giveback': (.text+0x3596e0): undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `rxstate': musb_gadget.c:(.text+0x3599d0): undefined reference to `dma_unmap_single' musb_gadget.c:(.text+0x3599f6): undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `musb_gadget_queue': musb_gadget.c:(.text+0x35a8c0): undefined reference to `dma_map_single' musb_gadget.c:(.text+0x35a8d0): undefined reference to `dma_mapping_error' musb_gadget.c:(.text+0x35a906): undefined reference to `dma_sync_single_for_cpu' musb_gadget.c:(.text+0x35a9a0): undefined reference to `dma_unmap_single' musb_gadget.c:(.text+0x35a9c8): undefined reference to `dma_sync_single_for_cpu' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 688dc8bb192d..8b789792f6fa 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -43,6 +43,7 @@ config USB_MUSB_HOST config USB_MUSB_GADGET bool "Gadget only mode" depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC + depends on HAS_DMA help Select this when you want to use MUSB in gadget mode only, thereby the host feature will be regressed. @@ -50,6 +51,7 @@ config USB_MUSB_GADGET config USB_MUSB_DUAL_ROLE bool "Dual Role mode" depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC)) + depends on HAS_DMA help This is the default mode of working of MUSB controller where both host and gadget features are enabled. -- cgit v1.2.1 From 0e06bcac79ce6bb49adee22a991206933b7ea52d Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Thu, 20 Feb 2014 09:26:25 +0100 Subject: usb: gadget: at91_udc: fix ep maxpacket initialisation This patch fixes problem with unnecessary usb_ep_set_maxpacket_limit() usage. It should not be used in at91udc_probe() function, where maxpacket values are set for field "maxpacket" of struct at91_ep, which is representation of endpoint in driver internals. Function usb_ep_set_maxpacket_limit() is called in udc_reinit() function, where struct usb_ep instances are initialised with values set previously in struct at91_ep instances. So it's very important to initialise it properly. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index cea8c20a1425..f605ad8c1902 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev) /* newer chips have more FIFO memory than rm9200 */ if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512); - usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512); + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + udc->ep[4].maxpacket = 512; + udc->ep[5].maxpacket = 512; } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); + udc->ep[3].maxpacket = 64; } else if (cpu_is_at91sam9263()) { - usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); - usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; } udc->udp_baseaddr = ioremap(res->start, resource_size(res)); -- cgit v1.2.1 From f3af36511e60669a2b5644d17378c7ea4e42d8b1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 Dec 2013 14:19:33 -0600 Subject: usb: dwc3: gadget: always enable IOC on bulk/interrupt transfers by setting IOC always, we can recycle TRBs a lot sooner at the expense of some increased CPU load. The extra load seems to be quite minimal on OMAP5 devices (instead of 1 IRQ for one MSC transfer, we get CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2da0a5a2803a..9e878d9bc909 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -771,9 +771,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; else trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; - - if (!req->request.no_interrupt && !chain) - trb->ctrl |= DWC3_TRB_CTRL_IOC; break; case USB_ENDPOINT_XFER_BULK: @@ -788,6 +785,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, BUG(); } + if (!req->request.no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_IOC; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_CSP; @@ -1855,9 +1855,6 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, return 1; } - if ((event->status & DEPEVT_STATUS_IOC) && - (trb->ctrl & DWC3_TRB_CTRL_IOC)) - return 0; return 1; } -- cgit v1.2.1 From 183ca11179f6d3b99e0431bae6acb84350b82dea Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Feb 2014 14:08:51 -0600 Subject: usb: dwc3: core: define bit 10 of GCTL register This bit is necessary for implemeting workaround for known issue with some revisions of this core. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 69c4583933d1..f2693b63b710 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -157,6 +157,7 @@ #define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SOFITPSYNC (1 << 10) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) #define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) -- cgit v1.2.1 From 32a4a135847b1e600c64756b7c7c7a91eb2f0aa9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Feb 2014 14:00:13 -0600 Subject: usb: dwc3: workaround: clock gating issues Revisions between 2.10a and 2.50a (included) have a known issue which may cause xHCI compliance tests to fail and/or quality issues with Isochronous transactions. Note that this issue only impacts certain configurations of those revisions, namely the ones which have clock gating enabled. The suggested workaround is to disable clock gating in known broken revisions, make sure HW LPM is disabled and set GCTL.SOFITPSYNC to 1. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a49217ae3533..785feeeac3c1 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -314,7 +314,25 @@ static int dwc3_core_init(struct dwc3 *dwc) switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: - reg &= ~DWC3_GCTL_DSBLCLKGTNG; + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; default: dev_dbg(dwc->dev, "No power optimization available\n"); @@ -479,6 +497,14 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + dwc->dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); @@ -494,14 +520,6 @@ static int dwc3_probe(struct platform_device *pdev) goto err1; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) - dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) - dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - - if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) - dwc->dr_mode = USB_DR_MODE_OTG; - switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); -- cgit v1.2.1 From e1dadd3b0f277e59847d6b7de86ff67306bee4b1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 25 Feb 2014 14:47:54 -0600 Subject: usb: dwc3: workaround: bogus hibernation events Revision 2.20a of the core has a known issue which would generate bogus hibernation events _and_ random failures on USB CV TD.9.23 test case. The suggested workaround is to ignore hibernation events which don't match currently connected speed. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9e878d9bc909..7f4e6dd63c00 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2378,6 +2378,30 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } +static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + unsigned int is_ss = evtinfo & BIT(4); + + /** + * WORKAROUND: DWC3 revison 2.20a with hibernation support + * have a known issue which can cause USB CV TD.9.23 to fail + * randomly. + * + * Because of this issue, core could generate bogus hibernation + * events which SW needs to ignore. + * + * Refers to: + * + * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0 + * Device Fallback from SuperSpeed + */ + if (is_ss ^ (dwc->speed == USB_SPEED_SUPER)) + return; + + /* enter hibernation here */ +} + static void dwc3_gadget_interrupt(struct dwc3 *dwc, const struct dwc3_event_devt *event) { @@ -2394,6 +2418,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, case DWC3_DEVICE_EVENT_WAKEUP: dwc3_gadget_wakeup_interrupt(dwc); break; + case DWC3_DEVICE_EVENT_HIBER_REQ: + if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation, + "unexpected hibernation event\n")) + break; + + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); + break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; -- cgit v1.2.1 From f2b685d5aad81facb85542227f5f8215eda8f4ce Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 12:12:37 -0600 Subject: usb: dwc3: cleanup struct dwc3 move 1-bit flags to the bottom of the structure, sort all bit flags alphabetically, add documentation which was missing. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f2693b63b710..eab166a02cd6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -616,14 +616,6 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register - * @is_selfpowered: true when we are selfpowered - * @three_stage_setup: set if we perform a three phase setup - * @ep0_bounced: true when we used bounce buffer - * @ep0_expect_in: true when we expect a DATA IN transfer - * @start_config_issued: true when StartConfig command has been issued - * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround - * @needs_fifo_resize: not all users might want fifo resizing, flag it - * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. * @isoch_delay: wValue from Set Isochronous Delay request; * @u2sel: parameter from Set SEL request. * @u2pel: parameter from Set SEL request. @@ -638,6 +630,19 @@ struct dwc3_scratchpad_array { * @mem: points to start of memory which is used for this struct. * @hwparams: copy of hwparams registers * @root: debugfs root folder pointer + * @regset: debugfs pointer to regdump file + * @test_mode: true when we're entering a USB test mode + * @test_mode_nr: test feature selector + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @is_selfpowered: true when we are selfpowered + * @needs_fifo_resize: not all users might want fifo resizing, flag it + * @pullups_connected: true when Run/Stop bit is set + * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @start_config_issued: true when StartConfig command has been issued + * @three_stage_setup: set if we perform a three phase setup */ struct dwc3 { struct usb_ctrlrequest *ctrl_req; @@ -697,17 +702,6 @@ struct dwc3 { #define DWC3_REVISION_240A 0x5533240a #define DWC3_REVISION_250A 0x5533250a - unsigned is_selfpowered:1; - unsigned three_stage_setup:1; - unsigned ep0_bounced:1; - unsigned ep0_expect_in:1; - unsigned start_config_issued:1; - unsigned setup_packet_pending:1; - unsigned delayed_status:1; - unsigned needs_fifo_resize:1; - unsigned resize_fifos:1; - unsigned pullups_connected:1; - enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; @@ -731,6 +725,17 @@ struct dwc3 { u8 test_mode; u8 test_mode_nr; + + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned is_selfpowered:1; + unsigned needs_fifo_resize:1; + unsigned pullups_connected:1; + unsigned resize_fifos:1; + unsigned setup_packet_pending:1; + unsigned start_config_issued:1; + unsigned three_stage_setup:1; }; /* -------------------------------------------------------------------------- */ -- cgit v1.2.1 From 81bc5599d66d741552fd8e519bcb91ed8d683786 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 12:14:29 -0600 Subject: usb: dwc3: add has_hibernation flag this will tell driver that this version of the core was configured with hibernation feature enabled. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eab166a02cd6..a2b01785bb3f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -636,6 +636,7 @@ struct dwc3_scratchpad_array { * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer + * @has_hibernation: true when dwc3 was configured with Hibernation * @is_selfpowered: true when we are selfpowered * @needs_fifo_resize: not all users might want fifo resizing, flag it * @pullups_connected: true when Run/Stop bit is set @@ -729,6 +730,7 @@ struct dwc3 { unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; + unsigned has_hibernation:1; unsigned is_selfpowered:1; unsigned needs_fifo_resize:1; unsigned pullups_connected:1; -- cgit v1.2.1 From 4cfcf876767ab845e1db0c0b2671db27b1a3586c Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 13:56:23 +0300 Subject: usb: dwc3: add 'saved_state' field to dwc3_ep structure This extra field will save endpoint state when we're about to enter hibernation. It will be used later to restore the endpoint state when resuming. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a2b01785bb3f..4c14796a5ffc 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -394,6 +394,7 @@ struct dwc3_event_buffer { * @busy_slot: first slot which is owned by HW * @desc: usb_endpoint_descriptor pointer * @dwc: pointer to DWC controller + * @saved_state: ep state saved during hibernation * @flags: endpoint flags (wedged, stalled, ...) * @current_trb: index of current used trb * @number: endpoint number (1 - 15) @@ -416,6 +417,7 @@ struct dwc3_ep { const struct usb_ss_ep_comp_descriptor *comp_desc; struct dwc3 *dwc; + u32 saved_state; unsigned flags; #define DWC3_EP_ENABLED (1 << 0) #define DWC3_EP_STALL (1 << 1) -- cgit v1.2.1 From 911f1f88cadf4b64bad5aa4c257d72494a62f928 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 13:35:15 +0300 Subject: usb: dwc3: gadget: implement dwc3_gadget_get_link_state This function will be used during hibernation to get the current link state. It will be needed at least for Hibernation support. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 16 ++++++++++++++++ drivers/usb/dwc3/gadget.h | 1 + 2 files changed, 17 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7f4e6dd63c00..7d00738b4b60 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -67,6 +67,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) return 0; } +/** + * dwc3_gadget_get_link_state - Gets current state of USB Link + * @dwc: pointer to our context structure + * + * Caller should take care of locking. This function will + * return the link state on success (>= 0) or -ETIMEDOUT. + */ +int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + return DWC3_DSTS_USBLNKST(reg); +} + /** * dwc3_gadget_set_link_state - Sets USB Link to a particular State * @dwc: pointer to our context structure diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index febe1aa7b714..d10124477872 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -86,6 +86,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); void dwc3_ep0_interrupt(struct dwc3 *dwc, -- cgit v1.2.1 From 265b70a73aff2a21e74f1fbe7ffaaeb4608adc98 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Thu, 19 Dec 2013 12:38:49 -0600 Subject: usb: dwc3: gadget: add a 'restore' argument to set_ep_config That argument will be used in later patches when we have working hibernation support. For now, always pass it as false. The idea of this patch is to decrease to size of following patches and slowly add hibernation building blocks to the gadget side of dwc3 so that it becomes very easy to review the actual hibernation code. [ balbi@ti.com : rewrote patch on top of current tree. Added commit log. ] Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7d00738b4b60..87b09bd6266e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -433,7 +433,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore) + bool ignore, bool restore) { struct dwc3_gadget_ep_cmd_params params; @@ -452,6 +452,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, if (ignore) params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM; + if (restore) { + params.param0 |= DWC3_DEPCFG_ACTION_RESTORE; + params.param2 |= dep->saved_state; + } + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; @@ -510,7 +515,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore) + bool ignore, bool restore) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -524,7 +529,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore); + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore, + restore); if (ret) return ret; @@ -675,7 +681,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, } spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1565,14 +1571,16 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err2; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err3; @@ -2276,14 +2284,16 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) } dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; @@ -2738,12 +2748,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc) dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) goto err0; dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); if (ret) goto err1; -- cgit v1.2.1 From 0ffcaf3798bfd83aed0a650cddb9d1ee825ff2bb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 13:04:28 -0600 Subject: usb: dwc3: core: allocate scratch buffers We must read HWPARAMS4 register to figure out how many scratch buffers we should allocate. Later patch will use "Set Scratchpad Buffer Array" command to pass the pointer to the IP so it can be used during hibernation. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 6 +++ 2 files changed, 111 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 785feeeac3c1..8c627c9a5130 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -242,6 +242,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, + DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); + if (!dwc->scratchbuf) + return -ENOMEM; + + return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ + dma_addr_t scratch_addr; + u32 param; + int ret; + + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return 0; + + scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->dev, scratch_addr)) { + dev_err(dwc->dev, "failed to map scratch buffer\n"); + ret = -EFAULT; + goto err0; + } + + dwc->scratch_addr = scratch_addr; + + param = lower_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); + if (ret < 0) + goto err1; + + param = upper_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); + if (ret < 0) + goto err1; + + return 0; + +err1: + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: + return ret; +} + +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return; + + if (!dwc->nr_scratch) + return; + + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return; + + dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); +} + static void dwc3_core_num_eps(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; @@ -277,6 +361,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) static int dwc3_core_init(struct dwc3 *dwc) { unsigned long timeout; + u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -334,6 +419,10 @@ static int dwc3_core_init(struct dwc3 *dwc) else reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + break; default: dev_dbg(dwc->dev, "No power optimization available\n"); } @@ -351,14 +440,30 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err1; + + ret = dwc3_setup_scratch_buffers(dwc); + if (ret) + goto err2; + return 0; +err2: + dwc3_free_scratch_buffers(dwc); + +err1: + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + err0: return ret; } static void dwc3_core_exit(struct dwc3 *dwc) { + dwc3_free_scratch_buffers(dwc); usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4c14796a5ffc..bbb5b78eaf01 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -36,6 +36,7 @@ #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 +#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ #define DWC3_EVENT_SIZE 4 /* bytes */ #define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */ #define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM) @@ -601,6 +602,7 @@ struct dwc3_scratchpad_array { * @ep0_trb: dma address of ep0_trb * @ep0_usb_req: dummy req used while handling STD USB requests * @ep0_bounce_addr: dma address of ep0_bounce + * @scratch_addr: dma address of scratchbuf * @lock: for synchronizing * @dev: pointer to our struct device * @xhci: pointer to our xHCI child @@ -609,6 +611,7 @@ struct dwc3_scratchpad_array { * @gadget_driver: pointer to the gadget driver * @regs: base address for our registers * @regs_size: address space size + * @nr_scratch: number of scratch buffers * @num_event_buffers: calculated number of event buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -651,10 +654,12 @@ struct dwc3 { struct usb_ctrlrequest *ctrl_req; struct dwc3_trb *ep0_trb; void *ep0_bounce; + void *scratchbuf; u8 *setup_buf; dma_addr_t ctrl_req_addr; dma_addr_t ep0_trb_addr; dma_addr_t ep0_bounce_addr; + dma_addr_t scratch_addr; struct dwc3_request ep0_usb_req; /* device lock */ @@ -683,6 +688,7 @@ struct dwc3 { u32 dcfg; u32 gctl; + u32 nr_scratch; u32 num_event_buffers; u32 u1u2; u32 maximum_speed; -- cgit v1.2.1 From 835fadb40c952562e4586b719ce15022ef7ced87 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 14:02:53 -0600 Subject: usb: dwc3: core: fix indentation no functional changes, just converting spaces into tab. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index bbb5b78eaf01..00b057860d74 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -320,7 +320,7 @@ /* Device Endpoint Command Register */ #define DWC3_DEPCMD_PARAM_SHIFT 16 #define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) -#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) #define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1) #define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) #define DWC3_DEPCMD_CMDACT (1 << 10) -- cgit v1.2.1 From 7b2a0368bbc9bc4a7936d8587eaff4b8c35b2247 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 13:43:19 -0600 Subject: usb: dwc3: gadget: set KEEP_CONNECT in case of hibernation if we have hibernation configured, Databook instructs us to set KEEP_CONNECT bit together with RUN_STOP bit, in step 9 of section 12.3.6.1 Initialization for Hibernation Support. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 87b09bd6266e..3934d0afc185 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1409,7 +1409,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } -static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg; u32 timeout = 500; @@ -1424,9 +1424,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) if (dwc->revision >= DWC3_REVISION_194A) reg &= ~DWC3_DCTL_KEEP_CONNECT; reg |= DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation) + reg |= DWC3_DCTL_KEEP_CONNECT; + dwc->pullups_connected = true; } else { reg &= ~DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation && !suspend) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + dwc->pullups_connected = false; } @@ -1464,7 +1472,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) is_on = !!is_on; spin_lock_irqsave(&dwc->lock, flags); - ret = dwc3_gadget_run_stop(dwc, is_on); + ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -2715,8 +2723,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int dwc3_gadget_prepare(struct dwc3 *dwc) { - if (dwc->pullups_connected) + if (dwc->pullups_connected) { dwc3_gadget_disable_irq(dwc); + dwc3_gadget_run_stop(dwc, true, true); + } return 0; } @@ -2725,7 +2735,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc) { if (dwc->pullups_connected) { dwc3_gadget_enable_irq(dwc); - dwc3_gadget_run_stop(dwc, true); + dwc3_gadget_run_stop(dwc, true, false); } } -- cgit v1.2.1 From 356363bf6b2fc4785d874ef87ed2fad4b8543490 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 19 Dec 2013 16:37:05 -0600 Subject: usb: dwc3: gadget: make sure HIRD threshold is 0 in superspeed During superspeed, HIRD threshold should always be zero. Curent driver wasn't making sure that was the case. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3934d0afc185..df9749eac6ac 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2288,6 +2288,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) */ reg |= DWC3_DCTL_HIRD_THRES(12); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } else { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_HIRD_THRES_MASK; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } -- cgit v1.2.1 From b992e6813e257269e9599ca07be8bd52df784bf5 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 14:17:35 +0300 Subject: usb: dwc3: gadget: add 'force' argument to stop_active_transfer It's not always we need to force a transfer to be removed from the core's internal cache. This extra argument will help differentiating those two cases. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index df9749eac6ac..f1cb0984046e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -570,13 +570,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return 0; } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; if (!list_empty(&dep->req_queued)) { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); /* - giveback all requests to gadget driver */ while (!list_empty(&dep->req_queued)) { @@ -1099,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { if (list_empty(&dep->req_queued)) { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); dep->flags = DWC3_EP_ENABLED; } return 0; @@ -1185,7 +1185,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } if (r == req) { /* wait until it is processed */ - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); goto out1; } dev_err(dwc->dev, "request %p was not queued to %s\n", @@ -1881,7 +1881,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, */ dep->flags = DWC3_EP_PENDING_REQUEST; } else { - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); dep->flags = DWC3_EP_ENABLED; } return 1; @@ -2028,7 +2028,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) } } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) { struct dwc3_ep *dep; struct dwc3_gadget_ep_cmd_params params; @@ -2060,7 +2060,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) */ cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); -- cgit v1.2.1 From bc5ba2e0b829c9397f96df1191c7d2319ebc36d9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 26 Feb 2014 10:17:07 -0600 Subject: usb: dwc3: gadget: call gadget driver's ->suspend/->resume When going into bus suspend/resume we _must_ call gadget driver's ->suspend/->resume callbacks accordingly. This patch implements that very feature which has been missing forever. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f1cb0984046e..9f82436b4b1e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2028,6 +2028,24 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) } } +static void dwc3_suspend_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->suspend(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_resume_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) { struct dwc3_ep *dep; @@ -2414,6 +2432,23 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, dwc->link_state = next; + switch (next) { + case DWC3_LINK_STATE_U1: + if (dwc->speed == USB_SPEED_SUPER) + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_U2: + case DWC3_LINK_STATE_U3: + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_RESUME: + dwc3_resume_gadget(dwc); + break; + default: + /* do nothing */ + break; + } + dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } -- cgit v1.2.1 From b997ada5db905e24494bcc689c7b2c07d278ca27 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Jul 2012 13:26:50 +0300 Subject: usb: dwc3: gadget: pre-start Stream transfers when they're queued we need to pre-start stream transfers otherwise we will never know when to start them. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9f82436b4b1e..1730cd0928c5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1129,6 +1129,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return ret; } + /* + * 4. Stream Capable Bulk Endpoints. We need to start the transfer + * right away, otherwise host will not know we have streams to be + * handled. + */ + if (dep->stream_capable) { + int ret; + + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + } + return 0; } -- cgit v1.2.1 From 610183051d8f9421f138c4203ca894387f9f8839 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 4 Mar 2014 09:23:50 -0600 Subject: usb: dwc3: fix randconfig build errors commit 388e5c5 (usb: dwc3: remove dwc3 dependency on host AND gadget.) created the possibility for host-only and peripheral-only dwc3 builds but left a possible randconfig build error when host-only builds are selected. Cc: # v3.8+ Reported-by: Jim Davis Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 33 +++++++++++++++++++++++++++++++++ drivers/usb/dwc3/gadget.h | 13 ------------- 2 files changed, 33 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 00b057860d74..5b92c9ed89bc 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -872,6 +872,19 @@ union dwc3_event { struct dwc3_event_gevt gevt; }; +/** + * struct dwc3_gadget_ep_cmd_params - representation of endpoint command + * parameters + * @param2: third parameter + * @param1: second parameter + * @param0: first parameter + */ +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + /* * DWC3 Features to be used as Driver Data */ @@ -897,11 +910,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc) #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ return 0; } +static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ return 0; } +static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, + enum dwc3_link_state state) +{ return 0; } + +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ return 0; } +static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, + int cmd, u32 param) +{ return 0; } #endif /* power management interface */ diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index d10124477872..a0ee75b68a80 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -56,12 +56,6 @@ struct dwc3; /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) -struct dwc3_gadget_ep_cmd_params { - u32 param2; - u32 param1; - u32 param0; -}; - /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) @@ -85,10 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); -int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); -int dwc3_gadget_get_link_state(struct dwc3 *dwc); -int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); - void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); @@ -96,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); -int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW -- cgit v1.2.1 From dbf5aaf7ce29fb623d4e74818bc4fb3e9c84632e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 4 Mar 2014 09:35:02 -0600 Subject: usb: dwc3: define more revisions few new revisions of the core have been released, add them to our list of revisions so we can apply workarounds if necessary. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5b92c9ed89bc..535bb6e1f2b8 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -710,6 +710,9 @@ struct dwc3 { #define DWC3_REVISION_230A 0x5533230a #define DWC3_REVISION_240A 0x5533240a #define DWC3_REVISION_250A 0x5533250a +#define DWC3_REVISION_260A 0x5533260a +#define DWC3_REVISION_270A 0x5533270a +#define DWC3_REVISION_280A 0x5533280a enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; -- cgit v1.2.1 From 122f06e60f90a43d9b2fb30662af688dfb759379 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:10 +0530 Subject: usb: dwc3: core: support optional PHYs Since PHYs for dwc3 is optional (not all SoCs having PHYs for DWC3 should be programmed), do not return from probe if the USB PHY library returns -ENODEV as that indicates the platform does not have a programmable PHY. While this can be considered as a temporary fix, a long term solution would be to add 'nop' PHY for platforms that does not have programmable PHY. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8c627c9a5130..c89570cc0689 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -534,32 +534,26 @@ static int dwc3_probe(struct platform_device *pdev) if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); - - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb2_phy = NULL; + } else if (ret == -EPROBE_DEFER) { return ret; - - dev_err(dev, "no usb2 phy configured\n"); - return -EPROBE_DEFER; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } } if (IS_ERR(dwc->usb3_phy)) { ret = PTR_ERR(dwc->usb3_phy); - - /* - * if -ENXIO is returned, it means PHY layer wasn't - * enabled, so it makes no sense to return -EPROBE_DEFER - * in that case, since no PHY driver will ever probe. - */ - if (ret == -ENXIO) + if (ret == -ENXIO || ret == -ENODEV) { + dwc->usb3_phy = NULL; + } else if (ret == -EPROBE_DEFER) { return ret; - - dev_err(dev, "no usb3 phy configured\n"); - return -EPROBE_DEFER; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } } dwc->xhci_resources[0].start = res->start; -- cgit v1.2.1 From 57303488cd37da58263e842de134dc65f7c626d5 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:11 +0530 Subject: usb: dwc3: adapt dwc3 core to use Generic PHY Framework Adapted dwc3 core to use the Generic PHY Framework. So for init, exit, power_on and power_off the following APIs are used phy_init(), phy_exit(), phy_power_on() and phy_power_off(). However using the old USB phy library wont be removed till the PHYs of all other SoC's using dwc3 core is adapted to the Generic PHY Framework. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/dwc3/core.h | 7 +++++ 2 files changed, 84 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c89570cc0689..d001417e8e37 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static void dwc3_core_soft_reset(struct dwc3 *dwc) +static int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; + int ret; /* Before Resetting PHY, put Core in Reset */ reg = dwc3_readl(dwc->regs, DWC3_GCTL); @@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) usb_phy_init(dwc->usb2_phy); usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + return ret; + } mdelay(100); /* Clear USB3 PHY reset */ @@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + return 0; } /** @@ -391,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); - dwc3_core_soft_reset(dwc); + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0; reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -456,6 +470,8 @@ err2: err1: usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); err0: return ret; @@ -466,6 +482,8 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_free_scratch_buffers(dwc); usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); } #define DWC3_ALIGN_MASK (16 - 1) @@ -556,6 +574,32 @@ static int dwc3_probe(struct platform_device *pdev) } } + dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy)) { + ret = PTR_ERR(dwc->usb2_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb2_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } + } + + dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy)) { + ret = PTR_ERR(dwc->usb3_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dwc->usb3_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } + dwc->xhci_resources[0].start = res->start; dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + DWC3_XHCI_REGS_END; @@ -612,11 +656,18 @@ static int dwc3_probe(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err1; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_power; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err1; + goto err_usb3phy_power; } switch (dwc->dr_mode) { @@ -685,6 +736,12 @@ err3: err2: dwc3_event_buffers_cleanup(dwc); +err_usb3phy_power: + phy_power_off(dwc->usb3_generic_phy); + +err_usb2phy_power: + phy_power_off(dwc->usb2_generic_phy); + err1: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); @@ -702,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev) usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -799,6 +858,8 @@ static int dwc3_suspend(struct device *dev) usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); return 0; } @@ -807,9 +868,17 @@ static int dwc3_resume(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); unsigned long flags; + int ret; usb_phy_init(dwc->usb3_phy); usb_phy_init(dwc->usb2_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) + goto err_usb2phy_init; spin_lock_irqsave(&dwc->lock, flags); @@ -833,6 +902,11 @@ static int dwc3_resume(struct device *dev) pm_runtime_enable(dev); return 0; + +err_usb2phy_init: + phy_exit(dwc->usb2_generic_phy); + + return ret; } static const struct dev_pm_ops dwc3_dev_pm_ops = { diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 535bb6e1f2b8..57332e3768e4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -31,6 +31,8 @@ #include #include +#include + /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 @@ -619,6 +621,8 @@ struct dwc3_scratchpad_array { * @dr_mode: requested mode of operation * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to USB2 PHY + * @usb3_generic_phy: pointer to USB3 PHY * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -679,6 +683,9 @@ struct dwc3 { struct usb_phy *usb2_phy; struct usb_phy *usb3_phy; + struct phy *usb2_generic_phy; + struct phy *usb3_generic_phy; + void __iomem *regs; size_t regs_size; -- cgit v1.2.1 From aecbc31d767cb549e93a44e50218e20d1bc66b59 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Thu, 27 Feb 2014 10:44:41 +0530 Subject: usb: musb: musb_cppi41: Dont reprogram DMA if tear down is initiated Reprogramming the DMA after tear down is initiated leads to warning. This is mainly seen with ISOCH since we do a delayed completion for ISOCH transfers. In ISOCH transfers dma_completion should not reprogram if the channel tear down is initiated. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 5b0f2a58f8ce..7b8bbf53127e 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -132,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; struct musb *musb = hw_ep->musb; - if (!cppi41_channel->prog_len) { + if (!cppi41_channel->prog_len || + (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) { /* done, complete */ cppi41_channel->channel.actual_len = -- cgit v1.2.1 From b83e333a4d03b73eb5ea7fc1cb86683c2575b237 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 28 Feb 2014 13:06:11 +0100 Subject: usb: gadget: s3c-hsotg: add proper suspend/resume support This patch adds suspend/resume support to s3c-hsotg driver. It makes UDC driver more power efficient. Signed-off-by: Marek Szyprowski Tested-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 53 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 99c8e3ca6a3c..2a9cb674926a 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3774,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev) return 0; } -#if 1 -#define s3c_hsotg_suspend NULL -#define s3c_hsotg_resume NULL -#endif +static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); + unsigned long flags; + int ret = 0; + + if (hsotg->driver) + dev_info(hsotg->dev, "suspending usb gadget %s\n", + hsotg->driver->driver.name); + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_disconnect(hsotg); + s3c_hsotg_phy_disable(hsotg); + hsotg->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&hsotg->lock, flags); + + if (hsotg->driver) { + int ep; + for (ep = 0; ep < hsotg->num_of_eps; ep++) + s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + + ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + } + + return ret; +} + +static int s3c_hsotg_resume(struct platform_device *pdev) +{ + struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); + unsigned long flags; + int ret = 0; + + if (hsotg->driver) { + dev_info(hsotg->dev, "resuming usb gadget %s\n", + hsotg->driver->driver.name); + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + } + + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->last_rst = jiffies; + s3c_hsotg_phy_enable(hsotg); + s3c_hsotg_core_init(hsotg); + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; +} #ifdef CONFIG_OF static const struct of_device_id s3c_hsotg_of_ids[] = { -- cgit v1.2.1 From 2400780ea18acd8df5895c6946c5eec3bf2f8859 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:20:54 +0800 Subject: usb: phy: mxs: Add platform judgement code The mxs-phy has several bugs and features at different versions, the driver code can get it through of_device_id.data. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 58 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index b42897b6474c..cf58d8ec9901 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut * on behalf of DENX Software Engineering GmbH * @@ -20,6 +20,7 @@ #include #include #include +#include #define DRIVER_NAME "mxs_phy" @@ -34,13 +35,55 @@ #define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) +#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) + +/* Do disconnection between PHY and controller without vbus */ +#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS BIT(0) + +/* + * The PHY will be in messy if there is a wakeup after putting + * bus to suspend (set portsc.suspendM) but before setting PHY to low + * power mode (set portsc.phcd). + */ +#define MXS_PHY_ABNORMAL_IN_SUSPEND BIT(1) + +/* + * The SOF sends too fast after resuming, it will cause disconnection + * between host and high speed device. + */ +#define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2) + +struct mxs_phy_data { + unsigned int flags; +}; + +static const struct mxs_phy_data imx23_phy_data = { + .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST, +}; + +static const struct mxs_phy_data imx6q_phy_data = { + .flags = MXS_PHY_SENDING_SOF_TOO_FAST | + MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, +}; + +static const struct mxs_phy_data imx6sl_phy_data = { + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, +}; + +static const struct of_device_id mxs_phy_dt_ids[] = { + { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, + { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, + { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); + struct mxs_phy { struct usb_phy phy; struct clk *clk; + const struct mxs_phy_data *data; }; -#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) - static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { int ret; @@ -138,6 +181,8 @@ static int mxs_phy_probe(struct platform_device *pdev) struct clk *clk; struct mxs_phy *mxs_phy; int ret; + const struct of_device_id *of_id = + of_match_device(mxs_phy_dt_ids, &pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); @@ -168,6 +213,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.type = USB_PHY_TYPE_USB2; mxs_phy->clk = clk; + mxs_phy->data = of_id->data; platform_set_drvdata(pdev, mxs_phy); @@ -187,12 +233,6 @@ static int mxs_phy_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id mxs_phy_dt_ids[] = { - { .compatible = "fsl,imx23-usbphy", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); - static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, .remove = mxs_phy_remove, -- cgit v1.2.1 From 1364414411acf0fe691ae7e045a64ee366692ba0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:20:55 +0800 Subject: usb: phy: mxs: Add auto clock and power setting The auto setting is used to open related power and clocks automatically after receiving wakeup signal. With this feature, the PHY's clock and power can be recovered correctly from low power mode, it is guaranteed by IC logic. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index cf58d8ec9901..d7adca3738ff 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -31,6 +31,11 @@ #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) +#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) +#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) +#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) +#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) #define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) #define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) @@ -96,9 +101,18 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) /* Power up the PHY */ writel(0, base + HW_USBPHY_PWD); - /* enable FS/LS device */ - writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | - BM_USBPHY_CTRL_ENUTMILEVEL3, + /* + * USB PHY Ctrl Setting + * - Auto clock/power on + * - Enable full/low speed support + */ + writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | + BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | + BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | + BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | + BM_USBPHY_CTRL_ENAUTO_PWRON_PLL | + BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, base + HW_USBPHY_CTRL_SET); return 0; -- cgit v1.2.1 From 0d896538d83642c329658f04d11b407a3cf71f62 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:20:57 +0800 Subject: usb: phy: mxs: Add anatop regmap It is needed by imx6 SoC series, but not for imx23 and imx28. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index d7adca3738ff..2411e050351f 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #define DRIVER_NAME "mxs_phy" @@ -87,6 +89,7 @@ struct mxs_phy { struct usb_phy phy; struct clk *clk; const struct mxs_phy_data *data; + struct regmap *regmap_anatop; }; static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) @@ -197,6 +200,7 @@ static int mxs_phy_probe(struct platform_device *pdev) int ret; const struct of_device_id *of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); @@ -216,6 +220,17 @@ static int mxs_phy_probe(struct platform_device *pdev) return -ENOMEM; } + /* Some SoCs don't have anatop registers */ + if (of_get_property(np, "fsl,anatop", NULL)) { + mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle + (np, "fsl,anatop"); + if (IS_ERR(mxs_phy->regmap_anatop)) { + dev_dbg(&pdev->dev, + "failed to find regmap for anatop\n"); + return PTR_ERR(mxs_phy->regmap_anatop); + } + } + mxs_phy->phy.io_priv = base; mxs_phy->phy.dev = &pdev->dev; mxs_phy->phy.label = DRIVER_NAME; -- cgit v1.2.1 From f6a158243e569785a995e2218d9521716cdc9d79 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:20:58 +0800 Subject: usb: phy: mxs: change description of usb device speed Change "high speed" to "HS" Change "non-high speed" to "FS/LS" Implementation of notify_suspend and notify_resume will be different according to mxs_phy_data->flags. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 2411e050351f..1663a6683ec1 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -168,8 +168,8 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) static int mxs_phy_on_connect(struct usb_phy *phy, enum usb_device_speed speed) { - dev_dbg(phy->dev, "%s speed device has connected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + dev_dbg(phy->dev, "%s device has connected\n", + (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); if (speed == USB_SPEED_HIGH) writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, @@ -181,8 +181,8 @@ static int mxs_phy_on_connect(struct usb_phy *phy, static int mxs_phy_on_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { - dev_dbg(phy->dev, "%s speed device has disconnected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + dev_dbg(phy->dev, "%s device has disconnected\n", + (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); if (speed == USB_SPEED_HIGH) writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, -- cgit v1.2.1 From 22db05ecf2bad318c87f5a721e93694b2bb68a75 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:20:59 +0800 Subject: usb: phy: mxs: Enable IC fixes for related SoCs Two PHY bugs are fixed by IC logic, but these bits are not enabled by default, so we enable them at driver. The two bugs are: MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST which are described at code. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 1663a6683ec1..cb7113543153 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -31,6 +31,10 @@ #define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_CLR 0x38 +#define HW_USBPHY_IP 0x90 +#define HW_USBPHY_IP_SET 0x94 +#define HW_USBPHY_IP_CLR 0x98 + #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) @@ -42,6 +46,8 @@ #define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) +#define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -60,6 +66,14 @@ */ #define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2) +/* + * IC has bug fixes logic, they include + * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST + * which are described at above flags, the RTL will handle it + * according to different versions. + */ +#define MXS_PHY_NEED_IP_FIX BIT(3) + struct mxs_phy_data { unsigned int flags; }; @@ -70,11 +84,13 @@ static const struct mxs_phy_data imx23_phy_data = { static const struct mxs_phy_data imx6q_phy_data = { .flags = MXS_PHY_SENDING_SOF_TOO_FAST | - MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, + MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, }; static const struct mxs_phy_data imx6sl_phy_data = { - .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, }; static const struct of_device_id mxs_phy_dt_ids[] = { @@ -118,6 +134,9 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) BM_USBPHY_CTRL_ENUTMILEVEL3, base + HW_USBPHY_CTRL_SET); + if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) + writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); + return 0; } -- cgit v1.2.1 From 83be181b64222630957b9f59fd2d8c8864cb8972 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:21:00 +0800 Subject: usb: phy: mxs: add controller id It is used to access un-regulator registers according to different controllers. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index cb7113543153..3009ab57614e 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -106,6 +106,7 @@ struct mxs_phy { struct clk *clk; const struct mxs_phy_data *data; struct regmap *regmap_anatop; + int port_id; }; static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) @@ -250,6 +251,11 @@ static int mxs_phy_probe(struct platform_device *pdev) } } + ret = of_alias_get_id(np, "usbphy"); + if (ret < 0) + dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); + mxs_phy->port_id = ret; + mxs_phy->phy.io_priv = base; mxs_phy->phy.dev = &pdev->dev; mxs_phy->phy.label = DRIVER_NAME; -- cgit v1.2.1 From 3f1265056be0a3a569a0409410d4b11048688216 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:21:02 +0800 Subject: usb: phy: mxs: Add implementation of set_wakeup When we need the PHY can be waken up by external signals, we can call this API. Besides, we call mxs_phy_disconnect_line at this API to close the connection between USB PHY and controller, after that, the line state from controller is SE0. Once the PHY is out of power, without calling mxs_phy_disconnect_line, there are unknown wakeups due to dp/dm floating at device mode. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 3009ab57614e..da2eb6c968ca 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -31,6 +31,9 @@ #define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_CLR 0x38 +#define HW_USBPHY_DEBUG_SET 0x54 +#define HW_USBPHY_DEBUG_CLR 0x58 + #define HW_USBPHY_IP 0x90 #define HW_USBPHY_IP_SET 0x94 #define HW_USBPHY_IP_CLR 0x98 @@ -39,6 +42,9 @@ #define BM_USBPHY_CTRL_CLKGATE BIT(30) #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) +#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) +#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22) +#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21) #define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) #define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) #define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) @@ -48,6 +54,25 @@ #define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) +#define BM_USBPHY_DEBUG_CLKGATE BIT(30) + +/* Anatop Registers */ +#define ANADIG_USB1_VBUS_DET_STAT 0x1c0 +#define ANADIG_USB2_VBUS_DET_STAT 0x220 + +#define ANADIG_USB1_LOOPBACK_SET 0x1e4 +#define ANADIG_USB1_LOOPBACK_CLR 0x1e8 +#define ANADIG_USB2_LOOPBACK_SET 0x244 +#define ANADIG_USB2_LOOPBACK_CLR 0x248 + +#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) +#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) + +#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5) +#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -141,6 +166,79 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) return 0; } +/* Return true if the vbus is there */ +static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) +{ + unsigned int vbus_value; + + if (mxs_phy->port_id == 0) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB1_VBUS_DET_STAT, + &vbus_value); + else if (mxs_phy->port_id == 1) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB2_VBUS_DET_STAT, + &vbus_value); + + if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID) + return true; + else + return false; +} + +static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 reg; + + if (disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_CLR); + + if (mxs_phy->port_id == 0) { + reg = disconnect ? ANADIG_USB1_LOOPBACK_SET + : ANADIG_USB1_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN); + } else if (mxs_phy->port_id == 1) { + reg = disconnect ? ANADIG_USB2_LOOPBACK_SET + : ANADIG_USB2_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN); + } + + if (!disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_SET); + + /* Delay some time, and let Linestate be SE0 for controller */ + if (disconnect) + usleep_range(500, 1000); +} + +static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) +{ + bool vbus_is_on = false; + + /* If the SoCs don't need to disconnect line without vbus, quit */ + if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) + return; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return; + + vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); + + if (on && !vbus_is_on) + __mxs_phy_disconnect_line(mxs_phy, true); + else + __mxs_phy_disconnect_line(mxs_phy, false); + +} + static int mxs_phy_init(struct usb_phy *phy) { int ret; @@ -185,6 +283,23 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) return 0; } +static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | + BM_USBPHY_CTRL_ENDPDMCHG_WKUP | + BM_USBPHY_CTRL_ENIDCHG_WKUP; + if (enabled) { + mxs_phy_disconnect_line(mxs_phy, true); + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET); + } else { + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR); + mxs_phy_disconnect_line(mxs_phy, false); + } + + return 0; +} + static int mxs_phy_on_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -265,6 +380,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_connect = mxs_phy_on_connect; mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; + mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; mxs_phy->clk = clk; mxs_phy->data = of_id->data; -- cgit v1.2.1 From bf78343800861c65d4004e7f60a03cc9bc2410b3 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:21:03 +0800 Subject: usb: phy: mxs: Add system suspend/resume API We need this to keep PHY's power on or off during the system suspend mode. If we need to enable USB wakeup, then we must keep PHY's power being on during the system suspend mode. Otherwise, we need to keep PHY's power being off to save power. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index da2eb6c968ca..31ef59f88901 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -57,6 +57,10 @@ #define BM_USBPHY_DEBUG_CLKGATE BIT(30) /* Anatop Registers */ +#define ANADIG_ANA_MISC0 0x150 +#define ANADIG_ANA_MISC0_SET 0x154 +#define ANADIG_ANA_MISC0_CLR 0x158 + #define ANADIG_USB1_VBUS_DET_STAT 0x1c0 #define ANADIG_USB2_VBUS_DET_STAT 0x220 @@ -65,6 +69,9 @@ #define ANADIG_USB2_LOOPBACK_SET 0x244 #define ANADIG_USB2_LOOPBACK_CLR 0x248 +#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) +#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) + #define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) #define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) @@ -134,6 +141,16 @@ struct mxs_phy { int port_id; }; +static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) +{ + return mxs_phy->data == &imx6q_phy_data; +} + +static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy) +{ + return mxs_phy->data == &imx6sl_phy_data; +} + static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { int ret; @@ -387,6 +404,8 @@ static int mxs_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mxs_phy); + device_set_wakeup_capable(&pdev->dev, true); + ret = usb_add_phy_dev(&mxs_phy->phy); if (ret) return ret; @@ -403,6 +422,47 @@ static int mxs_phy_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) +{ + unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return; + + if (is_imx6q_phy(mxs_phy)) + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG); + else if (is_imx6sl_phy(mxs_phy)) + regmap_write(mxs_phy->regmap_anatop, + reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL); +} + +static int mxs_phy_system_suspend(struct device *dev) +{ + struct mxs_phy *mxs_phy = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + mxs_phy_enable_ldo_in_suspend(mxs_phy, true); + + return 0; +} + +static int mxs_phy_system_resume(struct device *dev) +{ + struct mxs_phy *mxs_phy = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + mxs_phy_enable_ldo_in_suspend(mxs_phy, false); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, + mxs_phy_system_resume); + static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, .remove = mxs_phy_remove, @@ -410,6 +470,7 @@ static struct platform_driver mxs_phy_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = mxs_phy_dt_ids, + .pm = &mxs_phy_pm, }, }; -- cgit v1.2.1 From 47d1845ffac0af9e4b439277fee50b62aed650a7 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 24 Feb 2014 10:21:04 +0800 Subject: usb: phy: mxs: Add sync time after controller clear phcd After clear portsc.phcd, PHY needs 200us stable time for switch 32K clock to AHB clock. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 31ef59f88901..c42bdf0c4a1f 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -151,6 +151,15 @@ static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy) return mxs_phy->data == &imx6sl_phy_data; } +/* + * PHY needs some 32K cycles to switch from 32K clock to + * bus (such as AHB/AXI, etc) clock. + */ +static void mxs_phy_clock_switch_delay(void) +{ + usleep_range(300, 400); +} + static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { int ret; @@ -261,6 +270,7 @@ static int mxs_phy_init(struct usb_phy *phy) int ret; struct mxs_phy *mxs_phy = to_mxs_phy(phy); + mxs_phy_clock_switch_delay(); ret = clk_prepare_enable(mxs_phy->clk); if (ret) return ret; @@ -289,6 +299,7 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } else { + mxs_phy_clock_switch_delay(); ret = clk_prepare_enable(mxs_phy->clk); if (ret) return ret; -- cgit v1.2.1 From fb0e139d93f50f0cfa3ff4377d34c70dbc542797 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 27 Feb 2014 16:42:13 +0100 Subject: usb: gadget: atmel_usba: fix crash when no endpoint are specified If no endpoints are present in the device tree, the kernel will crash with the following error: Unable to handle kernel paging request at virtual address 00101008 [...] [] (composite_dev_prepare) from [] (composite_bind+0x5c/0x190) [] (composite_bind) from [] (udc_bind_to_driver+0x48/0xf0) [] (udc_bind_to_driver) from [] (usb_gadget_probe_driver+0x7c/0xa0) [] (usb_gadget_probe_driver) from [] (do_one_initcall+0x94/0x140) [] (do_one_initcall) from [] (kernel_init_freeable+0xec/0x1b4) [] (kernel_init_freeable) from [] (kernel_init+0x8/0xe4) [] (kernel_init) from [] (ret_from_fork+0x14/0x24) Code: e5950014 e1a04001 e5902008 e3a010d0 (e5922008) ---[ end trace 35c74bdd89b373d0 ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b This checks for that case and returns an error, not allowing the driver to be loaded with no endpoints. Signed-off-by: Alexandre Belloni Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 18a44919ae9f..e57991edcf67 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, i++; } + if (i == 0) { + dev_err(&pdev->dev, "of_probe: no endpoint specified\n"); + ret = -EINVAL; + goto err; + } + return eps; err: return ERR_PTR(ret); -- cgit v1.2.1 From d8eb6c653ef6b323d630de3c5685478469e248bc Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 3 Mar 2014 17:48:34 +0100 Subject: usb: gadget: atmel_usba: fix crashed during stopping when DEBUG is enabled commit 511f3c5 (usb: gadget: udc-core: fix a regression during gadget driver unbinding) introduced a crash when DEBUG is enabled. The debug trace in the atmel_usba_stop function made the assumption that the driver pointer passed in parameter was not NULL, but since the commit above, such assumption was no longer always true. This commit now uses the driver pointer stored in udc which fixes this issue. [ balbi@ti.com : improved commit log a bit ] Cc: # v3.2+ Acked-by: Alexandre Belloni Signed-off-by: Gregory CLEMENT Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index e57991edcf67..9f65324f9ae0 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget, toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - udc->driver = NULL; - clk_disable_unprepare(udc->hclk); clk_disable_unprepare(udc->pclk); - DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); + DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name); + + udc->driver = NULL; return 0; } -- cgit v1.2.1 From 8d4e897bd0150fab594a871484e554472ee01452 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Fri, 28 Feb 2014 16:50:22 +0530 Subject: usb: gadget: f_fs: Add support for SuperSpeed Mode Allow userspace to pass SuperSpeed descriptors and handle them in the driver accordingly. This change doesn't modify existing desc_header and thereby keeps the ABI changes backward compatible i.e. existing userspace drivers compiled with old header (functionfs.h) would continue to work with the updated kernel. Signed-off-by: Manu Gautam Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 182 ++++++++++++++++++++++++++++++++++------------ drivers/usb/gadget/u_fs.h | 10 ++- 2 files changed, 142 insertions(+), 50 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 1ae741fdace0..66f60b9a34a2 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -134,8 +134,8 @@ struct ffs_ep { struct usb_ep *ep; /* P: ffs->eps_lock */ struct usb_request *req; /* P: epfile->mutex */ - /* [0]: full speed, [1]: high speed */ - struct usb_endpoint_descriptor *descs[2]; + /* [0]: full speed, [1]: high speed, [2]: super speed */ + struct usb_endpoint_descriptor *descs[3]; u8 num; @@ -1450,10 +1450,11 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs->raw_strings = NULL; ffs->stringtabs = NULL; - ffs->raw_descs_length = 0; - ffs->raw_fs_descs_length = 0; + ffs->raw_fs_hs_descs_length = 0; + ffs->raw_ss_descs_length = 0; ffs->fs_descs_count = 0; ffs->hs_descs_count = 0; + ffs->ss_descs_count = 0; ffs->strings_count = 0; ffs->interfaces_count = 0; @@ -1596,7 +1597,24 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - ds = ep->descs[ep->descs[1] ? 1 : 0]; + int desc_idx; + + if (ffs->gadget->speed == USB_SPEED_SUPER) + desc_idx = 2; + else if (ffs->gadget->speed == USB_SPEED_HIGH) + desc_idx = 1; + else + desc_idx = 0; + + /* fall-back to lower speed if desc missing for current speed */ + do { + ds = ep->descs[desc_idx]; + } while (!ds && --desc_idx >= 0); + + if (!ds) { + ret = -EINVAL; + break; + } ep->ep->driver_data = ep; ep->ep->desc = ds; @@ -1731,6 +1749,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, } break; + case USB_DT_SS_ENDPOINT_COMP: + pr_vdebug("EP SS companion descriptor\n"); + if (length != sizeof(struct usb_ss_ep_comp_descriptor)) + goto inv_length; + break; + case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_INTERFACE_POWER: case USB_DT_DEBUG: @@ -1841,8 +1865,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { - unsigned fs_count, hs_count; - int fs_len, ret = -EINVAL; + unsigned fs_count, hs_count, ss_count = 0; + int fs_len, hs_len, ss_len, ret = -EINVAL; char *data = _data; ENTER(); @@ -1853,9 +1877,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, fs_count = get_unaligned_le32(data + 8); hs_count = get_unaligned_le32(data + 12); - if (!fs_count && !hs_count) - goto einval; - data += 16; len -= 16; @@ -1874,22 +1895,54 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, } if (likely(hs_count)) { - ret = ffs_do_descs(hs_count, data, len, + hs_len = ffs_do_descs(hs_count, data, len, __ffs_data_do_entity, ffs); - if (unlikely(ret < 0)) + if (unlikely(hs_len < 0)) { + ret = hs_len; + goto error; + } + + data += hs_len; + len -= hs_len; + } else { + hs_len = 0; + } + + if (len >= 8) { + /* Check SS_MAGIC for presence of ss_descs and get SS_COUNT */ + if (get_unaligned_le32(data) != FUNCTIONFS_SS_DESC_MAGIC) + goto einval; + + ss_count = get_unaligned_le32(data + 4); + data += 8; + len -= 8; + } + + if (!fs_count && !hs_count && !ss_count) + goto einval; + + if (ss_count) { + ss_len = ffs_do_descs(ss_count, data, len, + __ffs_data_do_entity, ffs); + if (unlikely(ss_len < 0)) { + ret = ss_len; goto error; + } + ret = ss_len; } else { + ss_len = 0; ret = 0; } if (unlikely(len != ret)) goto einval; - ffs->raw_fs_descs_length = fs_len; - ffs->raw_descs_length = fs_len + ret; - ffs->raw_descs = _data; - ffs->fs_descs_count = fs_count; - ffs->hs_descs_count = hs_count; + ffs->raw_fs_hs_descs_length = fs_len + hs_len; + ffs->raw_ss_descs_length = ss_len; + ffs->raw_descs = _data; + ffs->fs_descs_count = fs_count; + ffs->hs_descs_count = hs_count; + ffs->ss_descs_count = ss_count; return 0; @@ -2112,21 +2165,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_endpoint_descriptor *ds = (void *)desc; struct ffs_function *func = priv; struct ffs_ep *ffs_ep; - - /* - * If hs_descriptors is not NULL then we are reading hs - * descriptors now - */ - const int isHS = func->function.hs_descriptors != NULL; - unsigned idx; + unsigned ep_desc_id, idx; + static const char *speed_names[] = { "full", "high", "super" }; if (type != FFS_DESCRIPTOR) return 0; - if (isHS) + /* + * If ss_descriptors is not NULL, we are reading super speed + * descriptors; if hs_descriptors is not NULL, we are reading high + * speed descriptors; otherwise, we are reading full speed + * descriptors. + */ + if (func->function.ss_descriptors) { + ep_desc_id = 2; + func->function.ss_descriptors[(long)valuep] = desc; + } else if (func->function.hs_descriptors) { + ep_desc_id = 1; func->function.hs_descriptors[(long)valuep] = desc; - else + } else { + ep_desc_id = 0; func->function.fs_descriptors[(long)valuep] = desc; + } if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) return 0; @@ -2134,13 +2194,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; ffs_ep = func->eps + idx; - if (unlikely(ffs_ep->descs[isHS])) { - pr_vdebug("two %sspeed descriptors for EP %d\n", - isHS ? "high" : "full", + if (unlikely(ffs_ep->descs[ep_desc_id])) { + pr_err("two %sspeed descriptors for EP %d\n", + speed_names[ep_desc_id], ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); return -EINVAL; } - ffs_ep->descs[isHS] = ds; + ffs_ep->descs[ep_desc_id] = ds; ffs_dump_mem(": Original ep desc", ds, ds->bLength); if (ffs_ep->ep) { @@ -2284,8 +2344,10 @@ static int _ffs_func_bind(struct usb_configuration *c, const int full = !!func->ffs->fs_descs_count; const int high = gadget_is_dualspeed(func->gadget) && func->ffs->hs_descs_count; + const int super = gadget_is_superspeed(func->gadget) && + func->ffs->ss_descs_count; - int ret; + int fs_len, hs_len, ret; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2294,15 +2356,17 @@ static int _ffs_func_bind(struct usb_configuration *c, full ? ffs->fs_descs_count + 1 : 0); vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, high ? ffs->hs_descs_count + 1 : 0); + vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, + super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); vla_item_with_sz(d, char, raw_descs, - high ? ffs->raw_descs_length : ffs->raw_fs_descs_length); + ffs->raw_fs_hs_descs_length + ffs->raw_ss_descs_length); char *vlabuf; ENTER(); - /* Only high speed but not supported by gadget? */ - if (unlikely(!(full | high))) + /* Has descriptors only for speeds gadget does not support */ + if (unlikely(!(full | high | super))) return -ENOTSUPP; /* Allocate a single chunk, less management later on */ @@ -2312,8 +2376,16 @@ static int _ffs_func_bind(struct usb_configuration *c, /* Zero */ memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + /* Copy only raw (hs,fs) descriptors (until ss_magic and ss_count) */ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16, - d_raw_descs__sz); + ffs->raw_fs_hs_descs_length); + /* Copy SS descs present @ header + hs_fs_descs + ss_magic + ss_count */ + if (func->ffs->ss_descs_count) + memcpy(vla_ptr(vlabuf, d, raw_descs) + + ffs->raw_fs_hs_descs_length, + ffs->raw_descs + 16 + ffs->raw_fs_hs_descs_length + 8, + ffs->raw_ss_descs_length); + memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); for (ret = ffs->eps_count; ret; --ret) { struct ffs_ep *ptr; @@ -2335,22 +2407,38 @@ static int _ffs_func_bind(struct usb_configuration *c, */ if (likely(full)) { func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); - ret = ffs_do_descs(ffs->fs_descs_count, - vla_ptr(vlabuf, d, raw_descs), - d_raw_descs__sz, - __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + fs_len = ffs_do_descs(ffs->fs_descs_count, + vla_ptr(vlabuf, d, raw_descs), + d_raw_descs__sz, + __ffs_func_bind_do_descs, func); + if (unlikely(fs_len < 0)) { + ret = fs_len; goto error; + } } else { - ret = 0; + fs_len = 0; } if (likely(high)) { func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); - ret = ffs_do_descs(ffs->hs_descs_count, - vla_ptr(vlabuf, d, raw_descs) + ret, - d_raw_descs__sz - ret, - __ffs_func_bind_do_descs, func); + hs_len = ffs_do_descs(ffs->hs_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len, + d_raw_descs__sz - fs_len, + __ffs_func_bind_do_descs, func); + if (unlikely(hs_len < 0)) { + ret = hs_len; + goto error; + } + } else { + hs_len = 0; + } + + if (likely(super)) { + func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); + ret = ffs_do_descs(ffs->ss_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, + d_raw_descs__sz - fs_len - hs_len, + __ffs_func_bind_do_descs, func); if (unlikely(ret < 0)) goto error; } @@ -2361,7 +2449,8 @@ static int _ffs_func_bind(struct usb_configuration *c, * now. */ ret = ffs_do_descs(ffs->fs_descs_count + - (high ? ffs->hs_descs_count : 0), + (high ? ffs->hs_descs_count : 0) + + (super ? ffs->ss_descs_count : 0), vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_nums, func); if (unlikely(ret < 0)) @@ -2708,6 +2797,7 @@ static void ffs_func_unbind(struct usb_configuration *c, */ func->function.fs_descriptors = NULL; func->function.hs_descriptors = NULL; + func->function.ss_descriptors = NULL; func->interfaces_nums = NULL; ffs_event_add(ffs, FUNCTIONFS_UNBIND); diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index c39e805025b9..0deb6d5f7c35 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -208,14 +208,16 @@ struct ffs_data { /* * Real descriptors are 16 bytes after raw_descs (so you need * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. + * first full speed descriptor). + * raw_fs_hs_descs_length does not have those 16 bytes added. + * ss_descs are 8 bytes (ss_magic + count) pass the hs_descs */ const void *raw_descs; - unsigned raw_descs_length; - unsigned raw_fs_descs_length; + unsigned raw_fs_hs_descs_length; + unsigned raw_ss_descs_length; unsigned fs_descs_count; unsigned hs_descs_count; + unsigned ss_descs_count; unsigned short strings_count; unsigned short interfaces_count; -- cgit v1.2.1 From ac8dde11f2b397fe2282f585d5eb427a13675ea2 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 28 Feb 2014 16:50:23 +0530 Subject: usb: gadget: f_fs: Add flags to descriptors block This reworks the way SuperSpeed descriptors are added and instead of having a magic after full and high speed descriptors, it reworks the whole descriptors block to include a flags field which lists which descriptors are present and makes future extensions possible. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 132 ++++++++++++++++++++-------------------------- drivers/usb/gadget/u_fs.h | 12 ++--- 2 files changed, 61 insertions(+), 83 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 66f60b9a34a2..42f7a0e4be59 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1434,7 +1434,7 @@ static void ffs_data_clear(struct ffs_data *ffs) if (ffs->epfiles) ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); - kfree(ffs->raw_descs); + kfree(ffs->raw_descs_data); kfree(ffs->raw_strings); kfree(ffs->stringtabs); } @@ -1446,12 +1446,12 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs_data_clear(ffs); ffs->epfiles = NULL; + ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; ffs->raw_strings = NULL; ffs->stringtabs = NULL; - ffs->raw_fs_hs_descs_length = 0; - ffs->raw_ss_descs_length = 0; + ffs->raw_descs_length = 0; ffs->fs_descs_count = 0; ffs->hs_descs_count = 0; ffs->ss_descs_count = 0; @@ -1865,89 +1865,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { - unsigned fs_count, hs_count, ss_count = 0; - int fs_len, hs_len, ss_len, ret = -EINVAL; - char *data = _data; + char *data = _data, *raw_descs; + unsigned counts[3], flags; + int ret = -EINVAL, i; ENTER(); - if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC || - get_unaligned_le32(data + 4) != len)) + if (get_unaligned_le32(data + 4) != len) goto error; - fs_count = get_unaligned_le32(data + 8); - hs_count = get_unaligned_le32(data + 12); - - data += 16; - len -= 16; - if (likely(fs_count)) { - fs_len = ffs_do_descs(fs_count, data, len, - __ffs_data_do_entity, ffs); - if (unlikely(fs_len < 0)) { - ret = fs_len; + switch (get_unaligned_le32(data)) { + case FUNCTIONFS_DESCRIPTORS_MAGIC: + flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC; + data += 8; + len -= 8; + break; + case FUNCTIONFS_DESCRIPTORS_MAGIC_V2: + flags = get_unaligned_le32(data + 8); + if (flags & ~(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC | + FUNCTIONFS_HAS_SS_DESC)) { + ret = -ENOSYS; goto error; } - - data += fs_len; - len -= fs_len; - } else { - fs_len = 0; + data += 12; + len -= 12; + break; + default: + goto error; } - if (likely(hs_count)) { - hs_len = ffs_do_descs(hs_count, data, len, - __ffs_data_do_entity, ffs); - if (unlikely(hs_len < 0)) { - ret = hs_len; + /* Read fs_count, hs_count and ss_count (if present) */ + for (i = 0; i < 3; ++i) { + if (!(flags & (1 << i))) { + counts[i] = 0; + } else if (len < 4) { goto error; + } else { + counts[i] = get_unaligned_le32(data); + data += 4; + len -= 4; } - - data += hs_len; - len -= hs_len; - } else { - hs_len = 0; - } - - if (len >= 8) { - /* Check SS_MAGIC for presence of ss_descs and get SS_COUNT */ - if (get_unaligned_le32(data) != FUNCTIONFS_SS_DESC_MAGIC) - goto einval; - - ss_count = get_unaligned_le32(data + 4); - data += 8; - len -= 8; } - if (!fs_count && !hs_count && !ss_count) - goto einval; - - if (ss_count) { - ss_len = ffs_do_descs(ss_count, data, len, + /* Read descriptors */ + raw_descs = data; + for (i = 0; i < 3; ++i) { + if (!counts[i]) + continue; + ret = ffs_do_descs(counts[i], data, len, __ffs_data_do_entity, ffs); - if (unlikely(ss_len < 0)) { - ret = ss_len; + if (ret < 0) goto error; - } - ret = ss_len; - } else { - ss_len = 0; - ret = 0; + data += ret; + len -= ret; } - if (unlikely(len != ret)) - goto einval; + if (raw_descs == data || len) { + ret = -EINVAL; + goto error; + } - ffs->raw_fs_hs_descs_length = fs_len + hs_len; - ffs->raw_ss_descs_length = ss_len; - ffs->raw_descs = _data; - ffs->fs_descs_count = fs_count; - ffs->hs_descs_count = hs_count; - ffs->ss_descs_count = ss_count; + ffs->raw_descs_data = _data; + ffs->raw_descs = raw_descs; + ffs->raw_descs_length = data - raw_descs; + ffs->fs_descs_count = counts[0]; + ffs->hs_descs_count = counts[1]; + ffs->ss_descs_count = counts[2]; return 0; -einval: - ret = -EINVAL; error: kfree(_data); return ret; @@ -2359,8 +2346,7 @@ static int _ffs_func_bind(struct usb_configuration *c, vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); - vla_item_with_sz(d, char, raw_descs, - ffs->raw_fs_hs_descs_length + ffs->raw_ss_descs_length); + vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; ENTER(); @@ -2376,15 +2362,9 @@ static int _ffs_func_bind(struct usb_configuration *c, /* Zero */ memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); - /* Copy only raw (hs,fs) descriptors (until ss_magic and ss_count) */ - memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16, - ffs->raw_fs_hs_descs_length); - /* Copy SS descs present @ header + hs_fs_descs + ss_magic + ss_count */ - if (func->ffs->ss_descs_count) - memcpy(vla_ptr(vlabuf, d, raw_descs) + - ffs->raw_fs_hs_descs_length, - ffs->raw_descs + 16 + ffs->raw_fs_hs_descs_length + 8, - ffs->raw_ss_descs_length); + /* Copy descriptors */ + memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, + ffs->raw_descs_length); memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); for (ret = ffs->eps_count; ret; --ret) { diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 0deb6d5f7c35..bf0ba375d459 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -206,15 +206,13 @@ struct ffs_data { /* filled by __ffs_data_got_descs() */ /* - * Real descriptors are 16 bytes after raw_descs (so you need - * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). - * raw_fs_hs_descs_length does not have those 16 bytes added. - * ss_descs are 8 bytes (ss_magic + count) pass the hs_descs + * raw_descs is what you kfree, real_descs points inside of raw_descs, + * where full speed, high speed and super speed descriptors start. + * real_descs_length is the length of all those descriptors. */ + const void *raw_descs_data; const void *raw_descs; - unsigned raw_fs_hs_descs_length; - unsigned raw_ss_descs_length; + unsigned raw_descs_length; unsigned fs_descs_count; unsigned hs_descs_count; unsigned ss_descs_count; -- cgit v1.2.1 From a70143bbef6bf06050c32a26d99e917b3e82deb7 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:12 +0530 Subject: drivers: phy: usb3/pipe3: Adapt pipe3 driver to Generic PHY Framework Adapted omap-usb3 PHY driver to Generic PHY Framework and moved phy-omap-usb3 driver in drivers/usb/phy to drivers/phy and also renamed the file to phy-ti-pipe3 since this same driver will be used for SATA PHY and PCIE PHY. Signed-off-by: Kishon Vijay Abraham I --- drivers/usb/phy/Kconfig | 11 -- drivers/usb/phy/Makefile | 1 - drivers/usb/phy/phy-omap-usb3.c | 361 ---------------------------------------- 3 files changed, 373 deletions(-) delete mode 100644 drivers/usb/phy/phy-omap-usb3.c (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7d1451d5bbea..c337ba2d066b 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -85,17 +85,6 @@ config OMAP_CONTROL_USB power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an additional register to power on USB3 PHY. -config OMAP_USB3 - tristate "OMAP USB3 PHY Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - select OMAP_CONTROL_USB - select USB_PHY - help - Enable this to support the USB3 PHY that is part of SOC. This - driver takes care of all the PHY functionality apart from comparator. - This driver interacts with the "OMAP Control USB Driver" to power - on/off the PHY. - config AM335X_CONTROL_USB tristate diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index be58adae3496..15f1878388f4 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o -obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c deleted file mode 100644 index 0c6ba29bdddd..000000000000 --- a/drivers/usb/phy/phy-omap-usb3.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PLL_STATUS 0x00000004 -#define PLL_GO 0x00000008 -#define PLL_CONFIGURATION1 0x0000000C -#define PLL_CONFIGURATION2 0x00000010 -#define PLL_CONFIGURATION3 0x00000014 -#define PLL_CONFIGURATION4 0x00000020 - -#define PLL_REGM_MASK 0x001FFE00 -#define PLL_REGM_SHIFT 0x9 -#define PLL_REGM_F_MASK 0x0003FFFF -#define PLL_REGM_F_SHIFT 0x0 -#define PLL_REGN_MASK 0x000001FE -#define PLL_REGN_SHIFT 0x1 -#define PLL_SELFREQDCO_MASK 0x0000000E -#define PLL_SELFREQDCO_SHIFT 0x1 -#define PLL_SD_MASK 0x0003FC00 -#define PLL_SD_SHIFT 0x9 -#define SET_PLL_GO 0x1 -#define PLL_TICOPWDN 0x10000 -#define PLL_LOCK 0x2 -#define PLL_IDLE 0x1 - -/* - * This is an Empirical value that works, need to confirm the actual - * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status - * to be correctly reflected in the USB3PHY_PLL_STATUS register. - */ -# define PLL_IDLE_TIME 100; - -struct usb_dpll_map { - unsigned long rate; - struct usb_dpll_params params; -}; - -static struct usb_dpll_map dpll_map[] = { - {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ - {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ - {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ - {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ - {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ - {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ -}; - -static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dpll_map); i++) { - if (rate == dpll_map[i].rate) - return &dpll_map[i].params; - } - - return NULL; -} - -static int omap_usb3_suspend(struct usb_phy *x, int suspend) -{ - struct omap_usb *phy = phy_to_omapusb(x); - int val; - int timeout = PLL_IDLE_TIME; - - if (suspend && !phy->is_suspended) { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val |= PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_TICOPWDN) - break; - udelay(1); - } while (--timeout); - - omap_control_usb_phy_power(phy->control_dev, 0); - - phy->is_suspended = 1; - } else if (!suspend && phy->is_suspended) { - phy->is_suspended = 0; - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (!(val & PLL_TICOPWDN)) - break; - udelay(1); - } while (--timeout); - } - - return 0; -} - -static void omap_usb_dpll_relock(struct omap_usb *phy) -{ - u32 val; - unsigned long timeout; - - omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); - - timeout = jiffies + msecs_to_jiffies(20); - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_LOCK) - break; - } while (!WARN_ON(time_after(jiffies, timeout))); -} - -static int omap_usb_dpll_lock(struct omap_usb *phy) -{ - u32 val; - unsigned long rate; - struct usb_dpll_params *dpll_params; - - rate = clk_get_rate(phy->sys_clk); - dpll_params = omap_usb3_get_dpll_params(rate); - if (!dpll_params) { - dev_err(phy->dev, - "No DPLL configuration for %lu Hz SYS CLK\n", rate); - return -EINVAL; - } - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGN_MASK; - val |= dpll_params->n << PLL_REGN_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_SELFREQDCO_MASK; - val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGM_MASK; - val |= dpll_params->m << PLL_REGM_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); - val &= ~PLL_REGM_F_MASK; - val |= dpll_params->mf << PLL_REGM_F_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); - val &= ~PLL_SD_MASK; - val |= dpll_params->sd << PLL_SD_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); - - omap_usb_dpll_relock(phy); - - return 0; -} - -static int omap_usb3_init(struct usb_phy *x) -{ - struct omap_usb *phy = phy_to_omapusb(x); - int ret; - - ret = omap_usb_dpll_lock(phy); - if (ret) - return ret; - - omap_control_usb_phy_power(phy->control_dev, 1); - - return 0; -} - -static int omap_usb3_probe(struct platform_device *pdev) -{ - struct omap_usb *phy; - struct resource *res; - struct device_node *node = pdev->dev.of_node; - struct device_node *control_node; - struct platform_device *control_pdev; - - if (!node) - return -EINVAL; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n"); - return -ENOMEM; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); - - phy->dev = &pdev->dev; - - phy->phy.dev = phy->dev; - phy->phy.label = "omap-usb3"; - phy->phy.init = omap_usb3_init; - phy->phy.set_suspend = omap_usb3_suspend; - phy->phy.type = USB_PHY_TYPE_USB3; - - phy->is_suspended = 1; - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); - if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - return PTR_ERR(phy->wkupclk); - } - clk_prepare(phy->wkupclk); - - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); - if (IS_ERR(phy->optclk)) { - dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n"); - return PTR_ERR(phy->optclk); - } - clk_prepare(phy->optclk); - - phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin"); - if (IS_ERR(phy->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(&pdev->dev, "Failed to get control device phandle\n"); - return -EINVAL; - } - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(&pdev->dev, "Failed to get control device\n"); - return -EINVAL; - } - - phy->control_dev = &control_pdev->dev; - - omap_control_usb_phy_power(phy->control_dev, 0); - usb_add_phy_dev(&phy->phy); - - platform_set_drvdata(pdev, phy); - - pm_runtime_enable(phy->dev); - pm_runtime_get(&pdev->dev); - - return 0; -} - -static int omap_usb3_remove(struct platform_device *pdev) -{ - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_unprepare(phy->wkupclk); - clk_unprepare(phy->optclk); - usb_remove_phy(&phy->phy); - if (!pm_runtime_suspended(&pdev->dev)) - pm_runtime_put(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME - -static int omap_usb3_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_disable(phy->wkupclk); - clk_disable(phy->optclk); - - return 0; -} - -static int omap_usb3_runtime_resume(struct device *dev) -{ - u32 ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - ret = clk_enable(phy->optclk); - if (ret) { - dev_err(phy->dev, "Failed to enable optclk %d\n", ret); - goto err1; - } - - ret = clk_enable(phy->wkupclk); - if (ret) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto err2; - } - - return 0; - -err2: - clk_disable(phy->optclk); - -err1: - return ret; -} - -static const struct dev_pm_ops omap_usb3_pm_ops = { - SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume, - NULL) -}; - -#define DEV_PM_OPS (&omap_usb3_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -#ifdef CONFIG_OF -static const struct of_device_id omap_usb3_id_table[] = { - { .compatible = "ti,omap-usb3" }, - {} -}; -MODULE_DEVICE_TABLE(of, omap_usb3_id_table); -#endif - -static struct platform_driver omap_usb3_driver = { - .probe = omap_usb3_probe, - .remove = omap_usb3_remove, - .driver = { - .name = "omap-usb3", - .owner = THIS_MODULE, - .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(omap_usb3_id_table), - }, -}; - -module_platform_driver(omap_usb3_driver); - -MODULE_ALIAS("platform: omap_usb3"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP USB3 phy driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.1 From bcffae7708eb8352f44dc510b326541fe43a02a4 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Mon, 3 Mar 2014 19:30:17 +0200 Subject: xhci: Prevent runtime pm from autosuspending during initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xHCI driver has its own pci probe function that will call usb_hcd_pci_probe to register its usb-2 bus, and then continue to manually register the usb-3 bus. usb_hcd_pci_probe does a pm_runtime_put_noidle at the end and might thus trigger a runtime suspend before the usb-3 bus is ready. Prevent the runtime suspend by increasing the usage count in the beginning of xhci_pci_probe, and decrease it once the usb-3 bus is ready. xhci-platform driver is not using usb_hcd_pci_probe to set up busses and should not need to have it's usage count increased during probe. Signed-off-by: Mathias Nyman Acked-by: Dan Williams Acked-by: Alan Stern Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/host/xhci-pci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cbf0c5cffc92..47390e369cd4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -190,6 +190,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) struct usb_hcd *hcd; driver = (struct hc_driver *)id->driver_data; + + /* Prevent runtime suspending between USB-2 and USB-3 initialization */ + pm_runtime_get_noresume(&dev->dev); + /* Register the USB 2.0 roothub. * FIXME: USB core must know to register the USB 2.0 roothub first. * This is sort of silly, because we could just set the HCD driver flags @@ -199,7 +203,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) retval = usb_hcd_pci_probe(dev, id); if (retval) - return retval; + goto put_runtime_pm; /* USB 2.0 roothub is stored in the PCI device now. */ hcd = dev_get_drvdata(&dev->dev); @@ -225,12 +229,17 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; + /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ + pm_runtime_put_noidle(&dev->dev); + return 0; put_usb3_hcd: usb_put_hcd(xhci->shared_hcd); dealloc_usb2_hcd: usb_hcd_pci_remove(dev); +put_runtime_pm: + pm_runtime_put_noidle(&dev->dev); return retval; } -- cgit v1.2.1 From 7969943789df1196faa9ba67518d83fd93e4f9f6 Mon Sep 17 00:00:00 2001 From: Adrian Huang Date: Thu, 27 Feb 2014 11:26:03 +0000 Subject: xhci: add the meaningful IRQ description if it is empty When some xHCI host controllers fall back to use the legacy IRQ, the member irq_descr of the usb_hcd structure will be empty. This leads to the empty string of the xHCI host controller in /proc/interrupts. Here is the example (The irq 19 is the xHCI host controller): CPU0 0: 91 IO-APIC-edge timer 8: 1 IO-APIC-edge rtc0 9: 7191 IO-APIC-fasteoi acpi 18: 104 IR-IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2 19: 473 IR-IO-APIC-fasteoi After applying the patch, the name of the registered xHCI host controller can be displayed correctly. Here is the example: CPU0 0: 91 IO-APIC-edge timer 8: 1 IO-APIC-edge rtc0 9: 7191 IO-APIC-fasteoi acpi 18: 104 IR-IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2 19: 473 IR-IO-APIC-fasteoi xhci_hcd:usb3 Tested on v3.14-rc4. Signed-off-by: Adrian Huang Reviewed-by: Nagananda Chumbalkar Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1db3c2794a49..652be2138b4b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -390,6 +390,10 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) } legacy_irq: + if (!strlen(hcd->irq_descr)) + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", + hcd->driver->description, hcd->self.busnum); + /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); -- cgit v1.2.1 From 73a30bfc0d526db899033165db6f95c427e70505 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Mar 2014 14:19:57 +0300 Subject: usb: dwc3: gadget: cut and paste fixups in suspend/resume These were cut and paste from the ->disconnect function. Fixes commit 30d577b9bcc4 ('usb: dwc3: gadget: call gadget driver's ->suspend/->resume') Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1730cd0928c5..4af75d0e53dc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2047,7 +2047,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) static void dwc3_suspend_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + if (dwc->gadget_driver && dwc->gadget_driver->suspend) { spin_unlock(&dwc->lock); dwc->gadget_driver->suspend(&dwc->gadget); spin_lock(&dwc->lock); @@ -2056,7 +2056,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc) static void dwc3_resume_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + if (dwc->gadget_driver && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); dwc->gadget_driver->resume(&dwc->gadget); spin_lock(&dwc->lock); -- cgit v1.2.1 From 3f89204bae896e1d44468886a646b84acadc3c8f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Mar 2014 14:20:22 +0300 Subject: usb: dwc3: gadget: remove known conditions We know what "value" is and it upsets static checkers that we appear to have doubts about it. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4af75d0e53dc..a740eac74d56 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1233,8 +1233,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETSTALL, ¶ms); if (ret) - dev_err(dwc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", + dev_err(dwc->dev, "failed to set STALL on %s\n", dep->name); else dep->flags |= DWC3_EP_STALL; @@ -1242,8 +1241,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_CLEARSTALL, ¶ms); if (ret) - dev_err(dwc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", + dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); else dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); -- cgit v1.2.1 From 716fb91dfe1777bd6d5e598f3d3572214b3ed296 Mon Sep 17 00:00:00 2001 From: Weinn Jheng Date: Thu, 27 Feb 2014 17:49:00 +0800 Subject: usb: gadget: u_ether: move hardware transmit to RX NAPI In order to reduce the interrupt times in the embedded system, a receiving workqueue is introduced. This modification also enhanced the overall throughput as the benefits of reducing interrupt occurrence. This work was derived from previous work: u_ether: move hardware transmit to RX workqueue. Which should be base on codeaurora's work. However, the benchmark on my platform shows the throughput with workqueue is slightly better than NAPI. Signed-off-by: Weinn Jheng Cc: David Brownell Cc: David S. Miller Cc: Stephen Hemminger Cc: Greg Kroah-Hartman Cc: Manu Gautam Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_ether.c | 101 ++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 35 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index b7d4f82872b7..50d09c289137 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -48,6 +48,8 @@ #define UETH__VERSION "29-May-2008" +#define GETHER_NAPI_WEIGHT 32 + struct eth_dev { /* lock is held while accessing port_usb */ @@ -72,6 +74,7 @@ struct eth_dev { struct sk_buff_head *list); struct work_struct work; + struct napi_struct rx_napi; unsigned long todo; #define WORK_RX_MEMORY 0 @@ -253,18 +256,16 @@ enomem: DBG(dev, "rx submit --> %d\n", retval); if (skb) dev_kfree_skb_any(skb); - spin_lock_irqsave(&dev->req_lock, flags); - list_add(&req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->req_lock, flags); } return retval; } static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context, *skb2; + struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; int status = req->status; + bool rx_queue = 0; switch (status) { @@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) } else { skb_queue_tail(&dev->rx_frames, skb); } - skb = NULL; - - skb2 = skb_dequeue(&dev->rx_frames); - while (skb2) { - if (status < 0 - || ETH_HLEN > skb2->len - || skb2->len > VLAN_ETH_FRAME_LEN) { - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - DBG(dev, "rx length %d\n", skb2->len); - dev_kfree_skb_any(skb2); - goto next_frame; - } - skb2->protocol = eth_type_trans(skb2, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb2->len; - - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb2); -next_frame: - skb2 = skb_dequeue(&dev->rx_frames); - } + if (!status) + rx_queue = 1; break; /* software-driven interface shutdown */ @@ -334,22 +313,20 @@ quiesce: /* FALLTHROUGH */ default: + rx_queue = 1; + dev_kfree_skb_any(skb); dev->net->stats.rx_errors++; DBG(dev, "rx status %d\n", status); break; } - if (skb) - dev_kfree_skb_any(skb); - if (!netif_running(dev->net)) { clean: spin_lock(&dev->req_lock); list_add(&req->list, &dev->rx_reqs); spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit(dev, req, GFP_ATOMIC); + + if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) + __napi_schedule(&dev->rx_napi); } static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) @@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) { struct usb_request *req; unsigned long flags; + int rx_counts = 0; /* fill unused rxq slots with some skb */ spin_lock_irqsave(&dev->req_lock, flags); while (!list_empty(&dev->rx_reqs)) { + + if (++rx_counts > qlen(dev->gadget, dev->qmult)) + break; + req = container_of(dev->rx_reqs.next, struct usb_request, list); list_del_init(&req->list); spin_unlock_irqrestore(&dev->req_lock, flags); if (rx_submit(dev, req, gfp_flags) < 0) { + spin_lock_irqsave(&dev->req_lock, flags); + list_add(&req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); defer_kevent(dev, WORK_RX_MEMORY); return; } @@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) spin_unlock_irqrestore(&dev->req_lock, flags); } +static int gether_poll(struct napi_struct *napi, int budget) +{ + struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi); + struct sk_buff *skb; + unsigned int work_done = 0; + int status = 0; + + while ((skb = skb_dequeue(&dev->rx_frames))) { + if (status < 0 + || ETH_HLEN > skb->len + || skb->len > VLAN_ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb->len); + dev_kfree_skb_any(skb); + continue; + } + skb->protocol = eth_type_trans(skb, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb->len; + + status = netif_rx_ni(skb); + } + + if (netif_running(dev->net)) { + rx_fill(dev, GFP_KERNEL); + work_done++; + } + + if (work_done < budget) + napi_complete(&dev->rx_napi); + + return work_done; +} + static void eth_work(struct work_struct *work) { struct eth_dev *dev = container_of(work, struct eth_dev, work); @@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) /* and open the tx floodgates */ atomic_set(&dev->tx_qlen, 0); netif_wake_queue(dev->net); + napi_enable(&dev->rx_napi); } static int eth_open(struct net_device *net) @@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net) unsigned long flags; VDBG(dev, "%s\n", __func__); + napi_disable(&dev->rx_napi); netif_stop_queue(net); DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", @@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, return ERR_PTR(-ENOMEM); dev = netdev_priv(net); + netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); spin_lock_init(&dev->lock); spin_lock_init(&dev->req_lock); INIT_WORK(&dev->work, eth_work); @@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname) return ERR_PTR(-ENOMEM); dev = netdev_priv(net); + netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); spin_lock_init(&dev->lock); spin_lock_init(&dev->req_lock); INIT_WORK(&dev->work, eth_work); @@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link) { struct eth_dev *dev = link->ioport; struct usb_request *req; + struct sk_buff *skb; WARN_ON(!dev); if (!dev) @@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->req_lock); } spin_unlock(&dev->req_lock); + + spin_lock(&dev->rx_frames.lock); + while ((skb = __skb_dequeue(&dev->rx_frames))) + dev_kfree_skb_any(skb); + spin_unlock(&dev->rx_frames.lock); + link->in_ep->driver_data = NULL; link->in_ep->desc = NULL; -- cgit v1.2.1 From 8bebbe8dc6145303db05964fb09657aac2a7e909 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Thu, 27 Feb 2014 07:38:19 +0800 Subject: usb: phy: fsm: update OTG HNP state transition conditions according to OTG and EH 2.0 spec. According to:"On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification July 27, 2012 Revision 2.0 version 1.1a" - From a_host to a_wait_bcon if !b_conn - Add transition from a_host to a_wait_vfall if id state is high or a_bus_drop - From a_wait_vfall to a_idle if a_wait_vfall_tmout Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsm-usb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 7aa314ef4a8a..c47e5a6edde2 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -317,10 +317,12 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); break; case OTG_STATE_A_HOST: - if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && fsm->otg->host->b_hnp_enable) otg_set_state(fsm, OTG_STATE_A_SUSPEND); - else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + else if (!fsm->b_conn) otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); else if (!fsm->a_vbus_vld) otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); @@ -346,8 +348,7 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); break; case OTG_STATE_A_WAIT_VFALL: - if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req || - (!fsm->a_sess_vld && !fsm->b_conn)) + if (fsm->a_wait_vfall_tmout) otg_set_state(fsm, OTG_STATE_A_IDLE); break; case OTG_STATE_A_VBUS_ERR: -- cgit v1.2.1 From cfe919b53b807ab32e89e1c662c6d242948449bd Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Tue, 4 Mar 2014 15:34:57 +0800 Subject: usb: gadget: return the right length in ffs_epfile_io() When the request length is aligned to maxpacketsize, sometimes the return length ret > the user space requested len. At that time, we will use min_t(size_t, ret, len) to limit the size in case of user data buffer overflow. But we need return the min_t(size_t, ret, len) to tell the user space rightly also. [ balbi@ti.com: also fix comment's indentation ] Acked-by: Michal Nazarewicz Reviewed-by: David Cohen Signed-off-by: Chuansheng Liu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 42f7a0e4be59..b2e922dcb404 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -838,19 +838,21 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ret = -EINTR; usb_ep_dequeue(ep->ep, req); } else { - /* - * XXX We may end up silently droping data here. - * Since data_len (i.e. req->length) may be bigger - * than len (after being rounded up to maxpacketsize), - * we may end up with more data then user space has - * space for. - */ - ret = ep->status; - if (io_data->read && ret > 0 && - unlikely(copy_to_user(io_data->buf, data, - min_t(size_t, ret, - io_data->len)))) - ret = -EFAULT; + /* + * XXX We may end up silently droping data + * here. Since data_len (i.e. req->length) may + * be bigger than len (after being rounded up + * to maxpacketsize), we may end up with more + * data then user space has space for. + */ + ret = ep->status; + if (io_data->read && ret > 0) { + ret = min_t(size_t, ret, io_data->len); + + if (unlikely(copy_to_user(io_data->buf, + data, ret))) + ret = -EFAULT; + } } kfree(data); } -- cgit v1.2.1 From 6284be23db6b32e17ef34d082386967350d10d1a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Mar 2014 17:08:14 +0530 Subject: phy: omap-usb2: move omap_usb.h from linux/usb/ to linux/phy/ No functional change. Moved omap_usb.h from linux/usb/ to linux/phy/. Also removed the unused members of struct omap_usb (after phy-omap-pipe3 started using it's own header file) Signed-off-by: Kishon Vijay Abraham I --- drivers/usb/phy/phy-twl6030-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 214172b68d5d..04778cf80d60 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.1 From 2a6da97ff530650d26570a6a1ec0ac1deac927bd Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Tue, 4 Mar 2014 11:24:55 -0600 Subject: usb: wusbcore: fix potential double list_del on urb dequeue This patch locks rpipe->seg_lock around the entire transfer segment cleanup loop in wa_urb_dequeue instead of just one case of the switch statement. This fixes a race between __wa_xfer_delayed_run and wa_urb_dequeue where a transfer segment in the WA_SEG_DELAYED state could be removed from the rpipe seg_list twice leading to memory corruption. It also switches the spin_lock call to use the non-irqsave version since the xfer->lock is already held and irqs already disabled. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 404ce81b286c..6e0d377d437c 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1974,6 +1974,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) goto out_unlock; /* setup(), enqueue_b() completes */ /* Ok, the xfer is in flight already, it's been setup and submitted.*/ xfer_abort_pending = __wa_xfer_abort(xfer) >= 0; + /* + * grab the rpipe->seg_lock here to prevent racing with + * __wa_xfer_delayed_run. + */ + spin_lock(&rpipe->seg_lock); for (cnt = 0; cnt < xfer->segs; cnt++) { seg = xfer->seg[cnt]; pr_debug("%s: xfer id 0x%08X#%d status = %d\n", @@ -1994,10 +1999,8 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) */ seg->status = WA_SEG_ABORTED; seg->result = -ENOENT; - spin_lock_irqsave(&rpipe->seg_lock, flags2); list_del(&seg->list_node); xfer->segs_done++; - spin_unlock_irqrestore(&rpipe->seg_lock, flags2); break; case WA_SEG_DONE: case WA_SEG_ERROR: @@ -2026,6 +2029,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) break; } } + spin_unlock(&rpipe->seg_lock); xfer->result = urb->status; /* -ENOENT or -ECONNRESET */ done = __wa_xfer_is_done(xfer); spin_unlock_irqrestore(&xfer->lock, flags); -- cgit v1.2.1 From 5090ecea133325f762704f00963bca1b024ee691 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Tue, 4 Mar 2014 11:24:56 -0600 Subject: usb: wusbcore: don't mark WA_SEG_DTI_PENDING segs as done in urb_dequeue Data for transfer segments in the WA_SEG_DTI_PENDING state is actively being read by the driver. Let the buffer read callback handle the transfer cleanup since cleaning it up in wa_urb_dequeue will cause the read callback to access invalid memory if the transfer is completed. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 6e0d377d437c..cf7c95ceebe0 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2005,6 +2005,16 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) case WA_SEG_DONE: case WA_SEG_ERROR: case WA_SEG_ABORTED: + break; + /* + * The buf_in data for a segment in the + * WA_SEG_DTI_PENDING state is actively being read. + * Let wa_buf_in_cb handle it since it will be called + * and will increment xfer->segs_done. Cleaning up + * here could cause wa_buf_in_cb to access the xfer + * after it has been completed/freed. + */ + case WA_SEG_DTI_PENDING: break; /* * In the states below, the HWA device already knows @@ -2015,7 +2025,6 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) */ case WA_SEG_SUBMITTED: case WA_SEG_PENDING: - case WA_SEG_DTI_PENDING: /* * Check if the abort was successfully sent. This could * be false if the HWA has been removed but we haven't -- cgit v1.2.1 From 86e2864d7e9d429b94624a28ba3f05fc2db89051 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 6 Mar 2014 12:53:37 -0600 Subject: usb: wusbcore: disable transfer notifications for Alereon HWAs The HWA driver does not do anything with transfer notifications after receiving the first one and the Alereon HWA allows them to be disabled as a performance optimization. This patch sends a vendor specific command to the Alereon HWA on startup to disable transfer notifications. If the command is successful, the DTI system is started immediately since that would normally be started upon the first reception of a transfer notification which will no longer be sent. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/hwa-hc.c | 42 ++++++++++++++++++++- drivers/usb/wusbcore/wa-hc.h | 11 ++++++ drivers/usb/wusbcore/wa-xfer.c | 83 +++++++++++++++++++++++++----------------- 3 files changed, 100 insertions(+), 36 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index e07669993f58..d0d8fadf7066 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) dev_err(dev, "cannot listen to notifications: %d\n", result); goto error_stop; } + /* + * If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set, + * disable transfer notifications. + */ + if (hwahc->wa.quirks & + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) { + struct usb_host_interface *cur_altsetting = + hwahc->wa.usb_iface->cur_altsetting; + + result = usb_control_msg(hwahc->wa.usb_dev, + usb_sndctrlpipe(hwahc->wa.usb_dev, 0), + WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + WA_REQ_ALEREON_FEATURE_SET, + cur_altsetting->desc.bInterfaceNumber, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + /* + * If we successfully sent the control message, start DTI here + * because no transfer notifications will be received which is + * where DTI is normally started. + */ + if (result == 0) + result = wa_dti_start(&hwahc->wa); + else + result = 0; /* OK. Continue normally. */ + + if (result < 0) { + dev_err(dev, "cannot start DTI: %d\n", result); + goto error_dti_start; + } + } + return result; +error_dti_start: + wa_nep_disarm(&hwahc->wa); error_stop: __wa_clear_feature(&hwahc->wa, WA_ENABLE); return result; @@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface) static struct usb_device_id hwahc_id_table[] = { /* Alereon 5310 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, /* Alereon 5611 */ { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, + .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, /* FIXME: use class labels for this */ { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, {}, diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index a2ef84b8397e..7510960588dc 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -134,8 +134,18 @@ enum wa_quirks { * requests to be concatenated and not sent as separate packets. */ WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01, + /* + * The Alereon HWA can be instructed to not send transfer notifications + * as an optimization. + */ + WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS = 0x02, }; +enum wa_vendor_specific_requests { + WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C, + WA_REQ_ALEREON_FEATURE_SET = 0x01, + WA_REQ_ALEREON_FEATURE_CLEAR = 0x00, +}; /** * Instance of a HWA Host Controller * @@ -234,6 +244,7 @@ struct wahc { extern int wa_create(struct wahc *wa, struct usb_interface *iface, kernel_ulong_t); extern void __wa_destroy(struct wahc *wa); +extern int wa_dti_start(struct wahc *wa); void wa_reset_all(struct wahc *wa); diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index cf7c95ceebe0..52c7259705e6 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2741,41 +2741,15 @@ out: } /* - * Transfer complete notification - * - * Called from the notif.c code. We get a notification on EP2 saying - * that some endpoint has some transfer result data available. We are - * about to read it. - * - * To speed up things, we always have a URB reading the DTI URB; we - * don't really set it up and start it until the first xfer complete - * notification arrives, which is what we do here. - * - * Follow up in wa_dti_cb(), as that's where the whole state - * machine starts. - * - * So here we just initialize the DTI URB for reading transfer result - * notifications and also the buffer-in URB, for reading buffers. Then - * we just submit the DTI URB. - * - * @wa shall be referenced + * Initialize the DTI URB for reading transfer result notifications and also + * the buffer-in URB, for reading buffers. Then we just submit the DTI URB. */ -void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) +int wa_dti_start(struct wahc *wa) { - int result; - struct device *dev = &wa->usb_iface->dev; - struct wa_notif_xfer *notif_xfer; const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; + struct device *dev = &wa->usb_iface->dev; + int result = -ENOMEM; - notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr); - BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER); - - if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) { - /* FIXME: hardcoded limitation, adapt */ - dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n", - notif_xfer->bEndpoint, dti_epd->bEndpointAddress); - goto error; - } if (wa->dti_urb != NULL) /* DTI URB already started */ goto out; @@ -2786,7 +2760,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) } usb_fill_bulk_urb( wa->dti_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), + usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress), wa->dti_buf, wa->dti_buf_size, wa_dti_cb, wa); @@ -2797,7 +2771,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) } usb_fill_bulk_urb( wa->buf_in_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), + usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress), NULL, 0, wa_buf_in_cb, wa); result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); if (result < 0) { @@ -2806,7 +2780,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) goto error_dti_urb_submit; } out: - return; + return 0; error_dti_urb_submit: usb_put_urb(wa->buf_in_urb); @@ -2815,6 +2789,47 @@ error_buf_in_urb_alloc: usb_put_urb(wa->dti_urb); wa->dti_urb = NULL; error_dti_urb_alloc: + return result; +} +EXPORT_SYMBOL_GPL(wa_dti_start); +/* + * Transfer complete notification + * + * Called from the notif.c code. We get a notification on EP2 saying + * that some endpoint has some transfer result data available. We are + * about to read it. + * + * To speed up things, we always have a URB reading the DTI URB; we + * don't really set it up and start it until the first xfer complete + * notification arrives, which is what we do here. + * + * Follow up in wa_dti_cb(), as that's where the whole state + * machine starts. + * + * @wa shall be referenced + */ +void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) +{ + struct device *dev = &wa->usb_iface->dev; + struct wa_notif_xfer *notif_xfer; + const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; + + notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr); + BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER); + + if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) { + /* FIXME: hardcoded limitation, adapt */ + dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n", + notif_xfer->bEndpoint, dti_epd->bEndpointAddress); + goto error; + } + + /* attempt to start the DTI ep processing. */ + if (wa_dti_start(wa) < 0) + goto error; + + return; + error: wa_reset_all(wa); } -- cgit v1.2.1 From 04a378f36d9dc9e242ff206fcad23ba258dba818 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 7 Mar 2014 15:37:34 -0600 Subject: usb: wusbcore: combine iso transfer result frame reads when possible When reading the transfer result data for an isochronous in request, if the current frame actual_length is contiguous with the next frame and actual_length is a multiple of the DTI endpoint max packet size, combine the current frame with the next frame in a single URB. This reduces the number of URBs that must be submitted in that case which increases performance and reduces CPU interrupt overhead. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 106 +++++++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 52c7259705e6..f930bbb1a0ff 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2160,22 +2160,59 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, } /* Populate the wa->buf_in_urb based on the current isoc transfer state. */ -static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, - struct wa_seg *seg, int curr_iso_frame) +static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, + struct wa_seg *seg) { + int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset; + int seg_index, total_len = 0, urb_frame_index = urb_start_frame; + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + const int dti_packet_size = usb_endpoint_maxp(wa->dti_epd); + int next_frame_contiguous; + struct usb_iso_packet_descriptor *iso_frame; + BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + /* + * If the current frame actual_length is contiguous with the next frame + * and actual_length is a multiple of the DTI endpoint max packet size, + * combine the current frame with the next frame in a single URB. This + * reduces the number of URBs that must be submitted in that case. + */ + seg_index = seg->isoc_frame_index; + do { + next_frame_contiguous = 0; + + iso_frame = &iso_frame_desc[urb_frame_index]; + total_len += iso_frame->actual_length; + ++urb_frame_index; + ++seg_index; + + if (seg_index < seg->isoc_frame_count) { + struct usb_iso_packet_descriptor *next_iso_frame; + + next_iso_frame = &iso_frame_desc[urb_frame_index]; + + if ((iso_frame->offset + iso_frame->actual_length) == + next_iso_frame->offset) + next_frame_contiguous = 1; + } + } while (next_frame_contiguous + && ((iso_frame->actual_length % dti_packet_size) == 0)); + /* this should always be 0 before a resubmit. */ wa->buf_in_urb->num_mapped_sgs = 0; wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + - xfer->urb->iso_frame_desc[curr_iso_frame].offset; - wa->buf_in_urb->transfer_buffer_length = - xfer->urb->iso_frame_desc[curr_iso_frame].actual_length; + iso_frame_desc[urb_start_frame].offset; + wa->buf_in_urb->transfer_buffer_length = total_len; wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; wa->buf_in_urb->transfer_buffer = NULL; wa->buf_in_urb->sg = NULL; wa->buf_in_urb->num_sgs = 0; wa->buf_in_urb->context = seg; + + /* return the number of frames included in this URB. */ + return seg_index - seg->isoc_frame_index; } /* Populate the wa->buf_in_urb based on the current transfer state. */ @@ -2458,19 +2495,20 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) } if (xfer->is_inbound && data_frame_count) { - int result; + int result, urb_frame_count; seg->isoc_frame_index = first_frame_index; /* submit a read URB for the first frame with data. */ - __wa_populate_buf_in_urb_isoc(wa, xfer, seg, - seg->isoc_frame_index + seg->isoc_frame_offset); + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, xfer, seg); + /* advance index to start of next read URB. */ + seg->isoc_frame_index += urb_frame_count; result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); - } else if (data_frame_count > 1) + } else if (data_frame_count > urb_frame_count) /* If we need to read multiple frames, set DTI busy. */ dti_busy = 1; } else { @@ -2511,8 +2549,9 @@ static void wa_buf_in_cb(struct urb *urb) struct wahc *wa; struct device *dev; struct wa_rpipe *rpipe; - unsigned rpipe_ready = 0, seg_index, isoc_data_frame_count = 0; + unsigned rpipe_ready = 0, isoc_data_frame_count = 0; unsigned long flags; + int resubmit_dti = 0; u8 done = 0; /* free the sg if it was used. */ @@ -2524,17 +2563,16 @@ static void wa_buf_in_cb(struct urb *urb) dev = &wa->usb_iface->dev; if (usb_pipeisoc(xfer->urb->pipe)) { + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + int seg_index; + /* - * Find the next isoc frame with data. Bail out after - * isoc_data_frame_count > 1 since there is no need to walk - * the entire frame array. We just need to know if - * isoc_data_frame_count is 0, 1, or >1. + * Find the next isoc frame with data and count how many + * frames with data remain. */ - seg_index = seg->isoc_frame_index + 1; - while ((seg_index < seg->isoc_frame_count) - && (isoc_data_frame_count <= 1)) { - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; + seg_index = seg->isoc_frame_index; + while (seg_index < seg->isoc_frame_count) { const int urb_frame_index = seg->isoc_frame_offset + seg_index; @@ -2555,16 +2593,28 @@ static void wa_buf_in_cb(struct urb *urb) seg->result += urb->actual_length; if (isoc_data_frame_count > 0) { - int result; - /* submit a read URB for the first frame with data. */ - __wa_populate_buf_in_urb_isoc(wa, xfer, seg, - seg->isoc_frame_index + seg->isoc_frame_offset); + int result, urb_frame_count; + + /* submit a read URB for the next frame with data. */ + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, + xfer, seg); + /* advance index to start of next read URB. */ + seg->isoc_frame_index += urb_frame_count; result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); } + /* + * If we are in this callback and + * isoc_data_frame_count > 0, it means that the dti_urb + * submission was delayed in wa_dti_cb. Once + * we submit the last buf_in_urb, we can submit the + * delayed dti_urb. + */ + resubmit_dti = (isoc_data_frame_count == + urb_frame_count); } else { rpipe = xfer->ep->hcpriv; dev_dbg(dev, @@ -2585,6 +2635,7 @@ static void wa_buf_in_cb(struct urb *urb) case -ENOENT: /* as it was done by the who unlinked us */ break; default: /* Other errors ... */ + resubmit_dti = 1; spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) @@ -2607,13 +2658,8 @@ static void wa_buf_in_cb(struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); } - /* - * If we are in this callback and isoc_data_frame_count > 0, it means - * that the dti_urb submission was delayed in wa_dti_cb. Once - * isoc_data_frame_count gets to 1, we can submit the deferred URB - * since the last buf_in_urb was just submitted. - */ - if (isoc_data_frame_count == 1) { + + if (resubmit_dti) { int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", -- cgit v1.2.1 From 335053fe8c94f50c7f1cd7011b3088547480df3c Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Fri, 7 Mar 2014 15:37:35 -0600 Subject: usb: wusbcore: use multiple urbs for HWA iso transfer result frame reads Submit multiple concurrent urbs for HWA isochronous transfer result data frame reads. This keeps the read pipeline full and significantly improves performance in cases where the frame reads cannot be combined because they are not contiguous or multiples of the max packet size. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-hc.c | 2 - drivers/usb/wusbcore/wa-hc.h | 15 +++- drivers/usb/wusbcore/wa-xfer.c | 191 ++++++++++++++++++++++++++--------------- 3 files changed, 134 insertions(+), 74 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 368360f9a93a..252c7bd9218a 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -75,8 +75,6 @@ void __wa_destroy(struct wahc *wa) if (wa->dti_urb) { usb_kill_urb(wa->dti_urb); usb_put_urb(wa->dti_urb); - usb_kill_urb(wa->buf_in_urb); - usb_put_urb(wa->buf_in_urb); } kfree(wa->dti_buf); wa_nep_destroy(wa); diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index 7510960588dc..f2a8d29e17b9 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -125,7 +125,8 @@ struct wa_rpipe { enum wa_dti_state { WA_DTI_TRANSFER_RESULT_PENDING, - WA_DTI_ISOC_PACKET_STATUS_PENDING + WA_DTI_ISOC_PACKET_STATUS_PENDING, + WA_DTI_BUF_IN_DATA_PENDING }; enum wa_quirks { @@ -146,6 +147,8 @@ enum wa_vendor_specific_requests { WA_REQ_ALEREON_FEATURE_SET = 0x01, WA_REQ_ALEREON_FEATURE_CLEAR = 0x00, }; + +#define WA_MAX_BUF_IN_URBS 4 /** * Instance of a HWA Host Controller * @@ -216,7 +219,9 @@ struct wahc { u32 dti_isoc_xfer_in_progress; u8 dti_isoc_xfer_seg; struct urb *dti_urb; /* URB for reading xfer results */ - struct urb *buf_in_urb; /* URB for reading data in */ + /* URBs for reading data in */ + struct urb buf_in_urbs[WA_MAX_BUF_IN_URBS]; + int active_buf_in_urbs; /* number of buf_in_urbs active. */ struct edc dti_edc; /* DTI error density counter */ void *dti_buf; size_t dti_buf_size; @@ -286,6 +291,8 @@ static inline void wa_rpipe_init(struct wahc *wa) static inline void wa_init(struct wahc *wa) { + int index; + edc_init(&wa->nep_edc); atomic_set(&wa->notifs_queued, 0); wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; @@ -299,6 +306,10 @@ static inline void wa_init(struct wahc *wa) INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); wa->dto_in_use = 0; atomic_set(&wa->xfer_id_count, 1); + /* init the buf in URBs */ + for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) + usb_init_urb(&(wa->buf_in_urbs[index])); + wa->active_buf_in_urbs = 0; } /** diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index f930bbb1a0ff..c8e2a47d62a7 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2159,9 +2159,9 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, } } -/* Populate the wa->buf_in_urb based on the current isoc transfer state. */ -static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, - struct wa_seg *seg) +/* Populate the given urb based on the current isoc transfer state. */ +static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, + struct urb *buf_in_urb, struct wa_xfer *xfer, struct wa_seg *seg) { int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset; int seg_index, total_len = 0, urb_frame_index = urb_start_frame; @@ -2171,7 +2171,7 @@ static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, int next_frame_contiguous; struct usb_iso_packet_descriptor *iso_frame; - BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + BUG_ON(buf_in_urb->status == -EINPROGRESS); /* * If the current frame actual_length is contiguous with the next frame @@ -2201,68 +2201,68 @@ static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, && ((iso_frame->actual_length % dti_packet_size) == 0)); /* this should always be 0 before a resubmit. */ - wa->buf_in_urb->num_mapped_sgs = 0; - wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + + buf_in_urb->num_mapped_sgs = 0; + buf_in_urb->transfer_dma = xfer->urb->transfer_dma + iso_frame_desc[urb_start_frame].offset; - wa->buf_in_urb->transfer_buffer_length = total_len; - wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - wa->buf_in_urb->transfer_buffer = NULL; - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; - wa->buf_in_urb->context = seg; + buf_in_urb->transfer_buffer_length = total_len; + buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_buffer = NULL; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; + buf_in_urb->context = seg; /* return the number of frames included in this URB. */ return seg_index - seg->isoc_frame_index; } -/* Populate the wa->buf_in_urb based on the current transfer state. */ -static int wa_populate_buf_in_urb(struct wahc *wa, struct wa_xfer *xfer, +/* Populate the given urb based on the current transfer state. */ +static int wa_populate_buf_in_urb(struct urb *buf_in_urb, struct wa_xfer *xfer, unsigned int seg_idx, unsigned int bytes_transferred) { int result = 0; struct wa_seg *seg = xfer->seg[seg_idx]; - BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + BUG_ON(buf_in_urb->status == -EINPROGRESS); /* this should always be 0 before a resubmit. */ - wa->buf_in_urb->num_mapped_sgs = 0; + buf_in_urb->num_mapped_sgs = 0; if (xfer->is_dma) { - wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + buf_in_urb->transfer_dma = xfer->urb->transfer_dma + (seg_idx * xfer->seg_size); - wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - wa->buf_in_urb->transfer_buffer = NULL; - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; + buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_buffer = NULL; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; } else { /* do buffer or SG processing. */ - wa->buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; + buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; if (xfer->urb->transfer_buffer) { - wa->buf_in_urb->transfer_buffer = + buf_in_urb->transfer_buffer = xfer->urb->transfer_buffer + (seg_idx * xfer->seg_size); - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; + buf_in_urb->sg = NULL; + buf_in_urb->num_sgs = 0; } else { /* allocate an SG list to store seg_size bytes and copy the subset of the xfer->urb->sg that matches the buffer subset we are about to read. */ - wa->buf_in_urb->sg = wa_xfer_create_subset_sg( + buf_in_urb->sg = wa_xfer_create_subset_sg( xfer->urb->sg, seg_idx * xfer->seg_size, bytes_transferred, - &(wa->buf_in_urb->num_sgs)); + &(buf_in_urb->num_sgs)); - if (!(wa->buf_in_urb->sg)) { - wa->buf_in_urb->num_sgs = 0; + if (!(buf_in_urb->sg)) { + buf_in_urb->num_sgs = 0; result = -ENOMEM; } - wa->buf_in_urb->transfer_buffer = NULL; + buf_in_urb->transfer_buffer = NULL; } } - wa->buf_in_urb->transfer_buffer_length = bytes_transferred; - wa->buf_in_urb->context = seg; + buf_in_urb->transfer_buffer_length = bytes_transferred; + buf_in_urb->context = seg; return result; } @@ -2287,6 +2287,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, u8 usb_status; unsigned rpipe_ready = 0; unsigned bytes_transferred = le32_to_cpu(xfer_result->dwTransferLength); + struct urb *buf_in_urb = &(wa->buf_in_urbs[0]); spin_lock_irqsave(&xfer->lock, flags); seg_idx = xfer_result->bTransferSegment & 0x7f; @@ -2337,13 +2338,16 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, && (bytes_transferred > 0)) { /* IN data phase: read to buffer */ seg->status = WA_SEG_DTI_PENDING; - result = wa_populate_buf_in_urb(wa, xfer, seg_idx, + result = wa_populate_buf_in_urb(buf_in_urb, xfer, seg_idx, bytes_transferred); if (result < 0) goto error_buf_in_populate; - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); - if (result < 0) + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); + if (result < 0) { + --(wa->active_buf_in_urbs); goto error_submit_buf_in; + } } else { /* OUT data phase or no data, complete it -- */ seg->result = bytes_transferred; @@ -2367,8 +2371,8 @@ error_submit_buf_in: dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n", xfer, seg_idx, result); seg->result = result; - kfree(wa->buf_in_urb->sg); - wa->buf_in_urb->sg = NULL; + kfree(buf_in_urb->sg); + buf_in_urb->sg = NULL; error_buf_in_populate: __wa_xfer_abort(xfer); seg->status = WA_SEG_ERROR; @@ -2477,16 +2481,16 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) { struct usb_iso_packet_descriptor *iso_frame_desc = xfer->urb->iso_frame_desc; - const int urb_frame_index = + const int xfer_frame_index = seg->isoc_frame_offset + seg_index; - iso_frame_desc[urb_frame_index].status = + iso_frame_desc[xfer_frame_index].status = wa_xfer_status_to_errno( le16_to_cpu(status_array[seg_index].PacketStatus)); - iso_frame_desc[urb_frame_index].actual_length = + iso_frame_desc[xfer_frame_index].actual_length = le16_to_cpu(status_array[seg_index].PacketLength); /* track the number of frames successfully transferred. */ - if (iso_frame_desc[urb_frame_index].actual_length > 0) { + if (iso_frame_desc[xfer_frame_index].actual_length > 0) { /* save the starting frame index for buf_in_urb. */ if (!data_frame_count) first_frame_index = seg_index; @@ -2495,21 +2499,53 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) } if (xfer->is_inbound && data_frame_count) { - int result, urb_frame_count; + int result, total_frames_read = 0, urb_index = 0; + struct urb *buf_in_urb; + /* IN data phase: read to buffer */ + seg->status = WA_SEG_DTI_PENDING; + + /* start with the first frame with data. */ seg->isoc_frame_index = first_frame_index; - /* submit a read URB for the first frame with data. */ - urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, xfer, seg); - /* advance index to start of next read URB. */ - seg->isoc_frame_index += urb_frame_count; + /* submit up to WA_MAX_BUF_IN_URBS read URBs. */ + do { + int urb_frame_index, urb_frame_count; + struct usb_iso_packet_descriptor *iso_frame_desc; + + buf_in_urb = &(wa->buf_in_urbs[urb_index]); + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, + buf_in_urb, xfer, seg); + /* advance frame index to start of next read URB. */ + seg->isoc_frame_index += urb_frame_count; + total_frames_read += urb_frame_count; + + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); + + /* skip 0-byte frames. */ + urb_frame_index = + seg->isoc_frame_offset + seg->isoc_frame_index; + iso_frame_desc = + &(xfer->urb->iso_frame_desc[urb_frame_index]); + while ((seg->isoc_frame_index < + seg->isoc_frame_count) && + (iso_frame_desc->actual_length == 0)) { + ++(seg->isoc_frame_index); + ++iso_frame_desc; + } + ++urb_index; + + } while ((result == 0) && (urb_index < WA_MAX_BUF_IN_URBS) + && (seg->isoc_frame_index < + seg->isoc_frame_count)); - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); if (result < 0) { + --(wa->active_buf_in_urbs); dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); - } else if (data_frame_count > urb_frame_count) - /* If we need to read multiple frames, set DTI busy. */ + } else if (data_frame_count > total_frames_read) + /* If we need to read more frames, set DTI busy. */ dti_busy = 1; } else { /* OUT transfer or no more IN data, complete it -- */ @@ -2517,7 +2553,10 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); } spin_unlock_irqrestore(&xfer->lock, flags); - wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; + if (dti_busy) + wa->dti_state = WA_DTI_BUF_IN_DATA_PENDING; + else + wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; if (done) wa_xfer_completion(xfer); if (rpipe_ready) @@ -2551,7 +2590,7 @@ static void wa_buf_in_cb(struct urb *urb) struct wa_rpipe *rpipe; unsigned rpipe_ready = 0, isoc_data_frame_count = 0; unsigned long flags; - int resubmit_dti = 0; + int resubmit_dti = 0, active_buf_in_urbs; u8 done = 0; /* free the sg if it was used. */ @@ -2561,6 +2600,8 @@ static void wa_buf_in_cb(struct urb *urb) spin_lock_irqsave(&xfer->lock, flags); wa = xfer->wa; dev = &wa->usb_iface->dev; + --(wa->active_buf_in_urbs); + active_buf_in_urbs = wa->active_buf_in_urbs; if (usb_pipeisoc(xfer->urb->pipe)) { struct usb_iso_packet_descriptor *iso_frame_desc = @@ -2596,12 +2637,14 @@ static void wa_buf_in_cb(struct urb *urb) int result, urb_frame_count; /* submit a read URB for the next frame with data. */ - urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, + urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, urb, xfer, seg); /* advance index to start of next read URB. */ seg->isoc_frame_index += urb_frame_count; - result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); + ++(wa->active_buf_in_urbs); + result = usb_submit_urb(urb, GFP_ATOMIC); if (result < 0) { + --(wa->active_buf_in_urbs); dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", result); wa_reset_all(wa); @@ -2615,7 +2658,7 @@ static void wa_buf_in_cb(struct urb *urb) */ resubmit_dti = (isoc_data_frame_count == urb_frame_count); - } else { + } else if (active_buf_in_urbs == 0) { rpipe = xfer->ep->hcpriv; dev_dbg(dev, "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", @@ -2635,7 +2678,12 @@ static void wa_buf_in_cb(struct urb *urb) case -ENOENT: /* as it was done by the who unlinked us */ break; default: /* Other errors ... */ - resubmit_dti = 1; + /* + * Error on data buf read. Only resubmit DTI if it hasn't + * already been done by previously hitting this error or by a + * successful completion of the previous buf_in_urb. + */ + resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING; spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) @@ -2650,8 +2698,11 @@ static void wa_buf_in_cb(struct urb *urb) } seg->result = urb->status; rpipe_ready = rpipe_avail_inc(rpipe); - __wa_xfer_abort(xfer); - done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); + if (active_buf_in_urbs == 0) + done = __wa_xfer_mark_seg_as_done(xfer, seg, + WA_SEG_ERROR); + else + __wa_xfer_abort(xfer); spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); @@ -2660,7 +2711,11 @@ static void wa_buf_in_cb(struct urb *urb) } if (resubmit_dti) { - int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); + int result; + + wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; + + result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); if (result < 0) { dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", result); @@ -2794,7 +2849,7 @@ int wa_dti_start(struct wahc *wa) { const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; struct device *dev = &wa->usb_iface->dev; - int result = -ENOMEM; + int result = -ENOMEM, index; if (wa->dti_urb != NULL) /* DTI URB already started */ goto out; @@ -2810,15 +2865,14 @@ int wa_dti_start(struct wahc *wa) wa->dti_buf, wa->dti_buf_size, wa_dti_cb, wa); - wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (wa->buf_in_urb == NULL) { - dev_err(dev, "Can't allocate BUF-IN URB\n"); - goto error_buf_in_urb_alloc; + /* init the buf in URBs */ + for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) { + usb_fill_bulk_urb( + &(wa->buf_in_urbs[index]), wa->usb_dev, + usb_rcvbulkpipe(wa->usb_dev, + 0x80 | dti_epd->bEndpointAddress), + NULL, 0, wa_buf_in_cb, wa); } - usb_fill_bulk_urb( - wa->buf_in_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress), - NULL, 0, wa_buf_in_cb, wa); result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); if (result < 0) { dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n", @@ -2829,9 +2883,6 @@ out: return 0; error_dti_urb_submit: - usb_put_urb(wa->buf_in_urb); - wa->buf_in_urb = NULL; -error_buf_in_urb_alloc: usb_put_urb(wa->dti_urb); wa->dti_urb = NULL; error_dti_urb_alloc: -- cgit v1.2.1 From 14da699bc04212559d5dda19d3f07c807fb58dfd Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 6 Mar 2014 16:38:37 +0200 Subject: phy: rename struct omap_control_usb to struct omap_control_phy Rename struct omap_control_usb to struct omap_control_phy since it can be used to control PHY of USB, SATA and PCIE. Also move the driver and include files under *phy* and made the corresponding changes in the users of phy-omap-control. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Roger Quadros Acked-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/phy/Kconfig | 10 -- drivers/usb/phy/Makefile | 1 - drivers/usb/phy/phy-omap-control.c | 319 ------------------------------------- 4 files changed, 1 insertion(+), 331 deletions(-) delete mode 100644 drivers/usb/phy/phy-omap-control.c (limited to 'drivers/usb') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 8aa59a2c5eb2..d341c149a2f9 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include "musb_core.h" diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index c337ba2d066b..416e0c8cf6ff 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -75,16 +75,6 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. -config OMAP_CONTROL_USB - tristate "OMAP CONTROL USB Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - help - Enable this to add support for the USB part present in the control - module. This driver has API to power on the USB2 PHY and to write to - the mailbox. The mailbox is present only in omap4 and the register to - power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an - additional register to power on USB3 PHY. - config AM335X_CONTROL_USB tristate diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 15f1878388f4..f8fa719a31b9 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o -obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c deleted file mode 100644 index e7253182e47d..000000000000 --- a/drivers/usb/phy/phy-omap-control.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * omap-control-usb.c - The USB part of control module. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * omap_control_usb_phy_power - power on/off the phy using control module reg - * @dev: the control module device - * @on: 0 or 1, based on powering on or off the PHY - */ -void omap_control_usb_phy_power(struct device *dev, int on) -{ - u32 val; - unsigned long rate; - struct omap_control_usb *control_usb; - - if (IS_ERR(dev) || !dev) { - pr_err("%s: invalid device\n", __func__); - return; - } - - control_usb = dev_get_drvdata(dev); - if (!control_usb) { - dev_err(dev, "%s: invalid control usb device\n", __func__); - return; - } - - if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) - return; - - val = readl(control_usb->power); - - switch (control_usb->type) { - case OMAP_CTRL_TYPE_USB2: - if (on) - val &= ~OMAP_CTRL_DEV_PHY_PD; - else - val |= OMAP_CTRL_DEV_PHY_PD; - break; - - case OMAP_CTRL_TYPE_PIPE3: - rate = clk_get_rate(control_usb->sys_clk); - rate = rate/1000000; - - if (on) { - val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | - OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; - } else { - val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - } - break; - - case OMAP_CTRL_TYPE_DRA7USB2: - if (on) - val &= ~OMAP_CTRL_USB2_PHY_PD; - else - val |= OMAP_CTRL_USB2_PHY_PD; - break; - - case OMAP_CTRL_TYPE_AM437USB2: - if (on) { - val &= ~(AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - val |= (AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - } else { - val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - val |= (AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - } - break; - default: - dev_err(dev, "%s: type %d not recognized\n", - __func__, control_usb->type); - break; - } - - writel(val, control_usb->power); -} -EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); - -/** - * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that a usb - * device has been connected. - */ -static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); - val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that it has been - * connected to a usb host. - */ -static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~OMAP_CTRL_DEV_SESSEND; - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | - OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core it's now in - * disconnected state. - */ -static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode - * or device mode or to denote disconnected state - * @dev: the control module device - * @mode: The mode to which usb should be configured - * - * This is an API to write to the mailbox register to notify the usb core that - * a usb device has been connected. - */ -void omap_control_usb_set_mode(struct device *dev, - enum omap_control_usb_mode mode) -{ - struct omap_control_usb *ctrl_usb; - - if (IS_ERR(dev) || !dev) - return; - - ctrl_usb = dev_get_drvdata(dev); - - if (!ctrl_usb) { - dev_err(dev, "Invalid control usb device\n"); - return; - } - - if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS) - return; - - switch (mode) { - case USB_MODE_HOST: - omap_control_usb_host_mode(ctrl_usb); - break; - case USB_MODE_DEVICE: - omap_control_usb_device_mode(ctrl_usb); - break; - case USB_MODE_DISCONNECT: - omap_control_usb_set_sessionend(ctrl_usb); - break; - default: - dev_vdbg(dev, "invalid omap control usb mode\n"); - } -} -EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); - -#ifdef CONFIG_OF - -static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS; -static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2; -static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; -static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; -static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; - -static const struct of_device_id omap_control_usb_id_table[] = { - { - .compatible = "ti,control-phy-otghs", - .data = &otghs_data, - }, - { - .compatible = "ti,control-phy-usb2", - .data = &usb2_data, - }, - { - .compatible = "ti,control-phy-pipe3", - .data = &pipe3_data, - }, - { - .compatible = "ti,control-phy-dra7usb2", - .data = &dra7usb2_data, - }, - { - .compatible = "ti,control-phy-am437usb2", - .data = &am437usb2_data, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); -#endif - - -static int omap_control_usb_probe(struct platform_device *pdev) -{ - struct resource *res; - const struct of_device_id *of_id; - struct omap_control_usb *control_usb; - - of_id = of_match_device(of_match_ptr(omap_control_usb_id_table), - &pdev->dev); - if (!of_id) - return -EINVAL; - - control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), - GFP_KERNEL); - if (!control_usb) { - dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); - return -ENOMEM; - } - - control_usb->dev = &pdev->dev; - control_usb->type = *(enum omap_control_usb_type *)of_id->data; - - if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "otghs_control"); - control_usb->otghs_control = devm_ioremap_resource( - &pdev->dev, res); - if (IS_ERR(control_usb->otghs_control)) - return PTR_ERR(control_usb->otghs_control); - } else { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "power"); - control_usb->power = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(control_usb->power)) { - dev_err(&pdev->dev, "Couldn't get power register\n"); - return PTR_ERR(control_usb->power); - } - } - - if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) { - control_usb->sys_clk = devm_clk_get(control_usb->dev, - "sys_clkin"); - if (IS_ERR(control_usb->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - } - - dev_set_drvdata(control_usb->dev, control_usb); - - return 0; -} - -static struct platform_driver omap_control_usb_driver = { - .probe = omap_control_usb_probe, - .driver = { - .name = "omap-control-usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(omap_control_usb_id_table), - }, -}; - -static int __init omap_control_usb_init(void) -{ - return platform_driver_register(&omap_control_usb_driver); -} -subsys_initcall(omap_control_usb_init); - -static void __exit omap_control_usb_exit(void) -{ - platform_driver_unregister(&omap_control_usb_driver); -} -module_exit(omap_control_usb_exit); - -MODULE_ALIAS("platform: omap_control_usb"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP Control Module USB Driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.1 From d7b00e310b3342a6c5b8f648283ea55665614b67 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 11 Mar 2014 13:47:37 +0800 Subject: usb: chipidea: udc: refine isr_tr_complete_handler Matthieu CASTET and Michael Grzeschik mentioned isr_tr_complete_handler is a bit messy at below: http://marc.info/?l=linux-usb&m=139047775001152&w=2 This commit creates a new function isr_setup_packet_handler to handle setup packet, it makes isr_tr_complete_handler easy to read. This is no functional change at this commit, tested with g_mass_storage and g_ether. Cc: Michael Grzeschik Cc: Matthieu CASTET Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 294 ++++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 140 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0dc56aebb807..7739c64ef259 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -948,6 +948,156 @@ __acquires(hwep->lock) return retval; } +/** + * isr_setup_packet_handler: setup packet handler + * @ci: UDC descriptor + * + * This function handles setup packet + */ +static void isr_setup_packet_handler(struct ci_hdrc *ci) +__releases(ci->lock) +__acquires(ci->lock) +{ + struct ci_hw_ep *hwep = &ci->ci_hw_ep[0]; + struct usb_ctrlrequest req; + int type, num, dir, err = -EINVAL; + u8 tmode = 0; + + /* + * Flush data and handshake transactions of previous + * setup packet. + */ + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); + + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(ci); + memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard(ci)); + + type = req.bRequestType; + + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + if (!ci->ci_hw_ep[num].wedge) { + spin_unlock(&ci->lock); + err = usb_ep_clear_halt( + &ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (err) + break; + } + err = isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && + le16_to_cpu(req.wValue) == + USB_DEVICE_REMOTE_WAKEUP) { + if (req.wLength != 0) + break; + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); + } else { + goto delegate; + } + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && + type != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 2 || + le16_to_cpu(req.wValue) != 0) + break; + err = isr_get_status_response(ci, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); + break; + case USB_REQ_SET_FEATURE: + if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) == + USB_ENDPOINT_HALT) { + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; + num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += ci->hw_ep_max / 2; + + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + spin_lock(&ci->lock); + if (!err) + isr_setup_status_phase(ci); + } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { + if (req.wLength != 0) + break; + switch (le16_to_cpu(req.wValue)) { + case USB_DEVICE_REMOTE_WAKEUP: + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); + break; + case USB_DEVICE_TEST_MODE: + tmode = le16_to_cpu(req.wIndex) >> 8; + switch (tmode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + ci->test_mode = tmode; + err = isr_setup_status_phase( + ci); + break; + default: + break; + } + default: + goto delegate; + } + } else { + goto delegate; + } + break; + default: +delegate: + if (req.wLength == 0) /* no data phase */ + ci->ep0_dir = TX; + + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); + break; + } + + if (err < 0) { + spin_unlock(&ci->lock); + if (usb_ep_set_halt(&hwep->ep)) + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); + } +} + /** * isr_tr_complete_handler: transaction complete interrupt handler * @ci: UDC descriptor @@ -959,12 +1109,10 @@ __releases(ci->lock) __acquires(ci->lock) { unsigned i; - u8 tmode = 0; + int err; for (i = 0; i < ci->hw_ep_max; i++) { struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; - int type, num, dir, err = -EINVAL; - struct usb_ctrlrequest req; if (hwep->ep.desc == NULL) continue; /* not configured */ @@ -985,143 +1133,9 @@ __acquires(ci->lock) } /* Only handle setup packet below */ - if (i != 0 || - !hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) - continue; - - /* - * Flush data and handshake transactions of previous - * setup packet. - */ - _ep_nuke(ci->ep0out); - _ep_nuke(ci->ep0in); - - /* read_setup_packet */ - do { - hw_test_and_set_setup_guard(ci); - memcpy(&req, &hwep->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(ci)); - - type = req.bRequestType; - - ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; - - switch (req.bRequest) { - case USB_REQ_CLEAR_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - if (!ci->ci_hw_ep[num].wedge) { - spin_unlock(&ci->lock); - err = usb_ep_clear_halt( - &ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (err) - break; - } - err = isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && - le16_to_cpu(req.wValue) == - USB_DEVICE_REMOTE_WAKEUP) { - if (req.wLength != 0) - break; - ci->remote_wakeup = 0; - err = isr_setup_status_phase(ci); - } else { - goto delegate; - } - break; - case USB_REQ_GET_STATUS: - if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && - type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && - type != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 2 || - le16_to_cpu(req.wValue) != 0) - break; - err = isr_get_status_response(ci, &req); - break; - case USB_REQ_SET_ADDRESS: - if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 0 || - le16_to_cpu(req.wIndex) != 0) - break; - ci->address = (u8)le16_to_cpu(req.wValue); - ci->setaddr = true; - err = isr_setup_status_phase(ci); - break; - case USB_REQ_SET_FEATURE: - if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) == - USB_ENDPOINT_HALT) { - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - dir = num & USB_ENDPOINT_DIR_MASK; - num &= USB_ENDPOINT_NUMBER_MASK; - if (dir) /* TX */ - num += ci->hw_ep_max/2; - - spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); - spin_lock(&ci->lock); - if (!err) - isr_setup_status_phase(ci); - } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { - if (req.wLength != 0) - break; - switch (le16_to_cpu(req.wValue)) { - case USB_DEVICE_REMOTE_WAKEUP: - ci->remote_wakeup = 1; - err = isr_setup_status_phase(ci); - break; - case USB_DEVICE_TEST_MODE: - tmode = le16_to_cpu(req.wIndex) >> 8; - switch (tmode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: - ci->test_mode = tmode; - err = isr_setup_status_phase( - ci); - break; - default: - break; - } - default: - goto delegate; - } - } else { - goto delegate; - } - break; - default: -delegate: - if (req.wLength == 0) /* no data phase */ - ci->ep0_dir = TX; - - spin_unlock(&ci->lock); - err = ci->driver->setup(&ci->gadget, &req); - spin_lock(&ci->lock); - break; - } - - if (err < 0) { - spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&ci->lock); - } + if (i == 0 && + hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0))) + isr_setup_packet_handler(ci); } } -- cgit v1.2.1 From c844d6c884f372dcde158c84b61c373ebc529519 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 11 Mar 2014 13:47:38 +0800 Subject: usb: chipidea: imx: Use dev_name() for ci_hdrc name to distinguish USBs Use dev_name() for ci_hdrc name to distinguish USBs Signed-off-by: Peter Chen Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c00f77257d36..2e58f8dfd311 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; struct ci_hdrc_platform_data pdata = { - .name = "ci_hdrc_imx", + .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | CI_HDRC_DISABLE_STREAMING, -- cgit v1.2.1 From 7b92e1ddb17f37caf37d4477483a62a425ba428f Mon Sep 17 00:00:00 2001 From: Daniel Tang Date: Tue, 11 Mar 2014 13:47:39 +0800 Subject: usb: chipidea: add support for USB OTG controller on LSI Zevio SoCs The USB controller in TI-NSPIRE calculators (LSI Zevio SoC) are based off either Freescale's USB OTG controller or the USB controller found in the IMX233, both of which are Chipidea compatible. This patch adds a device tree binding for the controller. Signed-off-by: Peter Chen Signed-off-by: Daniel Tang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Makefile | 1 + drivers/usb/chipidea/ci_hdrc_zevio.c | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 drivers/usb/chipidea/ci_hdrc_zevio.c (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 7345d2115af2..480bd4d5710a 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o # Glue/Bridge layers go here obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o +obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o # PCI doesn't provide stubs, need to check ifneq ($(CONFIG_PCI),) diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c new file mode 100644 index 000000000000..3bf6489ef5ec --- /dev/null +++ b/drivers/usb/chipidea/ci_hdrc_zevio.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Daniel Tang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * Based off drivers/usb/chipidea/ci_hdrc_msm.c + * + */ + +#include +#include +#include +#include + +#include "ci.h" + +static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = { + .name = "ci_hdrc_zevio", + .flags = CI_HDRC_REGS_SHARED, + .capoffset = DEF_CAPOFFSET, +}; + +static int ci_hdrc_zevio_probe(struct platform_device *pdev) +{ + struct platform_device *ci_pdev; + + dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n"); + + ci_pdev = ci_hdrc_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci_hdrc_zevio_platdata); + + if (IS_ERR(ci_pdev)) { + dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); + return PTR_ERR(ci_pdev); + } + + platform_set_drvdata(pdev, ci_pdev); + + return 0; +} + +static int ci_hdrc_zevio_remove(struct platform_device *pdev) +{ + struct platform_device *ci_pdev = platform_get_drvdata(pdev); + + ci_hdrc_remove_device(ci_pdev); + + return 0; +} + +static const struct of_device_id ci_hdrc_zevio_dt_ids[] = { + { .compatible = "lsi,zevio-usb", }, + { /* sentinel */ } +}; + +static struct platform_driver ci_hdrc_zevio_driver = { + .probe = ci_hdrc_zevio_probe, + .remove = ci_hdrc_zevio_remove, + .driver = { + .name = "zevio_usb", + .owner = THIS_MODULE, + .of_match_table = ci_hdrc_zevio_dt_ids, + }, +}; + +MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids); +module_platform_driver(ci_hdrc_zevio_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.1 From d7c933ae7da0d0b112bfc7e86424d780aaeb2d2c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:37 +0100 Subject: USB: cypress_m8: fix potential scheduling while atomic Remove erroneous call to usb_clear_halt which is blocking and cannot be used in interrupt context. This code has possibly never been executed as it would cause an oops if it was. Simply treat a stalled-endpoint error as any other error condition. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index bccb1223143a..634f0d6605ed 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1224,7 +1224,6 @@ static void cypress_write_int_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); struct device *dev = &urb->dev->dev; - int result; int status = urb->status; switch (status) { @@ -1239,21 +1238,9 @@ static void cypress_write_int_callback(struct urb *urb) __func__, status); priv->write_urb_in_use = 0; return; - case -EPIPE: /* no break needed; clear halt and resubmit */ - if (!priv->comm_is_ok) - break; - usb_clear_halt(port->serial->dev, 0x02); - /* error in the urb, so we have to resubmit it */ - dev_dbg(dev, "%s - nonzero write bulk status received: %d\n", - __func__, status); - port->interrupt_out_urb->transfer_buffer_length = 1; - result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); - if (!result) - return; - dev_err(dev, "%s - failed resubmitting write urb, error %d\n", - __func__, result); - cypress_set_dead(port); - break; + case -EPIPE: + /* Cannot call usb_clear_halt while in_interrupt */ + /* FALLTHROUGH */ default: dev_err(dev, "%s - unexpected nonzero write status received: %d\n", __func__, status); -- cgit v1.2.1 From 5083fd7bdfe6760577235a724cf6dccae13652c2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:38 +0100 Subject: USB: serial: make bulk_out_size a lower limit Drivers are allowed to override the default bulk-out buffer size (endpoint maximum packet size) in order to increase throughput, but it does not make much sense to allow buffers smaller than the default. Note that this is already how bulk_in_size is defined. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 7c9dc28640bb..c68fc9fb7598 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -923,9 +923,8 @@ static int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL)) goto probe_error; - buffer_size = serial->type->bulk_out_size; - if (!buffer_size) - buffer_size = usb_endpoint_maxp(endpoint); + buffer_size = max_t(int, serial->type->bulk_out_size, + usb_endpoint_maxp(endpoint)); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; -- cgit v1.2.1 From fc11efe2800f2f9ba2ccb268321642b7e9f73a65 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:39 +0100 Subject: USB: serial: continue to read on errors Make sure to try to resubmit the read urb on errors. Currently a recoverable error would lead to reduced throughput as only one urb will be used until the port is closed and reopened (or resumed or unthrottled). Also upgrade error messages from debug to error log level. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index b63ce023f96f..d7f39ea7d6ac 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -359,16 +359,29 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, urb->actual_length); - - if (urb->status) { - dev_dbg(&port->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + case -EPIPE: + dev_err(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); return; + default: + dev_err(&port->dev, "%s - nonzero urb status: %d\n", + __func__, urb->status); + goto resubmit; } usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); port->serial->type->process_read_urb(urb); +resubmit: /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); port->throttled = port->throttle_req; -- cgit v1.2.1 From bd58c7bd6fd5f803b4127717bda9cc6c30d0c806 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:40 +0100 Subject: USB: serial: continue to write on errors Do not discard buffered data and make sure to try to resubmit the write urbs on errors. Currently a recoverable error would lead to more data than necessary being dropped. Also upgrade error messages from debug to error log level. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index d7f39ea7d6ac..33d7f4092308 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -397,7 +397,6 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; - int status = urb->status; int i; for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) @@ -409,17 +408,27 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) set_bit(i, &port->write_urbs_free); spin_unlock_irqrestore(&port->lock, flags); - if (status) { - dev_dbg(&port->dev, "%s - non-zero urb status: %d\n", - __func__, status); - - spin_lock_irqsave(&port->lock, flags); - kfifo_reset_out(&port->write_fifo); - spin_unlock_irqrestore(&port->lock, flags); - } else { - usb_serial_generic_write_start(port, GFP_ATOMIC); + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + case -EPIPE: + dev_err_console(port, "%s - urb stopped: %d\n", + __func__, urb->status); + return; + default: + dev_err_console(port, "%s - nonzero urb status: %d\n", + __func__, urb->status); + goto resubmit; } +resubmit: + usb_serial_generic_write_start(port, GFP_ATOMIC); usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); -- cgit v1.2.1 From ca0400d2caf0d6f18445feea79c8c5a4ccf77e61 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:41 +0100 Subject: USB: serial: add missing braces Add missing braces to conditional branches and one loop in usb-serial core and generic implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 11 ++++++----- drivers/usb/serial/usb-serial.c | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 33d7f4092308..1bd192290b08 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -332,9 +332,9 @@ void usb_serial_generic_process_read_urb(struct urb *urb) * stuff like 3G modems, so shortcircuit it in the 99.9999999% of * cases where the USB serial is not a console anyway. */ - if (!port->port.console || !port->sysrq) + if (!port->port.console || !port->sysrq) { tty_insert_flip_string(&port->port, ch, urb->actual_length); - else { + } else { for (i = 0; i < urb->actual_length; i++, ch++) { if (!usb_serial_handle_sysrq_char(port, *ch)) tty_insert_flip_char(&port->port, *ch, TTY_NORMAL); @@ -388,8 +388,9 @@ resubmit: if (!port->throttled) { spin_unlock_irqrestore(&port->lock, flags); usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); - } else + } else { spin_unlock_irqrestore(&port->lock, flags); + } } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); @@ -399,10 +400,10 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; int i; - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { if (port->write_urbs[i] == urb) break; - + } spin_lock_irqsave(&port->lock, flags); port->tx_bytes -= urb->transfer_buffer_length; set_bit(i, &port->write_urbs_free); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index c68fc9fb7598..4c3aeaf56dc1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1160,9 +1160,9 @@ static int usb_serial_reset_resume(struct usb_interface *intf) usb_serial_unpoison_port_urbs(serial); serial->suspending = 0; - if (serial->type->reset_resume) + if (serial->type->reset_resume) { rv = serial->type->reset_resume(serial); - else { + } else { rv = -EOPNOTSUPP; intf->needs_binding = 1; } @@ -1337,9 +1337,9 @@ static int usb_serial_register(struct usb_serial_driver *driver) if (retval) { pr_err("problem %d when registering driver %s\n", retval, driver->description); list_del(&driver->driver_list); - } else + } else { pr_info("USB Serial support registered for %s\n", driver->description); - + } mutex_unlock(&table_lock); return retval; } -- cgit v1.2.1 From d9a38a8741fdffabc32e6d0943b1cdcf22712bec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:42 +0100 Subject: USB: serial: add missing newlines to dev_ messages. Add missing newlines to dev_ messages. Also make some messages less verbose where appropriate. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 6 +++--- drivers/usb/serial/cyberjack.c | 2 +- drivers/usb/serial/cypress_m8.c | 2 +- drivers/usb/serial/iuu_phoenix.c | 2 +- drivers/usb/serial/keyspan_pda.c | 2 +- drivers/usb/serial/kl5kusb105.c | 4 ++-- drivers/usb/serial/kobil_sct.c | 3 ++- drivers/usb/serial/mos7720.c | 12 ++++++------ drivers/usb/serial/mos7840.c | 4 ++-- drivers/usb/serial/quatech2.c | 2 +- drivers/usb/serial/spcp8x5.c | 7 +++---- drivers/usb/serial/symbolserial.c | 4 +--- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- drivers/usb/serial/usb-serial.c | 4 ++-- 14 files changed, 28 insertions(+), 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 82371f61f23d..2d72aa3564a3 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -323,11 +323,11 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) if (r) goto out; - dev_dbg(&port->dev, "%s - submitting interrupt urb", __func__); + dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__); r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (r) { - dev_err(&port->dev, "%s - failed submitting interrupt urb," - " error %d\n", __func__, r); + dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", + __func__, r); ch341_close(port); goto out; } diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 0ac3b3b3236c..2916dea3ede8 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -220,7 +220,7 @@ static int cyberjack_write(struct tty_struct *tty, result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { dev_err(&port->dev, - "%s - failed submitting write urb, error %d", + "%s - failed submitting write urb, error %d\n", __func__, result); /* Throw away data. No better idea what to do with it. */ priv->wrfilled = 0; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 634f0d6605ed..01bf53392819 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -279,7 +279,7 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) * the generic firmware, but are not used with * NMEA and SiRF protocols */ dev_dbg(&port->dev, - "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS", + "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n", __func__, new_rate); return -1; } diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index d00dae17d520..5ad4a0fb4b26 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1151,7 +1151,7 @@ static ssize_t vcc_mode_store(struct device *dev, goto fail_store_vcc_mode; } - dev_dbg(dev, "%s: setting vcc_mode = %ld", __func__, v); + dev_dbg(dev, "%s: setting vcc_mode = %ld\n", __func__, v); if ((v != 3) && (v != 5)) { dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e972412b614b..742d827f876c 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -189,7 +189,7 @@ exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&port->dev, - "%s - usb_submit_urb failed with result %d", + "%s - usb_submit_urb failed with result %d\n", __func__, retval); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index c88cc4966b23..d7440b7557af 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -201,7 +201,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, else { status = get_unaligned_le16(status_buf); - dev_info(&port->serial->dev->dev, "read status %x %x", + dev_info(&port->serial->dev->dev, "read status %x %x\n", status_buf[0], status_buf[1]); *line_state_p = klsi_105_status2linestate(status); @@ -464,7 +464,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, priv->cfg.baudrate = kl5kusb105a_sio_b115200; break; default: - dev_dbg(dev, "KLSI USB->Serial converter: unsupported baudrate request, using default of 9600"); + dev_dbg(dev, "unsupported baudrate, using 9600\n"); priv->cfg.baudrate = kl5kusb105a_sio_b9600; baud = 9600; break; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 618c1c1f227e..fee242387f55 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -557,7 +557,8 @@ static int kobil_ioctl(struct tty_struct *tty, ); dev_dbg(&port->dev, - "%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result); + "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", + __func__, result); kfree(transfer_buffer); return (result < 0) ? -EIO: 0; default: diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 4eb277225a77..dfd728a263d2 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -209,7 +209,7 @@ static int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, index, NULL, 0, MOS_WDR_TIMEOUT); if (status < 0) dev_err(&usbdev->dev, - "mos7720: usb_control_msg() failed: %d", status); + "mos7720: usb_control_msg() failed: %d\n", status); return status; } @@ -240,7 +240,7 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, *data = *buf; else if (status < 0) dev_err(&usbdev->dev, - "mos7720: usb_control_msg() failed: %d", status); + "mos7720: usb_control_msg() failed: %d\n", status); kfree(buf); return status; @@ -399,7 +399,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, &mos_parport->deferred_urbs); spin_unlock_irqrestore(&mos_parport->listlock, flags); tasklet_schedule(&mos_parport->urb_tasklet); - dev_dbg(&usbdev->dev, "tasklet scheduled"); + dev_dbg(&usbdev->dev, "tasklet scheduled\n"); return 0; } @@ -418,7 +418,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, mutex_unlock(&serial->disc_mutex); if (ret_val) { dev_err(&usbdev->dev, - "%s: submit_urb() failed: %d", __func__, ret_val); + "%s: submit_urb() failed: %d\n", __func__, ret_val); spin_lock_irqsave(&mos_parport->listlock, flags); list_del(&urbtrack->urblist_entry); spin_unlock_irqrestore(&mos_parport->listlock, flags); @@ -656,7 +656,7 @@ static size_t parport_mos7715_write_compat(struct parport *pp, parport_epilogue(pp); if (retval) { dev_err(&mos_parport->serial->dev->dev, - "mos7720: usb_bulk_msg() failed: %d", retval); + "mos7720: usb_bulk_msg() failed: %d\n", retval); return 0; } return actual_len; @@ -875,7 +875,7 @@ static void mos7715_interrupt_callback(struct urb *urb) if (!(iir & 0x01)) { /* serial port interrupt pending */ switch (iir & 0x0f) { case SERIAL_IIR_RLS: - dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n\n"); + dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI: dev_dbg(dev, "Serial Port: Receiver time out\n"); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index e9d967ff521b..393be562d875 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -522,11 +522,11 @@ static void mos7840_set_led_callback(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* This urb is terminated, clean up */ - dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d", + dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n", __func__, urb->status); break; default: - dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d", + dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, urb->status); } } diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 7725ed261ed6..504f5bff79c0 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -372,7 +372,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) device_port, data, 2, QT2_USB_TIMEOUT); if (status < 0) { - dev_err(&port->dev, "%s - open port failed %i", __func__, + dev_err(&port->dev, "%s - open port failed %i\n", __func__, status); kfree(data); return status; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 4ec04f73c800..ef0dbf0703c5 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -220,9 +220,9 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); if (ret < 0) - dev_err(&port->dev, "failed to get modem status: %d", ret); + dev_err(&port->dev, "failed to get modem status: %d\n", ret); - dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *buf); + dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; kfree(buf); @@ -342,8 +342,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, case 1000000: buf[0] = 0x0b; break; default: - dev_err(&port->dev, "spcp825 driver does not support the " - "baudrate requested, using default of 9600.\n"); + dev_err(&port->dev, "unsupported baudrate, using 9600\n"); } /* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */ diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 9fa7dd413e83..8fceec7298e0 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -74,9 +74,7 @@ static void symbol_int_callback(struct urb *urb) tty_insert_flip_string(&port->port, &data[1], data_length); tty_flip_buffer_push(&port->port); } else { - dev_dbg(&port->dev, - "Improper amount of data received from the device, " - "%d bytes", urb->actual_length); + dev_dbg(&port->dev, "%s - short packet\n", __func__); } exit: diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index ec7cea585663..3dd3ff8c50d3 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -293,7 +293,7 @@ static int ti_startup(struct usb_serial *serial) int status; dev_dbg(&dev->dev, - "%s - product 0x%4X, num configurations %d, configuration value %d", + "%s - product 0x%4X, num configurations %d, configuration value %d\n", __func__, le16_to_cpu(dev->descriptor.idProduct), dev->descriptor.bNumConfigurations, dev->actconfig->desc.bConfigurationValue); @@ -803,7 +803,7 @@ static void ti_set_termios(struct tty_struct *tty, tty_encode_baud_rate(tty, baud, baud); dev_dbg(&port->dev, - "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d", + "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n", __func__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4c3aeaf56dc1..81fc0dfcfdcf 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -868,7 +868,7 @@ static int usb_serial_probe(struct usb_interface *interface, max_endpoints = max(max_endpoints, (int)serial->num_ports); serial->num_port_pointers = max_endpoints; - dev_dbg(ddev, "setting up %d port structures for this device", max_endpoints); + dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints); for (i = 0; i < max_endpoints; ++i) { port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) @@ -1033,7 +1033,7 @@ static int usb_serial_probe(struct usb_interface *interface, for (i = 0; i < num_ports; ++i) { port = serial->port[i]; dev_set_name(&port->dev, "ttyUSB%d", port->minor); - dev_dbg(ddev, "registering %s", dev_name(&port->dev)); + dev_dbg(ddev, "registering %s\n", dev_name(&port->dev)); device_enable_async_suspend(&port->dev); retval = device_add(&port->dev); -- cgit v1.2.1 From 36904592bc0d6da0e77278d9694e5e4c66bc0a11 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 12 Mar 2014 19:09:43 +0100 Subject: USB: keyspan: remove dead debugging code Remove out-commented and ifdeffed debugging code. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 265c6776b081..d3acaead5a81 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -397,17 +397,6 @@ static void usa26_instat_callback(struct urb *urb) msg = (struct keyspan_usa26_portStatusMessage *)data; -#if 0 - dev_dbg(&urb->dev->dev, - "%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d", - __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, - msg->ri, msg->_txOff, msg->_txXoff, msg->rxEnabled, - msg->controlResponse); -#endif - - /* Now do something useful with the data */ - - /* Check port number from message and retrieve private data */ if (msg->port >= serial->num_ports) { dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port); @@ -523,9 +512,6 @@ static void usa28_instat_callback(struct urb *urb) goto exit; } - /*dev_dbg(&urb->dev->dev, "%s %12ph", __func__, data);*/ - - /* Now do something useful with the data */ msg = (struct keyspan_usa28_portStatusMessage *)data; /* Check port number from message and retrieve private data */ @@ -605,9 +591,6 @@ static void usa49_instat_callback(struct urb *urb) goto exit; } - /*dev_dbg(&urb->dev->dev, "%s: %11ph", __func__, data);*/ - - /* Now do something useful with the data */ msg = (struct keyspan_usa49_portStatusMessage *)data; /* Check port number from message and retrieve private data */ @@ -1793,12 +1776,6 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err != 0) dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed\n", __func__); -#if 0 - else { - dev_dbg(&port->dev, "%s - usb_submit_urb(setup) OK %d bytes\n", __func__, - this_urb->transfer_buffer_length); - } -#endif return 0; } @@ -1976,13 +1953,6 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err != 0) dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err); -#if 0 - else { - dev_dbg(&port->dev, "%s - usb_submit_urb(%d) OK %d bytes (end %d)\n", __func__, - outcont_urb, this_urb->transfer_buffer_length, - usb_pipeendpoint(this_urb->pipe)); - } -#endif return 0; } -- cgit v1.2.1 From ade79d13a83a31cc0f68b47831bcbfe3a82f613f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Mar 2014 13:30:53 -0500 Subject: usb: gadget: lpc32xx_udc: fix wrong clk_put() sequence This patch fixes the following Coccinelle error: drivers/usb/gadget/lpc32xx_udc.c:3313:1-7: ERROR: \ missing clk_put; clk_get on line 3139 and \ execution via conditional on line 3146 Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/lpc32xx_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 049ebab0d360..a139894c600f 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3295,9 +3295,9 @@ usb_clk_enable_fail: pll_set_fail: clk_disable(udc->usb_pll_clk); pll_enable_fail: - clk_put(udc->usb_slv_clk); -usb_otg_clk_get_fail: clk_put(udc->usb_otg_clk); +usb_otg_clk_get_fail: + clk_put(udc->usb_slv_clk); usb_clk_get_fail: clk_put(udc->usb_pll_clk); pll_get_fail: -- cgit v1.2.1 From f3c73649828397ca6bae88bcb8cd70bbcbb65b18 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Mar 2014 13:30:54 -0500 Subject: usb: gadget: f_subset: switch over to PTR_RET this patch fixes the following Coccinelle warning: drivers/usb/gadget/f_subset.c:279:8-14: WARNING: \ PTR_RET can be used Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_subset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index f1a59190ac9a..df4a0dcbc993 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -276,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } net = gether_connect(&geth->port); - return IS_ERR(net) ? PTR_ERR(net) : 0; + return PTR_RET(net); } static void geth_disable(struct usb_function *f) -- cgit v1.2.1 From 3b74c73f8d6f053f422e85fce955b61fb181cfe7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Mar 2014 13:30:55 -0500 Subject: usb: gadget: inode: switch over to memdup_user() This patch fixes the following Coccinelle warning: drivers/usb/gadget/inode.c:442:8-15: WARNING \ opportunity for memdup_user Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/inode.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index b94c049ab0d0..b5be6f0308c2 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -439,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ value = -ENOMEM; - kbuf = kmalloc (len, GFP_KERNEL); - if (!kbuf) - goto free1; - if (copy_from_user (kbuf, buf, len)) { - value = -EFAULT; + kbuf = memdup_user(buf, len); + if (!kbuf) { + value = PTR_ERR(kbuf); goto free1; } @@ -452,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) data->name, len, (int) value); free1: mutex_unlock(&data->lock); - kfree (kbuf); return value; } -- cgit v1.2.1 From dad4babe419ef2f3e14447a650ce1f760a6ee9e0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Mar 2014 13:30:56 -0500 Subject: usb: gadget: composite: switch over to ERR_CAST() This patch fixes the following Coccinelle warning: drivers/usb/gadget/composite.c:1142:9-16: WARNING: \ ERR_CAST can be used with uc Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d742bed7a5fa..fab906429b80 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1139,7 +1139,7 @@ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, uc = copy_gadget_strings(sp, n_gstrings, n_strings); if (IS_ERR(uc)) - return ERR_PTR(PTR_ERR(uc)); + return ERR_CAST(uc); n_gs = get_containers_gs(uc); ret = usb_string_ids_tab(cdev, n_gs[0]->strings); -- cgit v1.2.1 From 48968f8d5f2234fb1768c55cf7d96d0b87446cd6 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 10 Mar 2014 09:33:37 +0100 Subject: usb: gadget: f_fs: add missing spinlock and mutex unlock This patch adds missing spin_unlock and mutex_unlock calls in error handling code. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_fs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 10c086afa2f4..2e164dca08e8 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -802,7 +802,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (io_data->aio) { req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); if (unlikely(!req)) - goto error; + goto error_lock; req->buf = data; req->length = io_data->len; @@ -817,7 +817,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); if (unlikely(ret)) { usb_ep_free_request(ep->ep, req); - goto error; + goto error_lock; } ret = -EIOCBQUEUED; @@ -865,6 +865,10 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) mutex_unlock(&epfile->mutex); return ret; + +error_lock: + spin_unlock_irq(&epfile->ffs->eps_lock); + mutex_unlock(&epfile->mutex); error: kfree(data); return ret; -- cgit v1.2.1 From aba37fd975f0dd58e025c99c2a79b61b20190831 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 11 Mar 2014 13:26:16 -0700 Subject: usb: gadget: tcm_usb_gadget: stop format strings This makes sure that the name coming out of configfs cannot be used accidentally as a format string. Signed-off-by: Kees Cook Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/tcm_usb_gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 0f8aad78b54f..460c266b8e24 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1613,7 +1613,7 @@ static struct se_wwn *usbg_make_tport( return ERR_PTR(-ENOMEM); } tport->tport_wwpn = wwpn; - snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name); + snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name); return &tport->tport_wwn; } -- cgit v1.2.1 From ead5178bf442dbae4008ee54bf4f66a1f6a317c9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 11 Mar 2014 13:23:14 +0100 Subject: usb: phy: Add ulpi IDs for SMSC USB3320 and TI TUSB1210 Add new ulpi IDs which are available on Xilinx Zynq boards. Signed-off-by: Michal Simek Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-ulpi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index 217339dd7a90..17ea3f271bd8 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -47,6 +47,8 @@ struct ulpi_info { static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), + ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"), + ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), }; static int ulpi_set_otg_flags(struct usb_phy *phy) -- cgit v1.2.1 From 6aec044cc2f5670cf3b143c151c8be846499bd15 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 12 Mar 2014 11:30:38 -0400 Subject: USB: unbind all interfaces before rebinding any When a driver doesn't have pre_reset, post_reset, or reset_resume methods, the USB core unbinds that driver when its device undergoes a reset or a reset-resume, and then rebinds it afterward. The existing straightforward implementation can lead to problems, because each interface gets unbound and rebound before the next interface is handled. If a driver claims additional interfaces, the claim may fail because the old binding instance may still own the additional interface when the new instance tries to claim it. This patch fixes the problem by first unbinding all the interfaces that are marked (i.e., their needs_binding flag is set) and then rebinding all of them. The patch also makes the helper functions in driver.c a little more uniform and adjusts some out-of-date comments. Signed-off-by: Alan Stern Reported-and-tested-by: "Poulain, Loic" Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 94 ++++++++++++++++++++++++++++------------------- drivers/usb/core/hub.c | 5 ++- drivers/usb/core/usb.h | 2 +- 3 files changed, 60 insertions(+), 41 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 08283d40616c..888881e5f292 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1011,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister); * it doesn't support pre_reset/post_reset/reset_resume or * because it doesn't support suspend/resume. * - * The caller must hold @intf's device's lock, but not its pm_mutex - * and not @intf->dev.sem. + * The caller must hold @intf's device's lock, but not @intf's lock. */ void usb_forced_unbind_intf(struct usb_interface *intf) { @@ -1025,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf) intf->needs_binding = 1; } +/* + * Unbind drivers for @udev's marked interfaces. These interfaces have + * the needs_binding flag set, for example by usb_resume_interface(). + * + * The caller must hold @udev's device lock. + */ +static void unbind_marked_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->dev.driver && intf->needs_binding) + usb_forced_unbind_intf(intf); + } + } +} + /* Delayed forced unbinding of a USB interface driver and scan * for rebinding. * - * The caller must hold @intf's device's lock, but not its pm_mutex - * and not @intf->dev.sem. + * The caller must hold @intf's device's lock, but not @intf's lock. * * Note: Rebinds will be skipped if a system sleep transition is in * progress and the PM "complete" callback hasn't occurred yet. */ -void usb_rebind_intf(struct usb_interface *intf) +static void usb_rebind_intf(struct usb_interface *intf) { int rc; @@ -1051,68 +1071,66 @@ void usb_rebind_intf(struct usb_interface *intf) } } -#ifdef CONFIG_PM - -/* Unbind drivers for @udev's interfaces that don't support suspend/resume - * There is no check for reset_resume here because it can be determined - * only during resume whether reset_resume is needed. +/* + * Rebind drivers to @udev's marked interfaces. These interfaces have + * the needs_binding flag set. * * The caller must hold @udev's device lock. */ -static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) +static void rebind_marked_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; struct usb_interface *intf; - struct usb_driver *drv; config = udev->actconfig; if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - - if (intf->dev.driver) { - drv = to_usb_driver(intf->dev.driver); - if (!drv->suspend || !drv->resume) - usb_forced_unbind_intf(intf); - } + if (intf->needs_binding) + usb_rebind_intf(intf); } } } -/* Unbind drivers for @udev's interfaces that failed to support reset-resume. - * These interfaces have the needs_binding flag set by usb_resume_interface(). +/* + * Unbind all of @udev's marked interfaces and then rebind all of them. + * This ordering is necessary because some drivers claim several interfaces + * when they are first probed. * * The caller must hold @udev's device lock. */ -static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) +void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev) { - struct usb_host_config *config; - int i; - struct usb_interface *intf; - - config = udev->actconfig; - if (config) { - for (i = 0; i < config->desc.bNumInterfaces; ++i) { - intf = config->interface[i]; - if (intf->dev.driver && intf->needs_binding) - usb_forced_unbind_intf(intf); - } - } + unbind_marked_interfaces(udev); + rebind_marked_interfaces(udev); } -static void do_rebind_interfaces(struct usb_device *udev) +#ifdef CONFIG_PM + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume + * There is no check for reset_resume here because it can be determined + * only during resume whether reset_resume is needed. + * + * The caller must hold @udev's device lock. + */ +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; struct usb_interface *intf; + struct usb_driver *drv; config = udev->actconfig; if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - if (intf->needs_binding) - usb_rebind_intf(intf); + + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } } } } @@ -1441,7 +1459,7 @@ int usb_resume_complete(struct device *dev) * whose needs_binding flag is set */ if (udev->state != USB_STATE_NOTATTACHED) - do_rebind_interfaces(udev); + rebind_marked_interfaces(udev); return 0; } @@ -1463,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - unbind_no_reset_resume_drivers_interfaces(udev); + unbind_marked_interfaces(udev); } /* Avoid PM error messages for devices disconnected while suspended diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5da5394127e9..2d74dfb9c989 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -5380,10 +5380,11 @@ int usb_reset_device(struct usb_device *udev) else if (cintf->condition == USB_INTERFACE_BOUND) rebind = 1; + if (rebind) + cintf->needs_binding = 1; } - if (ret == 0 && rebind) - usb_rebind_intf(cintf); } + usb_unbind_and_rebind_marked_interfaces(udev); } usb_autosuspend_device(udev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 823857767a16..0923add72b59 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev, extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); extern void usb_forced_unbind_intf(struct usb_interface *intf); -extern void usb_rebind_intf(struct usb_interface *intf); +extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, struct dev_state *owner); -- cgit v1.2.1 From 1d10255c1c496557a5674e651c4ebbe0f61279f2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 18 Mar 2014 10:39:05 -0400 Subject: USB: disable reset-resume when USB_QUIRK_RESET is set The USB_QUIRK_RESET flag indicates that a USB device changes its identity in some way when it is reset. It may lose its firmware, its descriptors may change, or it may switch back to a default mode of operation. If a device does this, the kernel needs to avoid resetting it. Resets are likely to fail, or worse, succeed while changing the device's state in a way the system can't detect. This means we should disable the reset-resume mechanism whenever this quirk flag is present. An attempted reset-resume will fail, the device will be logically disconnected, and later on the hub driver will rediscover and re-enumerate the device. This will cause the appropriate udev events to be generated, so that userspace will have a chance to switch the device into its normal operating mode, if necessary. Signed-off-by: Alan Stern CC: Oliver Neukum Reviewed-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2d74dfb9c989..d670aaf13a3d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3105,9 +3105,19 @@ static int finish_port_resume(struct usb_device *udev) * operation is carried out here, after the port has been * resumed. */ - if (udev->reset_resume) + if (udev->reset_resume) { + /* + * If the device morphs or switches modes when it is reset, + * we don't want to perform a reset-resume. We'll fail the + * resume, which will cause a logical disconnect, and then + * the device will be rediscovered. + */ retry_reset_resume: - status = usb_reset_and_verify_device(udev); + if (udev->quirks & USB_QUIRK_RESET) + status = -ENODEV; + else + status = usb_reset_and_verify_device(udev); + } /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, -- cgit v1.2.1