Commit 9fc37779 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB patches from Greg Kroah-Hartman:
 "Here's the big USB patch set for the 3.6-rc1 merge window.

  Lots of little changes in here, primarily for gadget controllers and
  drivers.  There's some scsi changes that I think also went in through
  the scsi tree, but they merge just fine.  All of these patches have
  been in the linux-next tree for a while now.

  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"

Fix up trivial conflicts in include/scsi/scsi_device.h (same libata
conflict that Jeff had already encountered)

* tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (207 commits)
  usb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams
  usb: Add quirk detection based on interface information
  usb: s3c-hsotg: Add header file protection macros in s3c-hsotg.h
  USB: ehci-s5p: Add vbus setup function to the s5p ehci glue layer
  USB: add USB_VENDOR_AND_INTERFACE_INFO() macro
  USB: notify phy when root hub port connect change
  USB: remove 8 bytes of padding from usb_host_interface on 64 bit builds
  USB: option: add ZTE MF821D
  USB: sierra: QMI mode MC7710 moved to qcserial
  USB: qcserial: adding Sierra Wireless devices
  USB: qcserial: support generic Qualcomm serial ports
  USB: qcserial: make probe more flexible
  USB: qcserial: centralize probe exit path
  USB: qcserial: consolidate usb_set_interface calls
  USB: ehci-s5p: Add support for device tree
  USB: ohci-exynos: Add support for device tree
  USB: ehci-omap: fix compile failure(v1)
  usb: host: tegra: pass correct pointer in ehci_setup()
  USB: ehci-fsl: Update ifdef check to work on 64-bit ppc
  USB: serial: keyspan: Removed unrequired parentheses.
  ...
parents 5e23ae49 e387ef5c
...@@ -208,3 +208,15 @@ Description: ...@@ -208,3 +208,15 @@ Description:
such as ACPI. This file will read either "removable" or such as ACPI. This file will read either "removable" or
"fixed" if the information is available, and "unknown" "fixed" if the information is available, and "unknown"
otherwise. otherwise.
What: /sys/bus/usb/devices/.../ltm_capable
Date: July 2012
Contact: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Description:
USB 3.0 devices may optionally support Latency Tolerance
Messaging (LTM). They indicate their support by setting a bit
in the bmAttributes field of their SuperSpeed BOS descriptors.
If that bit is set for the device, ltm_capable will read "yes".
If the device doesn't support LTM, the file will read "no".
The file will be present for all speeds of USB devices, and will
always read "no" for USB 1.1 and USB 2.0 devices.
* Freescale i.MX ci13xxx usb controllers
Required properties:
- compatible: Should be "fsl,imx27-usb"
- reg: Should contain registers location and length
- interrupts: Should contain controller interrupt
Optional properties:
- fsl,usbphy: phandler of usb phy that connects to the only one port
- vbus-supply: regulator for vbus
Examples:
usb@02184000 { /* USB OTG */
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
reg = <0x02184000 0x200>;
interrupts = <0 43 0x04>;
fsl,usbphy = <&usbphy1>;
};
* Freescale MXS USB Phy Device
Required properties:
- compatible: Should be "fsl,imx23-usbphy"
- reg: Should contain registers location and length
- interrupts: Should contain phy interrupt
Example:
usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>;
interrupts = <0 44 0x04>;
};
* Overview
Mass Storage Gadget (or MSG) acts as a USB Mass Storage device,
appearing to the host as a disk or a CD-ROM drive. It supports
multiple logical units (LUNs). Backing storage for each LUN is
provided by a regular file or a block device, access can be limited
to read-only, and gadget can indicate that it is removable and/or
CD-ROM (the latter implies read-only access).
Its requirements are modest; only a bulk-in and a bulk-out endpoint
are needed. The memory requirement amounts to two 16K buffers.
Support is included for full-speed, high-speed and SuperSpeed
operation.
Note that the driver is slightly non-portable in that it assumes
a single memory/DMA buffer will be useable for bulk-in and bulk-out
endpoints. With most device controllers this is not an issue, but
there may be some with hardware restrictions that prevent a buffer
from being used by more than one endpoint.
This document describes how to use the gadget from user space, its
relation to mass storage function (or MSF) and different gadgets
using it, and how it differs from File Storage Gadget (or FSG). It
will talk only briefly about how to use MSF within composite
gadgets.
* Module parameters
The mass storage gadget accepts the following mass storage specific
module parameters:
- file=filename[,filename...]
This parameter lists paths to files or block devices used for
backing storage for each logical unit. There may be at most
FSG_MAX_LUNS (8) LUNs set. If more files are specified, they will
be silently ignored. See also “luns” parameter.
*BEWARE* that if a file is used as a backing storage, it may not
be modified by any other process. This is because the host
assumes the data does not change without its knowledge. It may be
read, but (if the logical unit is writable) due to buffering on
the host side, the contents are not well defined.
The size of the logical unit will be rounded down to a full
logical block. The logical block size is 2048 bytes for LUNs
simulating CD-ROM, block size of the device if the backing file is
a block device, or 512 bytes otherwise.
- removable=b[,b...]
This parameter specifies whether each logical unit should be
removable. “b” here is either “y”, “Y” or “1” for true or “n”,
“N” or “0” for false.
If this option is set for a logical unit, gadget will accept an
“eject” SCSI request (Start/Stop Unit). When it is sent, the
backing file will be closed to simulate ejection and the logical
unit will not be mountable by the host until a new backing file is
specified by userspace on the device (see “sysfs entries”
section).
If a logical unit is not removable (the default), a backing file
must be specified for it with the “file” parameter as the module
is loaded. The same applies if the module is built in, no
exceptions.
The default value of the flag is false, *HOWEVER* it used to be
true. This has been changed to better match File Storage Gadget
and because it seems like a saner default after all. Thus to
maintain compatibility with older kernels, it's best to specify
the default values. Also, if one relied on old default, explicit
“n” needs to be specified now.
Note that “removable” means the logical unit's media can be
ejected or removed (as is true for a CD-ROM drive or a card
reader). It does *not* mean that the entire gadget can be
unplugged from the host; the proper term for that is
“hot-unpluggable”.
- cdrom=b[,b...]
This parameter specifies whether each logical unit should simulate
CD-ROM. The default is false.
- ro=b[,b...]
This parameter specifies whether each logical unit should be
reported as read only. This will prevent host from modifying the
backing files.
Note that if this flag for given logical unit is false but the
backing file could not be opened in read/write mode, the gadget
will fall back to read only mode anyway.
The default value for non-CD-ROM logical units is false; for
logical units simulating CD-ROM it is forced to true.
- nofua=b[,b...]
This parameter specifies whether FUA flag should be ignored in SCSI
Write10 and Write12 commands sent to given logical units.
MS Windows mounts removable storage in “Removal optimised mode” by
default. All the writes to the media are synchronous, which is
achieved by setting the FUA (Force Unit Access) bit in SCSI
Write(10,12) commands. This forces each write to wait until the
data has actually been written out and prevents I/O requests
aggregation in block layer dramatically decreasing performance.
Note that this may mean that if the device is powered from USB and
the user unplugs the device without unmounting it first (which at
least some Windows users do), the data may be lost.
The default value is false.
- luns=N
This parameter specifies number of logical units the gadget will
have. It is limited by FSG_MAX_LUNS (8) and higher value will be
capped.
If this parameter is provided, and the number of files specified
in “file” argument is greater then the value of “luns”, all excess
files will be ignored.
If this parameter is not present, the number of logical units will
be deduced from the number of files specified in the “file”
parameter. If the file parameter is missing as well, one is
assumed.
- stall=b
Specifies whether the gadget is allowed to halt bulk endpoints.
The default is determined according to the type of USB device
controller, but usually true.
In addition to the above, the gadget also accepts the following
parameters defined by the composite framework (they are common to
all composite gadgets so just a quick listing):
- idVendor -- USB Vendor ID (16 bit integer)
- idProduct -- USB Product ID (16 bit integer)
- bcdDevice -- USB Device version (BCD) (16 bit integer)
- iManufacturer -- USB Manufacturer string (string)
- iProduct -- USB Product string (string)
- iSerialNumber -- SerialNumber string (sting)
* sysfs entries
For each logical unit, the gadget creates a directory in the sysfs
hierarchy. Inside of it the following three files are created:
- file
When read it returns the path to the backing file for the given
logical unit. If there is no backing file (possible only if the
logical unit is removable), the content is empty.
When written into, it changes the backing file for given logical
unit. This change can be performed even if given logical unit is
not specified as removable (but that may look strange to the
host). It may fail, however, if host disallowed medium removal
with the Prevent-Allow Medium Removal SCSI command.
- ro
Reflects the state of ro flag for the given logical unit. It can
be read any time, and written to when there is no backing file
open for given logical unit.
- nofua
Reflects the state of nofua flag for given logical unit. It can
be read and written.
Other then those, as usual, the values of module parameters can be
read from /sys/module/g_mass_storage/parameters/* files.
* Other gadgets using mass storage function
The Mass Storage Gadget uses the Mass Storage Function to handle
mass storage protocol. As a composite function, MSF may be used by
other gadgets as well (eg. g_multi and acm_ms).
All of the information in previous sections are valid for other
gadgets using MSF, except that support for mass storage related
module parameters may be missing, or the parameters may have
a prefix. To figure out whether any of this is true one needs to
consult the gadget's documentation or its source code.
For examples of how to include mass storage function in gadgets, one
may take a look at mass_storage.c, acm_ms.c and multi.c (sorted by
complexity).
* Relation to file storage gadget
The Mass Storage Function and thus the Mass Storage Gadget has been
based on the File Storage Gadget. The difference between the two is
that MSG is a composite gadget (ie. uses the composite framework)
while file storage gadget is a traditional gadget. From userspace
point of view this distinction does not really matter, but from
kernel hacker's point of view, this means that (i) MSG does not
duplicate code needed for handling basic USB protocol commands and
(ii) MSF can be used in any other composite gadget.
Because of that, File Storage Gadget has been deprecated and
scheduled to be removed in Linux 3.8. All users need to transition
to the Mass Storage Gadget by that time. The two gadgets behave
mostly the same from the outside except:
1. In FSG the “removable” and “cdrom” module parameters set the flag
for all logical units whereas in MSG they accept a list of y/n
values for each logical unit. If one uses only a single logical
unit this does not matter, but if there are more, the y/n value
needs to be repeated for each logical unit.
2. FSG's “serial”, “vendor”, “product” and “release” module
parameters are handled in MSG by the composite layer's parameters
named respectively: “iSerialnumber”, “idVendor”, “idProduct” and
“bcdDevice”.
3. MSG does not support FSG's test mode, thus “transport”,
“protocol” and “buflen” FSG's module parameters are not
supported. MSG always uses SCSI protocol with bulk only
transport mode and 16 KiB buffers.
...@@ -3376,15 +3376,15 @@ static struct omap_clk omap3xxx_clks[] = { ...@@ -3376,15 +3376,15 @@ static struct omap_clk omap3xxx_clks[] = {
CLK(NULL, "usbhost_48m_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "usbhost_48m_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK(NULL, "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK(NULL, "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("usbhs_omap", "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX), CLK("usbhs_omap", "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
CLK("usbhs_omap", "utmi_p1_gfclk", &dummy_ck, CK_3XXX), CLK(NULL, "utmi_p1_gfclk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "utmi_p2_gfclk", &dummy_ck, CK_3XXX), CLK(NULL, "utmi_p2_gfclk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "xclk60mhsp1_ck", &dummy_ck, CK_3XXX), CLK(NULL, "xclk60mhsp1_ck", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "xclk60mhsp2_ck", &dummy_ck, CK_3XXX), CLK(NULL, "xclk60mhsp2_ck", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "usb_host_hs_utmi_p1_clk", &dummy_ck, CK_3XXX), CLK(NULL, "usb_host_hs_utmi_p1_clk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "usb_host_hs_utmi_p2_clk", &dummy_ck, CK_3XXX), CLK(NULL, "usb_host_hs_utmi_p2_clk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "usb_tll_hs_usb_ch0_clk", &dummy_ck, CK_3XXX), CLK("usbhs_omap", "usb_tll_hs_usb_ch0_clk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "usb_tll_hs_usb_ch1_clk", &dummy_ck, CK_3XXX), CLK("usbhs_omap", "usb_tll_hs_usb_ch1_clk", &dummy_ck, CK_3XXX),
CLK("usbhs_omap", "init_60m_fclk", &dummy_ck, CK_3XXX), CLK(NULL, "init_60m_fclk", &dummy_ck, CK_3XXX),
CLK(NULL, "usim_fck", &usim_fck, CK_3430ES2PLUS | CK_36XX), CLK(NULL, "usim_fck", &usim_fck, CK_3430ES2PLUS | CK_36XX),
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX), CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX), CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
......
...@@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) ...@@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
dev_err(di->dev, "%s mask and set failed\n", __func__); dev_err(di->dev, "%s mask and set failed\n", __func__);
usb_unregister_notifier(di->usb_phy, &di->nb); usb_unregister_notifier(di->usb_phy, &di->nb);
usb_put_transceiver(di->usb_phy); usb_put_phy(di->usb_phy);
/* Delete the work queue */ /* Delete the work queue */
destroy_workqueue(di->charger_wq); destroy_workqueue(di->charger_wq);
...@@ -2688,8 +2688,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) ...@@ -2688,8 +2688,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
goto free_ac; goto free_ac;
} }
di->usb_phy = usb_get_transceiver(); di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!di->usb_phy) { if (IS_ERR_OR_NULL(di->usb_phy)) {
dev_err(di->dev, "failed to get usb transceiver\n"); dev_err(di->dev, "failed to get usb transceiver\n");
ret = -EINVAL; ret = -EINVAL;
goto free_usb; goto free_usb;
...@@ -2747,7 +2747,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) ...@@ -2747,7 +2747,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
free_irq(irq, di); free_irq(irq, di);
} }
put_usb_phy: put_usb_phy:
usb_put_transceiver(di->usb_phy); usb_put_phy(di->usb_phy);
free_usb: free_usb:
power_supply_unregister(&di->usb_chg.psy); power_supply_unregister(&di->usb_chg.psy);
free_ac: free_ac:
......
...@@ -415,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -415,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
if (!isp) if (!isp)
return -ENOMEM; return -ENOMEM;
isp->phy = usb_get_transceiver(); isp->phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!isp->phy) if (IS_ERR_OR_NULL(isp->phy))
goto fail0; goto fail0;
isp->dev = &pdev->dev; isp->dev = &pdev->dev;
...@@ -475,7 +475,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) ...@@ -475,7 +475,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
fail1: fail1:
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
usb_put_transceiver(isp->phy); usb_put_phy(isp->phy);
fail0: fail0:
kfree(isp); kfree(isp);
...@@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) ...@@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev)
usb_unregister_notifier(isp->phy, &isp->nb); usb_unregister_notifier(isp->phy, &isp->nb);
power_supply_unregister(&isp->psy); power_supply_unregister(&isp->psy);
usb_put_transceiver(isp->phy); usb_put_phy(isp->phy);
isp1704_charger_set_power(isp, 0); isp1704_charger_set_power(isp, 0);
kfree(isp); kfree(isp);
......
...@@ -321,11 +321,11 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -321,11 +321,11 @@ static int pda_power_probe(struct platform_device *pdev)
} }
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
transceiver = usb_get_transceiver(); transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (transceiver && !pdata->is_usb_online) { if (!IS_ERR_OR_NULL(transceiver)) {
if (!pdata->is_usb_online)
pdata->is_usb_online = otg_is_usb_online; pdata->is_usb_online = otg_is_usb_online;
} if (!pdata->is_ac_online)
if (transceiver && !pdata->is_ac_online) {
pdata->is_ac_online = otg_is_ac_online; pdata->is_ac_online = otg_is_ac_online;
} }
#endif #endif
...@@ -373,7 +373,7 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -373,7 +373,7 @@ static int pda_power_probe(struct platform_device *pdev)
} }
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver && pdata->use_otg_notifier) { if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
otg_nb.notifier_call = otg_handle_notification; otg_nb.notifier_call = otg_handle_notification;
ret = usb_register_notifier(transceiver, &otg_nb); ret = usb_register_notifier(transceiver, &otg_nb);
if (ret) { if (ret) {
...@@ -408,8 +408,8 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -408,8 +408,8 @@ static int pda_power_probe(struct platform_device *pdev)
if (pdata->is_ac_online && ac_irq) if (pdata->is_ac_online && ac_irq)
free_irq(ac_irq->start, &pda_psy_ac); free_irq(ac_irq->start, &pda_psy_ac);
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver) if (!IS_ERR_OR_NULL(transceiver))
usb_put_transceiver(transceiver); usb_put_phy(transceiver);
#endif #endif
ac_irq_failed: ac_irq_failed:
if (pdata->is_ac_online) if (pdata->is_ac_online)
...@@ -443,8 +443,8 @@ static int pda_power_remove(struct platform_device *pdev) ...@@ -443,8 +443,8 @@ static int pda_power_remove(struct platform_device *pdev)
if (pdata->is_ac_online) if (pdata->is_ac_online)
power_supply_unregister(&pda_psy_ac); power_supply_unregister(&pda_psy_ac);
#ifdef CONFIG_USB_OTG_UTILS #ifdef CONFIG_USB_OTG_UTILS
if (transceiver) if (!IS_ERR_OR_NULL(transceiver))
usb_put_transceiver(transceiver); usb_put_phy(transceiver);
#endif #endif
if (ac_draw) { if (ac_draw) {
regulator_put(ac_draw); regulator_put(ac_draw);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
...@@ -479,8 +480,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -479,8 +480,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
INIT_WORK(&bci->work, twl4030_bci_usb_work); INIT_WORK(&bci->work, twl4030_bci_usb_work);
bci->transceiver = usb_get_transceiver(); bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (bci->transceiver != NULL) { if (!IS_ERR_OR_NULL(bci->transceiver)) {
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
usb_register_notifier(bci->transceiver, &bci->usb_nb); usb_register_notifier(bci->transceiver, &bci->usb_nb);
} }
...@@ -507,9 +508,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ...@@ -507,9 +508,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
return 0; return 0;
fail_unmask_interrupts: fail_unmask_interrupts:
if (bci->transceiver != NULL) { if (!IS_ERR_OR_NULL(bci->transceiver)) {
usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
usb_put_transceiver(bci->transceiver); usb_put_phy(bci->transceiver);
} }
free_irq(bci->irq_bci, bci); free_irq(bci->irq_bci, bci);
fail_bci_irq: fail_bci_irq:
...@@ -538,9 +539,9 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) ...@@ -538,9 +539,9 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
TWL4030_INTERRUPTS_BCIIMR2A); TWL4030_INTERRUPTS_BCIIMR2A);
if (bci->transceiver != NULL) { if (!IS_ERR_OR_NULL(bci->transceiver)) {
usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
usb_put_transceiver(bci->transceiver); usb_put_phy(bci->transceiver);
} }
free_irq(bci->irq_bci, bci); free_irq(bci->irq_bci, bci);
free_irq(bci->irq_chg, bci); free_irq(bci->irq_chg, bci);
......
...@@ -20,6 +20,7 @@ config USB_CHIPIDEA_UDC ...@@ -20,6 +20,7 @@ config USB_CHIPIDEA_UDC
config USB_CHIPIDEA_HOST config USB_CHIPIDEA_HOST
bool "ChipIdea host controller" bool "ChipIdea host controller"
select USB_EHCI_ROOT_HUB_TT
help help
Say Y here to enable host controller functionality of the Say Y here to enable host controller functionality of the
ChipIdea driver. ChipIdea driver.
......
...@@ -5,10 +5,15 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ...@@ -5,10 +5,15 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
# Glue/Bridge layers go here
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_msm.o
# PCI doesn't provide stubs, need to check
ifneq ($(CONFIG_PCI),) ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o
endif endif
ifneq ($(CONFIG_ARCH_MSM),) ifneq ($(CONFIG_OF_DEVICE),)
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_msm.o obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o
endif endif
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* @name: string description of the endpoint * @name: string description of the endpoint
* @qh: queue head for this endpoint * @qh: queue head for this endpoint
* @wedge: is the endpoint wedged * @wedge: is the endpoint wedged
* @udc: pointer to the controller * @ci: pointer to the controller
* @lock: pointer to controller's spinlock * @lock: pointer to controller's spinlock
* @td_pool: pointer to controller's TD pool * @td_pool: pointer to controller's TD pool
*/ */
...@@ -54,7 +54,7 @@ struct ci13xxx_ep { ...@@ -54,7 +54,7 @@ struct ci13xxx_ep {
int wedge; int wedge;
/* global resources */ /* global resources */
struct ci13xxx *udc; struct ci13xxx *ci;
spinlock_t *lock; spinlock_t *lock;
struct dma_pool *td_pool; struct dma_pool *td_pool;
}; };
...@@ -125,7 +125,7 @@ struct hw_bank { ...@@ -125,7 +125,7 @@ struct hw_bank {
* @remote_wakeup: host-enabled remote wakeup * @remote_wakeup: host-enabled remote wakeup
* @suspended: suspended by host * @suspended: suspended by host
* @test_mode: the selected test mode * @test_mode: the selected test mode
* @udc_driver: platform specific information supplied by parent device * @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active * @vbus_active: is VBUS active
* @transceiver: pointer to USB PHY, if any * @transceiver: pointer to USB PHY, if any
* @hcd: pointer to usb_hcd for ehci host driver * @hcd: pointer to usb_hcd for ehci host driver
...@@ -158,8 +158,10 @@ struct ci13xxx { ...@@ -158,8 +158,10 @@ struct ci13xxx {
u8 suspended; u8 suspended;
u8 test_mode; u8 test_mode;
struct ci13xxx_udc_driver *udc_driver; struct ci13xxx_platform_data *platdata;
int vbus_active; int vbus_active;
/* FIXME: some day, we'll not use global phy */
bool global_phy;
struct usb_phy *transceiver; struct usb_phy *transceiver;
struct usb_hcd *hcd; struct usb_hcd *hcd;
}; };
...@@ -250,9 +252,9 @@ static inline int ffs_nr(u32 x) ...@@ -250,9 +252,9 @@ static inline int ffs_nr(u32 x)
* *
* This function returns register contents * This function returns register contents
*/ */
static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) static inline u32 hw_read(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask)
{ {
return ioread32(udc->hw_bank.regmap[reg]) & mask; return ioread32(ci->hw_bank.regmap[reg]) & mask;
} }
/** /**
...@@ -261,14 +263,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) ...@@ -261,14 +263,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
* @mask: bitfield mask * @mask: bitfield mask
* @data: new value * @data: new value
*/ */
static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, static inline void hw_write(struct ci13xxx *ci, enum ci13xxx_regs reg,
u32 mask, u32 data) u32 mask, u32 data)
{ {
if (~mask) if (~mask)
data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask)
| (data & mask); | (data & mask);
iowrite32(data, udc->hw_bank.regmap[reg]); iowrite32(data, ci->hw_bank.regmap[reg]);
} }
/** /**
...@@ -278,12 +280,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, ...@@ -278,12 +280,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
* *
* This function returns register contents * This function returns register contents
*/ */
static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, static inline u32 hw_test_and_clear(struct ci13xxx *ci, enum ci13xxx_regs reg,
u32 mask) u32 mask)
{ {
u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask;
iowrite32(val, udc->hw_bank.regmap[reg]); iowrite32(val, ci->hw_bank.regmap[reg]);
return val; return val;
} }
...@@ -295,12 +297,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, ...@@ -295,12 +297,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
* *
* This function returns register contents * This function returns register contents
*/ */
static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg,
u32 mask, u32 data) u32 mask, u32 data)
{ {
u32 val = hw_read(udc, reg, ~0); u32 val = hw_read(ci, reg, ~0);
hw_write(udc, reg, mask, data); hw_write(ci, reg, mask, data);
return (val & mask) >> ffs_nr(mask); return (val & mask) >> ffs_nr(mask);
} }
......
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
* on behalf of DENX Software Engineering GmbH
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include "ci.h"
#define pdev_to_phy(pdev) \
((struct usb_phy *)platform_get_drvdata(pdev))
struct ci13xxx_imx_data {
struct device_node *phy_np;
struct usb_phy *phy;
struct platform_device *ci_pdev;
struct clk *clk;
struct regulator *reg_vbus;
};
static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = {
.name = "ci13xxx_imx",
.flags = CI13XXX_REQUIRE_TRANSCEIVER |
CI13XXX_PULLUP_ON_VBUS |
CI13XXX_DISABLE_STREAMING,
.capoffset = DEF_CAPOFFSET,
};
static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
{
struct ci13xxx_imx_data *data;
struct platform_device *plat_ci, *phy_pdev;
struct device_node *phy_np;
struct resource *res;
struct regulator *reg_vbus;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Can't get device resources!\n");
return -ENOENT;
}
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev,
"Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
return PTR_ERR(data->clk);
}
ret = clk_prepare_enable(data->clk);
if (ret) {
dev_err(&pdev->dev,
"Failed to prepare or enable clock, err=%d\n", ret);
return ret;
}
phy_np = of_parse_phandle(pdev->dev.of_node, "fsl,usbphy", 0);
if (phy_np) {
data->phy_np = phy_np;
phy_pdev = of_find_device_by_node(phy_np);
if (phy_pdev) {
struct usb_phy *phy;
phy = pdev_to_phy(phy_pdev);
if (phy &&
try_module_get(phy_pdev->dev.driver->owner)) {
usb_phy_init(phy);
data->phy = phy;
}
}
}
/* we only support host now, so enable vbus here */
reg_vbus = devm_regulator_get(&pdev->dev, "vbus");
if (!IS_ERR(reg_vbus)) {
ret = regulator_enable(reg_vbus);
if (ret) {
dev_err(&pdev->dev,
"Failed to enable vbus regulator, err=%d\n",
ret);
goto put_np;
}
data->reg_vbus = reg_vbus;
} else {
reg_vbus = NULL;
}
ci13xxx_imx_platdata.phy = data->phy;
if (!pdev->dev.dma_mask) {
pdev->dev.dma_mask = devm_kzalloc(&pdev->dev,
sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
if (!pdev->dev.dma_mask) {
ret = -ENOMEM;
dev_err(&pdev->dev, "Failed to alloc dma_mask!\n");
goto err;
}
*pdev->dev.dma_mask = DMA_BIT_MASK(32);
dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask);
}
plat_ci = ci13xxx_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci13xxx_imx_platdata);
if (IS_ERR(plat_ci)) {
ret = PTR_ERR(plat_ci);
dev_err(&pdev->dev,
"Can't register ci_hdrc platform device, err=%d\n",
ret);
goto err;
}
data->ci_pdev = plat_ci;
platform_set_drvdata(pdev, data);
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
err:
if (reg_vbus)
regulator_disable(reg_vbus);
put_np:
if (phy_np)
of_node_put(phy_np);
clk_disable_unprepare(data->clk);
return ret;
}
static int __devexit ci13xxx_imx_remove(struct platform_device *pdev)
{
struct ci13xxx_imx_data *data = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
ci13xxx_remove_device(data->ci_pdev);
if (data->reg_vbus)
regulator_disable(data->reg_vbus);
if (data->phy) {
usb_phy_shutdown(data->phy);
module_put(data->phy->dev->driver->owner);
}
of_node_put(data->phy_np);
clk_disable_unprepare(data->clk);
platform_set_drvdata(pdev, NULL);
return 0;
}
static const struct of_device_id ci13xxx_imx_dt_ids[] = {
{ .compatible = "fsl,imx27-usb", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci13xxx_imx_dt_ids);
static struct platform_driver ci13xxx_imx_driver = {
.probe = ci13xxx_imx_probe,
.remove = __devexit_p(ci13xxx_imx_remove),
.driver = {
.name = "imx_usb",
.owner = THIS_MODULE,
.of_match_table = ci13xxx_imx_dt_ids,
},
};
module_platform_driver(ci13xxx_imx_driver);
MODULE_ALIAS("platform:imx-usb");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CI13xxx i.MX USB binding");
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
#include "ci.h" #include "ci.h"
#define MSM_USB_BASE (udc->hw_bank.abs) #define MSM_USB_BASE (ci->hw_bank.abs)
static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) static void ci13xxx_msm_notify_event(struct ci13xxx *ci, unsigned event)
{ {
struct device *dev = udc->gadget.dev.parent; struct device *dev = ci->gadget.dev.parent;
int val; int val;
switch (event) { switch (event) {
...@@ -34,18 +34,18 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) ...@@ -34,18 +34,18 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
* Put the transceiver in non-driving mode. Otherwise host * Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection. * may not detect soft-disconnection.
*/ */
val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL); val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL);
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL);
break; break;
default: default:
dev_dbg(dev, "unknown ci13xxx_udc event\n"); dev_dbg(dev, "unknown ci13xxx event\n");
break; break;
} }
} }
static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { static struct ci13xxx_platform_data ci13xxx_msm_platdata = {
.name = "ci13xxx_msm", .name = "ci13xxx_msm",
.flags = CI13XXX_REGS_SHARED | .flags = CI13XXX_REGS_SHARED |
CI13XXX_REQUIRE_TRANSCEIVER | CI13XXX_REQUIRE_TRANSCEIVER |
...@@ -55,56 +55,45 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { ...@@ -55,56 +55,45 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
.notify_event = ci13xxx_msm_notify_event, .notify_event = ci13xxx_msm_notify_event,
}; };
static int ci13xxx_msm_probe(struct platform_device *pdev) static int __devinit ci13xxx_msm_probe(struct platform_device *pdev)
{ {
struct platform_device *plat_ci; struct platform_device *plat_ci;
int ret;
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
plat_ci = platform_device_alloc("ci_hdrc", -1); plat_ci = ci13xxx_add_device(&pdev->dev,
if (!plat_ci) { pdev->resource, pdev->num_resources,
dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); &ci13xxx_msm_platdata);
return -ENOMEM; if (IS_ERR(plat_ci)) {
dev_err(&pdev->dev, "ci13xxx_add_device failed!\n");
return PTR_ERR(plat_ci);
} }
ret = platform_device_add_resources(plat_ci, pdev->resource, platform_set_drvdata(pdev, plat_ci);
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "can't add resources to platform device\n");
goto put_platform;
}
ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver,
sizeof(ci13xxx_msm_udc_driver));
if (ret)
goto put_platform;
ret = platform_device_add(plat_ci);
if (ret)
goto put_platform;
pm_runtime_no_callbacks(&pdev->dev); pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
return 0; return 0;
}
static int __devexit ci13xxx_msm_remove(struct platform_device *pdev)
{
struct platform_device *plat_ci = platform_get_drvdata(pdev);
put_platform: pm_runtime_disable(&pdev->dev);
platform_device_put(plat_ci); ci13xxx_remove_device(plat_ci);
return ret; return 0;
} }
static struct platform_driver ci13xxx_msm_driver = { static struct platform_driver ci13xxx_msm_driver = {
.probe = ci13xxx_msm_probe, .probe = ci13xxx_msm_probe,
.remove = __devexit_p(ci13xxx_msm_remove),
.driver = { .name = "msm_hsusb", }, .driver = { .name = "msm_hsusb", },
}; };
MODULE_ALIAS("platform:msm_hsusb");
static int __init ci13xxx_msm_init(void) module_platform_driver(ci13xxx_msm_driver);
{
return platform_driver_register(&ci13xxx_msm_driver);
}
module_init(ci13xxx_msm_init);
MODULE_ALIAS("platform:msm_hsusb");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -23,17 +23,17 @@ ...@@ -23,17 +23,17 @@
/****************************************************************************** /******************************************************************************
* PCI block * PCI block
*****************************************************************************/ *****************************************************************************/
struct ci13xxx_udc_driver pci_driver = { struct ci13xxx_platform_data pci_platdata = {
.name = UDC_DRIVER_NAME, .name = UDC_DRIVER_NAME,
.capoffset = DEF_CAPOFFSET, .capoffset = DEF_CAPOFFSET,
}; };
struct ci13xxx_udc_driver langwell_pci_driver = { struct ci13xxx_platform_data langwell_pci_platdata = {
.name = UDC_DRIVER_NAME, .name = UDC_DRIVER_NAME,
.capoffset = 0, .capoffset = 0,
}; };
struct ci13xxx_udc_driver penwell_pci_driver = { struct ci13xxx_platform_data penwell_pci_platdata = {
.name = UDC_DRIVER_NAME, .name = UDC_DRIVER_NAME,
.capoffset = 0, .capoffset = 0,
.power_budget = 200, .power_budget = 200,
...@@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = { ...@@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = {
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
struct ci13xxx_udc_driver *driver = (void *)id->driver_data; struct ci13xxx_platform_data *platdata = (void *)id->driver_data;
struct platform_device *plat_ci; struct platform_device *plat_ci;
struct resource res[3]; struct resource res[3];
int retval = 0, nres = 2; int retval = 0, nres = 2;
if (!driver) { if (!platdata) {
dev_err(&pdev->dev, "device doesn't provide driver data\n"); dev_err(&pdev->dev, "device doesn't provide driver data\n");
return -ENODEV; return -ENODEV;
} }
...@@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, ...@@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
pci_try_set_mwi(pdev); pci_try_set_mwi(pdev);
plat_ci = platform_device_alloc("ci_hdrc", -1);
if (!plat_ci) {
dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");
retval = -ENOMEM;
goto disable_device;
}
memset(res, 0, sizeof(res)); memset(res, 0, sizeof(res));
res[0].start = pci_resource_start(pdev, 0); res[0].start = pci_resource_start(pdev, 0);
res[0].end = pci_resource_end(pdev, 0); res[0].end = pci_resource_end(pdev, 0);
...@@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, ...@@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
res[1].start = pdev->irq; res[1].start = pdev->irq;
res[1].flags = IORESOURCE_IRQ; res[1].flags = IORESOURCE_IRQ;
retval = platform_device_add_resources(plat_ci, res, nres); plat_ci = ci13xxx_add_device(&pdev->dev, res, nres, platdata);
if (retval) { if (IS_ERR(plat_ci)) {
dev_err(&pdev->dev, "can't add resources to platform device\n"); dev_err(&pdev->dev, "ci13xxx_add_device failed!\n");
goto put_platform; retval = PTR_ERR(plat_ci);
goto disable_device;
} }
retval = platform_device_add_data(plat_ci, driver, sizeof(*driver));
if (retval)
goto put_platform;
dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask);
plat_ci->dev.dma_mask = pdev->dev.dma_mask;
plat_ci->dev.dma_parms = pdev->dev.dma_parms;
plat_ci->dev.parent = &pdev->dev;
pci_set_drvdata(pdev, plat_ci); pci_set_drvdata(pdev, plat_ci);
retval = platform_device_add(plat_ci);
if (retval)
goto put_platform;
return 0; return 0;
put_platform:
pci_set_drvdata(pdev, NULL);
platform_device_put(plat_ci);
disable_device: disable_device:
pci_disable_device(pdev); pci_disable_device(pdev);
done: done:
...@@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) ...@@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
{ {
struct platform_device *plat_ci = pci_get_drvdata(pdev); struct platform_device *plat_ci = pci_get_drvdata(pdev);
platform_device_unregister(plat_ci); ci13xxx_remove_device(plat_ci);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev); pci_disable_device(pdev);
} }
...@@ -147,19 +125,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) ...@@ -147,19 +125,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
{ {
PCI_DEVICE(0x153F, 0x1004), PCI_DEVICE(0x153F, 0x1004),
.driver_data = (kernel_ulong_t)&pci_driver, .driver_data = (kernel_ulong_t)&pci_platdata,
}, },
{ {
PCI_DEVICE(0x153F, 0x1006), PCI_DEVICE(0x153F, 0x1006),
.driver_data = (kernel_ulong_t)&pci_driver, .driver_data = (kernel_ulong_t)&pci_platdata,
}, },
{ {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811),
.driver_data = (kernel_ulong_t)&langwell_pci_driver, .driver_data = (kernel_ulong_t)&langwell_pci_platdata,
}, },
{ {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829),
.driver_data = (kernel_ulong_t)&penwell_pci_driver, .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
}, },
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
}; };
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/idr.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -179,7 +180,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ...@@ -179,7 +180,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
ci->hw_bank.abs = base; ci->hw_bank.abs = base;
ci->hw_bank.cap = ci->hw_bank.abs; ci->hw_bank.cap = ci->hw_bank.abs;
ci->hw_bank.cap += ci->udc_driver->capoffset; ci->hw_bank.cap += ci->platdata->capoffset;
ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap);
hw_alloc_regmap(ci, false); hw_alloc_regmap(ci, false);
...@@ -227,11 +228,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode) ...@@ -227,11 +228,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode)
udelay(10); /* not RTOS friendly */ udelay(10); /* not RTOS friendly */
if (ci->udc_driver->notify_event) if (ci->platdata->notify_event)
ci->udc_driver->notify_event(ci, ci->platdata->notify_event(ci,
CI13XXX_CONTROLLER_RESET_EVENT); CI13XXX_CONTROLLER_RESET_EVENT);
if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING) if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
/* USBMODE should be configured step by step */ /* USBMODE should be configured step by step */
...@@ -332,6 +333,59 @@ static irqreturn_t ci_irq(int irq, void *data) ...@@ -332,6 +333,59 @@ static irqreturn_t ci_irq(int irq, void *data)
return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci);
} }
static DEFINE_IDA(ci_ida);
struct platform_device *ci13xxx_add_device(struct device *dev,
struct resource *res, int nres,
struct ci13xxx_platform_data *platdata)
{
struct platform_device *pdev;
int id, ret;
id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
pdev = platform_device_alloc("ci_hdrc", id);
if (!pdev) {
ret = -ENOMEM;
goto put_id;
}
pdev->dev.parent = dev;
pdev->dev.dma_mask = dev->dma_mask;
pdev->dev.dma_parms = dev->dma_parms;
dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask);
ret = platform_device_add_resources(pdev, res, nres);
if (ret)
goto err;
ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));
if (ret)
goto err;
ret = platform_device_add(pdev);
if (ret)
goto err;
return pdev;
err:
platform_device_put(pdev);
put_id:
ida_simple_remove(&ci_ida, id);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(ci13xxx_add_device);
void ci13xxx_remove_device(struct platform_device *pdev)
{
platform_device_unregister(pdev);
ida_simple_remove(&ci_ida, pdev->id);
}
EXPORT_SYMBOL_GPL(ci13xxx_remove_device);
static int __devinit ci_hdrc_probe(struct platform_device *pdev) static int __devinit ci_hdrc_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -364,7 +418,11 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) ...@@ -364,7 +418,11 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
} }
ci->dev = dev; ci->dev = dev;
ci->udc_driver = dev->platform_data; ci->platdata = dev->platform_data;
if (ci->platdata->phy)
ci->transceiver = ci->platdata->phy;
else
ci->global_phy = true;
ret = hw_device_init(ci, base); ret = hw_device_init(ci, base);
if (ret < 0) { if (ret < 0) {
...@@ -419,7 +477,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) ...@@ -419,7 +477,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
} }
platform_set_drvdata(pdev, ci); platform_set_drvdata(pdev, ci);
ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name, ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
ci); ci);
if (ret) if (ret)
goto stop; goto stop;
......
This diff is collapsed.
...@@ -116,7 +116,8 @@ static int host_start(struct ci13xxx *ci) ...@@ -116,7 +116,8 @@ static int host_start(struct ci13xxx *ci)
hcd->regs = ci->hw_bank.abs; hcd->regs = ci->hw_bank.abs;
hcd->has_tt = 1; hcd->has_tt = 1;
hcd->power_budget = ci->udc_driver->power_budget; hcd->power_budget = ci->platdata->power_budget;
hcd->phy = ci->transceiver;
ehci = hcd_to_ehci(hcd); ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap; ehci->caps = ci->hw_bank.cap;
......
This diff is collapsed.
...@@ -996,7 +996,7 @@ static int acm_probe(struct usb_interface *intf, ...@@ -996,7 +996,7 @@ static int acm_probe(struct usb_interface *intf,
case USB_CDC_CALL_MANAGEMENT_TYPE: case USB_CDC_CALL_MANAGEMENT_TYPE:
call_management_function = buffer[3]; call_management_function = buffer[3];
call_interface_num = buffer[4]; call_interface_num = buffer[4];
if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
break; break;
default: default:
......
...@@ -32,8 +32,6 @@ ...@@ -32,8 +32,6 @@
#define DRIVER_AUTHOR "Oliver Neukum" #define DRIVER_AUTHOR "Oliver Neukum"
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
#define HUAWEI_VENDOR_ID 0x12D1
static const struct usb_device_id wdm_ids[] = { static const struct usb_device_id wdm_ids[] = {
{ {
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
...@@ -41,29 +39,6 @@ static const struct usb_device_id wdm_ids[] = { ...@@ -41,29 +39,6 @@ static const struct usb_device_id wdm_ids[] = {
.bInterfaceClass = USB_CLASS_COMM, .bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
}, },
{
/*
* Huawei E392, E398 and possibly other Qualcomm based modems
* embed the Qualcomm QMI protocol inside CDC on CDC ECM like
* control interfaces. Userspace access to this is required
* to configure the accompanying data interface
*/
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = HUAWEI_VENDOR_ID,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
},
{
/* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = HUAWEI_VENDOR_ID,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 57, /* NOTE: CDC ECM control interface! */
},
{ } { }
}; };
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/scatterlist.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
#define USB_MAXBUS 64 #define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128 #define USB_DEVICE_MAX USB_MAXBUS * 128
#define USB_SG_SIZE 16384 /* split-size for large txs */
/* Mutual exclusion for removal, open, and release */ /* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex); DEFINE_MUTEX(usbfs_mutex);
...@@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes) ...@@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as) static void free_async(struct async *as)
{ {
int i;
put_pid(as->pid); put_pid(as->pid);
if (as->cred) if (as->cred)
put_cred(as->cred); put_cred(as->cred);
for (i = 0; i < as->urb->num_sgs; i++) {
if (sg_page(&as->urb->sg[i]))
kfree(sg_virt(&as->urb->sg[i]));
}
kfree(as->urb->sg);
kfree(as->urb->transfer_buffer); kfree(as->urb->transfer_buffer);
kfree(as->urb->setup_packet); kfree(as->urb->setup_packet);
usb_free_urb(as->urb); usb_free_urb(as->urb);
...@@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev, ...@@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev,
} }
} }
static void snoop_urb_data(struct urb *urb, unsigned len)
{
int i, size;
if (!usbfs_snoop)
return;
if (urb->num_sgs == 0) {
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
urb->transfer_buffer, len, 1);
return;
}
for (i = 0; i < urb->num_sgs && len; i++) {
size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
sg_virt(&urb->sg[i]), size, 1);
len -= size;
}
}
static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
{
unsigned i, len, size;
if (urb->number_of_packets > 0) /* Isochronous */
len = urb->transfer_buffer_length;
else /* Non-Isoc */
len = urb->actual_length;
if (urb->num_sgs == 0) {
if (copy_to_user(userbuffer, urb->transfer_buffer, len))
return -EFAULT;
return 0;
}
for (i = 0; i < urb->num_sgs && len; i++) {
size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size))
return -EFAULT;
userbuffer += size;
len -= size;
}
return 0;
}
#define AS_CONTINUATION 1 #define AS_CONTINUATION 1
#define AS_UNLINK 2 #define AS_UNLINK 2
...@@ -454,9 +510,10 @@ static void async_completed(struct urb *urb) ...@@ -454,9 +510,10 @@ static void async_completed(struct urb *urb)
} }
snoop(&urb->dev->dev, "urb complete\n"); snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
as->status, COMPLETE, as->status, COMPLETE, NULL, 0);
((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
NULL : urb->transfer_buffer, urb->actual_length); snoop_urb_data(urb, urb->actual_length);
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
as->status != -ENOENT) as->status != -ENOENT)
cancel_bulk_urbs(ps, as->bulk_addr); cancel_bulk_urbs(ps, as->bulk_addr);
...@@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ...@@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct async *as = NULL; struct async *as = NULL;
struct usb_ctrlrequest *dr = NULL; struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen; unsigned int u, totlen, isofrmlen;
int ret, ifnum = -1; int i, ret, is_in, num_sgs = 0, ifnum = -1;
int is_in; void *buf;
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
USBDEVFS_URB_SHORT_NOT_OK | USBDEVFS_URB_SHORT_NOT_OK |
...@@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ...@@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
goto interrupt_urb; goto interrupt_urb;
} }
uurb->number_of_packets = 0; 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;
break; break;
case USBDEVFS_URB_TYPE_INTERRUPT: case USBDEVFS_URB_TYPE_INTERRUPT:
...@@ -1255,27 +1315,68 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ...@@ -1255,27 +1315,68 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto error;
} }
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length;
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
num_sgs * sizeof(struct scatterlist);
ret = usbfs_increase_memory_usage(u); ret = usbfs_increase_memory_usage(u);
if (ret) if (ret)
goto error; goto error;
as->mem_usage = u; as->mem_usage = u;
if (uurb->buffer_length > 0) { if (num_sgs) {
as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist),
GFP_KERNEL);
if (!as->urb->sg) {
ret = -ENOMEM;
goto error;
}
as->urb->num_sgs = num_sgs;
sg_init_table(as->urb->sg, as->urb->num_sgs);
totlen = uurb->buffer_length;
for (i = 0; i < as->urb->num_sgs; i++) {
u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
buf = kmalloc(u, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto error;
}
sg_set_buf(&as->urb->sg[i], buf, u);
if (!is_in) {
if (copy_from_user(buf, uurb->buffer, u)) {
ret = -EFAULT;
goto error;
}
}
totlen -= u;
}
} else if (uurb->buffer_length > 0) {
as->urb->transfer_buffer = kmalloc(uurb->buffer_length, as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
GFP_KERNEL); GFP_KERNEL);
if (!as->urb->transfer_buffer) { if (!as->urb->transfer_buffer) {
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto error;
} }
/* Isochronous input data may end up being discontiguous
* if some of the packets are short. Clear the buffer so if (!is_in) {
* that the gaps don't leak kernel data to userspace. if (copy_from_user(as->urb->transfer_buffer,
uurb->buffer,
uurb->buffer_length)) {
ret = -EFAULT;
goto error;
}
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
/*
* Isochronous input data may end up being
* discontiguous if some of the packets are short.
* Clear the buffer so that the gaps don't leak
* kernel data to userspace.
*/ */
if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
memset(as->urb->transfer_buffer, 0, memset(as->urb->transfer_buffer, 0,
uurb->buffer_length); uurb->buffer_length);
} }
}
as->urb->dev = ps->dev; as->urb->dev = ps->dev;
as->urb->pipe = (uurb->type << 30) | as->urb->pipe = (uurb->type << 30) |
__create_pipe(ps->dev, uurb->endpoint & 0xf) | __create_pipe(ps->dev, uurb->endpoint & 0xf) |
...@@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ...@@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->pid = get_pid(task_pid(current)); as->pid = get_pid(task_pid(current));
as->cred = get_current_cred(); as->cred = get_current_cred();
security_task_getsecid(current, &as->secid); security_task_getsecid(current, &as->secid);
if (!is_in && uurb->buffer_length > 0) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
uurb->buffer_length)) {
ret = -EFAULT;
goto error;
}
}
snoop_urb(ps->dev, as->userurb, as->urb->pipe, snoop_urb(ps->dev, as->userurb, as->urb->pipe,
as->urb->transfer_buffer_length, 0, SUBMIT, as->urb->transfer_buffer_length, 0, SUBMIT,
is_in ? NULL : as->urb->transfer_buffer, NULL, 0);
uurb->buffer_length); if (!is_in)
snoop_urb_data(as->urb, as->urb->transfer_buffer_length);
async_newpending(as); async_newpending(as);
if (usb_endpoint_xfer_bulk(&ep->desc)) { if (usb_endpoint_xfer_bulk(&ep->desc)) {
...@@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg) ...@@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg)
unsigned int i; unsigned int i;
if (as->userbuffer && urb->actual_length) { if (as->userbuffer && urb->actual_length) {
if (urb->number_of_packets > 0) /* Isochronous */ if (copy_urb_data_to_user(as->userbuffer, urb))
i = urb->transfer_buffer_length;
else /* Non-Isoc */
i = urb->actual_length;
if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
goto err_out; goto err_out;
} }
if (put_user(as->status, &userurb->status)) if (put_user(as->status, &userurb->status))
...@@ -1604,10 +1696,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) ...@@ -1604,10 +1696,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb; void __user *addr = as->userurb;
unsigned int i; unsigned int i;
if (as->userbuffer && urb->actual_length) if (as->userbuffer && urb->actual_length) {
if (copy_to_user(as->userbuffer, urb->transfer_buffer, if (copy_urb_data_to_user(as->userbuffer, urb))
urb->actual_length))
return -EFAULT; return -EFAULT;
}
if (put_user(as->status, &userurb->status)) if (put_user(as->status, &userurb->status))
return -EFAULT; return -EFAULT;
if (put_user(urb->actual_length, &userurb->actual_length)) if (put_user(urb->actual_length, &userurb->actual_length))
...@@ -1820,6 +1912,22 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) ...@@ -1820,6 +1912,22 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)
return usb_hub_release_port(ps->dev, portnum, ps); return usb_hub_release_port(ps->dev, portnum, ps);
} }
static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
{
__u32 caps;
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER;
if (put_user(caps, (__u32 __user *)arg))
return -EFAULT;
return 0;
}
/* /*
* NOTE: All requests here that have interface numbers as parameters * NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from * are assuming that somehow the configuration has been prevented from
...@@ -1990,6 +2098,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, ...@@ -1990,6 +2098,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
ret = proc_release_port(ps, p); ret = proc_release_port(ps, p);
break; break;
case USBDEVFS_GET_CAPABILITIES:
ret = proc_get_capabilities(ps, p);
break;
} }
usb_unlock_device(dev); usb_unlock_device(dev);
if (ret >= 0) if (ret >= 0)
......
...@@ -367,6 +367,7 @@ static int usb_probe_interface(struct device *dev) ...@@ -367,6 +367,7 @@ static int usb_probe_interface(struct device *dev)
return error; return error;
err: err:
usb_set_intfdata(intf, NULL);
intf->needs_remote_wakeup = 0; intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND; intf->condition = USB_INTERFACE_UNBOUND;
usb_cancel_queued_reset(intf); usb_cancel_queued_reset(intf);
...@@ -606,30 +607,19 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) ...@@ -606,30 +607,19 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
} }
/* returns 0 if no match, 1 if match */ /* returns 0 if no match, 1 if match */
int usb_match_one_id(struct usb_interface *interface, int usb_match_one_id_intf(struct usb_device *dev,
struct usb_host_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_host_interface *intf; /* The interface class, subclass, protocol and number should never be
struct usb_device *dev;
/* proc_connectinfo in devio.c may call us with id == NULL. */
if (id == NULL)
return 0;
intf = interface->cur_altsetting;
dev = interface_to_usbdev(interface);
if (!usb_match_device(dev, id))
return 0;
/* The interface class, subclass, and protocol should never be
* checked for a match if the device class is Vendor Specific, * checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */ * unless the match record specifies the Vendor ID. */
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL))) USB_DEVICE_ID_MATCH_INT_PROTOCOL |
USB_DEVICE_ID_MATCH_INT_NUMBER)))
return 0; return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
...@@ -644,8 +634,32 @@ int usb_match_one_id(struct usb_interface *interface, ...@@ -644,8 +634,32 @@ int usb_match_one_id(struct usb_interface *interface,
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
return 0; return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
(id->bInterfaceNumber != intf->desc.bInterfaceNumber))
return 0;
return 1; return 1;
} }
/* returns 0 if no match, 1 if match */
int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_host_interface *intf;
struct usb_device *dev;
/* proc_connectinfo in devio.c may call us with id == NULL. */
if (id == NULL)
return 0;
intf = interface->cur_altsetting;
dev = interface_to_usbdev(interface);
if (!usb_match_device(dev, id))
return 0;
return usb_match_one_id_intf(dev, intf, id);
}
EXPORT_SYMBOL_GPL(usb_match_one_id); EXPORT_SYMBOL_GPL(usb_match_one_id);
/** /**
......
...@@ -92,7 +92,7 @@ static int init_usb_class(void) ...@@ -92,7 +92,7 @@ static int init_usb_class(void)
} }
kref_init(&usb_class->kref); kref_init(&usb_class->kref);
usb_class->class = class_create(THIS_MODULE, "usb"); usb_class->class = class_create(THIS_MODULE, "usbmisc");
if (IS_ERR(usb_class->class)) { if (IS_ERR(usb_class->class)) {
result = IS_ERR(usb_class->class); result = IS_ERR(usb_class->class);
printk(KERN_ERR "class_create failed for usb devices\n"); printk(KERN_ERR "class_create failed for usb devices\n");
......
...@@ -1398,7 +1398,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, ...@@ -1398,7 +1398,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
if (hcd->self.uses_dma) { if (hcd->self.uses_dma) {
if (urb->num_sgs) { if (urb->num_sgs) {
int n = dma_map_sg( int n;
/* We don't support sg for isoc transfers ! */
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
WARN_ON(1);
return -EINVAL;
}
n = dma_map_sg(
hcd->self.controller, hcd->self.controller,
urb->sg, urb->sg,
urb->num_sgs, urb->num_sgs,
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usbdevice_fs.h> #include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h> #include <linux/usb/quirks.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -81,7 +82,7 @@ struct usb_hub { ...@@ -81,7 +82,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN]; u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds; struct delayed_work leds;
struct delayed_work init_work; struct delayed_work init_work;
void **port_owners; struct dev_state **port_owners;
}; };
static inline int hub_is_superspeed(struct usb_device *hdev) static inline int hub_is_superspeed(struct usb_device *hdev)
...@@ -1271,7 +1272,8 @@ static int hub_configure(struct usb_hub *hub, ...@@ -1271,7 +1272,8 @@ static int hub_configure(struct usb_hub *hub,
hdev->children = kzalloc(hdev->maxchild * hdev->children = kzalloc(hdev->maxchild *
sizeof(struct usb_device *), GFP_KERNEL); sizeof(struct usb_device *), GFP_KERNEL);
hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
GFP_KERNEL);
if (!hdev->children || !hub->port_owners) { if (!hdev->children || !hub->port_owners) {
ret = -ENOMEM; ret = -ENOMEM;
goto fail; goto fail;
...@@ -1649,7 +1651,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) ...@@ -1649,7 +1651,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
* to one of these "claimed" ports, the program will "own" the device. * to one of these "claimed" ports, the program will "own" the device.
*/ */
static int find_port_owner(struct usb_device *hdev, unsigned port1, static int find_port_owner(struct usb_device *hdev, unsigned port1,
void ***ppowner) struct dev_state ***ppowner)
{ {
if (hdev->state == USB_STATE_NOTATTACHED) if (hdev->state == USB_STATE_NOTATTACHED)
return -ENODEV; return -ENODEV;
...@@ -1664,10 +1666,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1, ...@@ -1664,10 +1666,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
} }
/* In the following three functions, the caller must hold hdev's lock */ /* In the following three functions, the caller must hold hdev's lock */
int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
struct dev_state *owner)
{ {
int rc; int rc;
void **powner; struct dev_state **powner;
rc = find_port_owner(hdev, port1, &powner); rc = find_port_owner(hdev, port1, &powner);
if (rc) if (rc)
...@@ -1678,10 +1681,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) ...@@ -1678,10 +1681,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
return rc; return rc;
} }
int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
struct dev_state *owner)
{ {
int rc; int rc;
void **powner; struct dev_state **powner;
rc = find_port_owner(hdev, port1, &powner); rc = find_port_owner(hdev, port1, &powner);
if (rc) if (rc)
...@@ -1692,10 +1696,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) ...@@ -1692,10 +1696,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
return rc; return rc;
} }
void usb_hub_release_all_ports(struct usb_device *hdev, void *owner) void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
{ {
int n; int n;
void **powner; struct dev_state **powner;
n = find_port_owner(hdev, 1, &powner); n = find_port_owner(hdev, 1, &powner);
if (n == 0) { if (n == 0) {
...@@ -2065,7 +2069,7 @@ static int usb_enumerate_device(struct usb_device *udev) ...@@ -2065,7 +2069,7 @@ static int usb_enumerate_device(struct usb_device *udev)
if (err < 0) { if (err < 0) {
dev_err(&udev->dev, "can't read configurations, error %d\n", dev_err(&udev->dev, "can't read configurations, error %d\n",
err); err);
goto fail; return err;
} }
} }
if (udev->wusb == 1 && udev->authorized == 0) { if (udev->wusb == 1 && udev->authorized == 0) {
...@@ -2081,8 +2085,12 @@ static int usb_enumerate_device(struct usb_device *udev) ...@@ -2081,8 +2085,12 @@ static int usb_enumerate_device(struct usb_device *udev)
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
} }
err = usb_enumerate_device_otg(udev); err = usb_enumerate_device_otg(udev);
fail: if (err < 0)
return err; return err;
usb_detect_interface_quirks(udev);
return 0;
} }
static void set_usb_port_removable(struct usb_device *udev) static void set_usb_port_removable(struct usb_device *udev)
...@@ -2611,6 +2619,50 @@ static int check_port_resume_type(struct usb_device *udev, ...@@ -2611,6 +2619,50 @@ static int check_port_resume_type(struct usb_device *udev,
return status; return status;
} }
int usb_disable_ltm(struct usb_device *udev)
{
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
/* Check if the roothub and device supports LTM. */
if (!usb_device_supports_ltm(hcd->self.root_hub) ||
!usb_device_supports_ltm(udev))
return 0;
/* Clear Feature LTM Enable can only be sent if the device is
* configured.
*/
if (!udev->actconfig)
return 0;
return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
EXPORT_SYMBOL_GPL(usb_disable_ltm);
void usb_enable_ltm(struct usb_device *udev)
{
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
/* Check if the roothub and device supports LTM. */
if (!usb_device_supports_ltm(hcd->self.root_hub) ||
!usb_device_supports_ltm(udev))
return;
/* Set Feature LTM Enable can only be sent if the device is
* configured.
*/
if (!udev->actconfig)
return;
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
EXPORT_SYMBOL_GPL(usb_enable_ltm);
#ifdef CONFIG_USB_SUSPEND #ifdef CONFIG_USB_SUSPEND
/* /*
...@@ -2706,6 +2758,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) ...@@ -2706,6 +2758,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_enabled == 1) if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0); usb_set_usb2_hardware_lpm(udev, 0);
if (usb_disable_ltm(udev)) {
dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
__func__);
return -ENOMEM;
}
if (usb_unlocked_disable_lpm(udev)) { if (usb_unlocked_disable_lpm(udev)) {
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
__func__); __func__);
...@@ -2735,7 +2792,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) ...@@ -2735,7 +2792,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_capable == 1) if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 1); usb_set_usb2_hardware_lpm(udev, 1);
/* Try to enable USB3 LPM again */ /* Try to enable USB3 LTM and LPM again */
usb_enable_ltm(udev);
usb_unlocked_enable_lpm(udev); usb_unlocked_enable_lpm(udev);
/* System sleep transitions should never fail */ /* System sleep transitions should never fail */
...@@ -2936,7 +2994,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) ...@@ -2936,7 +2994,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_capable == 1) if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 1); usb_set_usb2_hardware_lpm(udev, 1);
/* Try to enable USB3 LPM */ /* Try to enable USB3 LTM and LPM */
usb_enable_ltm(udev);
usb_unlocked_enable_lpm(udev); usb_unlocked_enable_lpm(udev);
} }
...@@ -3489,6 +3548,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); ...@@ -3489,6 +3548,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
void usb_unlocked_enable_lpm(struct usb_device *udev) { } void usb_unlocked_enable_lpm(struct usb_device *udev) { }
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
int usb_disable_ltm(struct usb_device *udev)
{
return 0;
}
EXPORT_SYMBOL_GPL(usb_disable_ltm);
void usb_enable_ltm(struct usb_device *udev) { }
EXPORT_SYMBOL_GPL(usb_enable_ltm);
#endif #endif
...@@ -4038,6 +4106,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, ...@@ -4038,6 +4106,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
} }
} }
if (hcd->phy && !hdev->parent) {
if (portstatus & USB_PORT_STAT_CONNECTION)
usb_phy_notify_connect(hcd->phy, port1);
else
usb_phy_notify_disconnect(hcd->phy, port1);
}
/* Return now if debouncing failed or nothing is connected or /* Return now if debouncing failed or nothing is connected or
* the device was "removed". * the device was "removed".
*/ */
...@@ -4672,6 +4747,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4672,6 +4747,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
} }
parent_hub = hdev_to_hub(parent_hdev); parent_hub = hdev_to_hub(parent_hdev);
/* Disable LPM and LTM while we reset the device and reinstall the alt
* settings. Device-initiated LPM settings, and system exit latency
* settings are cleared when the device is reset, so we have to set
* them up again.
*/
ret = usb_unlocked_disable_lpm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
goto re_enumerate;
}
ret = usb_disable_ltm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LTM\n.",
__func__);
goto re_enumerate;
}
set_bit(port1, parent_hub->busy_bits); set_bit(port1, parent_hub->busy_bits);
for (i = 0; i < SET_CONFIG_TRIES; ++i) { for (i = 0; i < SET_CONFIG_TRIES; ++i) {
...@@ -4699,22 +4791,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4699,22 +4791,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
goto done; goto done;
mutex_lock(hcd->bandwidth_mutex); mutex_lock(hcd->bandwidth_mutex);
/* Disable LPM while we reset the device and reinstall the alt settings.
* Device-initiated LPM settings, and system exit latency settings are
* cleared when the device is reset, so we have to set them up again.
*/
ret = usb_disable_lpm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex);
goto done;
}
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) { if (ret < 0) {
dev_warn(&udev->dev, dev_warn(&udev->dev,
"Busted HC? Not enough HCD resources for " "Busted HC? Not enough HCD resources for "
"old configuration.\n"); "old configuration.\n");
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate; goto re_enumerate;
} }
...@@ -4726,7 +4807,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4726,7 +4807,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
dev_err(&udev->dev, dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n", "can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret); udev->actconfig->desc.bConfigurationValue, ret);
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate; goto re_enumerate;
} }
...@@ -4765,17 +4845,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4765,17 +4845,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
desc->bInterfaceNumber, desc->bInterfaceNumber,
desc->bAlternateSetting, desc->bAlternateSetting,
ret); ret);
usb_unlocked_enable_lpm(udev);
goto re_enumerate; goto re_enumerate;
} }
} }
/* Now that the alt settings are re-installed, enable LPM. */
usb_unlocked_enable_lpm(udev);
done: done:
/* Now that the alt settings are re-installed, enable LTM and LPM. */
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
return 0; return 0;
re_enumerate: re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1); hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV; return -ENODEV;
} }
......
...@@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) ...@@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
put_device(&dev->actconfig->interface[i]->dev); put_device(&dev->actconfig->interface[i]->dev);
dev->actconfig->interface[i] = NULL; dev->actconfig->interface[i] = NULL;
} }
usb_unlocked_disable_lpm(dev);
usb_disable_ltm(dev);
dev->actconfig = NULL; dev->actconfig = NULL;
if (dev->state == USB_STATE_CONFIGURED) if (dev->state == USB_STATE_CONFIGURED)
usb_set_device_state(dev, USB_STATE_ADDRESS); usb_set_device_state(dev, USB_STATE_ADDRESS);
...@@ -1559,7 +1561,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -1559,7 +1561,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
if (add_uevent_var(env, if (add_uevent_var(env,
"MODALIAS=usb:" "MODALIAS=usb:"
"v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X",
le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct), le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice), le16_to_cpu(usb_dev->descriptor.bcdDevice),
...@@ -1568,7 +1570,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -1568,7 +1570,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
usb_dev->descriptor.bDeviceProtocol, usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass, alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass, alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol)) alt->desc.bInterfaceProtocol,
alt->desc.bInterfaceNumber))
return -ENOMEM; return -ENOMEM;
return 0; return 0;
...@@ -1791,13 +1794,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1791,13 +1794,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
* installed, so that the xHCI driver can recalculate the U1/U2 * installed, so that the xHCI driver can recalculate the U1/U2
* timeouts. * timeouts.
*/ */
if (usb_disable_lpm(dev)) { if (dev->actconfig && usb_disable_lpm(dev)) {
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM; return -ENOMEM;
} }
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) { if (ret < 0) {
if (dev->actconfig)
usb_enable_lpm(dev); usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev); usb_autosuspend_device(dev);
...@@ -1818,7 +1822,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1818,7 +1822,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (!cp) { if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS); usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
usb_enable_lpm(dev); /* Leave LPM disabled while the device is unconfigured. */
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev); usb_autosuspend_device(dev);
goto free_interfaces; goto free_interfaces;
...@@ -1876,6 +1880,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1876,6 +1880,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
/* Now that the interfaces are installed, re-enable LPM. */ /* Now that the interfaces are installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev); usb_unlocked_enable_lpm(dev);
/* Enable LTM if it was turned off by usb_disable_device. */
usb_enable_ltm(dev);
/* Now that all the interfaces are set up, register them /* Now that all the interfaces are set up, register them
* to trigger binding of drivers to interfaces. probe() * to trigger binding of drivers to interfaces. probe()
......
...@@ -15,17 +15,22 @@ ...@@ -15,17 +15,22 @@
#include <linux/usb/quirks.h> #include <linux/usb/quirks.h>
#include "usb.h" #include "usb.h"
/* List of quirky USB devices. Please keep this list ordered by: /* Lists of quirky USB devices, split in device quirks and interface quirks.
* Device quirks are applied at the very beginning of the enumeration process,
* right after reading the device descriptor. They can thus only match on device
* information.
*
* Interface quirks are applied after reading all the configuration descriptors.
* They can match on both device and interface information.
*
* Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as
* interface quirks, as they only influence the enumeration process which is run
* before processing the interface quirks.
*
* Please keep the lists ordered by:
* 1) Vendor ID * 1) Vendor ID
* 2) Product ID * 2) Product ID
* 3) Class ID * 3) Class ID
*
* as we want specific devices to be overridden first, and only after that, any
* class specific quirks.
*
* Right now the logic aborts if it finds a valid device in the table, we might
* want to change that in the future if it turns out that a whole class of
* devices is broken...
*/ */
static const struct usb_device_id usb_quirk_list[] = { static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */ /* CBM - Flash disk */
...@@ -38,53 +43,23 @@ static const struct usb_device_id usb_quirk_list[] = { ...@@ -38,53 +43,23 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */ /* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C200 */ /* Logitech Quickcam Fusion */
{ USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C250 */
{ USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C300 */
{ USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam B/C500 */
{ USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C600 */
{ USB_DEVICE(0x046d, 0x0808), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam Pro 9000 */
{ USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C905 */ /* Logitech Quickcam Orbit MP */
{ USB_DEVICE(0x046d, 0x080a), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C210 */ /* Logitech Quickcam Pro for Notebook */
{ USB_DEVICE(0x046d, 0x0819), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C260 */ /* Logitech Quickcam Pro 5000 */
{ USB_DEVICE(0x046d, 0x081a), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C310 */ /* Logitech Quickcam OEM Dell Notebook */
{ USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C910 */ /* Logitech Quickcam OEM Cisco VT Camera II */
{ USB_DEVICE(0x046d, 0x0821), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C160 */
{ USB_DEVICE(0x046d, 0x0824), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Webcam C270 */
{ USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Quickcam Pro 9000 */
{ USB_DEVICE(0x046d, 0x0990), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Quickcam E3500 */
{ USB_DEVICE(0x046d, 0x09a4), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Quickcam Vision Pro */
{ USB_DEVICE(0x046d, 0x09a6), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Harmony 700-series */ /* Logitech Harmony 700-series */
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
...@@ -156,16 +131,57 @@ static const struct usb_device_id usb_quirk_list[] = { ...@@ -156,16 +131,57 @@ static const struct usb_device_id usb_quirk_list[] = {
{ } /* terminating entry must be last */ { } /* terminating entry must be last */
}; };
static const struct usb_device_id *find_id(struct usb_device *udev) static const struct usb_device_id usb_interface_quirk_list[] = {
/* Logitech UVC Cameras */
{ USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0),
.driver_info = USB_QUIRK_RESET_RESUME },
{ } /* terminating entry must be last */
};
static bool usb_match_any_interface(struct usb_device *udev,
const struct usb_device_id *id)
{ {
const struct usb_device_id *id = usb_quirk_list; unsigned int i;
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
id->driver_info; id++) { struct usb_host_config *cfg = &udev->config[i];
if (usb_match_device(udev, id)) unsigned int j;
return id;
for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
struct usb_interface_cache *cache;
struct usb_host_interface *intf;
cache = cfg->intf_cache[j];
if (cache->num_altsetting == 0)
continue;
intf = &cache->altsetting[0];
if (usb_match_one_id_intf(udev, intf, id))
return true;
} }
return NULL; }
return false;
}
static u32 __usb_detect_quirks(struct usb_device *udev,
const struct usb_device_id *id)
{
u32 quirks = 0;
for (; id->match_flags; id++) {
if (!usb_match_device(udev, id))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
!usb_match_any_interface(udev, id))
continue;
quirks |= (u32)(id->driver_info);
}
return quirks;
} }
/* /*
...@@ -173,11 +189,7 @@ static const struct usb_device_id *find_id(struct usb_device *udev) ...@@ -173,11 +189,7 @@ static const struct usb_device_id *find_id(struct usb_device *udev)
*/ */
void usb_detect_quirks(struct usb_device *udev) void usb_detect_quirks(struct usb_device *udev)
{ {
const struct usb_device_id *id = usb_quirk_list; udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
id = find_id(udev);
if (id)
udev->quirks = (u32)(id->driver_info);
if (udev->quirks) if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n", dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
udev->quirks); udev->quirks);
...@@ -197,3 +209,16 @@ void usb_detect_quirks(struct usb_device *udev) ...@@ -197,3 +209,16 @@ void usb_detect_quirks(struct usb_device *udev)
udev->persist_enabled = 1; udev->persist_enabled = 1;
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
} }
void usb_detect_interface_quirks(struct usb_device *udev)
{
u32 quirks;
quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
if (quirks == 0)
return;
dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
quirks);
udev->quirks |= quirks;
}
...@@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf)
} }
static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
static ssize_t
show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf)
{
if (usb_device_supports_ltm(to_usb_device(dev)))
return sprintf(buf, "%s\n", "yes");
return sprintf(buf, "%s\n", "no");
}
static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static ssize_t static ssize_t
...@@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = { ...@@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_authorized.attr, &dev_attr_authorized.attr,
&dev_attr_remove.attr, &dev_attr_remove.attr,
&dev_attr_removable.attr, &dev_attr_removable.attr,
&dev_attr_ltm_capable.attr,
NULL, NULL,
}; };
static struct attribute_group dev_attr_grp = { static struct attribute_group dev_attr_grp = {
...@@ -840,7 +850,7 @@ static ssize_t show_modalias(struct device *dev, ...@@ -840,7 +850,7 @@ static ssize_t show_modalias(struct device *dev,
alt = intf->cur_altsetting; alt = intf->cur_altsetting;
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
"ic%02Xisc%02Xip%02X\n", "ic%02Xisc%02Xip%02Xin%02X\n",
le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct), le16_to_cpu(udev->descriptor.idProduct),
le16_to_cpu(udev->descriptor.bcdDevice), le16_to_cpu(udev->descriptor.bcdDevice),
...@@ -849,7 +859,8 @@ static ssize_t show_modalias(struct device *dev, ...@@ -849,7 +859,8 @@ static ssize_t show_modalias(struct device *dev,
udev->descriptor.bDeviceProtocol, udev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass, alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass, alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol); alt->desc.bInterfaceProtocol,
alt->desc.bInterfaceNumber);
} }
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
......
...@@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, ...@@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.dma_mask = bus->controller->dma_mask; dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller)); set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED; dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
atomic_set(&dev->urbnum, 0); atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list); INIT_LIST_HEAD(&dev->ep0.urb_list);
......
#include <linux/pm.h> #include <linux/pm.h>
struct dev_state;
/* Functions local to drivers/usb/core/ */ /* Functions local to drivers/usb/core/ */
extern int usb_create_sysfs_dev_files(struct usb_device *dev); extern int usb_create_sysfs_dev_files(struct usb_device *dev);
...@@ -24,6 +26,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0); ...@@ -24,6 +26,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *); extern int usb_deauthorize_device(struct usb_device *);
extern int usb_authorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *);
extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_quirks(struct usb_device *udev);
extern void usb_detect_interface_quirks(struct usb_device *udev);
extern int usb_remove_device(struct usb_device *udev); extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev, extern int usb_get_device_descriptor(struct usb_device *dev,
...@@ -35,16 +38,20 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration); ...@@ -35,16 +38,20 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev); extern int usb_choose_configuration(struct usb_device *udev);
extern void usb_kick_khubd(struct usb_device *dev); extern void usb_kick_khubd(struct usb_device *dev);
extern int usb_match_one_id_intf(struct usb_device *dev,
struct usb_host_interface *intf,
const struct usb_device_id *id);
extern int usb_match_device(struct usb_device *dev, extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id); const struct usb_device_id *id);
extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf); extern void usb_rebind_intf(struct usb_interface *intf);
extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
void *owner); struct dev_state *owner);
extern int usb_hub_release_port(struct usb_device *hdev, unsigned port, extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
void *owner); struct dev_state *owner);
extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner); extern void usb_hub_release_all_ports(struct usb_device *hdev,
struct dev_state *owner);
extern bool usb_device_is_owned(struct usb_device *udev); extern bool usb_device_is_owned(struct usb_device *udev);
extern int usb_hub_init(void); extern int usb_hub_init(void);
......
...@@ -148,6 +148,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) ...@@ -148,6 +148,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
/* After PHYs are stable we can take Core out of reset state */ /* After PHYs are stable we can take Core out of reset state */
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET; reg &= ~DWC3_GCTL_CORESOFTRESET;
...@@ -255,7 +257,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) ...@@ -255,7 +257,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
* *
* Returns 0 on success otherwise negative errno. * Returns 0 on success otherwise negative errno.
*/ */
static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) static int dwc3_event_buffers_setup(struct dwc3 *dwc)
{ {
struct dwc3_event_buffer *evt; struct dwc3_event_buffer *evt;
int n; int n;
...@@ -266,6 +268,8 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) ...@@ -266,6 +268,8 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
evt->buf, (unsigned long long) evt->dma, evt->buf, (unsigned long long) evt->dma,
evt->length); evt->length);
evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
lower_32_bits(evt->dma)); lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
...@@ -285,6 +289,9 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) ...@@ -285,6 +289,9 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
for (n = 0; n < dwc->num_event_buffers; n++) { for (n = 0; n < dwc->num_event_buffers; n++) {
evt = dwc->ev_buffs[n]; evt = dwc->ev_buffs[n];
evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
...@@ -328,8 +335,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) ...@@ -328,8 +335,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
} }
dwc->revision = reg; dwc->revision = reg;
dwc3_core_soft_reset(dwc);
/* issue device SoftReset too */ /* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500); timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
...@@ -347,6 +352,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) ...@@ -347,6 +352,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
cpu_relax(); cpu_relax();
} while (true); } while (true);
dwc3_core_soft_reset(dwc);
dwc3_cache_hwparams(dwc); dwc3_cache_hwparams(dwc);
reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg = dwc3_readl(dwc->regs, DWC3_GCTL);
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 #define DWC3_DEVICE_EVENT_CONNECT_DONE 2
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
#define DWC3_DEVICE_EVENT_WAKEUP 4 #define DWC3_DEVICE_EVENT_WAKEUP 4
#define DWC3_DEVICE_EVENT_HIBER_REQ 5
#define DWC3_DEVICE_EVENT_EOPF 6 #define DWC3_DEVICE_EVENT_EOPF 6
#define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_SOF 7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
...@@ -175,6 +176,7 @@ ...@@ -175,6 +176,7 @@
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3)
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
/* Global USB2 PHY Configuration Register */ /* Global USB2 PHY Configuration Register */
...@@ -193,6 +195,13 @@ ...@@ -193,6 +195,13 @@
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
/* Global HWPARAMS4 Register */
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Device Configuration Register */ /* Device Configuration Register */
#define DWC3_DCFG_LPM_CAP (1 << 22) #define DWC3_DCFG_LPM_CAP (1 << 22)
...@@ -206,25 +215,33 @@ ...@@ -206,25 +215,33 @@
#define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_FULLSPEED1 (3 << 0) #define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_LPM_CAP (1 << 22)
/* Device Control Register */ /* Device Control Register */
#define DWC3_DCTL_RUN_STOP (1 << 31) #define DWC3_DCTL_RUN_STOP (1 << 31)
#define DWC3_DCTL_CSFTRST (1 << 30) #define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_DCTL_LSFTRST (1 << 29) #define DWC3_DCTL_LSFTRST (1 << 29)
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) #define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) #define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
#define DWC3_DCTL_APPL1RES (1 << 23) #define DWC3_DCTL_APPL1RES (1 << 23)
/* These apply for core versions 1.87a and earlier */
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) #define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
#define DWC3_DCTL_TRGTULST(n) ((n) << 17) #define DWC3_DCTL_TRGTULST(n) ((n) << 17)
#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) #define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) #define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) #define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) #define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
#define DWC3_DCTL_CRS (1 << 17)
#define DWC3_DCTL_CSS (1 << 16)
#define DWC3_DCTL_INITU2ENA (1 << 12) #define DWC3_DCTL_INITU2ENA (1 << 12)
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) #define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
#define DWC3_DCTL_INITU1ENA (1 << 10) #define DWC3_DCTL_INITU1ENA (1 << 10)
...@@ -249,6 +266,7 @@ ...@@ -249,6 +266,7 @@
#define DWC3_DEVTEN_ERRTICERREN (1 << 9) #define DWC3_DEVTEN_ERRTICERREN (1 << 9)
#define DWC3_DEVTEN_SOFEN (1 << 7) #define DWC3_DEVTEN_SOFEN (1 << 7)
#define DWC3_DEVTEN_EOPFEN (1 << 6) #define DWC3_DEVTEN_EOPFEN (1 << 6)
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) #define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) #define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) #define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
...@@ -256,7 +274,15 @@ ...@@ -256,7 +274,15 @@
#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) #define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
/* Device Status Register */ /* Device Status Register */
#define DWC3_DSTS_DCNRD (1 << 29)
/* This applies for core versions 1.87a and earlier */
#define DWC3_DSTS_PWRUPREQ (1 << 24) #define DWC3_DSTS_PWRUPREQ (1 << 24)
/* These apply for core versions 1.94a and later */
#define DWC3_DSTS_RSS (1 << 25)
#define DWC3_DSTS_SSS (1 << 24)
#define DWC3_DSTS_COREIDLE (1 << 23) #define DWC3_DSTS_COREIDLE (1 << 23)
#define DWC3_DSTS_DEVCTRLHLT (1 << 22) #define DWC3_DSTS_DEVCTRLHLT (1 << 22)
...@@ -265,7 +291,7 @@ ...@@ -265,7 +291,7 @@
#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) #define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) #define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) #define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
#define DWC3_DSTS_CONNECTSPD (7 << 0) #define DWC3_DSTS_CONNECTSPD (7 << 0)
...@@ -280,6 +306,11 @@ ...@@ -280,6 +306,11 @@
#define DWC3_DGCMD_SET_LMP 0x01 #define DWC3_DGCMD_SET_LMP 0x01
#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 #define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
#define DWC3_DGCMD_XMIT_FUNCTION 0x03 #define DWC3_DGCMD_XMIT_FUNCTION 0x03
/* These apply for core versions 1.94a and later */
#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
...@@ -287,6 +318,15 @@ ...@@ -287,6 +318,15 @@
#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1) #define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
#define DWC3_DGCMD_CMDACT (1 << 10) #define DWC3_DGCMD_CMDACT (1 << 10)
#define DWC3_DGCMD_CMDIOC (1 << 8)
/* Device Generic Command Parameter Register */
#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
/* Device Endpoint Command Register */ /* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16 #define DWC3_DEPCMD_PARAM_SHIFT 16
...@@ -303,7 +343,10 @@ ...@@ -303,7 +343,10 @@
#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) #define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) #define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
#define DWC3_DEPCMD_SETSTALL (0x04 << 0) #define DWC3_DEPCMD_SETSTALL (0x04 << 0)
/* This applies for core versions 1.90a and earlier */
#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) #define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
/* This applies for core versions 1.94a and later */
#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) #define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) #define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
...@@ -361,7 +404,8 @@ struct dwc3_event_buffer { ...@@ -361,7 +404,8 @@ struct dwc3_event_buffer {
* @current_trb: index of current used trb * @current_trb: index of current used trb
* @number: endpoint number (1 - 15) * @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @res_trans_idx: Resource transfer index * @resource_index: Resource transfer index
* @current_uf: Current uf received through last event parameter
* @interval: the intervall on which the ISOC transfer is started * @interval: the intervall on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk * @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX * @direction: true for TX, false for RX
...@@ -385,6 +429,7 @@ struct dwc3_ep { ...@@ -385,6 +429,7 @@ struct dwc3_ep {
#define DWC3_EP_WEDGE (1 << 2) #define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4) #define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5) #define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31) #define DWC3_EP0_DIR_IN (1 << 31)
...@@ -393,7 +438,8 @@ struct dwc3_ep { ...@@ -393,7 +438,8 @@ struct dwc3_ep {
u8 number; u8 number;
u8 type; u8 type;
u8 res_trans_idx; u8 resource_index;
u16 current_uf;
u32 interval; u32 interval;
char name[20]; char name[20];
...@@ -437,6 +483,8 @@ enum dwc3_link_state { ...@@ -437,6 +483,8 @@ enum dwc3_link_state {
DWC3_LINK_STATE_HRESET = 0x09, DWC3_LINK_STATE_HRESET = 0x09,
DWC3_LINK_STATE_CMPLY = 0x0a, DWC3_LINK_STATE_CMPLY = 0x0a,
DWC3_LINK_STATE_LPBK = 0x0b, DWC3_LINK_STATE_LPBK = 0x0b,
DWC3_LINK_STATE_RESET = 0x0e,
DWC3_LINK_STATE_RESUME = 0x0f,
DWC3_LINK_STATE_MASK = 0x0f, DWC3_LINK_STATE_MASK = 0x0f,
}; };
...@@ -450,11 +498,12 @@ enum dwc3_device_state { ...@@ -450,11 +498,12 @@ enum dwc3_device_state {
#define DWC3_TRB_SIZE_MASK (0x00ffffff) #define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24) #define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28)) #define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28)
#define DWC3_TRBSTS_OK 0 #define DWC3_TRBSTS_OK 0
#define DWC3_TRBSTS_MISSED_ISOC 1 #define DWC3_TRBSTS_MISSED_ISOC 1
#define DWC3_TRBSTS_SETUP_PENDING 2 #define DWC3_TRBSTS_SETUP_PENDING 2
#define DWC3_TRB_STS_XFER_IN_PROG 4
/* TRB Control */ /* TRB Control */
#define DWC3_TRB_CTRL_HWO (1 << 0) #define DWC3_TRB_CTRL_HWO (1 << 0)
...@@ -543,6 +592,14 @@ struct dwc3_request { ...@@ -543,6 +592,14 @@ struct dwc3_request {
unsigned queued:1; unsigned queued:1;
}; };
/*
* struct dwc3_scratchpad_array - hibernation scratchpad array
* (format defined by hw)
*/
struct dwc3_scratchpad_array {
__le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
};
/** /**
* struct dwc3 - representation of our controller * struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0 * @ctrl_req: usb control request which is used for ep0
...@@ -624,8 +681,10 @@ struct dwc3 { ...@@ -624,8 +681,10 @@ struct dwc3 {
#define DWC3_REVISION_180A 0x5533180a #define DWC3_REVISION_180A 0x5533180a
#define DWC3_REVISION_183A 0x5533183a #define DWC3_REVISION_183A 0x5533183a
#define DWC3_REVISION_185A 0x5533185a #define DWC3_REVISION_185A 0x5533185a
#define DWC3_REVISION_187A 0x5533187a
#define DWC3_REVISION_188A 0x5533188a #define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a #define DWC3_REVISION_190A 0x5533190a
#define DWC3_REVISION_194A 0x5533194a
#define DWC3_REVISION_200A 0x5533200a #define DWC3_REVISION_200A 0x5533200a
#define DWC3_REVISION_202A 0x5533202a #define DWC3_REVISION_202A 0x5533202a
#define DWC3_REVISION_210A 0x5533210a #define DWC3_REVISION_210A 0x5533210a
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h> #include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/clk.h> #include <linux/clk.h>
#include "core.h" #include "core.h"
......
...@@ -54,7 +54,9 @@ ...@@ -54,7 +54,9 @@
#include "gadget.h" #include "gadget.h"
#include "io.h" #include "io.h"
static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{ {
...@@ -111,7 +113,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ...@@ -111,7 +113,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
} }
dep->flags |= DWC3_EP_BUSY; dep->flags |= DWC3_EP_BUSY;
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number); dep->number);
dwc->ep0_next_event = DWC3_EP0_COMPLETE; dwc->ep0_next_event = DWC3_EP0_COMPLETE;
...@@ -150,16 +152,15 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, ...@@ -150,16 +152,15 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
return 0; return 0;
} }
ret = dwc3_ep0_start_trans(dwc, direction, __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
req->request.dma, req->request.length,
DWC3_TRBCTL_CONTROL_DATA);
dep->flags &= ~(DWC3_EP_PENDING_REQUEST | dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
DWC3_EP0_DIR_IN); DWC3_EP0_DIR_IN);
} else if (dwc->delayed_status) { } else if (dwc->delayed_status) {
dwc->delayed_status = false; dwc->delayed_status = false;
if (dwc->ep0state == EP0_STATUS_PHASE) if (dwc->ep0state == EP0_STATUS_PHASE)
dwc3_ep0_do_control_status(dwc, 1); __dwc3_ep0_do_control_status(dwc, dwc->eps[1]);
else else
dev_dbg(dwc->dev, "too early for delayed status\n"); dev_dbg(dwc->dev, "too early for delayed status\n");
} }
...@@ -224,6 +225,16 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) ...@@ -224,6 +225,16 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
dwc3_ep0_out_start(dwc); dwc3_ep0_out_start(dwc);
} }
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
dwc3_ep0_stall_and_restart(dwc);
return 0;
}
void dwc3_ep0_out_start(struct dwc3 *dwc) void dwc3_ep0_out_start(struct dwc3 *dwc)
{ {
int ret; int ret;
...@@ -463,6 +474,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ...@@ -463,6 +474,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{ {
u32 cfg; u32 cfg;
int ret; int ret;
u32 reg;
dwc->start_config_issued = false; dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue); cfg = le16_to_cpu(ctrl->wValue);
...@@ -477,6 +489,14 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ...@@ -477,6 +489,14 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
/* if the cfg matches and the cfg is non zero */ /* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
dwc->dev_state = DWC3_CONFIGURED_STATE; dwc->dev_state = DWC3_CONFIGURED_STATE;
/*
* Enable transition to U1/U2 state when
* nothing is pending from application.
*/
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc->resize_fifos = true; dwc->resize_fifos = true;
dev_dbg(dwc->dev, "resize fifos flag SET\n"); dev_dbg(dwc->dev, "resize fifos flag SET\n");
} }
...@@ -514,8 +534,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) ...@@ -514,8 +534,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
dwc->u1sel = timing.u1sel; dwc->u1sel = timing.u1sel;
dwc->u1pel = timing.u1pel; dwc->u1pel = timing.u1pel;
dwc->u2sel = timing.u2sel; dwc->u2sel = le16_to_cpu(timing.u2sel);
dwc->u2pel = timing.u2pel; dwc->u2pel = le16_to_cpu(timing.u2pel);
reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU2ENA) if (reg & DWC3_DCTL_INITU2ENA)
...@@ -640,11 +660,11 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, ...@@ -640,11 +660,11 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event) const struct dwc3_event_depevt *event)
{ {
struct usb_ctrlrequest *ctrl = dwc->ctrl_req; struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
int ret; int ret = -EINVAL;
u32 len; u32 len;
if (!dwc->gadget_driver) if (!dwc->gadget_driver)
goto err; goto out;
len = le16_to_cpu(ctrl->wLength); len = le16_to_cpu(ctrl->wLength);
if (!len) { if (!len) {
...@@ -665,10 +685,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, ...@@ -665,10 +685,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (ret == USB_GADGET_DELAYED_STATUS) if (ret == USB_GADGET_DELAYED_STATUS)
dwc->delayed_status = true; dwc->delayed_status = true;
if (ret >= 0) out:
return; if (ret < 0)
err:
dwc3_ep0_stall_and_restart(dwc); dwc3_ep0_stall_and_restart(dwc);
} }
...@@ -723,7 +741,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ...@@ -723,7 +741,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
} }
} }
static void dwc3_ep0_complete_req(struct dwc3 *dwc, static void dwc3_ep0_complete_status(struct dwc3 *dwc,
const struct dwc3_event_depevt *event) const struct dwc3_event_depevt *event)
{ {
struct dwc3_request *r; struct dwc3_request *r;
...@@ -745,6 +763,7 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc, ...@@ -745,6 +763,7 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc,
dev_dbg(dwc->dev, "Invalid Test #%d\n", dev_dbg(dwc->dev, "Invalid Test #%d\n",
dwc->test_mode_nr); dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc); dwc3_ep0_stall_and_restart(dwc);
return;
} }
} }
...@@ -758,7 +777,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, ...@@ -758,7 +777,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
dep->flags &= ~DWC3_EP_BUSY; dep->flags &= ~DWC3_EP_BUSY;
dep->res_trans_idx = 0; dep->resource_index = 0;
dwc->setup_packet_pending = false; dwc->setup_packet_pending = false;
switch (dwc->ep0state) { switch (dwc->ep0state) {
...@@ -774,7 +793,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, ...@@ -774,7 +793,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
case EP0_STATUS_PHASE: case EP0_STATUS_PHASE:
dev_vdbg(dwc->dev, "Status Phase\n"); dev_vdbg(dwc->dev, "Status Phase\n");
dwc3_ep0_complete_req(dwc, event); dwc3_ep0_complete_status(dwc, event);
break; break;
default: default:
WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
...@@ -787,68 +806,81 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, ...@@ -787,68 +806,81 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
dwc3_ep0_out_start(dwc); dwc3_ep0_out_start(dwc);
} }
static void dwc3_ep0_do_control_data(struct dwc3 *dwc, static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event) struct dwc3_ep *dep, struct dwc3_request *req)
{ {
struct dwc3_ep *dep;
struct dwc3_request *req;
int ret; int ret;
dep = dwc->eps[0]; req->direction = !!dep->number;
if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
dep->flags |= DWC3_EP_PENDING_REQUEST;
if (event->endpoint_number)
dep->flags |= DWC3_EP0_DIR_IN;
return;
}
req = next_request(&dep->request_list);
req->direction = !!event->endpoint_number;
if (req->request.length == 0) { if (req->request.length == 0) {
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ctrl_req_addr, 0, dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA); DWC3_TRBCTL_CONTROL_DATA);
} else if ((req->request.length % dep->endpoint.maxpacket) } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (event->endpoint_number == 0)) { && (dep->number == 0)) {
u32 transfer_size;
ret = usb_gadget_map_request(&dwc->gadget, &req->request, ret = usb_gadget_map_request(&dwc->gadget, &req->request,
event->endpoint_number); dep->number);
if (ret) { if (ret) {
dev_dbg(dwc->dev, "failed to map request\n"); dev_dbg(dwc->dev, "failed to map request\n");
return; return;
} }
WARN_ON(req->request.length > dep->endpoint.maxpacket); WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
transfer_size = roundup(req->request.length,
(u32) dep->endpoint.maxpacket);
dwc->ep0_bounced = true; dwc->ep0_bounced = true;
/* /*
* REVISIT in case request length is bigger than EP0 * REVISIT in case request length is bigger than
* wMaxPacketSize, we will need two chained TRBs to handle * DWC3_EP0_BOUNCE_SIZE we will need two chained
* the transfer. * TRBs to handle the transfer.
*/ */
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ep0_bounce_addr, dep->endpoint.maxpacket, dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA); DWC3_TRBCTL_CONTROL_DATA);
} else { } else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request, ret = usb_gadget_map_request(&dwc->gadget, &req->request,
event->endpoint_number); dep->number);
if (ret) { if (ret) {
dev_dbg(dwc->dev, "failed to map request\n"); dev_dbg(dwc->dev, "failed to map request\n");
return; return;
} }
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
req->request.dma, req->request.length, req->request.length, DWC3_TRBCTL_CONTROL_DATA);
DWC3_TRBCTL_CONTROL_DATA);
} }
WARN_ON(ret < 0); WARN_ON(ret < 0);
} }
static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_ep *dep;
struct dwc3_request *req;
dep = dwc->eps[0];
if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
dep->flags |= DWC3_EP_PENDING_REQUEST;
if (event->endpoint_number)
dep->flags |= DWC3_EP0_DIR_IN;
return;
}
req = next_request(&dep->request_list);
dep = dwc->eps[event->endpoint_number];
__dwc3_ep0_do_control_data(dwc, dep, req);
}
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
{ {
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
...@@ -861,10 +893,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) ...@@ -861,10 +893,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
dwc->ctrl_req_addr, 0, type); dwc->ctrl_req_addr, 0, type);
} }
static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{ {
struct dwc3_ep *dep = dwc->eps[epnum];
if (dwc->resize_fifos) { if (dwc->resize_fifos) {
dev_dbg(dwc->dev, "starting to resize fifos\n"); dev_dbg(dwc->dev, "starting to resize fifos\n");
dwc3_gadget_resize_tx_fifos(dwc); dwc3_gadget_resize_tx_fifos(dwc);
...@@ -874,13 +904,21 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) ...@@ -874,13 +904,21 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
WARN_ON(dwc3_ep0_start_control_status(dep)); WARN_ON(dwc3_ep0_start_control_status(dep));
} }
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
__dwc3_ep0_do_control_status(dwc, dep);
}
static void dwc3_ep0_xfernotready(struct dwc3 *dwc, static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event) const struct dwc3_event_depevt *event)
{ {
dwc->setup_packet_pending = true; dwc->setup_packet_pending = true;
/* /*
* This part is very tricky: If we has just handled * This part is very tricky: If we have just handled
* XferNotReady(Setup) and we're now expecting a * XferNotReady(Setup) and we're now expecting a
* XferComplete but, instead, we receive another * XferComplete but, instead, we receive another
* XferNotReady(Setup), we should STALL and restart * XferNotReady(Setup), we should STALL and restart
...@@ -974,7 +1012,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, ...@@ -974,7 +1012,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
return; return;
} }
dwc3_ep0_do_control_status(dwc, event->endpoint_number); dwc3_ep0_do_control_status(dwc, event);
} }
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -334,7 +334,7 @@ static int dbgp_control_msg(unsigned devnum, int requesttype, ...@@ -334,7 +334,7 @@ static int dbgp_control_msg(unsigned devnum, int requesttype,
int ret; int ret;
read = (requesttype & USB_DIR_IN) != 0; read = (requesttype & USB_DIR_IN) != 0;
if (size > (read ? DBGP_MAX_PACKET:0)) if (size > (read ? DBGP_MAX_PACKET : 0))
return -1; return -1;
/* Compute the control message */ /* Compute the control message */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment