USB patches for 3.6-rc1
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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAlAQhpcACgkQMUfUDdst+yms4wCgpMbfFYTZ8lHpDnXHYFv6qw8j FIsAmwT4mbT7m8C0k+ELV/5T2e0eYJEQ =QetG -----END PGP SIGNATURE----- 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. ...
This commit is contained in:
commit
9fc377799b
|
@ -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.
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
* 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>;
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
* 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>;
|
||||||
|
};
|
|
@ -0,0 +1,226 @@
|
||||||
|
* 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[] = {
|
||||||
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)
|
||||||
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)
|
||||||
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 @@ free_irq:
|
||||||
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)
|
||||||
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 @@ fail2:
|
||||||
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)
|
||||||
|
|
||||||
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,12 +321,12 @@ 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)) {
|
||||||
pdata->is_usb_online = otg_is_usb_online;
|
if (!pdata->is_usb_online)
|
||||||
}
|
pdata->is_usb_online = otg_is_usb_online;
|
||||||
if (transceiver && !pdata->is_ac_online) {
|
if (!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)
|
||||||
}
|
}
|
||||||
|
|
||||||
#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 @@ usb_supply_failed:
|
||||||
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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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 @@
|
||||||
* @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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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)
|
||||||
*
|
*
|
||||||
* 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)
|
||||||
* @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,
|
||||||
*
|
*
|
||||||
* 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,
|
||||||
*
|
*
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* 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 @@
|
||||||
|
|
||||||
#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)
|
||||||
* 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 = {
|
||||||
.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;
|
||||||
|
}
|
||||||
|
|
||||||
put_platform:
|
static int __devexit ci13xxx_msm_remove(struct platform_device *pdev)
|
||||||
platform_device_put(plat_ci);
|
{
|
||||||
|
struct platform_device *plat_ci = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
return ret;
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
ci13xxx_remove_device(plat_ci);
|
||||||
|
|
||||||
|
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_platform_driver(ci13xxx_msm_driver);
|
||||||
|
|
||||||
MODULE_ALIAS("platform:msm_hsusb");
|
MODULE_ALIAS("platform:msm_hsusb");
|
||||||
|
|
||||||
static int __init ci13xxx_msm_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&ci13xxx_msm_driver);
|
|
||||||
}
|
|
||||||
module_init(ci13xxx_msm_init);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -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 = {
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -68,15 +68,15 @@ void dbg_interrupt(u32 intmask)
|
||||||
*
|
*
|
||||||
* This function returns number of registers read
|
* This function returns number of registers read
|
||||||
*/
|
*/
|
||||||
static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
|
static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
if (size > udc->hw_bank.size)
|
if (size > ci->hw_bank.size)
|
||||||
size = udc->hw_bank.size;
|
size = ci->hw_bank.size;
|
||||||
|
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++)
|
||||||
buf[i] = hw_read(udc, i * sizeof(u32), ~0);
|
buf[i] = hw_read(ci, i * sizeof(u32), ~0);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
@ -88,18 +88,18 @@ static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
|
||||||
*
|
*
|
||||||
* This function returns an error code
|
* This function returns an error code
|
||||||
*/
|
*/
|
||||||
static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
|
static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data)
|
||||||
{
|
{
|
||||||
/* align */
|
/* align */
|
||||||
addr /= sizeof(u32);
|
addr /= sizeof(u32);
|
||||||
|
|
||||||
if (addr >= udc->hw_bank.size)
|
if (addr >= ci->hw_bank.size)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* align */
|
/* align */
|
||||||
addr *= sizeof(u32);
|
addr *= sizeof(u32);
|
||||||
|
|
||||||
hw_write(udc, addr, ~0, data);
|
hw_write(ci, addr, ~0, data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +110,13 @@ static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
|
||||||
*
|
*
|
||||||
* This function returns an error code
|
* This function returns an error code
|
||||||
*/
|
*/
|
||||||
static int hw_intr_clear(struct ci13xxx *udc, int n)
|
static int hw_intr_clear(struct ci13xxx *ci, int n)
|
||||||
{
|
{
|
||||||
if (n >= REG_BITS)
|
if (n >= REG_BITS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hw_write(udc, OP_USBINTR, BIT(n), 0);
|
hw_write(ci, OP_USBINTR, BIT(n), 0);
|
||||||
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
|
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,15 +127,15 @@ static int hw_intr_clear(struct ci13xxx *udc, int n)
|
||||||
*
|
*
|
||||||
* This function returns an error code
|
* This function returns an error code
|
||||||
*/
|
*/
|
||||||
static int hw_intr_force(struct ci13xxx *udc, int n)
|
static int hw_intr_force(struct ci13xxx *ci, int n)
|
||||||
{
|
{
|
||||||
if (n >= REG_BITS)
|
if (n >= REG_BITS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
|
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
|
||||||
hw_write(udc, OP_USBINTR, BIT(n), BIT(n));
|
hw_write(ci, OP_USBINTR, BIT(n), BIT(n));
|
||||||
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
|
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
|
||||||
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0);
|
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,12 +147,12 @@ static int hw_intr_force(struct ci13xxx *udc, int n)
|
||||||
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
struct usb_gadget *gadget = &udc->gadget;
|
struct usb_gadget *gadget = &ci->gadget;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +188,8 @@ static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
|
||||||
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
struct usb_gadget_driver *driver = udc->driver;
|
struct usb_gadget_driver *driver = ci->driver;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
|
@ -412,22 +412,22 @@ static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
|
||||||
static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 intr;
|
u32 intr;
|
||||||
unsigned i, j, n = 0;
|
unsigned i, j, n = 0;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
|
|
||||||
/*n += scnprintf(buf + n, PAGE_SIZE - n,
|
/*n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
"status = %08x\n", hw_read_intr_status(udc));
|
"status = %08x\n", hw_read_intr_status(ci));
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
"enable = %08x\n", hw_read_intr_enable(udc));*/
|
"enable = %08x\n", hw_read_intr_enable(ci));*/
|
||||||
|
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
|
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
|
||||||
isr_statistics.test);
|
isr_statistics.test);
|
||||||
|
@ -471,7 +471,7 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
|
n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -485,31 +485,31 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
||||||
static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
|
static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned en, bit;
|
unsigned en, bit;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "EINVAL\n");
|
dev_err(ci->dev, "EINVAL\n");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
|
if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
|
||||||
dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n");
|
dev_err(ci->dev, "<1|0> <bit>: enable|disable interrupt\n");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
if (en) {
|
if (en) {
|
||||||
if (hw_intr_force(udc, bit))
|
if (hw_intr_force(ci, bit))
|
||||||
dev_err(dev, "invalid bit number\n");
|
dev_err(dev, "invalid bit number\n");
|
||||||
else
|
else
|
||||||
isr_statistics.test++;
|
isr_statistics.test++;
|
||||||
} else {
|
} else {
|
||||||
if (hw_intr_clear(udc, bit))
|
if (hw_intr_clear(ci, bit))
|
||||||
dev_err(dev, "invalid bit number\n");
|
dev_err(dev, "invalid bit number\n");
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return count;
|
return count;
|
||||||
|
@ -524,18 +524,18 @@ static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
|
||||||
static ssize_t show_port_test(struct device *dev,
|
static ssize_t show_port_test(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "EINVAL\n");
|
dev_err(ci->dev, "EINVAL\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
mode = hw_port_test_get(udc);
|
mode = hw_port_test_get(ci);
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
|
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
|
||||||
}
|
}
|
||||||
|
@ -549,24 +549,24 @@ static ssize_t store_port_test(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sscanf(buf, "%u", &mode) != 1) {
|
if (sscanf(buf, "%u", &mode) != 1) {
|
||||||
dev_err(udc->dev, "<mode>: set port test mode");
|
dev_err(ci->dev, "<mode>: set port test mode");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
if (hw_port_test_set(udc, mode))
|
if (hw_port_test_set(ci, mode))
|
||||||
dev_err(udc->dev, "invalid mode\n");
|
dev_err(ci->dev, "invalid mode\n");
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return count;
|
return count;
|
||||||
|
@ -582,20 +582,20 @@ static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
|
||||||
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned i, j, n = 0;
|
unsigned i, j, n = 0;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
for (i = 0; i < udc->hw_ep_max/2; i++) {
|
for (i = 0; i < ci->hw_ep_max/2; i++) {
|
||||||
struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
|
struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i];
|
||||||
struct ci13xxx_ep *mEpTx =
|
struct ci13xxx_ep *mEpTx =
|
||||||
&udc->ci13xxx_ep[i + udc->hw_ep_max/2];
|
&ci->ci13xxx_ep[i + ci->hw_ep_max/2];
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
"EP=%02i: RX=%08X TX=%08X\n",
|
"EP=%02i: RX=%08X TX=%08X\n",
|
||||||
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
|
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
|
||||||
|
@ -606,7 +606,7 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
||||||
*((u32 *)mEpTx->qh.ptr + j));
|
*((u32 *)mEpTx->qh.ptr + j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -621,25 +621,25 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
|
||||||
static ssize_t show_registers(struct device *dev,
|
static ssize_t show_registers(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 *dump;
|
u32 *dump;
|
||||||
unsigned i, k, n = 0;
|
unsigned i, k, n = 0;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
|
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
|
||||||
if (!dump) {
|
if (!dump) {
|
||||||
dev_err(udc->dev, "%s: out of memory\n", __func__);
|
dev_err(ci->dev, "%s: out of memory\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
k = hw_register_read(udc, dump, DUMP_ENTRIES);
|
k = hw_register_read(ci, dump, DUMP_ENTRIES);
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
for (i = 0; i < k; i++) {
|
for (i = 0; i < k; i++) {
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
|
@ -660,24 +660,24 @@ static ssize_t store_registers(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long addr, data, flags;
|
unsigned long addr, data, flags;
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
|
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
|
||||||
dev_err(udc->dev,
|
dev_err(ci->dev,
|
||||||
"<addr> <data>: write data to register address\n");
|
"<addr> <data>: write data to register address\n");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
if (hw_register_write(udc, addr, data))
|
if (hw_register_write(ci, addr, data))
|
||||||
dev_err(udc->dev, "invalid address range\n");
|
dev_err(ci->dev, "invalid address range\n");
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return count;
|
return count;
|
||||||
|
@ -693,34 +693,34 @@ static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
|
||||||
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
|
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct list_head *ptr = NULL;
|
struct list_head *ptr = NULL;
|
||||||
struct ci13xxx_req *req = NULL;
|
struct ci13xxx_req *req = NULL;
|
||||||
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
|
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
|
||||||
|
|
||||||
if (attr == NULL || buf == NULL) {
|
if (attr == NULL || buf == NULL) {
|
||||||
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
|
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&ci->lock, flags);
|
||||||
for (i = 0; i < udc->hw_ep_max; i++)
|
for (i = 0; i < ci->hw_ep_max; i++)
|
||||||
list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
|
list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue)
|
||||||
{
|
{
|
||||||
req = list_entry(ptr, struct ci13xxx_req, queue);
|
req = list_entry(ptr, struct ci13xxx_req, queue);
|
||||||
|
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
"EP=%02i: TD=%08X %s\n",
|
"EP=%02i: TD=%08X %s\n",
|
||||||
i % udc->hw_ep_max/2, (u32)req->dma,
|
i % ci->hw_ep_max/2, (u32)req->dma,
|
||||||
((i < udc->hw_ep_max/2) ? "RX" : "TX"));
|
((i < ci->hw_ep_max/2) ? "RX" : "TX"));
|
||||||
|
|
||||||
for (j = 0; j < qSize; j++)
|
for (j = 0; j < qSize; j++)
|
||||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||||
" %04X: %08X\n", j,
|
" %04X: %08X\n", j,
|
||||||
*((u32 *)req->ptr + j));
|
*((u32 *)req->ptr + j));
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&ci->lock, flags);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 @@
|
||||||
#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[] = {
|
||||||
.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 @@
|
||||||
#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 @@
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
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,
|
||||||
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,
|
||||||
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,26 +1315,67 @@ 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,
|
||||||
if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
|
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.
|
||||||
|
*/
|
||||||
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) |
|
||||||
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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)
|
||||||
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);
|
||||||
|
@ -605,6 +606,41 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns 0 if no match, 1 if match */
|
||||||
|
int usb_match_one_id_intf(struct usb_device *dev,
|
||||||
|
struct usb_host_interface *intf,
|
||||||
|
const struct usb_device_id *id)
|
||||||
|
{
|
||||||
|
/* The interface class, subclass, protocol and number should never be
|
||||||
|
* checked for a match if the device class is Vendor Specific,
|
||||||
|
* unless the match record specifies the Vendor ID. */
|
||||||
|
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
|
||||||
|
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
||||||
|
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
|
||||||
|
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
|
||||||
|
USB_DEVICE_ID_MATCH_INT_PROTOCOL |
|
||||||
|
USB_DEVICE_ID_MATCH_INT_NUMBER)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
|
||||||
|
(id->bInterfaceClass != intf->desc.bInterfaceClass))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
|
||||||
|
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
|
||||||
|
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
|
||||||
|
(id->bInterfaceNumber != intf->desc.bInterfaceNumber))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 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(struct usb_interface *interface,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
|
@ -622,29 +658,7 @@ int usb_match_one_id(struct usb_interface *interface,
|
||||||
if (!usb_match_device(dev, id))
|
if (!usb_match_device(dev, id))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* The interface class, subclass, and protocol should never be
|
return usb_match_one_id_intf(dev, intf, id);
|
||||||
* checked for a match if the device class is Vendor Specific,
|
|
||||||
* unless the match record specifies the Vendor ID. */
|
|
||||||
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
|
|
||||||
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
|
||||||
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
|
|
||||||
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
|
|
||||||
USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
|
|
||||||
(id->bInterfaceClass != intf->desc.bInterfaceClass))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
|
|
||||||
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
|
|
||||||
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usb_match_one_id);
|
EXPORT_SYMBOL_GPL(usb_match_one_id);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
&& !(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 @@
|
||||||
#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 {
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
* 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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);
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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,14 +1794,15 @@ free_interfaces:
|
||||||
* 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) {
|
||||||
usb_enable_lpm(dev);
|
if (dev->actconfig)
|
||||||
|
usb_enable_lpm(dev);
|
||||||
mutex_unlock(hcd->bandwidth_mutex);
|
mutex_unlock(hcd->bandwidth_mutex);
|
||||||
usb_autosuspend_device(dev);
|
usb_autosuspend_device(dev);
|
||||||
goto free_interfaces;
|
goto free_interfaces;
|
||||||
|
@ -1818,7 +1822,7 @@ free_interfaces:
|
||||||
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 @@ free_interfaces:
|
||||||
|
|
||||||
/* 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 @@
|
||||||
#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[] = {
|
||||||
/* 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 */
|
/* Logitech Quickcam Orbit MP */
|
||||||
{ USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* Logitech Webcam C300 */
|
/* Logitech Quickcam Pro for Notebook */
|
||||||
{ USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* Logitech Webcam B/C500 */
|
/* Logitech Quickcam Pro 5000 */
|
||||||
{ USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* Logitech Webcam C600 */
|
/* Logitech Quickcam OEM Dell Notebook */
|
||||||
{ USB_DEVICE(0x046d, 0x0808), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* Logitech Webcam Pro 9000 */
|
/* Logitech Quickcam OEM Cisco VT Camera II */
|
||||||
{ USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* Logitech Webcam C905 */
|
|
||||||
{ USB_DEVICE(0x046d, 0x080a), .driver_info = USB_QUIRK_RESET_RESUME },
|
|
||||||
|
|
||||||
/* Logitech Webcam C210 */
|
|
||||||
{ USB_DEVICE(0x046d, 0x0819), .driver_info = USB_QUIRK_RESET_RESUME },
|
|
||||||
|
|
||||||
/* Logitech Webcam C260 */
|
|
||||||
{ USB_DEVICE(0x046d, 0x081a), .driver_info = USB_QUIRK_RESET_RESUME },
|
|
||||||
|
|
||||||
/* Logitech Webcam C310 */
|
|
||||||
{ USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME },
|
|
||||||
|
|
||||||
/* Logitech Webcam C910 */
|
|
||||||
{ USB_DEVICE(0x046d, 0x0821), .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[] = {
|
||||||
{ } /* 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 */
|
||||||
const struct usb_device_id *id = usb_quirk_list;
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0),
|
||||||
|
.driver_info = USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
|
{ } /* terminating entry must be last */
|
||||||
id->driver_info; id++) {
|
};
|
||||||
if (usb_match_device(udev, id))
|
|
||||||
return id;
|
static bool usb_match_any_interface(struct usb_device *udev,
|
||||||
|
const struct usb_device_id *id)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
|
||||||
|
struct usb_host_config *cfg = &udev->config[i];
|
||||||
|
unsigned int j;
|
||||||
|
|
||||||
|
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,14 +189,10 @@ 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);
|
||||||
|
|
||||||
/* For the present, all devices default to USB-PERSIST enabled */
|
/* For the present, all devices default to USB-PERSIST enabled */
|
||||||
#if 0 /* was: #ifdef CONFIG_PM */
|
#if 0 /* was: #ifdef CONFIG_PM */
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
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[] = {
|
||||||
&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,
|
||||||
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,
|
||||||
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,
|
||||||
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);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#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);
|
||||||
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);
|
||||||
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)
|
||||||
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)
|
||||||
*
|
*
|
||||||
* 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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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 @@
|
||||||
#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
|
||||||
|
@ -171,28 +172,36 @@
|
||||||
#define DWC3_GCTL_PRTCAP_DEVICE 2
|
#define DWC3_GCTL_PRTCAP_DEVICE 2
|
||||||
#define DWC3_GCTL_PRTCAP_OTG 3
|
#define DWC3_GCTL_PRTCAP_OTG 3
|
||||||
|
|
||||||
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
||||||
#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_DSBLCLKGTNG (1 << 0)
|
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
|
||||||
|
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
|
||||||
|
|
||||||
/* Global USB2 PHY Configuration Register */
|
/* Global USB2 PHY Configuration Register */
|
||||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
|
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
|
||||||
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
|
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
|
||||||
|
|
||||||
/* Global USB3 PIPE Control Register */
|
/* Global USB3 PIPE Control Register */
|
||||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||||
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
|
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
|
||||||
|
|
||||||
/* Global TX Fifo Size Register */
|
/* Global TX Fifo Size Register */
|
||||||
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
|
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
|
||||||
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
|
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
|
||||||
|
|
||||||
/* Global HWPARAMS1 Register */
|
/* Global HWPARAMS1 Register */
|
||||||
#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,24 +215,32 @@
|
||||||
#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)
|
||||||
|
|
||||||
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
|
/* These apply for core versions 1.87a and earlier */
|
||||||
#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
|
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
|
||||||
|
#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
|
||||||
|
#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
|
||||||
|
#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
|
||||||
|
#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
|
||||||
|
#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
|
||||||
|
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
|
||||||
|
|
||||||
#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
|
/* These apply for core versions 1.94a and later */
|
||||||
#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
|
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
|
||||||
#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
|
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
|
||||||
#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
|
#define DWC3_DCTL_CRS (1 << 17)
|
||||||
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
|
#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)
|
||||||
|
@ -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 @@
|
||||||
#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 @@
|
||||||
|
|
||||||
#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 @@
|
||||||
#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 @@
|
||||||
|
|
||||||
#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 @@
|
||||||
#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 {
|
||||||
* @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 {
|
||||||
#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 {
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
#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 {
|
||||||
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 {
|
||||||
#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 @@
|
||||||
#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 @@
|
||||||
#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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
/* 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)
|
||||||
|
|
||||||
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,
|
||||||
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,11 +685,9 @@ 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)
|
||||||
|
dwc3_ep0_stall_and_restart(dwc);
|
||||||
err:
|
|
||||||
dwc3_ep0_stall_and_restart(dwc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,12 +806,63 @@ 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,
|
||||||
|
struct dwc3_ep *dep, struct dwc3_request *req)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
req->direction = !!dep->number;
|
||||||
|
|
||||||
|
if (req->request.length == 0) {
|
||||||
|
ret = dwc3_ep0_start_trans(dwc, dep->number,
|
||||||
|
dwc->ctrl_req_addr, 0,
|
||||||
|
DWC3_TRBCTL_CONTROL_DATA);
|
||||||
|
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
|
||||||
|
&& (dep->number == 0)) {
|
||||||
|
u32 transfer_size;
|
||||||
|
|
||||||
|
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||||
|
dep->number);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(dwc->dev, "failed to map request\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
|
||||||
|
|
||||||
|
transfer_size = roundup(req->request.length,
|
||||||
|
(u32) dep->endpoint.maxpacket);
|
||||||
|
|
||||||
|
dwc->ep0_bounced = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* REVISIT in case request length is bigger than
|
||||||
|
* DWC3_EP0_BOUNCE_SIZE we will need two chained
|
||||||
|
* TRBs to handle the transfer.
|
||||||
|
*/
|
||||||
|
ret = dwc3_ep0_start_trans(dwc, dep->number,
|
||||||
|
dwc->ep0_bounce_addr, transfer_size,
|
||||||
|
DWC3_TRBCTL_CONTROL_DATA);
|
||||||
|
} else {
|
||||||
|
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||||
|
dep->number);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(dwc->dev, "failed to map request\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
|
||||||
|
req->request.length, DWC3_TRBCTL_CONTROL_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(ret < 0);
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
const struct dwc3_event_depevt *event)
|
||||||
{
|
{
|
||||||
struct dwc3_ep *dep;
|
struct dwc3_ep *dep;
|
||||||
struct dwc3_request *req;
|
struct dwc3_request *req;
|
||||||
int ret;
|
|
||||||
|
|
||||||
dep = dwc->eps[0];
|
dep = dwc->eps[0];
|
||||||
|
|
||||||
|
@ -806,47 +876,9 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||||
}
|
}
|
||||||
|
|
||||||
req = next_request(&dep->request_list);
|
req = next_request(&dep->request_list);
|
||||||
req->direction = !!event->endpoint_number;
|
dep = dwc->eps[event->endpoint_number];
|
||||||
|
|
||||||
if (req->request.length == 0) {
|
__dwc3_ep0_do_control_data(dwc, dep, req);
|
||||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
|
||||||
dwc->ctrl_req_addr, 0,
|
|
||||||
DWC3_TRBCTL_CONTROL_DATA);
|
|
||||||
} else if ((req->request.length % dep->endpoint.maxpacket)
|
|
||||||
&& (event->endpoint_number == 0)) {
|
|
||||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
|
||||||
event->endpoint_number);
|
|
||||||
if (ret) {
|
|
||||||
dev_dbg(dwc->dev, "failed to map request\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(req->request.length > dep->endpoint.maxpacket);
|
|
||||||
|
|
||||||
dwc->ep0_bounced = true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* REVISIT in case request length is bigger than EP0
|
|
||||||
* wMaxPacketSize, we will need two chained TRBs to handle
|
|
||||||
* the transfer.
|
|
||||||
*/
|
|
||||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
|
||||||
dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
|
|
||||||
DWC3_TRBCTL_CONTROL_DATA);
|
|
||||||
} else {
|
|
||||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
|
||||||
event->endpoint_number);
|
|
||||||
if (ret) {
|
|
||||||
dev_dbg(dwc->dev, "failed to map request\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
|
||||||
req->request.dma, req->request.length,
|
|
||||||
DWC3_TRBCTL_CONTROL_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(ret < 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
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)
|
||||||
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,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dwc3_ep0_do_control_status(dwc, event->endpoint_number);
|
dwc3_ep0_do_control_status(dwc, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
||||||
int retries = 10000;
|
int retries = 10000;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait until device controller is ready. Only applies to 1.94a and
|
||||||
|
* later RTL.
|
||||||
|
*/
|
||||||
|
if (dwc->revision >= DWC3_REVISION_194A) {
|
||||||
|
while (--retries) {
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||||
|
if (reg & DWC3_DSTS_DCNRD)
|
||||||
|
udelay(5);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retries <= 0)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||||
|
|
||||||
|
@ -107,7 +124,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
||||||
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
|
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
|
||||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following code is racy when called from dwc3_gadget_wakeup,
|
||||||
|
* and is not needed, at least on newer versions
|
||||||
|
*/
|
||||||
|
if (dwc->revision >= DWC3_REVISION_194A)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* wait for a change in DSTS */
|
/* wait for a change in DSTS */
|
||||||
|
retries = 10000;
|
||||||
while (--retries) {
|
while (--retries) {
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||||
|
|
||||||
|
@ -265,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
||||||
return "Clear Stall";
|
return "Clear Stall";
|
||||||
case DWC3_DEPCMD_SETSTALL:
|
case DWC3_DEPCMD_SETSTALL:
|
||||||
return "Set Stall";
|
return "Set Stall";
|
||||||
case DWC3_DEPCMD_GETSEQNUMBER:
|
case DWC3_DEPCMD_GETEPSTATE:
|
||||||
return "Get Data Sequence Number";
|
return "Get Endpoint State";
|
||||||
case DWC3_DEPCMD_SETTRANSFRESOURCE:
|
case DWC3_DEPCMD_SETTRANSFRESOURCE:
|
||||||
return "Set Endpoint Transfer Resource";
|
return "Set Endpoint Transfer Resource";
|
||||||
case DWC3_DEPCMD_SETEPCONFIG:
|
case DWC3_DEPCMD_SETEPCONFIG:
|
||||||
|
@ -414,7 +439,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||||
|
|
||||||
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
|
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
|
||||||
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
|
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
|
||||||
| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
|
| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
|
||||||
|
|
||||||
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||||
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
||||||
|
@ -530,9 +555,37 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||||
{
|
{
|
||||||
struct dwc3_request *req;
|
struct dwc3_request *req;
|
||||||
|
|
||||||
if (!list_empty(&dep->req_queued))
|
if (!list_empty(&dep->req_queued)) {
|
||||||
dwc3_stop_active_transfer(dwc, dep->number);
|
dwc3_stop_active_transfer(dwc, dep->number);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTICE: We are violating what the Databook says about the
|
||||||
|
* EndTransfer command. Ideally we would _always_ wait for the
|
||||||
|
* EndTransfer Command Completion IRQ, but that's causing too
|
||||||
|
* much trouble synchronizing between us and gadget driver.
|
||||||
|
*
|
||||||
|
* We have discussed this with the IP Provider and it was
|
||||||
|
* suggested to giveback all requests here, but give HW some
|
||||||
|
* extra time to synchronize with the interconnect. We're using
|
||||||
|
* an arbitraty 100us delay for that.
|
||||||
|
*
|
||||||
|
* Note also that a similar handling was tested by Synopsys
|
||||||
|
* (thanks a lot Paul) and nothing bad has come out of it.
|
||||||
|
* In short, what we're doing is:
|
||||||
|
*
|
||||||
|
* - Issue EndTransfer WITH CMDIOC bit set
|
||||||
|
* - Wait 100us
|
||||||
|
* - giveback all requests to gadget driver
|
||||||
|
*/
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
while (!list_empty(&dep->req_queued)) {
|
||||||
|
req = next_request(&dep->req_queued);
|
||||||
|
|
||||||
|
dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (!list_empty(&dep->request_list)) {
|
while (!list_empty(&dep->request_list)) {
|
||||||
req = next_request(&dep->request_list);
|
req = next_request(&dep->request_list);
|
||||||
|
|
||||||
|
@ -741,8 +794,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||||
case USB_ENDPOINT_XFER_ISOC:
|
case USB_ENDPOINT_XFER_ISOC:
|
||||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||||
|
|
||||||
/* IOC every DWC3_TRB_NUM / 4 so we can refill */
|
if (!req->request.no_interrupt)
|
||||||
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
|
|
||||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -958,14 +1010,42 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||||
dep->flags |= DWC3_EP_BUSY;
|
dep->flags |= DWC3_EP_BUSY;
|
||||||
|
|
||||||
if (start_new) {
|
if (start_new) {
|
||||||
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);
|
||||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
WARN_ON_ONCE(!dep->resource_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||||
|
struct dwc3_ep *dep, u32 cur_uf)
|
||||||
|
{
|
||||||
|
u32 uf;
|
||||||
|
|
||||||
|
if (list_empty(&dep->request_list)) {
|
||||||
|
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
|
||||||
|
dep->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4 micro frames in the future */
|
||||||
|
uf = cur_uf + dep->interval * 4;
|
||||||
|
|
||||||
|
__dwc3_gadget_kick_transfer(dep, uf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||||
|
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
||||||
|
{
|
||||||
|
u32 cur_uf, mask;
|
||||||
|
|
||||||
|
mask = ~(dep->interval - 1);
|
||||||
|
cur_uf = event->parameters & mask;
|
||||||
|
|
||||||
|
__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
|
||||||
|
}
|
||||||
|
|
||||||
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||||
{
|
{
|
||||||
struct dwc3 *dwc = dep->dwc;
|
struct dwc3 *dwc = dep->dwc;
|
||||||
|
@ -995,11 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||||
|
|
||||||
list_add_tail(&req->list, &dep->request_list);
|
list_add_tail(&req->list, &dep->request_list);
|
||||||
|
|
||||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
|
|
||||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two special cases:
|
* There are a few special cases:
|
||||||
*
|
*
|
||||||
* 1. XferNotReady with empty list of requests. We need to kick the
|
* 1. XferNotReady with empty list of requests. We need to kick the
|
||||||
* transfer here in that situation, otherwise we will be NAKing
|
* transfer here in that situation, otherwise we will be NAKing
|
||||||
|
@ -1008,31 +1085,46 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||||
* able to receive the data until the next request is queued.
|
* able to receive the data until the next request is queued.
|
||||||
* The following code is handling exactly that.
|
* The following code is handling exactly that.
|
||||||
*
|
*
|
||||||
* 2. XferInProgress on Isoc EP with an active transfer. We need to
|
|
||||||
* kick the transfer here after queuing a request, otherwise the
|
|
||||||
* core may not see the modified TRB(s).
|
|
||||||
*/
|
*/
|
||||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||||
int ret;
|
int ret;
|
||||||
int start_trans = 1;
|
|
||||||
u8 trans_idx = dep->res_trans_idx;
|
|
||||||
|
|
||||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
||||||
(dep->flags & DWC3_EP_BUSY)) {
|
|
||||||
start_trans = 0;
|
|
||||||
WARN_ON_ONCE(!trans_idx);
|
|
||||||
} else {
|
|
||||||
trans_idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
|
|
||||||
if (ret && ret != -EBUSY) {
|
if (ret && ret != -EBUSY) {
|
||||||
struct dwc3 *dwc = dep->dwc;
|
struct dwc3 *dwc = dep->dwc;
|
||||||
|
|
||||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||||
dep->name);
|
dep->name);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2. XferInProgress on Isoc EP with an active transfer. We need to
|
||||||
|
* kick the transfer here after queuing a request, otherwise the
|
||||||
|
* core may not see the modified TRB(s).
|
||||||
|
*/
|
||||||
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||||
|
(dep->flags & DWC3_EP_BUSY)) {
|
||||||
|
WARN_ON_ONCE(!dep->resource_index);
|
||||||
|
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
|
||||||
|
false);
|
||||||
|
if (ret && ret != -EBUSY) {
|
||||||
|
struct dwc3 *dwc = dep->dwc;
|
||||||
|
|
||||||
|
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||||
|
dep->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3. Missed ISOC Handling. We need to start isoc transfer on the saved
|
||||||
|
* uframe number.
|
||||||
|
*/
|
||||||
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||||
|
(dep->flags & DWC3_EP_MISSED_ISOC)) {
|
||||||
|
__dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
|
||||||
|
dep->flags &= ~DWC3_EP_MISSED_ISOC;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1118,15 +1210,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
||||||
memset(¶ms, 0x00, sizeof(params));
|
memset(¶ms, 0x00, sizeof(params));
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
if (dep->number == 0 || dep->number == 1) {
|
|
||||||
/*
|
|
||||||
* Whenever EP0 is stalled, we will restart
|
|
||||||
* the state machine, thus moving back to
|
|
||||||
* Setup Phase
|
|
||||||
*/
|
|
||||||
dwc->ep0state = EP0_SETUP_PHASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||||
DWC3_DEPCMD_SETSTALL, ¶ms);
|
DWC3_DEPCMD_SETSTALL, ¶ms);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1186,7 +1269,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||||
dep->flags |= DWC3_EP_WEDGE;
|
dep->flags |= DWC3_EP_WEDGE;
|
||||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return dwc3_gadget_ep_set_halt(ep, 1);
|
if (dep->number == 0 || dep->number == 1)
|
||||||
|
return dwc3_gadget_ep0_set_halt(ep, 1);
|
||||||
|
else
|
||||||
|
return dwc3_gadget_ep_set_halt(ep, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
@ -1204,7 +1290,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
|
||||||
.free_request = dwc3_gadget_ep_free_request,
|
.free_request = dwc3_gadget_ep_free_request,
|
||||||
.queue = dwc3_gadget_ep0_queue,
|
.queue = dwc3_gadget_ep0_queue,
|
||||||
.dequeue = dwc3_gadget_ep_dequeue,
|
.dequeue = dwc3_gadget_ep_dequeue,
|
||||||
.set_halt = dwc3_gadget_ep_set_halt,
|
.set_halt = dwc3_gadget_ep0_set_halt,
|
||||||
.set_wedge = dwc3_gadget_ep_set_wedge,
|
.set_wedge = dwc3_gadget_ep_set_wedge,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1280,9 +1366,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write zeroes to Link Change Request */
|
/* Recent versions do this automatically */
|
||||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
if (dwc->revision < DWC3_REVISION_194A) {
|
||||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
/* write zeroes to Link Change Request */
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
|
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||||
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* poll until Link State changes to ON */
|
/* poll until Link State changes to ON */
|
||||||
timeout = jiffies + msecs_to_jiffies(100);
|
timeout = jiffies + msecs_to_jiffies(100);
|
||||||
|
@ -1319,16 +1409,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
u32 timeout = 500;
|
u32 timeout = 500;
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
if (is_on) {
|
if (is_on) {
|
||||||
reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
if (dwc->revision <= DWC3_REVISION_187A) {
|
||||||
reg |= (DWC3_DCTL_RUN_STOP
|
reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
||||||
| DWC3_DCTL_TRGTULST_RX_DET);
|
reg |= DWC3_DCTL_TRGTULST_RX_DET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwc->revision >= DWC3_REVISION_194A)
|
||||||
|
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||||
|
reg |= DWC3_DCTL_RUN_STOP;
|
||||||
} else {
|
} else {
|
||||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||||
}
|
}
|
||||||
|
@ -1346,7 +1441,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||||
}
|
}
|
||||||
timeout--;
|
timeout--;
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
break;
|
return -ETIMEDOUT;
|
||||||
udelay(1);
|
udelay(1);
|
||||||
} while (1);
|
} while (1);
|
||||||
|
|
||||||
|
@ -1354,20 +1449,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||||
dwc->gadget_driver
|
dwc->gadget_driver
|
||||||
? dwc->gadget_driver->function : "no-function",
|
? dwc->gadget_driver->function : "no-function",
|
||||||
is_on ? "connect" : "disconnect");
|
is_on ? "connect" : "disconnect");
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||||
{
|
{
|
||||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
is_on = !!is_on;
|
is_on = !!is_on;
|
||||||
|
|
||||||
spin_lock_irqsave(&dwc->lock, flags);
|
spin_lock_irqsave(&dwc->lock, flags);
|
||||||
dwc3_gadget_run_stop(dwc, is_on);
|
ret = dwc3_gadget_run_stop(dwc, is_on);
|
||||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dwc3_gadget_start(struct usb_gadget *g,
|
static int dwc3_gadget_start(struct usb_gadget *g,
|
||||||
|
@ -1468,6 +1566,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct usb_gadget_ops dwc3_gadget_ops = {
|
static const struct usb_gadget_ops dwc3_gadget_ops = {
|
||||||
.get_frame = dwc3_gadget_get_frame,
|
.get_frame = dwc3_gadget_get_frame,
|
||||||
.wakeup = dwc3_gadget_wakeup,
|
.wakeup = dwc3_gadget_wakeup,
|
||||||
|
@ -1558,6 +1657,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||||
struct dwc3_trb *trb;
|
struct dwc3_trb *trb;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
unsigned int s_pkt = 0;
|
unsigned int s_pkt = 0;
|
||||||
|
unsigned int trb_status;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
req = next_request(&dep->req_queued);
|
req = next_request(&dep->req_queued);
|
||||||
|
@ -1583,9 +1683,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||||
|
|
||||||
if (dep->direction) {
|
if (dep->direction) {
|
||||||
if (count) {
|
if (count) {
|
||||||
dev_err(dwc->dev, "incomplete IN transfer %s\n",
|
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||||
dep->name);
|
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
||||||
status = -ECONNRESET;
|
dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
|
||||||
|
dep->name);
|
||||||
|
dep->current_uf = event->parameters &
|
||||||
|
~(dep->interval - 1);
|
||||||
|
dep->flags |= DWC3_EP_MISSED_ISOC;
|
||||||
|
} else {
|
||||||
|
dev_err(dwc->dev, "incomplete IN transfer %s\n",
|
||||||
|
dep->name);
|
||||||
|
status = -ECONNRESET;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (count && (event->status & DEPEVT_STATUS_SHORT))
|
if (count && (event->status & DEPEVT_STATUS_SHORT))
|
||||||
|
@ -1604,7 +1713,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||||
if (s_pkt)
|
if (s_pkt)
|
||||||
break;
|
break;
|
||||||
if ((event->status & DEPEVT_STATUS_LST) &&
|
if ((event->status & DEPEVT_STATUS_LST) &&
|
||||||
(trb->ctrl & DWC3_TRB_CTRL_LST))
|
(trb->ctrl & (DWC3_TRB_CTRL_LST |
|
||||||
|
DWC3_TRB_CTRL_HWO)))
|
||||||
break;
|
break;
|
||||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||||
|
@ -1657,65 +1767,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
|
||||||
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
|
||||||
{
|
|
||||||
u32 uf, mask;
|
|
||||||
|
|
||||||
if (list_empty(&dep->request_list)) {
|
|
||||||
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
|
|
||||||
dep->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mask = ~(dep->interval - 1);
|
|
||||||
uf = event->parameters & mask;
|
|
||||||
/* 4 micro frames in the future */
|
|
||||||
uf += dep->interval * 4;
|
|
||||||
|
|
||||||
__dwc3_gadget_kick_transfer(dep, uf, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
|
|
||||||
const struct dwc3_event_depevt *event)
|
|
||||||
{
|
|
||||||
struct dwc3 *dwc = dep->dwc;
|
|
||||||
struct dwc3_event_depevt mod_ev = *event;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We were asked to remove one request. It is possible that this
|
|
||||||
* request and a few others were started together and have the same
|
|
||||||
* transfer index. Since we stopped the complete endpoint we don't
|
|
||||||
* know how many requests were already completed (and not yet)
|
|
||||||
* reported and how could be done (later). We purge them all until
|
|
||||||
* the end of the list.
|
|
||||||
*/
|
|
||||||
mod_ev.status = DEPEVT_STATUS_LST;
|
|
||||||
dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
|
|
||||||
dep->flags &= ~DWC3_EP_BUSY;
|
|
||||||
/* pending requests are ignored and are queued on XferNotReady */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
|
|
||||||
const struct dwc3_event_depevt *event)
|
|
||||||
{
|
|
||||||
u32 param = event->parameters;
|
|
||||||
u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
|
|
||||||
|
|
||||||
switch (cmd_type) {
|
|
||||||
case DWC3_DEPCMD_ENDTRANSFER:
|
|
||||||
dwc3_process_ep_cmd_complete(dep, event);
|
|
||||||
break;
|
|
||||||
case DWC3_DEPCMD_STARTTRANSFER:
|
|
||||||
dep->res_trans_idx = param & 0x7f;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
|
|
||||||
__func__, cmd_type);
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||||
const struct dwc3_event_depevt *event)
|
const struct dwc3_event_depevt *event)
|
||||||
{
|
{
|
||||||
|
@ -1724,6 +1775,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||||
|
|
||||||
dep = dwc->eps[epnum];
|
dep = dwc->eps[epnum];
|
||||||
|
|
||||||
|
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||||
|
return;
|
||||||
|
|
||||||
dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
|
dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
|
||||||
dwc3_ep_event_string(event->endpoint_event));
|
dwc3_ep_event_string(event->endpoint_event));
|
||||||
|
|
||||||
|
@ -1734,7 +1788,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||||
|
|
||||||
switch (event->endpoint_event) {
|
switch (event->endpoint_event) {
|
||||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||||
dep->res_trans_idx = 0;
|
dep->resource_index = 0;
|
||||||
|
|
||||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||||
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
||||||
|
@ -1797,7 +1851,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||||
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
|
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
|
||||||
break;
|
break;
|
||||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||||
dwc3_ep_cmd_compl(dep, event);
|
dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1820,16 +1874,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||||
|
|
||||||
dep = dwc->eps[epnum];
|
dep = dwc->eps[epnum];
|
||||||
|
|
||||||
WARN_ON(!dep->res_trans_idx);
|
if (!dep->resource_index)
|
||||||
if (dep->res_trans_idx) {
|
return;
|
||||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
|
||||||
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
|
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||||
cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
|
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
|
||||||
memset(¶ms, 0, sizeof(params));
|
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
memset(¶ms, 0, sizeof(params));
|
||||||
WARN_ON_ONCE(ret);
|
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||||
dep->res_trans_idx = 0;
|
WARN_ON_ONCE(ret);
|
||||||
}
|
dep->resource_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
||||||
|
@ -1872,11 +1926,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
|
||||||
|
|
||||||
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
||||||
{
|
{
|
||||||
|
int reg;
|
||||||
|
|
||||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||||
#if 0
|
|
||||||
XXX
|
|
||||||
U1/U2 is powersave optimization. Skip it for now. Anyway we need to
|
|
||||||
enable it before we can disable it.
|
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||||
|
@ -1884,9 +1936,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
||||||
|
|
||||||
reg &= ~DWC3_DCTL_INITU2ENA;
|
reg &= ~DWC3_DCTL_INITU2ENA;
|
||||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
#endif
|
|
||||||
|
|
||||||
dwc3_stop_active_transfers(dwc);
|
|
||||||
dwc3_disconnect_gadget(dwc);
|
dwc3_disconnect_gadget(dwc);
|
||||||
dwc->start_config_issued = false;
|
dwc->start_config_issued = false;
|
||||||
|
|
||||||
|
@ -1894,30 +1944,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
||||||
dwc->setup_packet_pending = false;
|
dwc->setup_packet_pending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
|
static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||||
|
|
||||||
if (on)
|
if (suspend)
|
||||||
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
|
|
||||||
else
|
|
||||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||||
|
else
|
||||||
|
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
|
||||||
|
|
||||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
|
static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||||
|
|
||||||
if (on)
|
if (suspend)
|
||||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
|
||||||
else
|
|
||||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||||
|
else
|
||||||
|
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||||
|
|
||||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||||
}
|
}
|
||||||
|
@ -1962,16 +2012,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||||
/* after reset -> Default State */
|
/* after reset -> Default State */
|
||||||
dwc->dev_state = DWC3_DEFAULT_STATE;
|
dwc->dev_state = DWC3_DEFAULT_STATE;
|
||||||
|
|
||||||
/* Enable PHYs */
|
/* Recent versions support automatic phy suspend and don't need this */
|
||||||
dwc3_gadget_usb2_phy_power(dwc, true);
|
if (dwc->revision < DWC3_REVISION_194A) {
|
||||||
dwc3_gadget_usb3_phy_power(dwc, true);
|
/* Resume PHYs */
|
||||||
|
dwc3_gadget_usb2_phy_suspend(dwc, false);
|
||||||
|
dwc3_gadget_usb3_phy_suspend(dwc, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
|
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
|
||||||
dwc3_disconnect_gadget(dwc);
|
dwc3_disconnect_gadget(dwc);
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||||
reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
|
|
||||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
dwc->test_mode = false;
|
dwc->test_mode = false;
|
||||||
|
|
||||||
|
@ -2010,16 +2062,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
|
||||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
|
static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
|
||||||
{
|
{
|
||||||
switch (speed) {
|
switch (speed) {
|
||||||
case USB_SPEED_SUPER:
|
case USB_SPEED_SUPER:
|
||||||
dwc3_gadget_usb2_phy_power(dwc, false);
|
dwc3_gadget_usb2_phy_suspend(dwc, true);
|
||||||
break;
|
break;
|
||||||
case USB_SPEED_HIGH:
|
case USB_SPEED_HIGH:
|
||||||
case USB_SPEED_FULL:
|
case USB_SPEED_FULL:
|
||||||
case USB_SPEED_LOW:
|
case USB_SPEED_LOW:
|
||||||
dwc3_gadget_usb3_phy_power(dwc, false);
|
dwc3_gadget_usb3_phy_suspend(dwc, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2082,8 +2134,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable unneded PHY */
|
/* Recent versions support automatic phy suspend and don't need this */
|
||||||
dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
|
if (dwc->revision < DWC3_REVISION_194A) {
|
||||||
|
/* Suspend unneeded PHY */
|
||||||
|
dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
|
||||||
|
}
|
||||||
|
|
||||||
dep = dwc->eps[0];
|
dep = dwc->eps[0];
|
||||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||||
|
@ -2373,10 +2428,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||||
reg |= DWC3_DCFG_LPM_CAP;
|
reg |= DWC3_DCFG_LPM_CAP;
|
||||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
|
||||||
reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
|
|
||||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
|
||||||
|
|
||||||
/* Enable all but Start and End of Frame IRQs */
|
/* Enable all but Start and End of Frame IRQs */
|
||||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||||
|
@ -2389,6 +2440,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||||
DWC3_DEVTEN_DISCONNEVTEN);
|
DWC3_DEVTEN_DISCONNEVTEN);
|
||||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||||
|
|
||||||
|
/* Enable USB2 LPM and automatic phy suspend only on recent versions */
|
||||||
|
if (dwc->revision >= DWC3_REVISION_194A) {
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||||
|
reg |= DWC3_DCFG_LPM_CAP;
|
||||||
|
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||||
|
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
|
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||||
|
|
||||||
|
/* TODO: This should be configurable */
|
||||||
|
reg |= DWC3_DCTL_HIRD_THRES(28);
|
||||||
|
|
||||||
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
|
|
||||||
|
dwc3_gadget_usb2_phy_suspend(dwc, false);
|
||||||
|
dwc3_gadget_usb3_phy_suspend(dwc, false);
|
||||||
|
}
|
||||||
|
|
||||||
ret = device_register(&dwc->gadget.dev);
|
ret = device_register(&dwc->gadget.dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dwc->dev, "failed to register gadget device\n");
|
dev_err(dwc->dev, "failed to register gadget device\n");
|
||||||
|
|
|
@ -66,7 +66,12 @@ struct dwc3;
|
||||||
#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
|
#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
|
||||||
#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
|
#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
|
||||||
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
|
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
|
||||||
|
/* This applies for core versions earlier than 1.94a */
|
||||||
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
|
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
|
||||||
|
/* These apply for core versions 1.94a and later */
|
||||||
|
#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
|
||||||
|
#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
|
||||||
|
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
|
||||||
|
|
||||||
/* DEPXFERCFG parameter 0 */
|
/* DEPXFERCFG parameter 0 */
|
||||||
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
|
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
|
||||||
|
@ -106,6 +111,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||||
const struct dwc3_event_depevt *event);
|
const struct dwc3_event_depevt *event);
|
||||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||||
|
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||||
gfp_t gfp_flags);
|
gfp_t gfp_flags);
|
||||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -321,6 +321,15 @@ config USB_MV_UDC
|
||||||
USB2.0 OTG controller, which can be configured as high speed or
|
USB2.0 OTG controller, which can be configured as high speed or
|
||||||
full speed USB peripheral.
|
full speed USB peripheral.
|
||||||
|
|
||||||
|
config USB_MV_U3D
|
||||||
|
tristate "MARVELL PXA2128 USB 3.0 controller"
|
||||||
|
depends on CPU_MMP3
|
||||||
|
select USB_GADGET_DUALSPEED
|
||||||
|
select USB_GADGET_SUPERSPEED
|
||||||
|
help
|
||||||
|
MARVELL PXA2128 Processor series include a super speed USB3.0 device
|
||||||
|
controller, which support super speed USB peripheral.
|
||||||
|
|
||||||
#
|
#
|
||||||
# Controllers available in both integrated and discrete versions
|
# Controllers available in both integrated and discrete versions
|
||||||
#
|
#
|
||||||
|
|
|
@ -29,6 +29,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||||
mv_udc-y := mv_udc_core.o
|
mv_udc-y := mv_udc_core.o
|
||||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||||
|
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||||
|
|
||||||
#
|
#
|
||||||
# USB gadget drivers
|
# USB gadget drivers
|
||||||
|
|
|
@ -235,6 +235,7 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
|
||||||
static struct usb_composite_driver acm_ms_driver = {
|
static struct usb_composite_driver acm_ms_driver = {
|
||||||
.name = "g_acm_ms",
|
.name = "g_acm_ms",
|
||||||
.dev = &device_desc,
|
.dev = &device_desc,
|
||||||
|
.max_speed = USB_SPEED_SUPER,
|
||||||
.strings = dev_strings,
|
.strings = dev_strings,
|
||||||
.unbind = __exit_p(acm_ms_unbind),
|
.unbind = __exit_p(acm_ms_unbind),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1634,6 +1634,7 @@ static int at91_start(struct usb_gadget *gadget,
|
||||||
udc = container_of(gadget, struct at91_udc, gadget);
|
udc = container_of(gadget, struct at91_udc, gadget);
|
||||||
udc->driver = driver;
|
udc->driver = driver;
|
||||||
udc->gadget.dev.driver = &driver->driver;
|
udc->gadget.dev.driver = &driver->driver;
|
||||||
|
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
|
||||||
dev_set_drvdata(&udc->gadget.dev, &driver->driver);
|
dev_set_drvdata(&udc->gadget.dev, &driver->driver);
|
||||||
udc->enabled = 1;
|
udc->enabled = 1;
|
||||||
udc->selfpowered = 1;
|
udc->selfpowered = 1;
|
||||||
|
|
|
@ -117,6 +117,7 @@ int config_ep_by_speed(struct usb_gadget *g,
|
||||||
struct usb_function *f,
|
struct usb_function *f,
|
||||||
struct usb_ep *_ep)
|
struct usb_ep *_ep)
|
||||||
{
|
{
|
||||||
|
struct usb_composite_dev *cdev = get_gadget_data(g);
|
||||||
struct usb_endpoint_descriptor *chosen_desc = NULL;
|
struct usb_endpoint_descriptor *chosen_desc = NULL;
|
||||||
struct usb_descriptor_header **speed_desc = NULL;
|
struct usb_descriptor_header **speed_desc = NULL;
|
||||||
|
|
||||||
|
@ -180,10 +181,12 @@ ep_found:
|
||||||
_ep->mult = comp_desc->bmAttributes & 0x3;
|
_ep->mult = comp_desc->bmAttributes & 0x3;
|
||||||
case USB_ENDPOINT_XFER_BULK:
|
case USB_ENDPOINT_XFER_BULK:
|
||||||
case USB_ENDPOINT_XFER_INT:
|
case USB_ENDPOINT_XFER_INT:
|
||||||
_ep->maxburst = comp_desc->bMaxBurst;
|
_ep->maxburst = comp_desc->bMaxBurst + 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Do nothing for control endpoints */
|
if (comp_desc->bMaxBurst != 0)
|
||||||
|
ERROR(cdev, "ep0 bMaxBurst must be 0\n");
|
||||||
|
_ep->maxburst = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/hid.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#include <linux/usb/composite.h>
|
#include <linux/usb/composite.h>
|
||||||
|
@ -1671,6 +1672,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HID_DT_HID:
|
||||||
|
pr_vdebug("hid descriptor\n");
|
||||||
|
if (length != sizeof(struct hid_descriptor))
|
||||||
|
goto inv_length;
|
||||||
|
break;
|
||||||
|
|
||||||
case USB_DT_OTG:
|
case USB_DT_OTG:
|
||||||
if (length != sizeof(struct usb_otg_descriptor))
|
if (length != sizeof(struct usb_otg_descriptor))
|
||||||
goto inv_length;
|
goto inv_length;
|
||||||
|
|
|
@ -26,6 +26,12 @@ static struct class *hidg_class;
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
/* HID gadget struct */
|
/* HID gadget struct */
|
||||||
|
|
||||||
|
struct f_hidg_req_list {
|
||||||
|
struct usb_request *req;
|
||||||
|
unsigned int pos;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
struct f_hidg {
|
struct f_hidg {
|
||||||
/* configuration */
|
/* configuration */
|
||||||
unsigned char bInterfaceSubClass;
|
unsigned char bInterfaceSubClass;
|
||||||
|
@ -35,10 +41,10 @@ struct f_hidg {
|
||||||
unsigned short report_length;
|
unsigned short report_length;
|
||||||
|
|
||||||
/* recv report */
|
/* recv report */
|
||||||
char *set_report_buff;
|
struct list_head completed_out_req;
|
||||||
unsigned short set_report_length;
|
|
||||||
spinlock_t spinlock;
|
spinlock_t spinlock;
|
||||||
wait_queue_head_t read_queue;
|
wait_queue_head_t read_queue;
|
||||||
|
unsigned int qlen;
|
||||||
|
|
||||||
/* send report */
|
/* send report */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
@ -49,7 +55,9 @@ struct f_hidg {
|
||||||
int minor;
|
int minor;
|
||||||
struct cdev cdev;
|
struct cdev cdev;
|
||||||
struct usb_function func;
|
struct usb_function func;
|
||||||
|
|
||||||
struct usb_ep *in_ep;
|
struct usb_ep *in_ep;
|
||||||
|
struct usb_ep *out_ep;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
|
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
|
||||||
|
@ -65,7 +73,7 @@ static struct usb_interface_descriptor hidg_interface_desc = {
|
||||||
.bDescriptorType = USB_DT_INTERFACE,
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
/* .bInterfaceNumber = DYNAMIC */
|
/* .bInterfaceNumber = DYNAMIC */
|
||||||
.bAlternateSetting = 0,
|
.bAlternateSetting = 0,
|
||||||
.bNumEndpoints = 1,
|
.bNumEndpoints = 2,
|
||||||
.bInterfaceClass = USB_CLASS_HID,
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
/* .bInterfaceSubClass = DYNAMIC */
|
/* .bInterfaceSubClass = DYNAMIC */
|
||||||
/* .bInterfaceProtocol = DYNAMIC */
|
/* .bInterfaceProtocol = DYNAMIC */
|
||||||
|
@ -96,10 +104,23 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_DIR_OUT,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
/*.wMaxPacketSize = DYNAMIC */
|
||||||
|
.bInterval = 4, /* FIXME: Add this field in the
|
||||||
|
* HID gadget configuration?
|
||||||
|
* (struct hidg_func_descriptor)
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
static struct usb_descriptor_header *hidg_hs_descriptors[] = {
|
static struct usb_descriptor_header *hidg_hs_descriptors[] = {
|
||||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||||
(struct usb_descriptor_header *)&hidg_desc,
|
(struct usb_descriptor_header *)&hidg_desc,
|
||||||
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
|
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
|
||||||
|
(struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,10 +138,23 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_DIR_OUT,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
/*.wMaxPacketSize = DYNAMIC */
|
||||||
|
.bInterval = 10, /* FIXME: Add this field in the
|
||||||
|
* HID gadget configuration?
|
||||||
|
* (struct hidg_func_descriptor)
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||||
(struct usb_descriptor_header *)&hidg_interface_desc,
|
(struct usb_descriptor_header *)&hidg_interface_desc,
|
||||||
(struct usb_descriptor_header *)&hidg_desc,
|
(struct usb_descriptor_header *)&hidg_desc,
|
||||||
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
|
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
|
||||||
|
(struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,9 +164,11 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||||
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||||
size_t count, loff_t *ptr)
|
size_t count, loff_t *ptr)
|
||||||
{
|
{
|
||||||
struct f_hidg *hidg = file->private_data;
|
struct f_hidg *hidg = file->private_data;
|
||||||
char *tmp_buff = NULL;
|
struct f_hidg_req_list *list;
|
||||||
unsigned long flags;
|
struct usb_request *req;
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!count)
|
if (!count)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -142,8 +178,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||||
|
|
||||||
spin_lock_irqsave(&hidg->spinlock, flags);
|
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||||
|
|
||||||
#define READ_COND (hidg->set_report_buff != NULL)
|
#define READ_COND (!list_empty(&hidg->completed_out_req))
|
||||||
|
|
||||||
|
/* wait for at least one buffer to complete */
|
||||||
while (!READ_COND) {
|
while (!READ_COND) {
|
||||||
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||||
if (file->f_flags & O_NONBLOCK)
|
if (file->f_flags & O_NONBLOCK)
|
||||||
|
@ -155,19 +192,34 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
||||||
spin_lock_irqsave(&hidg->spinlock, flags);
|
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* pick the first one */
|
||||||
count = min_t(unsigned, count, hidg->set_report_length);
|
list = list_first_entry(&hidg->completed_out_req,
|
||||||
tmp_buff = hidg->set_report_buff;
|
struct f_hidg_req_list, list);
|
||||||
hidg->set_report_buff = NULL;
|
req = list->req;
|
||||||
|
count = min_t(unsigned int, count, req->actual - list->pos);
|
||||||
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||||
|
|
||||||
if (tmp_buff != NULL) {
|
/* copy to user outside spinlock */
|
||||||
/* copy to user outside spinlock */
|
count -= copy_to_user(buffer, req->buf + list->pos, count);
|
||||||
count -= copy_to_user(buffer, tmp_buff, count);
|
list->pos += count;
|
||||||
kfree(tmp_buff);
|
|
||||||
} else
|
/*
|
||||||
count = -ENOMEM;
|
* if this request is completely handled and transfered to
|
||||||
|
* userspace, remove its entry from the list and requeue it
|
||||||
|
* again. Otherwise, we will revisit it again upon the next
|
||||||
|
* call, taking into account its current read position.
|
||||||
|
*/
|
||||||
|
if (list->pos == req->actual) {
|
||||||
|
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||||
|
list_del(&list->list);
|
||||||
|
kfree(list);
|
||||||
|
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||||
|
|
||||||
|
req->length = hidg->report_length;
|
||||||
|
ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -282,28 +334,37 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
/* usb_function */
|
/* usb_function */
|
||||||
|
|
||||||
|
static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length)
|
||||||
|
{
|
||||||
|
struct usb_request *req;
|
||||||
|
|
||||||
|
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||||
|
if (req) {
|
||||||
|
req->length = length;
|
||||||
|
req->buf = kmalloc(length, GFP_ATOMIC);
|
||||||
|
if (!req->buf) {
|
||||||
|
usb_ep_free_request(ep, req);
|
||||||
|
req = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
|
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
|
||||||
{
|
{
|
||||||
struct f_hidg *hidg = (struct f_hidg *)req->context;
|
struct f_hidg *hidg = (struct f_hidg *) req->context;
|
||||||
|
struct f_hidg_req_list *req_list;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (req->status != 0 || req->buf == NULL || req->actual == 0) {
|
req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
|
||||||
ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__);
|
if (!req_list)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&hidg->spinlock);
|
req_list->req = req;
|
||||||
|
|
||||||
hidg->set_report_buff = krealloc(hidg->set_report_buff,
|
spin_lock_irqsave(&hidg->spinlock, flags);
|
||||||
req->actual, GFP_ATOMIC);
|
list_add_tail(&req_list->list, &hidg->completed_out_req);
|
||||||
|
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
||||||
if (hidg->set_report_buff == NULL) {
|
|
||||||
spin_unlock(&hidg->spinlock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hidg->set_report_length = req->actual;
|
|
||||||
memcpy(hidg->set_report_buff, req->buf, req->actual);
|
|
||||||
|
|
||||||
spin_unlock(&hidg->spinlock);
|
|
||||||
|
|
||||||
wake_up(&hidg->read_queue);
|
wake_up(&hidg->read_queue);
|
||||||
}
|
}
|
||||||
|
@ -344,9 +405,7 @@ static int hidg_setup(struct usb_function *f,
|
||||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||||
| HID_REQ_SET_REPORT):
|
| HID_REQ_SET_REPORT):
|
||||||
VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
|
VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
|
||||||
req->context = hidg;
|
goto stall;
|
||||||
req->complete = hidg_set_report_complete;
|
|
||||||
goto respond;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
|
||||||
|
@ -403,16 +462,25 @@ respond:
|
||||||
static void hidg_disable(struct usb_function *f)
|
static void hidg_disable(struct usb_function *f)
|
||||||
{
|
{
|
||||||
struct f_hidg *hidg = func_to_hidg(f);
|
struct f_hidg *hidg = func_to_hidg(f);
|
||||||
|
struct f_hidg_req_list *list, *next;
|
||||||
|
|
||||||
usb_ep_disable(hidg->in_ep);
|
usb_ep_disable(hidg->in_ep);
|
||||||
hidg->in_ep->driver_data = NULL;
|
hidg->in_ep->driver_data = NULL;
|
||||||
|
|
||||||
|
usb_ep_disable(hidg->out_ep);
|
||||||
|
hidg->out_ep->driver_data = NULL;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
|
||||||
|
list_del(&list->list);
|
||||||
|
kfree(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||||
{
|
{
|
||||||
struct usb_composite_dev *cdev = f->config->cdev;
|
struct usb_composite_dev *cdev = f->config->cdev;
|
||||||
struct f_hidg *hidg = func_to_hidg(f);
|
struct f_hidg *hidg = func_to_hidg(f);
|
||||||
int status = 0;
|
int i, status = 0;
|
||||||
|
|
||||||
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
|
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
|
||||||
|
|
||||||
|
@ -429,11 +497,55 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||||
}
|
}
|
||||||
status = usb_ep_enable(hidg->in_ep);
|
status = usb_ep_enable(hidg->in_ep);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
ERROR(cdev, "Enable endpoint FAILED!\n");
|
ERROR(cdev, "Enable IN endpoint FAILED!\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
hidg->in_ep->driver_data = hidg;
|
hidg->in_ep->driver_data = hidg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (hidg->out_ep != NULL) {
|
||||||
|
/* restart endpoint */
|
||||||
|
if (hidg->out_ep->driver_data != NULL)
|
||||||
|
usb_ep_disable(hidg->out_ep);
|
||||||
|
|
||||||
|
status = config_ep_by_speed(f->config->cdev->gadget, f,
|
||||||
|
hidg->out_ep);
|
||||||
|
if (status) {
|
||||||
|
ERROR(cdev, "config_ep_by_speed FAILED!\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
status = usb_ep_enable(hidg->out_ep);
|
||||||
|
if (status < 0) {
|
||||||
|
ERROR(cdev, "Enable IN endpoint FAILED!\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
hidg->out_ep->driver_data = hidg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate a bunch of read buffers and queue them all at once.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < hidg->qlen && status == 0; i++) {
|
||||||
|
struct usb_request *req =
|
||||||
|
hidg_alloc_ep_req(hidg->out_ep,
|
||||||
|
hidg->report_length);
|
||||||
|
if (req) {
|
||||||
|
req->complete = hidg_set_report_complete;
|
||||||
|
req->context = hidg;
|
||||||
|
status = usb_ep_queue(hidg->out_ep, req,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (status)
|
||||||
|
ERROR(cdev, "%s queue req --> %d\n",
|
||||||
|
hidg->out_ep->name, status);
|
||||||
|
} else {
|
||||||
|
usb_ep_disable(hidg->out_ep);
|
||||||
|
hidg->out_ep->driver_data = NULL;
|
||||||
|
status = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -470,13 +582,18 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
ep->driver_data = c->cdev; /* claim */
|
ep->driver_data = c->cdev; /* claim */
|
||||||
hidg->in_ep = ep;
|
hidg->in_ep = ep;
|
||||||
|
|
||||||
|
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
|
||||||
|
if (!ep)
|
||||||
|
goto fail;
|
||||||
|
ep->driver_data = c->cdev; /* claim */
|
||||||
|
hidg->out_ep = ep;
|
||||||
|
|
||||||
/* preallocate request and buffer */
|
/* preallocate request and buffer */
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
|
hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
|
||||||
if (!hidg->req)
|
if (!hidg->req)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
||||||
hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
|
hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
|
||||||
if (!hidg->req->buf)
|
if (!hidg->req->buf)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -486,12 +603,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
|
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
|
||||||
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||||
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||||
|
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||||
|
hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||||
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
|
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
|
||||||
hidg_desc.desc[0].wDescriptorLength =
|
hidg_desc.desc[0].wDescriptorLength =
|
||||||
cpu_to_le16(hidg->report_desc_length);
|
cpu_to_le16(hidg->report_desc_length);
|
||||||
|
|
||||||
hidg->set_report_buff = NULL;
|
|
||||||
|
|
||||||
/* copy descriptors */
|
/* copy descriptors */
|
||||||
f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
|
f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
|
||||||
if (!f->descriptors)
|
if (!f->descriptors)
|
||||||
|
@ -500,6 +617,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||||
hidg_hs_in_ep_desc.bEndpointAddress =
|
hidg_hs_in_ep_desc.bEndpointAddress =
|
||||||
hidg_fs_in_ep_desc.bEndpointAddress;
|
hidg_fs_in_ep_desc.bEndpointAddress;
|
||||||
|
hidg_hs_out_ep_desc.bEndpointAddress =
|
||||||
|
hidg_fs_out_ep_desc.bEndpointAddress;
|
||||||
f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
|
f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
|
||||||
if (!f->hs_descriptors)
|
if (!f->hs_descriptors)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -509,6 +628,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
spin_lock_init(&hidg->spinlock);
|
spin_lock_init(&hidg->spinlock);
|
||||||
init_waitqueue_head(&hidg->write_queue);
|
init_waitqueue_head(&hidg->write_queue);
|
||||||
init_waitqueue_head(&hidg->read_queue);
|
init_waitqueue_head(&hidg->read_queue);
|
||||||
|
INIT_LIST_HEAD(&hidg->completed_out_req);
|
||||||
|
|
||||||
/* create char device */
|
/* create char device */
|
||||||
cdev_init(&hidg->cdev, &f_hidg_fops);
|
cdev_init(&hidg->cdev, &f_hidg_fops);
|
||||||
|
@ -553,7 +673,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||||
usb_free_descriptors(f->descriptors);
|
usb_free_descriptors(f->descriptors);
|
||||||
|
|
||||||
kfree(hidg->report_desc);
|
kfree(hidg->report_desc);
|
||||||
kfree(hidg->set_report_buff);
|
|
||||||
kfree(hidg);
|
kfree(hidg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,6 +743,9 @@ int __init hidg_bind_config(struct usb_configuration *c,
|
||||||
hidg->func.disable = hidg_disable;
|
hidg->func.disable = hidg_disable;
|
||||||
hidg->func.setup = hidg_setup;
|
hidg->func.setup = hidg_setup;
|
||||||
|
|
||||||
|
/* this could me made configurable at some point */
|
||||||
|
hidg->qlen = 4;
|
||||||
|
|
||||||
status = usb_add_function(c, &hidg->func);
|
status = usb_add_function(c, &hidg->func);
|
||||||
if (status)
|
if (status)
|
||||||
kfree(hidg);
|
kfree(hidg);
|
||||||
|
|
|
@ -44,12 +44,12 @@
|
||||||
* function for a USB device, it also illustrates a technique of
|
* function for a USB device, it also illustrates a technique of
|
||||||
* double-buffering for increased throughput.
|
* double-buffering for increased throughput.
|
||||||
*
|
*
|
||||||
* Function supports multiple logical units (LUNs). Backing storage
|
* For more information about MSF and in particular its module
|
||||||
* for each LUN is provided by a regular file or a block device.
|
* parameters and sysfs interface read the
|
||||||
* Access for each LUN can be limited to read-only. Moreover, the
|
* <Documentation/usb/mass-storage.txt> file.
|
||||||
* function can indicate that LUN is removable and/or CD-ROM. (The
|
*/
|
||||||
* later implies read-only access.)
|
|
||||||
*
|
/*
|
||||||
* MSF is configured by specifying a fsg_config structure. It has the
|
* MSF is configured by specifying a fsg_config structure. It has the
|
||||||
* following fields:
|
* following fields:
|
||||||
*
|
*
|
||||||
|
@ -75,25 +75,6 @@
|
||||||
* ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12)
|
* ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12)
|
||||||
* commands for this LUN shall be ignored.
|
* commands for this LUN shall be ignored.
|
||||||
*
|
*
|
||||||
* lun_name_format A printf-like format for names of the LUN
|
|
||||||
* devices. This determines how the
|
|
||||||
* directory in sysfs will be named.
|
|
||||||
* Unless you are using several MSFs in
|
|
||||||
* a single gadget (as opposed to single
|
|
||||||
* MSF in many configurations) you may
|
|
||||||
* leave it as NULL (in which case
|
|
||||||
* "lun%d" will be used). In the format
|
|
||||||
* you can use "%d" to index LUNs for
|
|
||||||
* MSF's with more than one LUN. (Beware
|
|
||||||
* that there is only one integer given
|
|
||||||
* as an argument for the format and
|
|
||||||
* specifying invalid format may cause
|
|
||||||
* unspecified behaviour.)
|
|
||||||
* thread_name Name of the kernel thread process used by the
|
|
||||||
* MSF. You can safely set it to NULL
|
|
||||||
* (in which case default "file-storage"
|
|
||||||
* will be used).
|
|
||||||
*
|
|
||||||
* vendor_name
|
* vendor_name
|
||||||
* product_name
|
* product_name
|
||||||
* release Information used as a reply to INQUIRY
|
* release Information used as a reply to INQUIRY
|
||||||
|
@ -114,62 +95,6 @@
|
||||||
* data track and no audio tracks; hence there need be only one
|
* data track and no audio tracks; hence there need be only one
|
||||||
* backing file per LUN.
|
* backing file per LUN.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* MSF includes support for module parameters. If gadget using it
|
|
||||||
* decides to use it, the following module parameters will be
|
|
||||||
* available:
|
|
||||||
*
|
|
||||||
* file=filename[,filename...]
|
|
||||||
* Names of the files or block devices used for
|
|
||||||
* backing storage.
|
|
||||||
* ro=b[,b...] Default false, boolean for read-only access.
|
|
||||||
* removable=b[,b...]
|
|
||||||
* Default true, boolean for removable media.
|
|
||||||
* cdrom=b[,b...] Default false, boolean for whether to emulate
|
|
||||||
* a CD-ROM drive.
|
|
||||||
* nofua=b[,b...] Default false, booleans for ignore FUA flag
|
|
||||||
* in SCSI WRITE(10,12) commands
|
|
||||||
* luns=N Default N = number of filenames, number of
|
|
||||||
* LUNs to support.
|
|
||||||
* stall Default determined according to the type of
|
|
||||||
* USB device controller (usually true),
|
|
||||||
* boolean to permit the driver to halt
|
|
||||||
* bulk endpoints.
|
|
||||||
*
|
|
||||||
* The module parameters may be prefixed with some string. You need
|
|
||||||
* to consult gadget's documentation or source to verify whether it is
|
|
||||||
* using those module parameters and if it does what are the prefixes
|
|
||||||
* (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is
|
|
||||||
* the prefix).
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Requirements are modest; only a bulk-in and a bulk-out endpoint are
|
|
||||||
* needed. The memory requirement amounts to two 16K buffers, size
|
|
||||||
* configurable by a parameter. Support is included for both
|
|
||||||
* full-speed and high-speed operation.
|
|
||||||
*
|
|
||||||
* Note that the driver is slightly non-portable in that it assumes a
|
|
||||||
* single memory/DMA buffer will be useable for bulk-in, bulk-out, and
|
|
||||||
* interrupt-in endpoints. With most device controllers this isn't an
|
|
||||||
* issue, but there may be some with hardware restrictions that prevent
|
|
||||||
* a buffer from being used by more than one endpoint.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The pathnames of the backing files and the ro settings are
|
|
||||||
* available in the attribute files "file" and "ro" in the lun<n> (or
|
|
||||||
* to be more precise in a directory which name comes from
|
|
||||||
* "lun_name_format" option!) subdirectory of the gadget's sysfs
|
|
||||||
* directory. If the "removable" option is set, writing to these
|
|
||||||
* files will simulate ejecting/loading the medium (writing an empty
|
|
||||||
* line means eject) and adjusting a write-enable tab. Changes to the
|
|
||||||
* ro setting are not allowed when the medium is loaded or if CD-ROM
|
|
||||||
* emulation is being used.
|
|
||||||
*
|
|
||||||
* When a LUN receive an "eject" SCSI request (Start/Stop Unit),
|
|
||||||
* if the LUN is removable, the backing file is released to simulate
|
|
||||||
* ejection.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This function is heavily based on "File-backed Storage Gadget" by
|
* This function is heavily based on "File-backed Storage Gadget" by
|
||||||
* Alan Stern which in turn is heavily based on "Gadget Zero" by David
|
* Alan Stern which in turn is heavily based on "Gadget Zero" by David
|
||||||
* Brownell. The driver's SCSI command interface was based on the
|
* Brownell. The driver's SCSI command interface was based on the
|
||||||
|
@ -211,7 +136,7 @@
|
||||||
* In normal operation the main thread is started during the gadget's
|
* In normal operation the main thread is started during the gadget's
|
||||||
* fsg_bind() callback and stopped during fsg_unbind(). But it can
|
* fsg_bind() callback and stopped during fsg_unbind(). But it can
|
||||||
* also exit when it receives a signal, and there's no point leaving
|
* also exit when it receives a signal, and there's no point leaving
|
||||||
* the gadget running when the thread is dead. At of this moment, MSF
|
* the gadget running when the thread is dead. As of this moment, MSF
|
||||||
* provides no way to deregister the gadget when thread dies -- maybe
|
* provides no way to deregister the gadget when thread dies -- maybe
|
||||||
* a callback functions is needed.
|
* a callback functions is needed.
|
||||||
*
|
*
|
||||||
|
@ -417,9 +342,6 @@ struct fsg_config {
|
||||||
char nofua;
|
char nofua;
|
||||||
} luns[FSG_MAX_LUNS];
|
} luns[FSG_MAX_LUNS];
|
||||||
|
|
||||||
const char *lun_name_format;
|
|
||||||
const char *thread_name;
|
|
||||||
|
|
||||||
/* Callback functions. */
|
/* Callback functions. */
|
||||||
const struct fsg_operations *ops;
|
const struct fsg_operations *ops;
|
||||||
/* Gadget's private data. */
|
/* Gadget's private data. */
|
||||||
|
@ -2687,11 +2609,15 @@ static int fsg_main_thread(void *common_)
|
||||||
|
|
||||||
/*************************** DEVICE ATTRIBUTES ***************************/
|
/*************************** DEVICE ATTRIBUTES ***************************/
|
||||||
|
|
||||||
/* Write permission is checked per LUN in store_*() functions. */
|
|
||||||
static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
|
static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
|
||||||
static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
|
static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
|
||||||
static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
|
static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
|
||||||
|
|
||||||
|
static struct device_attribute dev_attr_ro_cdrom =
|
||||||
|
__ATTR(ro, 0444, fsg_show_ro, NULL);
|
||||||
|
static struct device_attribute dev_attr_file_nonremovable =
|
||||||
|
__ATTR(file, 0444, fsg_show_file, NULL);
|
||||||
|
|
||||||
|
|
||||||
/****************************** FSG COMMON ******************************/
|
/****************************** FSG COMMON ******************************/
|
||||||
|
|
||||||
|
@ -2792,11 +2718,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
||||||
curlun->dev.parent = &gadget->dev;
|
curlun->dev.parent = &gadget->dev;
|
||||||
/* curlun->dev.driver = &fsg_driver.driver; XXX */
|
/* curlun->dev.driver = &fsg_driver.driver; XXX */
|
||||||
dev_set_drvdata(&curlun->dev, &common->filesem);
|
dev_set_drvdata(&curlun->dev, &common->filesem);
|
||||||
dev_set_name(&curlun->dev,
|
dev_set_name(&curlun->dev, "lun%d", i);
|
||||||
cfg->lun_name_format
|
|
||||||
? cfg->lun_name_format
|
|
||||||
: "lun%d",
|
|
||||||
i);
|
|
||||||
|
|
||||||
rc = device_register(&curlun->dev);
|
rc = device_register(&curlun->dev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -2806,10 +2728,16 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
||||||
goto error_release;
|
goto error_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = device_create_file(&curlun->dev, &dev_attr_ro);
|
rc = device_create_file(&curlun->dev,
|
||||||
|
curlun->cdrom
|
||||||
|
? &dev_attr_ro_cdrom
|
||||||
|
: &dev_attr_ro);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto error_luns;
|
goto error_luns;
|
||||||
rc = device_create_file(&curlun->dev, &dev_attr_file);
|
rc = device_create_file(&curlun->dev,
|
||||||
|
curlun->removable
|
||||||
|
? &dev_attr_file
|
||||||
|
: &dev_attr_file_nonremovable);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto error_luns;
|
goto error_luns;
|
||||||
rc = device_create_file(&curlun->dev, &dev_attr_nofua);
|
rc = device_create_file(&curlun->dev, &dev_attr_nofua);
|
||||||
|
@ -2878,8 +2806,7 @@ buffhds_first_it:
|
||||||
|
|
||||||
/* Tell the thread to start working */
|
/* Tell the thread to start working */
|
||||||
common->thread_task =
|
common->thread_task =
|
||||||
kthread_create(fsg_main_thread, common,
|
kthread_create(fsg_main_thread, common, "file-storage");
|
||||||
cfg->thread_name ?: "file-storage");
|
|
||||||
if (IS_ERR(common->thread_task)) {
|
if (IS_ERR(common->thread_task)) {
|
||||||
rc = PTR_ERR(common->thread_task);
|
rc = PTR_ERR(common->thread_task);
|
||||||
goto error_release;
|
goto error_release;
|
||||||
|
@ -2945,8 +2872,14 @@ static void fsg_common_release(struct kref *ref)
|
||||||
/* In error recovery common->nluns may be zero. */
|
/* In error recovery common->nluns may be zero. */
|
||||||
for (; i; --i, ++lun) {
|
for (; i; --i, ++lun) {
|
||||||
device_remove_file(&lun->dev, &dev_attr_nofua);
|
device_remove_file(&lun->dev, &dev_attr_nofua);
|
||||||
device_remove_file(&lun->dev, &dev_attr_ro);
|
device_remove_file(&lun->dev,
|
||||||
device_remove_file(&lun->dev, &dev_attr_file);
|
lun->cdrom
|
||||||
|
? &dev_attr_ro_cdrom
|
||||||
|
: &dev_attr_ro);
|
||||||
|
device_remove_file(&lun->dev,
|
||||||
|
lun->removable
|
||||||
|
? &dev_attr_file
|
||||||
|
: &dev_attr_file_nonremovable);
|
||||||
fsg_lun_close(lun);
|
fsg_lun_close(lun);
|
||||||
device_unregister(&lun->dev);
|
device_unregister(&lun->dev);
|
||||||
}
|
}
|
||||||
|
@ -3167,8 +3100,7 @@ fsg_config_from_params(struct fsg_config *cfg,
|
||||||
for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
|
for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
|
||||||
lun->ro = !!params->ro[i];
|
lun->ro = !!params->ro[i];
|
||||||
lun->cdrom = !!params->cdrom[i];
|
lun->cdrom = !!params->cdrom[i];
|
||||||
lun->removable = /* Removable by default */
|
lun->removable = !!params->removable[i];
|
||||||
params->removable_count <= i || params->removable[i];
|
|
||||||
lun->filename =
|
lun->filename =
|
||||||
params->file_count > i && params->file[i][0]
|
params->file_count > i && params->file[i][0]
|
||||||
? params->file[i]
|
? params->file[i]
|
||||||
|
@ -3176,8 +3108,6 @@ fsg_config_from_params(struct fsg_config *cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let MSF use defaults */
|
/* Let MSF use defaults */
|
||||||
cfg->lun_name_format = 0;
|
|
||||||
cfg->thread_name = 0;
|
|
||||||
cfg->vendor_name = 0;
|
cfg->vendor_name = 0;
|
||||||
cfg->product_name = 0;
|
cfg->product_name = 0;
|
||||||
cfg->release = 0xffff;
|
cfg->release = 0xffff;
|
||||||
|
@ -3203,4 +3133,3 @@ fsg_common_from_params(struct fsg_common *common,
|
||||||
fsg_config_from_params(&cfg, params);
|
fsg_config_from_params(&cfg, params);
|
||||||
return fsg_common_init(common, cdev, &cfg);
|
return fsg_common_init(common, cdev, &cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,25 @@
|
||||||
|
|
||||||
unsigned int uvc_gadget_trace_param;
|
unsigned int uvc_gadget_trace_param;
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* module parameters specific to the Video streaming endpoint */
|
||||||
|
static unsigned streaming_interval = 1;
|
||||||
|
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(streaming_interval, "1 - 16");
|
||||||
|
|
||||||
|
static unsigned streaming_maxpacket = 1024;
|
||||||
|
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(streaming_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
|
||||||
|
|
||||||
|
static unsigned streaming_mult;
|
||||||
|
module_param(streaming_mult, uint, S_IRUGO|S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(streaming_mult, "0 - 2 (hs/ss only)");
|
||||||
|
|
||||||
|
static unsigned streaming_maxburst;
|
||||||
|
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Function descriptors
|
* Function descriptors
|
||||||
*/
|
*/
|
||||||
|
@ -59,6 +78,8 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
|
||||||
#define UVC_INTF_VIDEO_CONTROL 0
|
#define UVC_INTF_VIDEO_CONTROL 0
|
||||||
#define UVC_INTF_VIDEO_STREAMING 1
|
#define UVC_INTF_VIDEO_STREAMING 1
|
||||||
|
|
||||||
|
#define STATUS_BYTECOUNT 16 /* 16 bytes status */
|
||||||
|
|
||||||
static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
|
static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
|
||||||
.bLength = sizeof(uvc_iad),
|
.bLength = sizeof(uvc_iad),
|
||||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||||
|
@ -82,12 +103,12 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = {
|
||||||
.iInterface = 0,
|
.iInterface = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
|
static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = {
|
||||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_DIR_IN,
|
.bEndpointAddress = USB_DIR_IN,
|
||||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
.wMaxPacketSize = cpu_to_le16(16),
|
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||||
.bInterval = 8,
|
.bInterval = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +116,7 @@ static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
|
||||||
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
|
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||||
.bDescriptorSubType = UVC_EP_INTERRUPT,
|
.bDescriptorSubType = UVC_EP_INTERRUPT,
|
||||||
.wMaxTransferSize = cpu_to_le16(16),
|
.wMaxTransferSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
|
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
|
||||||
|
@ -122,7 +143,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
|
||||||
.iInterface = 0,
|
.iInterface = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_endpoint_descriptor uvc_streaming_ep = {
|
static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
|
||||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
.bDescriptorType = USB_DT_ENDPOINT,
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
.bEndpointAddress = USB_DIR_IN,
|
.bEndpointAddress = USB_DIR_IN,
|
||||||
|
@ -131,15 +152,72 @@ static struct usb_endpoint_descriptor uvc_streaming_ep = {
|
||||||
.bInterval = 1,
|
.bInterval = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_DIR_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(1024),
|
||||||
|
.bInterval = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* super speed support */
|
||||||
|
static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
|
||||||
|
.bEndpointAddress = USB_DIR_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
|
||||||
|
.bInterval = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
|
||||||
|
.bLength = sizeof uvc_ss_control_comp,
|
||||||
|
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||||
|
|
||||||
|
/* the following 3 values can be tweaked if necessary */
|
||||||
|
/* .bMaxBurst = 0, */
|
||||||
|
/* .bmAttributes = 0, */
|
||||||
|
.wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
|
||||||
|
.bEndpointAddress = USB_DIR_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(1024),
|
||||||
|
.bInterval = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
|
||||||
|
.bLength = sizeof uvc_ss_streaming_comp,
|
||||||
|
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||||
|
|
||||||
|
/* the following 3 values can be tweaked if necessary */
|
||||||
|
.bMaxBurst = 0,
|
||||||
|
.bmAttributes = 0,
|
||||||
|
.wBytesPerInterval = cpu_to_le16(1024),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
|
static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
|
||||||
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
||||||
(struct usb_descriptor_header *) &uvc_streaming_ep,
|
(struct usb_descriptor_header *) &uvc_fs_streaming_ep,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
|
static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
|
||||||
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
||||||
(struct usb_descriptor_header *) &uvc_streaming_ep,
|
(struct usb_descriptor_header *) &uvc_hs_streaming_ep,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
|
||||||
|
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
|
||||||
|
(struct usb_descriptor_header *) &uvc_ss_streaming_ep,
|
||||||
|
(struct usb_descriptor_header *) &uvc_ss_streaming_comp,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -215,6 +293,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
|
||||||
struct uvc_device *uvc = to_uvc(f);
|
struct uvc_device *uvc = to_uvc(f);
|
||||||
struct v4l2_event v4l2_event;
|
struct v4l2_event v4l2_event;
|
||||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
|
INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
|
||||||
|
|
||||||
|
@ -262,7 +341,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (uvc->video.ep) {
|
if (uvc->video.ep) {
|
||||||
uvc->video.ep->desc = &uvc_streaming_ep;
|
ret = config_ep_by_speed(f->config->cdev->gadget,
|
||||||
|
&(uvc->func), uvc->video.ep);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
usb_ep_enable(uvc->video.ep);
|
usb_ep_enable(uvc->video.ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,9 +450,11 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||||
{
|
{
|
||||||
struct uvc_input_header_descriptor *uvc_streaming_header;
|
struct uvc_input_header_descriptor *uvc_streaming_header;
|
||||||
struct uvc_header_descriptor *uvc_control_header;
|
struct uvc_header_descriptor *uvc_control_header;
|
||||||
|
const struct uvc_descriptor_header * const *uvc_control_desc;
|
||||||
const struct uvc_descriptor_header * const *uvc_streaming_cls;
|
const struct uvc_descriptor_header * const *uvc_streaming_cls;
|
||||||
const struct usb_descriptor_header * const *uvc_streaming_std;
|
const struct usb_descriptor_header * const *uvc_streaming_std;
|
||||||
const struct usb_descriptor_header * const *src;
|
const struct usb_descriptor_header * const *src;
|
||||||
|
static struct usb_endpoint_descriptor *uvc_control_ep;
|
||||||
struct usb_descriptor_header **dst;
|
struct usb_descriptor_header **dst;
|
||||||
struct usb_descriptor_header **hdr;
|
struct usb_descriptor_header **hdr;
|
||||||
unsigned int control_size;
|
unsigned int control_size;
|
||||||
|
@ -379,10 +463,29 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||||
unsigned int bytes;
|
unsigned int bytes;
|
||||||
void *mem;
|
void *mem;
|
||||||
|
|
||||||
uvc_streaming_cls = (speed == USB_SPEED_FULL)
|
switch (speed) {
|
||||||
? uvc->desc.fs_streaming : uvc->desc.hs_streaming;
|
case USB_SPEED_SUPER:
|
||||||
uvc_streaming_std = (speed == USB_SPEED_FULL)
|
uvc_control_desc = uvc->desc.ss_control;
|
||||||
? uvc_fs_streaming : uvc_hs_streaming;
|
uvc_streaming_cls = uvc->desc.ss_streaming;
|
||||||
|
uvc_streaming_std = uvc_ss_streaming;
|
||||||
|
uvc_control_ep = &uvc_ss_control_ep;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
|
uvc_control_desc = uvc->desc.fs_control;
|
||||||
|
uvc_streaming_cls = uvc->desc.hs_streaming;
|
||||||
|
uvc_streaming_std = uvc_hs_streaming;
|
||||||
|
uvc_control_ep = &uvc_fs_control_ep;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_SPEED_FULL:
|
||||||
|
default:
|
||||||
|
uvc_control_desc = uvc->desc.fs_control;
|
||||||
|
uvc_streaming_cls = uvc->desc.fs_streaming;
|
||||||
|
uvc_streaming_std = uvc_fs_streaming;
|
||||||
|
uvc_control_ep = &uvc_fs_control_ep;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Descriptors layout
|
/* Descriptors layout
|
||||||
*
|
*
|
||||||
|
@ -400,16 +503,24 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||||
control_size = 0;
|
control_size = 0;
|
||||||
streaming_size = 0;
|
streaming_size = 0;
|
||||||
bytes = uvc_iad.bLength + uvc_control_intf.bLength
|
bytes = uvc_iad.bLength + uvc_control_intf.bLength
|
||||||
+ uvc_control_ep.bLength + uvc_control_cs_ep.bLength
|
+ uvc_control_ep->bLength + uvc_control_cs_ep.bLength
|
||||||
+ uvc_streaming_intf_alt0.bLength;
|
+ uvc_streaming_intf_alt0.bLength;
|
||||||
n_desc = 5;
|
|
||||||
|
|
||||||
for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) {
|
if (speed == USB_SPEED_SUPER) {
|
||||||
|
bytes += uvc_ss_control_comp.bLength;
|
||||||
|
n_desc = 6;
|
||||||
|
} else {
|
||||||
|
n_desc = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (src = (const struct usb_descriptor_header **)uvc_control_desc;
|
||||||
|
*src; ++src) {
|
||||||
control_size += (*src)->bLength;
|
control_size += (*src)->bLength;
|
||||||
bytes += (*src)->bLength;
|
bytes += (*src)->bLength;
|
||||||
n_desc++;
|
n_desc++;
|
||||||
}
|
}
|
||||||
for (src = (const struct usb_descriptor_header**)uvc_streaming_cls; *src; ++src) {
|
for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
|
||||||
|
*src; ++src) {
|
||||||
streaming_size += (*src)->bLength;
|
streaming_size += (*src)->bLength;
|
||||||
bytes += (*src)->bLength;
|
bytes += (*src)->bLength;
|
||||||
n_desc++;
|
n_desc++;
|
||||||
|
@ -433,12 +544,15 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||||
|
|
||||||
uvc_control_header = mem;
|
uvc_control_header = mem;
|
||||||
UVC_COPY_DESCRIPTORS(mem, dst,
|
UVC_COPY_DESCRIPTORS(mem, dst,
|
||||||
(const struct usb_descriptor_header**)uvc->desc.control);
|
(const struct usb_descriptor_header **)uvc_control_desc);
|
||||||
uvc_control_header->wTotalLength = cpu_to_le16(control_size);
|
uvc_control_header->wTotalLength = cpu_to_le16(control_size);
|
||||||
uvc_control_header->bInCollection = 1;
|
uvc_control_header->bInCollection = 1;
|
||||||
uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
|
uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
|
||||||
|
|
||||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
|
UVC_COPY_DESCRIPTOR(mem, dst, uvc_control_ep);
|
||||||
|
if (speed == USB_SPEED_SUPER)
|
||||||
|
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
|
||||||
|
|
||||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
|
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
|
||||||
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
|
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
|
||||||
|
|
||||||
|
@ -446,7 +560,8 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||||
UVC_COPY_DESCRIPTORS(mem, dst,
|
UVC_COPY_DESCRIPTORS(mem, dst,
|
||||||
(const struct usb_descriptor_header**)uvc_streaming_cls);
|
(const struct usb_descriptor_header**)uvc_streaming_cls);
|
||||||
uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
|
uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
|
||||||
uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress;
|
uvc_streaming_header->bEndpointAddress =
|
||||||
|
uvc_fs_streaming_ep.bEndpointAddress;
|
||||||
|
|
||||||
UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
|
UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
|
||||||
|
|
||||||
|
@ -482,6 +597,7 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||||
|
|
||||||
kfree(f->descriptors);
|
kfree(f->descriptors);
|
||||||
kfree(f->hs_descriptors);
|
kfree(f->hs_descriptors);
|
||||||
|
kfree(f->ss_descriptors);
|
||||||
|
|
||||||
kfree(uvc);
|
kfree(uvc);
|
||||||
}
|
}
|
||||||
|
@ -496,8 +612,26 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
|
|
||||||
INFO(cdev, "uvc_function_bind\n");
|
INFO(cdev, "uvc_function_bind\n");
|
||||||
|
|
||||||
|
/* sanity check the streaming endpoint module parameters */
|
||||||
|
if (streaming_interval < 1)
|
||||||
|
streaming_interval = 1;
|
||||||
|
if (streaming_interval > 16)
|
||||||
|
streaming_interval = 16;
|
||||||
|
if (streaming_mult > 2)
|
||||||
|
streaming_mult = 2;
|
||||||
|
if (streaming_maxburst > 15)
|
||||||
|
streaming_maxburst = 15;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fill in the FS video streaming specific descriptors from the
|
||||||
|
* module parameters
|
||||||
|
*/
|
||||||
|
uvc_fs_streaming_ep.wMaxPacketSize = streaming_maxpacket > 1023 ?
|
||||||
|
1023 : streaming_maxpacket;
|
||||||
|
uvc_fs_streaming_ep.bInterval = streaming_interval;
|
||||||
|
|
||||||
/* Allocate endpoints. */
|
/* Allocate endpoints. */
|
||||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
|
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_control_ep);
|
||||||
if (!ep) {
|
if (!ep) {
|
||||||
INFO(cdev, "Unable to allocate control EP\n");
|
INFO(cdev, "Unable to allocate control EP\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -505,7 +639,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
uvc->control_ep = ep;
|
uvc->control_ep = ep;
|
||||||
ep->driver_data = uvc;
|
ep->driver_data = uvc;
|
||||||
|
|
||||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep);
|
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
|
||||||
if (!ep) {
|
if (!ep) {
|
||||||
INFO(cdev, "Unable to allocate streaming EP\n");
|
INFO(cdev, "Unable to allocate streaming EP\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -526,9 +660,52 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||||
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
|
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
|
||||||
uvc->streaming_intf = ret;
|
uvc->streaming_intf = ret;
|
||||||
|
|
||||||
/* Copy descriptors. */
|
/* sanity check the streaming endpoint module parameters */
|
||||||
|
if (streaming_maxpacket > 1024)
|
||||||
|
streaming_maxpacket = 1024;
|
||||||
|
|
||||||
|
/* Copy descriptors for FS. */
|
||||||
f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
|
f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
|
||||||
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
|
|
||||||
|
/* support high speed hardware */
|
||||||
|
if (gadget_is_dualspeed(cdev->gadget)) {
|
||||||
|
/*
|
||||||
|
* Fill in the HS descriptors from the module parameters for the
|
||||||
|
* Video Streaming endpoint.
|
||||||
|
* NOTE: We assume that the user knows what they are doing and
|
||||||
|
* won't give parameters that their UDC doesn't support.
|
||||||
|
*/
|
||||||
|
uvc_hs_streaming_ep.wMaxPacketSize = streaming_maxpacket;
|
||||||
|
uvc_hs_streaming_ep.wMaxPacketSize |= streaming_mult << 11;
|
||||||
|
uvc_hs_streaming_ep.bInterval = streaming_interval;
|
||||||
|
uvc_hs_streaming_ep.bEndpointAddress =
|
||||||
|
uvc_fs_streaming_ep.bEndpointAddress;
|
||||||
|
|
||||||
|
/* Copy descriptors. */
|
||||||
|
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* support super speed hardware */
|
||||||
|
if (gadget_is_superspeed(c->cdev->gadget)) {
|
||||||
|
/*
|
||||||
|
* Fill in the SS descriptors from the module parameters for the
|
||||||
|
* Video Streaming endpoint.
|
||||||
|
* NOTE: We assume that the user knows what they are doing and
|
||||||
|
* won't give parameters that their UDC doesn't support.
|
||||||
|
*/
|
||||||
|
uvc_ss_streaming_ep.wMaxPacketSize = streaming_maxpacket;
|
||||||
|
uvc_ss_streaming_ep.bInterval = streaming_interval;
|
||||||
|
uvc_ss_streaming_comp.bmAttributes = streaming_mult;
|
||||||
|
uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
|
||||||
|
uvc_ss_streaming_comp.wBytesPerInterval =
|
||||||
|
streaming_maxpacket * (streaming_mult + 1) *
|
||||||
|
(streaming_maxburst + 1);
|
||||||
|
uvc_ss_streaming_ep.bEndpointAddress =
|
||||||
|
uvc_fs_streaming_ep.bEndpointAddress;
|
||||||
|
|
||||||
|
/* Copy descriptors. */
|
||||||
|
f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
|
||||||
|
}
|
||||||
|
|
||||||
/* Preallocate control endpoint request. */
|
/* Preallocate control endpoint request. */
|
||||||
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
|
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
|
||||||
|
@ -583,9 +760,11 @@ error:
|
||||||
*/
|
*/
|
||||||
int __init
|
int __init
|
||||||
uvc_bind_config(struct usb_configuration *c,
|
uvc_bind_config(struct usb_configuration *c,
|
||||||
const struct uvc_descriptor_header * const *control,
|
const struct uvc_descriptor_header * const *fs_control,
|
||||||
|
const struct uvc_descriptor_header * const *ss_control,
|
||||||
const struct uvc_descriptor_header * const *fs_streaming,
|
const struct uvc_descriptor_header * const *fs_streaming,
|
||||||
const struct uvc_descriptor_header * const *hs_streaming)
|
const struct uvc_descriptor_header * const *hs_streaming,
|
||||||
|
const struct uvc_descriptor_header * const *ss_streaming)
|
||||||
{
|
{
|
||||||
struct uvc_device *uvc;
|
struct uvc_device *uvc;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -603,38 +782,54 @@ uvc_bind_config(struct usb_configuration *c,
|
||||||
uvc->state = UVC_STATE_DISCONNECTED;
|
uvc->state = UVC_STATE_DISCONNECTED;
|
||||||
|
|
||||||
/* Validate the descriptors. */
|
/* Validate the descriptors. */
|
||||||
if (control == NULL || control[0] == NULL ||
|
if (fs_control == NULL || fs_control[0] == NULL ||
|
||||||
control[0]->bDescriptorSubType != UVC_VC_HEADER)
|
fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (ss_control == NULL || ss_control[0] == NULL ||
|
||||||
|
ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (fs_streaming == NULL || fs_streaming[0] == NULL ||
|
if (fs_streaming == NULL || fs_streaming[0] == NULL ||
|
||||||
fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (hs_streaming == NULL || hs_streaming[0] == NULL ||
|
if (hs_streaming == NULL || hs_streaming[0] == NULL ||
|
||||||
hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
uvc->desc.control = control;
|
if (ss_streaming == NULL || ss_streaming[0] == NULL ||
|
||||||
|
ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
uvc->desc.fs_control = fs_control;
|
||||||
|
uvc->desc.ss_control = ss_control;
|
||||||
uvc->desc.fs_streaming = fs_streaming;
|
uvc->desc.fs_streaming = fs_streaming;
|
||||||
uvc->desc.hs_streaming = hs_streaming;
|
uvc->desc.hs_streaming = hs_streaming;
|
||||||
|
uvc->desc.ss_streaming = ss_streaming;
|
||||||
|
|
||||||
/* Allocate string descriptor numbers. */
|
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) {
|
||||||
goto error;
|
/* Allocate string descriptor numbers. */
|
||||||
uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
|
ret = usb_string_id(c->cdev);
|
||||||
uvc_iad.iFunction = ret;
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
|
||||||
|
uvc_iad.iFunction = ret;
|
||||||
|
|
||||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
ret = usb_string_id(c->cdev);
|
||||||
goto error;
|
if (ret < 0)
|
||||||
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
|
goto error;
|
||||||
uvc_control_intf.iInterface = ret;
|
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
|
||||||
|
uvc_control_intf.iInterface = ret;
|
||||||
|
|
||||||
if ((ret = usb_string_id(c->cdev)) < 0)
|
ret = usb_string_id(c->cdev);
|
||||||
goto error;
|
if (ret < 0)
|
||||||
uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
|
goto error;
|
||||||
uvc_streaming_intf_alt0.iInterface = ret;
|
uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
|
||||||
uvc_streaming_intf_alt1.iInterface = ret;
|
uvc_streaming_intf_alt0.iInterface = ret;
|
||||||
|
uvc_streaming_intf_alt1.iInterface = ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Register the function. */
|
/* Register the function. */
|
||||||
uvc->func.name = "uvc";
|
uvc->func.name = "uvc";
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
#include <linux/usb/video.h>
|
#include <linux/usb/video.h>
|
||||||
|
|
||||||
extern int uvc_bind_config(struct usb_configuration *c,
|
extern int uvc_bind_config(struct usb_configuration *c,
|
||||||
const struct uvc_descriptor_header * const *control,
|
const struct uvc_descriptor_header * const *fs_control,
|
||||||
const struct uvc_descriptor_header * const *fs_streaming,
|
const struct uvc_descriptor_header * const *hs_control,
|
||||||
const struct uvc_descriptor_header * const *hs_streaming);
|
const struct uvc_descriptor_header * const *fs_streaming,
|
||||||
|
const struct uvc_descriptor_header * const *hs_streaming,
|
||||||
|
const struct uvc_descriptor_header * const *ss_streaming);
|
||||||
|
|
||||||
#endif /* _F_UVC_H_ */
|
#endif /* _F_UVC_H_ */
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
|
|
||||||
static struct clk *mxc_ahb_clk;
|
static struct clk *mxc_ahb_clk;
|
||||||
static struct clk *mxc_usb_clk;
|
static struct clk *mxc_per_clk;
|
||||||
|
static struct clk *mxc_ipg_clk;
|
||||||
|
|
||||||
/* workaround ENGcm09152 for i.MX35 */
|
/* workaround ENGcm09152 for i.MX35 */
|
||||||
#define USBPHYCTRL_OTGBASE_OFFSET 0x608
|
#define USBPHYCTRL_OTGBASE_OFFSET 0x608
|
||||||
|
@ -35,28 +36,31 @@ int fsl_udc_clk_init(struct platform_device *pdev)
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = pdev->dev.platform_data;
|
||||||
|
|
||||||
if (!cpu_is_mx35() && !cpu_is_mx25()) {
|
mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg");
|
||||||
mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
|
if (IS_ERR(mxc_ipg_clk)) {
|
||||||
if (IS_ERR(mxc_ahb_clk))
|
dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n");
|
||||||
return PTR_ERR(mxc_ahb_clk);
|
return PTR_ERR(mxc_ipg_clk);
|
||||||
|
|
||||||
ret = clk_enable(mxc_ahb_clk);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
|
|
||||||
goto eenahb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb");
|
||||||
|
if (IS_ERR(mxc_ahb_clk)) {
|
||||||
|
dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n");
|
||||||
|
return PTR_ERR(mxc_ahb_clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
mxc_per_clk = devm_clk_get(&pdev->dev, "per");
|
||||||
|
if (IS_ERR(mxc_per_clk)) {
|
||||||
|
dev_err(&pdev->dev, "clk_get(\"per\") failed\n");
|
||||||
|
return PTR_ERR(mxc_per_clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_prepare_enable(mxc_ipg_clk);
|
||||||
|
clk_prepare_enable(mxc_ahb_clk);
|
||||||
|
clk_prepare_enable(mxc_per_clk);
|
||||||
|
|
||||||
/* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
|
/* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
|
||||||
mxc_usb_clk = clk_get(&pdev->dev, "usb");
|
|
||||||
if (IS_ERR(mxc_usb_clk)) {
|
|
||||||
dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
|
|
||||||
ret = PTR_ERR(mxc_usb_clk);
|
|
||||||
goto egusb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cpu_is_mx51()) {
|
if (!cpu_is_mx51()) {
|
||||||
freq = clk_get_rate(mxc_usb_clk);
|
freq = clk_get_rate(mxc_per_clk);
|
||||||
if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
|
if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
|
||||||
(freq < 59999000 || freq > 60001000)) {
|
(freq < 59999000 || freq > 60001000)) {
|
||||||
dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
|
dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
|
||||||
|
@ -65,24 +69,13 @@ int fsl_udc_clk_init(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_enable(mxc_usb_clk);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
|
|
||||||
goto eenusb;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
eenusb:
|
|
||||||
eclkrate:
|
eclkrate:
|
||||||
clk_put(mxc_usb_clk);
|
clk_disable_unprepare(mxc_ipg_clk);
|
||||||
mxc_usb_clk = NULL;
|
clk_disable_unprepare(mxc_ahb_clk);
|
||||||
egusb:
|
clk_disable_unprepare(mxc_per_clk);
|
||||||
if (!cpu_is_mx35())
|
mxc_per_clk = NULL;
|
||||||
clk_disable(mxc_ahb_clk);
|
|
||||||
eenahb:
|
|
||||||
if (!cpu_is_mx35())
|
|
||||||
clk_put(mxc_ahb_clk);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,20 +97,15 @@ void fsl_udc_clk_finalize(struct platform_device *pdev)
|
||||||
|
|
||||||
/* ULPI transceivers don't need usbpll */
|
/* ULPI transceivers don't need usbpll */
|
||||||
if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
|
if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
|
||||||
clk_disable(mxc_usb_clk);
|
clk_disable_unprepare(mxc_per_clk);
|
||||||
clk_put(mxc_usb_clk);
|
mxc_per_clk = NULL;
|
||||||
mxc_usb_clk = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsl_udc_clk_release(void)
|
void fsl_udc_clk_release(void)
|
||||||
{
|
{
|
||||||
if (mxc_usb_clk) {
|
if (mxc_per_clk)
|
||||||
clk_disable(mxc_usb_clk);
|
clk_disable_unprepare(mxc_per_clk);
|
||||||
clk_put(mxc_usb_clk);
|
clk_disable_unprepare(mxc_ahb_clk);
|
||||||
}
|
clk_disable_unprepare(mxc_ipg_clk);
|
||||||
if (!cpu_is_mx35()) {
|
|
||||||
clk_disable(mxc_ahb_clk);
|
|
||||||
clk_put(mxc_ahb_clk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
@ -1229,7 +1230,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||||
struct fsl_udc *udc;
|
struct fsl_udc *udc;
|
||||||
|
|
||||||
udc = container_of(gadget, struct fsl_udc, gadget);
|
udc = container_of(gadget, struct fsl_udc, gadget);
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return usb_phy_set_power(udc->transceiver, mA);
|
return usb_phy_set_power(udc->transceiver, mA);
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -1983,13 +1984,13 @@ static int fsl_start(struct usb_gadget_driver *driver,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udc_controller->transceiver) {
|
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||||
/* Suspend the controller until OTG enable it */
|
/* Suspend the controller until OTG enable it */
|
||||||
udc_controller->stopped = 1;
|
udc_controller->stopped = 1;
|
||||||
printk(KERN_INFO "Suspend udc for OTG auto detect\n");
|
printk(KERN_INFO "Suspend udc for OTG auto detect\n");
|
||||||
|
|
||||||
/* connect to bus through transceiver */
|
/* connect to bus through transceiver */
|
||||||
if (udc_controller->transceiver) {
|
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||||
retval = otg_set_peripheral(
|
retval = otg_set_peripheral(
|
||||||
udc_controller->transceiver->otg,
|
udc_controller->transceiver->otg,
|
||||||
&udc_controller->gadget);
|
&udc_controller->gadget);
|
||||||
|
@ -2030,7 +2031,7 @@ static int fsl_stop(struct usb_gadget_driver *driver)
|
||||||
if (!driver || driver != udc_controller->driver || !driver->unbind)
|
if (!driver || driver != udc_controller->driver || !driver->unbind)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (udc_controller->transceiver)
|
if (!IS_ERR_OR_NULL(udc_controller->transceiver))
|
||||||
otg_set_peripheral(udc_controller->transceiver->otg, NULL);
|
otg_set_peripheral(udc_controller->transceiver->otg, NULL);
|
||||||
|
|
||||||
/* stop DR, disable intr */
|
/* stop DR, disable intr */
|
||||||
|
@ -2455,8 +2456,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
#ifdef CONFIG_USB_OTG
|
#ifdef CONFIG_USB_OTG
|
||||||
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||||
udc_controller->transceiver = usb_get_transceiver();
|
udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
if (!udc_controller->transceiver) {
|
if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||||
ERR("Can't find OTG driver!\n");
|
ERR("Can't find OTG driver!\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
|
@ -2540,7 +2541,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!udc_controller->transceiver) {
|
if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||||
/* initialize usb hw reg except for regs for EP,
|
/* initialize usb hw reg except for regs for EP,
|
||||||
* leave usbintr reg untouched */
|
* leave usbintr reg untouched */
|
||||||
dr_controller_setup(udc_controller);
|
dr_controller_setup(udc_controller);
|
||||||
|
@ -2560,11 +2561,12 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||||
dev_set_name(&udc_controller->gadget.dev, "gadget");
|
dev_set_name(&udc_controller->gadget.dev, "gadget");
|
||||||
udc_controller->gadget.dev.release = fsl_udc_release;
|
udc_controller->gadget.dev.release = fsl_udc_release;
|
||||||
udc_controller->gadget.dev.parent = &pdev->dev;
|
udc_controller->gadget.dev.parent = &pdev->dev;
|
||||||
|
udc_controller->gadget.dev.of_node = pdev->dev.of_node;
|
||||||
ret = device_register(&udc_controller->gadget.dev);
|
ret = device_register(&udc_controller->gadget.dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
|
|
||||||
if (udc_controller->transceiver)
|
if (!IS_ERR_OR_NULL(udc_controller->transceiver))
|
||||||
udc_controller->gadget.is_otg = 1;
|
udc_controller->gadget.is_otg = 1;
|
||||||
|
|
||||||
/* setup QH and epctrl for ep0 */
|
/* setup QH and epctrl for ep0 */
|
||||||
|
|
|
@ -1432,7 +1432,7 @@ static int __init imx_udc_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev, "can't get USB clock\n");
|
dev_err(&pdev->dev, "can't get USB clock\n");
|
||||||
goto fail2;
|
goto fail2;
|
||||||
}
|
}
|
||||||
clk_enable(clk);
|
clk_prepare_enable(clk);
|
||||||
|
|
||||||
if (clk_get_rate(clk) != 48000000) {
|
if (clk_get_rate(clk) != 48000000) {
|
||||||
D_INI(&pdev->dev,
|
D_INI(&pdev->dev,
|
||||||
|
@ -1496,7 +1496,7 @@ fail4:
|
||||||
free_irq(imx_usb->usbd_int[i], imx_usb);
|
free_irq(imx_usb->usbd_int[i], imx_usb);
|
||||||
fail3:
|
fail3:
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
clk_disable(clk);
|
clk_disable_unprepare(clk);
|
||||||
fail2:
|
fail2:
|
||||||
iounmap(base);
|
iounmap(base);
|
||||||
fail1:
|
fail1:
|
||||||
|
@ -1521,7 +1521,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)
|
||||||
free_irq(imx_usb->usbd_int[i], imx_usb);
|
free_irq(imx_usb->usbd_int[i], imx_usb);
|
||||||
|
|
||||||
clk_put(imx_usb->clk);
|
clk_put(imx_usb->clk);
|
||||||
clk_disable(imx_usb->clk);
|
clk_disable_unprepare(imx_usb->clk);
|
||||||
iounmap(imx_usb->base);
|
iounmap(imx_usb->base);
|
||||||
|
|
||||||
release_mem_region(imx_usb->res->start, resource_size(imx_usb->res));
|
release_mem_region(imx_usb->res->start, resource_size(imx_usb->res));
|
||||||
|
|
|
@ -165,6 +165,7 @@ struct lpc32xx_udc {
|
||||||
int udp_irq[4];
|
int udp_irq[4];
|
||||||
struct clk *usb_pll_clk;
|
struct clk *usb_pll_clk;
|
||||||
struct clk *usb_slv_clk;
|
struct clk *usb_slv_clk;
|
||||||
|
struct clk *usb_otg_clk;
|
||||||
|
|
||||||
/* DMA support */
|
/* DMA support */
|
||||||
u32 *udca_v_base;
|
u32 *udca_v_base;
|
||||||
|
@ -227,33 +228,15 @@ static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g)
|
||||||
#define UDCA_BUFF_SIZE (128)
|
#define UDCA_BUFF_SIZE (128)
|
||||||
|
|
||||||
/* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will
|
/* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will
|
||||||
* be replaced with an inremap()ed pointer, see USB_OTG_CLK_CTRL()
|
* be replaced with an inremap()ed pointer
|
||||||
* */
|
* */
|
||||||
#define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64)
|
#define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64)
|
||||||
#define USB_CLOCK_MASK (AHB_M_CLOCK_ON | OTG_CLOCK_ON | \
|
|
||||||
DEV_CLOCK_ON | I2C_CLOCK_ON)
|
|
||||||
|
|
||||||
/* USB_CTRL bit defines */
|
/* USB_CTRL bit defines */
|
||||||
#define USB_SLAVE_HCLK_EN (1 << 24)
|
#define USB_SLAVE_HCLK_EN (1 << 24)
|
||||||
#define USB_HOST_NEED_CLK_EN (1 << 21)
|
#define USB_HOST_NEED_CLK_EN (1 << 21)
|
||||||
#define USB_DEV_NEED_CLK_EN (1 << 22)
|
#define USB_DEV_NEED_CLK_EN (1 << 22)
|
||||||
|
|
||||||
#define USB_OTG_CLK_CTRL(udc) ((udc)->udp_baseaddr + 0xFF4)
|
|
||||||
#define USB_OTG_CLK_STAT(udc) ((udc)->udp_baseaddr + 0xFF8)
|
|
||||||
|
|
||||||
/* USB_OTG_CLK_CTRL bit defines */
|
|
||||||
#define AHB_M_CLOCK_ON (1 << 4)
|
|
||||||
#define OTG_CLOCK_ON (1 << 3)
|
|
||||||
#define I2C_CLOCK_ON (1 << 2)
|
|
||||||
#define DEV_CLOCK_ON (1 << 1)
|
|
||||||
#define HOST_CLOCK_ON (1 << 0)
|
|
||||||
|
|
||||||
#define USB_OTG_STAT_CONTROL(udc) (udc->udp_baseaddr + 0x110)
|
|
||||||
|
|
||||||
/* USB_OTG_STAT_CONTROL bit defines */
|
|
||||||
#define TRANSPARENT_I2C_EN (1 << 7)
|
|
||||||
#define HOST_EN (1 << 0)
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* USB device controller register offsets
|
* USB device controller register offsets
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
@ -677,7 +660,7 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
||||||
ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
|
ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
|
||||||
|
|
||||||
/* Enable usb_need_clk clock after transceiver is initialized */
|
/* Enable usb_need_clk clock after transceiver is initialized */
|
||||||
writel((readl(USB_CTRL) | (1 << 22)), USB_CTRL);
|
writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL);
|
||||||
|
|
||||||
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n",
|
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n",
|
||||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
|
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
|
||||||
|
@ -1010,11 +993,8 @@ static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd)
|
||||||
/* Enables or disables most of the USB system clocks when low power mode is
|
/* Enables or disables most of the USB system clocks when low power mode is
|
||||||
* needed. Clocks are typically started on a connection event, and disabled
|
* needed. Clocks are typically started on a connection event, and disabled
|
||||||
* when a cable is disconnected */
|
* when a cable is disconnected */
|
||||||
#define OTGOFF_CLK_MASK (AHB_M_CLOCK_ON | I2C_CLOCK_ON)
|
|
||||||
static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
||||||
{
|
{
|
||||||
int to = 1000;
|
|
||||||
|
|
||||||
if (enable != 0) {
|
if (enable != 0) {
|
||||||
if (udc->clocked)
|
if (udc->clocked)
|
||||||
return;
|
return;
|
||||||
|
@ -1028,14 +1008,7 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
||||||
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN,
|
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN,
|
||||||
USB_CTRL);
|
USB_CTRL);
|
||||||
|
|
||||||
/* Set to enable all needed USB OTG clocks */
|
clk_enable(udc->usb_otg_clk);
|
||||||
writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc));
|
|
||||||
|
|
||||||
while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) !=
|
|
||||||
USB_CLOCK_MASK) && (to > 0))
|
|
||||||
to--;
|
|
||||||
if (!to)
|
|
||||||
dev_dbg(udc->dev, "Cannot enable USB OTG clocking\n");
|
|
||||||
} else {
|
} else {
|
||||||
if (!udc->clocked)
|
if (!udc->clocked)
|
||||||
return;
|
return;
|
||||||
|
@ -1047,19 +1020,11 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
||||||
/* 48MHz PLL dpwn */
|
/* 48MHz PLL dpwn */
|
||||||
clk_disable(udc->usb_pll_clk);
|
clk_disable(udc->usb_pll_clk);
|
||||||
|
|
||||||
/* Enable the USB device clock */
|
/* Disable the USB device clock */
|
||||||
writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN,
|
writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN,
|
||||||
USB_CTRL);
|
USB_CTRL);
|
||||||
|
|
||||||
/* Set to enable all needed USB OTG clocks */
|
clk_disable(udc->usb_otg_clk);
|
||||||
writel(OTGOFF_CLK_MASK, USB_OTG_CLK_CTRL(udc));
|
|
||||||
|
|
||||||
while (((readl(USB_OTG_CLK_STAT(udc)) &
|
|
||||||
OTGOFF_CLK_MASK) !=
|
|
||||||
OTGOFF_CLK_MASK) && (to > 0))
|
|
||||||
to--;
|
|
||||||
if (!to)
|
|
||||||
dev_dbg(udc->dev, "Cannot disable USB OTG clocking\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3041,6 +3006,7 @@ static int lpc32xx_start(struct usb_gadget_driver *driver,
|
||||||
|
|
||||||
udc->driver = driver;
|
udc->driver = driver;
|
||||||
udc->gadget.dev.driver = &driver->driver;
|
udc->gadget.dev.driver = &driver->driver;
|
||||||
|
udc->gadget.dev.of_node = udc->dev->of_node;
|
||||||
udc->enabled = 1;
|
udc->enabled = 1;
|
||||||
udc->selfpowered = 1;
|
udc->selfpowered = 1;
|
||||||
udc->vbus = 0;
|
udc->vbus = 0;
|
||||||
|
@ -3239,6 +3205,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
|
||||||
retval = PTR_ERR(udc->usb_slv_clk);
|
retval = PTR_ERR(udc->usb_slv_clk);
|
||||||
goto usb_clk_get_fail;
|
goto usb_clk_get_fail;
|
||||||
}
|
}
|
||||||
|
udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
|
||||||
|
if (IS_ERR(udc->usb_otg_clk)) {
|
||||||
|
dev_err(udc->dev, "failed to acquire USB otg clock\n");
|
||||||
|
retval = PTR_ERR(udc->usb_slv_clk);
|
||||||
|
goto usb_otg_clk_get_fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* Setup PLL clock to 48MHz */
|
/* Setup PLL clock to 48MHz */
|
||||||
retval = clk_enable(udc->usb_pll_clk);
|
retval = clk_enable(udc->usb_pll_clk);
|
||||||
|
@ -3262,15 +3234,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
|
||||||
goto usb_clk_enable_fail;
|
goto usb_clk_enable_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set to enable all needed USB OTG clocks */
|
/* Enable USB OTG clock */
|
||||||
writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc));
|
retval = clk_enable(udc->usb_otg_clk);
|
||||||
|
if (retval < 0) {
|
||||||
i = 1000;
|
dev_err(udc->dev, "failed to start USB otg clock\n");
|
||||||
while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) !=
|
goto usb_otg_clk_enable_fail;
|
||||||
USB_CLOCK_MASK) && (i > 0))
|
}
|
||||||
i--;
|
|
||||||
if (!i)
|
|
||||||
dev_dbg(udc->dev, "USB OTG clocks not correctly enabled\n");
|
|
||||||
|
|
||||||
/* Setup deferred workqueue data */
|
/* Setup deferred workqueue data */
|
||||||
udc->poweron = udc->pullup = 0;
|
udc->poweron = udc->pullup = 0;
|
||||||
|
@ -3390,12 +3359,16 @@ dma_alloc_fail:
|
||||||
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
||||||
udc->udca_v_base, udc->udca_p_base);
|
udc->udca_v_base, udc->udca_p_base);
|
||||||
i2c_fail:
|
i2c_fail:
|
||||||
|
clk_disable(udc->usb_otg_clk);
|
||||||
|
usb_otg_clk_enable_fail:
|
||||||
clk_disable(udc->usb_slv_clk);
|
clk_disable(udc->usb_slv_clk);
|
||||||
usb_clk_enable_fail:
|
usb_clk_enable_fail:
|
||||||
pll_set_fail:
|
pll_set_fail:
|
||||||
clk_disable(udc->usb_pll_clk);
|
clk_disable(udc->usb_pll_clk);
|
||||||
pll_enable_fail:
|
pll_enable_fail:
|
||||||
clk_put(udc->usb_slv_clk);
|
clk_put(udc->usb_slv_clk);
|
||||||
|
usb_otg_clk_get_fail:
|
||||||
|
clk_put(udc->usb_otg_clk);
|
||||||
usb_clk_get_fail:
|
usb_clk_get_fail:
|
||||||
clk_put(udc->usb_pll_clk);
|
clk_put(udc->usb_pll_clk);
|
||||||
pll_get_fail:
|
pll_get_fail:
|
||||||
|
@ -3433,6 +3406,8 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
device_unregister(&udc->gadget.dev);
|
device_unregister(&udc->gadget.dev);
|
||||||
|
|
||||||
|
clk_disable(udc->usb_otg_clk);
|
||||||
|
clk_put(udc->usb_otg_clk);
|
||||||
clk_disable(udc->usb_slv_clk);
|
clk_disable(udc->usb_slv_clk);
|
||||||
clk_put(udc->usb_slv_clk);
|
clk_put(udc->usb_slv_clk);
|
||||||
clk_disable(udc->usb_pll_clk);
|
clk_disable(udc->usb_pll_clk);
|
||||||
|
@ -3446,7 +3421,6 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
int to = 1000;
|
|
||||||
struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
|
struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (udc->clocked) {
|
if (udc->clocked) {
|
||||||
|
@ -3461,15 +3435,6 @@ static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||||
on resume */
|
on resume */
|
||||||
udc->clocked = 1;
|
udc->clocked = 1;
|
||||||
|
|
||||||
/* Kill OTG and I2C clocks */
|
|
||||||
writel(0, USB_OTG_CLK_CTRL(udc));
|
|
||||||
while (((readl(USB_OTG_CLK_STAT(udc)) & OTGOFF_CLK_MASK) !=
|
|
||||||
OTGOFF_CLK_MASK) && (to > 0))
|
|
||||||
to--;
|
|
||||||
if (!to)
|
|
||||||
dev_dbg(udc->dev,
|
|
||||||
"USB OTG clocks not correctly enabled\n");
|
|
||||||
|
|
||||||
/* Kill global USB clock */
|
/* Kill global USB clock */
|
||||||
clk_disable(udc->usb_slv_clk);
|
clk_disable(udc->usb_slv_clk);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MV_U3D_H
|
||||||
|
#define __MV_U3D_H
|
||||||
|
|
||||||
|
#define MV_U3D_EP_CONTEXT_ALIGNMENT 32
|
||||||
|
#define MV_U3D_TRB_ALIGNMENT 16
|
||||||
|
#define MV_U3D_DMA_BOUNDARY 4096
|
||||||
|
#define MV_U3D_EP0_MAX_PKT_SIZE 512
|
||||||
|
|
||||||
|
/* ep0 transfer state */
|
||||||
|
#define MV_U3D_WAIT_FOR_SETUP 0
|
||||||
|
#define MV_U3D_DATA_STATE_XMIT 1
|
||||||
|
#define MV_U3D_DATA_STATE_NEED_ZLP 2
|
||||||
|
#define MV_U3D_WAIT_FOR_OUT_STATUS 3
|
||||||
|
#define MV_U3D_DATA_STATE_RECV 4
|
||||||
|
#define MV_U3D_STATUS_STAGE 5
|
||||||
|
|
||||||
|
#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000
|
||||||
|
|
||||||
|
/* USB3 Interrupt Status */
|
||||||
|
#define MV_U3D_USBINT_SETUP 0x00000001
|
||||||
|
#define MV_U3D_USBINT_RX_COMPLETE 0x00000002
|
||||||
|
#define MV_U3D_USBINT_TX_COMPLETE 0x00000004
|
||||||
|
#define MV_U3D_USBINT_UNDER_RUN 0x00000008
|
||||||
|
#define MV_U3D_USBINT_RXDESC_ERR 0x00000010
|
||||||
|
#define MV_U3D_USBINT_TXDESC_ERR 0x00000020
|
||||||
|
#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040
|
||||||
|
#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080
|
||||||
|
#define MV_U3D_USBINT_VBUS_VALID 0x00010000
|
||||||
|
#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000
|
||||||
|
#define MV_U3D_USBINT_LINK_CHG 0x01000000
|
||||||
|
|
||||||
|
/* USB3 Interrupt Enable */
|
||||||
|
#define MV_U3D_INTR_ENABLE_SETUP 0x00000001
|
||||||
|
#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002
|
||||||
|
#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004
|
||||||
|
#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008
|
||||||
|
#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010
|
||||||
|
#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020
|
||||||
|
#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040
|
||||||
|
#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080
|
||||||
|
#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100
|
||||||
|
#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000
|
||||||
|
#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000
|
||||||
|
#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000
|
||||||
|
#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000
|
||||||
|
|
||||||
|
/* USB3 Link Change */
|
||||||
|
#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001
|
||||||
|
#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002
|
||||||
|
#define MV_U3D_LINK_CHANGE_RESUME 0x00000004
|
||||||
|
#define MV_U3D_LINK_CHANGE_WRESET 0x00000008
|
||||||
|
#define MV_U3D_LINK_CHANGE_HRESET 0x00000010
|
||||||
|
#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020
|
||||||
|
#define MV_U3D_LINK_CHANGE_INACT 0x00000040
|
||||||
|
#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080
|
||||||
|
#define MV_U3D_LINK_CHANGE_U1 0x00000100
|
||||||
|
#define MV_U3D_LINK_CHANGE_U2 0x00000200
|
||||||
|
#define MV_U3D_LINK_CHANGE_U3 0x00000400
|
||||||
|
|
||||||
|
/* bridge setting */
|
||||||
|
#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16)
|
||||||
|
|
||||||
|
/* Command Register Bit Masks */
|
||||||
|
#define MV_U3D_CMD_RUN_STOP 0x00000001
|
||||||
|
#define MV_U3D_CMD_CTRL_RESET 0x00000002
|
||||||
|
|
||||||
|
/* ep control register */
|
||||||
|
#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0
|
||||||
|
#define MV_U3D_EPXCR_EP_TYPE_ISOC 1
|
||||||
|
#define MV_U3D_EPXCR_EP_TYPE_BULK 2
|
||||||
|
#define MV_U3D_EPXCR_EP_TYPE_INT 3
|
||||||
|
#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4
|
||||||
|
#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12
|
||||||
|
#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16
|
||||||
|
#define MV_U3D_USB_BULK_BURST_OUT 6
|
||||||
|
#define MV_U3D_USB_BULK_BURST_IN 14
|
||||||
|
|
||||||
|
#define MV_U3D_EPXCR_EP_FLUSH (1 << 7)
|
||||||
|
#define MV_U3D_EPXCR_EP_HALT (1 << 1)
|
||||||
|
#define MV_U3D_EPXCR_EP_INIT (1)
|
||||||
|
|
||||||
|
/* TX/RX Status Register */
|
||||||
|
#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24
|
||||||
|
#define MV_U3D_COMPLETE_INVALID 0
|
||||||
|
#define MV_U3D_COMPLETE_SUCCESS 1
|
||||||
|
#define MV_U3D_COMPLETE_BUFF_ERR 2
|
||||||
|
#define MV_U3D_COMPLETE_SHORT_PACKET 3
|
||||||
|
#define MV_U3D_COMPLETE_TRB_ERR 5
|
||||||
|
#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF)
|
||||||
|
|
||||||
|
#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8
|
||||||
|
|
||||||
|
#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000
|
||||||
|
#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000
|
||||||
|
|
||||||
|
#define MV_U3D_USB3_OP_REGS_OFFSET 0x100
|
||||||
|
#define MV_U3D_USB3_PHY_OFFSET 0xB800
|
||||||
|
|
||||||
|
#define DCS_ENABLE 0x1
|
||||||
|
|
||||||
|
/* timeout */
|
||||||
|
#define MV_U3D_RESET_TIMEOUT 10000
|
||||||
|
#define MV_U3D_FLUSH_TIMEOUT 100000
|
||||||
|
#define MV_U3D_OWN_TIMEOUT 10000
|
||||||
|
#define LOOPS_USEC_SHIFT 4
|
||||||
|
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
|
||||||
|
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
|
||||||
|
|
||||||
|
/* ep direction */
|
||||||
|
#define MV_U3D_EP_DIR_IN 1
|
||||||
|
#define MV_U3D_EP_DIR_OUT 0
|
||||||
|
#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \
|
||||||
|
((ep)->u3d->ep0_dir) : ((ep)->direction))
|
||||||
|
|
||||||
|
/* usb capability registers */
|
||||||
|
struct mv_u3d_cap_regs {
|
||||||
|
u32 rsvd[5];
|
||||||
|
u32 dboff; /* doorbell register offset */
|
||||||
|
u32 rtsoff; /* runtime register offset */
|
||||||
|
u32 vuoff; /* vendor unique register offset */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* operation registers */
|
||||||
|
struct mv_u3d_op_regs {
|
||||||
|
u32 usbcmd; /* Command register */
|
||||||
|
u32 rsvd1[11];
|
||||||
|
u32 dcbaapl; /* Device Context Base Address low register */
|
||||||
|
u32 dcbaaph; /* Device Context Base Address high register */
|
||||||
|
u32 rsvd2[243];
|
||||||
|
u32 portsc; /* port status and control register*/
|
||||||
|
u32 portlinkinfo; /* port link info register*/
|
||||||
|
u32 rsvd3[9917];
|
||||||
|
u32 doorbell; /* doorbell register */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* control enpoint enable registers */
|
||||||
|
struct epxcr {
|
||||||
|
u32 epxoutcr0; /* ep out control 0 register */
|
||||||
|
u32 epxoutcr1; /* ep out control 1 register */
|
||||||
|
u32 epxincr0; /* ep in control 0 register */
|
||||||
|
u32 epxincr1; /* ep in control 1 register */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* transfer status registers */
|
||||||
|
struct xferstatus {
|
||||||
|
u32 curdeqlo; /* current TRB pointer low */
|
||||||
|
u32 curdeqhi; /* current TRB pointer high */
|
||||||
|
u32 statuslo; /* transfer status low */
|
||||||
|
u32 statushi; /* transfer status high */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* vendor unique control registers */
|
||||||
|
struct mv_u3d_vuc_regs {
|
||||||
|
u32 ctrlepenable; /* control endpoint enable register */
|
||||||
|
u32 setuplock; /* setup lock register */
|
||||||
|
u32 endcomplete; /* endpoint transfer complete register */
|
||||||
|
u32 intrcause; /* interrupt cause register */
|
||||||
|
u32 intrenable; /* interrupt enable register */
|
||||||
|
u32 trbcomplete; /* TRB complete register */
|
||||||
|
u32 linkchange; /* link change register */
|
||||||
|
u32 rsvd1[5];
|
||||||
|
u32 trbunderrun; /* TRB underrun register */
|
||||||
|
u32 rsvd2[43];
|
||||||
|
u32 bridgesetting; /* bridge setting register */
|
||||||
|
u32 rsvd3[7];
|
||||||
|
struct xferstatus txst[16]; /* TX status register */
|
||||||
|
struct xferstatus rxst[16]; /* RX status register */
|
||||||
|
u32 ltssm; /* LTSSM control register */
|
||||||
|
u32 pipe; /* PIPE control register */
|
||||||
|
u32 linkcr0; /* link control 0 register */
|
||||||
|
u32 linkcr1; /* link control 1 register */
|
||||||
|
u32 rsvd6[60];
|
||||||
|
u32 mib0; /* MIB0 counter register */
|
||||||
|
u32 usblink; /* usb link control register */
|
||||||
|
u32 ltssmstate; /* LTSSM state register */
|
||||||
|
u32 linkerrorcause; /* link error cause register */
|
||||||
|
u32 rsvd7[60];
|
||||||
|
u32 devaddrtiebrkr; /* device address and tie breaker */
|
||||||
|
u32 itpinfo0; /* ITP info 0 register */
|
||||||
|
u32 itpinfo1; /* ITP info 1 register */
|
||||||
|
u32 rsvd8[61];
|
||||||
|
struct epxcr epcr[16]; /* ep control register */
|
||||||
|
u32 rsvd9[64];
|
||||||
|
u32 phyaddr; /* PHY address register */
|
||||||
|
u32 phydata; /* PHY data register */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Endpoint context structure */
|
||||||
|
struct mv_u3d_ep_context {
|
||||||
|
u32 rsvd0;
|
||||||
|
u32 rsvd1;
|
||||||
|
u32 trb_addr_lo; /* TRB address low 32 bit */
|
||||||
|
u32 trb_addr_hi; /* TRB address high 32 bit */
|
||||||
|
u32 rsvd2;
|
||||||
|
u32 rsvd3;
|
||||||
|
struct usb_ctrlrequest setup_buffer; /* setup data buffer */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TRB control data structure */
|
||||||
|
struct mv_u3d_trb_ctrl {
|
||||||
|
u32 own:1; /* owner of TRB */
|
||||||
|
u32 rsvd1:3;
|
||||||
|
u32 chain:1; /* associate this TRB with the
|
||||||
|
next TRB on the Ring */
|
||||||
|
u32 ioc:1; /* interrupt on complete */
|
||||||
|
u32 rsvd2:4;
|
||||||
|
u32 type:6; /* TRB type */
|
||||||
|
#define TYPE_NORMAL 1
|
||||||
|
#define TYPE_DATA 3
|
||||||
|
#define TYPE_LINK 6
|
||||||
|
u32 dir:1; /* Working at data stage of control endpoint
|
||||||
|
operation. 0 is OUT and 1 is IN. */
|
||||||
|
u32 rsvd3:15;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TRB data structure
|
||||||
|
* For multiple TRB, all the TRBs' physical address should be continuous.
|
||||||
|
*/
|
||||||
|
struct mv_u3d_trb_hw {
|
||||||
|
u32 buf_addr_lo; /* data buffer address low 32 bit */
|
||||||
|
u32 buf_addr_hi; /* data buffer address high 32 bit */
|
||||||
|
u32 trb_len; /* transfer length */
|
||||||
|
struct mv_u3d_trb_ctrl ctrl; /* TRB control data */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TRB structure */
|
||||||
|
struct mv_u3d_trb {
|
||||||
|
struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */
|
||||||
|
dma_addr_t trb_dma; /* dma address for this trb_hw */
|
||||||
|
struct list_head trb_list; /* trb list */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* device data structure */
|
||||||
|
struct mv_u3d {
|
||||||
|
struct usb_gadget gadget;
|
||||||
|
struct usb_gadget_driver *driver;
|
||||||
|
spinlock_t lock; /* device lock */
|
||||||
|
struct completion *done;
|
||||||
|
struct device *dev;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
/* usb controller registers */
|
||||||
|
struct mv_u3d_cap_regs __iomem *cap_regs;
|
||||||
|
struct mv_u3d_op_regs __iomem *op_regs;
|
||||||
|
struct mv_u3d_vuc_regs __iomem *vuc_regs;
|
||||||
|
void __iomem *phy_regs;
|
||||||
|
|
||||||
|
unsigned int max_eps;
|
||||||
|
struct mv_u3d_ep_context *ep_context;
|
||||||
|
size_t ep_context_size;
|
||||||
|
dma_addr_t ep_context_dma;
|
||||||
|
|
||||||
|
struct dma_pool *trb_pool; /* for TRB data structure */
|
||||||
|
struct mv_u3d_ep *eps;
|
||||||
|
|
||||||
|
struct mv_u3d_req *status_req; /* ep0 status request */
|
||||||
|
struct usb_ctrlrequest local_setup_buff; /* store setup data*/
|
||||||
|
|
||||||
|
unsigned int resume_state; /* USB state to resume */
|
||||||
|
unsigned int usb_state; /* USB current state */
|
||||||
|
unsigned int ep0_state; /* Endpoint zero state */
|
||||||
|
unsigned int ep0_dir;
|
||||||
|
|
||||||
|
unsigned int dev_addr; /* device address */
|
||||||
|
|
||||||
|
unsigned int errors;
|
||||||
|
|
||||||
|
unsigned softconnect:1;
|
||||||
|
unsigned vbus_active:1; /* vbus is active or not */
|
||||||
|
unsigned remote_wakeup:1; /* support remote wakeup */
|
||||||
|
unsigned clock_gating:1; /* clock gating or not */
|
||||||
|
unsigned active:1; /* udc is active or not */
|
||||||
|
unsigned vbus_valid_detect:1; /* udc vbus detection */
|
||||||
|
|
||||||
|
struct mv_usb_addon_irq *vbus;
|
||||||
|
unsigned int power;
|
||||||
|
|
||||||
|
struct clk *clk;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* endpoint data structure */
|
||||||
|
struct mv_u3d_ep {
|
||||||
|
struct usb_ep ep;
|
||||||
|
struct mv_u3d *u3d;
|
||||||
|
struct list_head queue; /* ep request queued hardware */
|
||||||
|
struct list_head req_list; /* list of ep request */
|
||||||
|
struct mv_u3d_ep_context *ep_context; /* ep context */
|
||||||
|
u32 direction;
|
||||||
|
char name[14];
|
||||||
|
u32 processing; /* there is ep request
|
||||||
|
queued on haredware */
|
||||||
|
spinlock_t req_lock; /* ep lock */
|
||||||
|
unsigned wedge:1;
|
||||||
|
unsigned enabled:1;
|
||||||
|
unsigned ep_type:2;
|
||||||
|
unsigned ep_num:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* request data structure */
|
||||||
|
struct mv_u3d_req {
|
||||||
|
struct usb_request req;
|
||||||
|
struct mv_u3d_ep *ep;
|
||||||
|
struct list_head queue; /* ep requst queued on hardware */
|
||||||
|
struct list_head list; /* ep request list */
|
||||||
|
struct list_head trb_list; /* trb list of a request */
|
||||||
|
|
||||||
|
struct mv_u3d_trb *trb_head; /* point to first trb of a request */
|
||||||
|
unsigned trb_count; /* TRB number in the chain */
|
||||||
|
unsigned chain; /* TRB chain or not */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
@ -1381,7 +1382,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udc->transceiver) {
|
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||||
&udc->gadget);
|
&udc->gadget);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
|
@ -2107,7 +2108,7 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
|
||||||
* then vbus irq will not be requested in udc driver.
|
* then vbus irq will not be requested in udc driver.
|
||||||
*/
|
*/
|
||||||
if (udc->pdata && udc->pdata->vbus
|
if (udc->pdata && udc->pdata->vbus
|
||||||
&& udc->clock_gating && udc->transceiver == NULL)
|
&& udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
|
||||||
free_irq(udc->pdata->vbus->irq, &dev->dev);
|
free_irq(udc->pdata->vbus->irq, &dev->dev);
|
||||||
|
|
||||||
/* free memory allocated in probe */
|
/* free memory allocated in probe */
|
||||||
|
@ -2180,7 +2181,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
||||||
|
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
if (pdata->mode == MV_USB_MODE_OTG)
|
if (pdata->mode == MV_USB_MODE_OTG)
|
||||||
udc->transceiver = usb_get_transceiver();
|
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
udc->clknum = pdata->clknum;
|
udc->clknum = pdata->clknum;
|
||||||
|
@ -2325,7 +2326,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
||||||
eps_init(udc);
|
eps_init(udc);
|
||||||
|
|
||||||
/* VBUS detect: we can disable/enable clock on demand.*/
|
/* VBUS detect: we can disable/enable clock on demand.*/
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
udc->clock_gating = 1;
|
udc->clock_gating = 1;
|
||||||
else if (pdata->vbus) {
|
else if (pdata->vbus) {
|
||||||
udc->clock_gating = 1;
|
udc->clock_gating = 1;
|
||||||
|
@ -2369,7 +2370,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
||||||
|
|
||||||
err_unregister:
|
err_unregister:
|
||||||
if (udc->pdata && udc->pdata->vbus
|
if (udc->pdata && udc->pdata->vbus
|
||||||
&& udc->clock_gating && udc->transceiver == NULL)
|
&& udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver))
|
||||||
free_irq(pdata->vbus->irq, &dev->dev);
|
free_irq(pdata->vbus->irq, &dev->dev);
|
||||||
device_unregister(&udc->gadget.dev);
|
device_unregister(&udc->gadget.dev);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
|
@ -2404,7 +2405,7 @@ static int mv_udc_suspend(struct device *_dev)
|
||||||
struct mv_udc *udc = the_controller;
|
struct mv_udc *udc = the_controller;
|
||||||
|
|
||||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (udc->pdata->vbus && udc->pdata->vbus->poll)
|
if (udc->pdata->vbus && udc->pdata->vbus->poll)
|
||||||
|
@ -2437,7 +2438,7 @@ static int mv_udc_resume(struct device *_dev)
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!udc->clock_gating) {
|
if (!udc->clock_gating) {
|
||||||
|
|
|
@ -35,10 +35,11 @@
|
||||||
#include <linux/usb/otg.h>
|
#include <linux/usb/otg.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/prefetch.h>
|
#include <linux/prefetch.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
#include <asm/io.h>
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
@ -60,11 +61,6 @@
|
||||||
#define DRIVER_DESC "OMAP UDC driver"
|
#define DRIVER_DESC "OMAP UDC driver"
|
||||||
#define DRIVER_VERSION "4 October 2004"
|
#define DRIVER_VERSION "4 October 2004"
|
||||||
|
|
||||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
|
||||||
|
|
||||||
#define OMAP2_DMA_CH(ch) (((ch) - 1) << 1)
|
|
||||||
#define OMAP24XX_DMA(name, ch) (OMAP24XX_DMA_##name + OMAP2_DMA_CH(ch))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The OMAP UDC needs _very_ early endpoint setup: before enabling the
|
* The OMAP UDC needs _very_ early endpoint setup: before enabling the
|
||||||
* D+ pullup to allow enumeration. That's too early for the gadget
|
* D+ pullup to allow enumeration. That's too early for the gadget
|
||||||
|
@ -88,14 +84,14 @@
|
||||||
#ifdef USE_ISO
|
#ifdef USE_ISO
|
||||||
static unsigned fifo_mode = 3;
|
static unsigned fifo_mode = 3;
|
||||||
#else
|
#else
|
||||||
static unsigned fifo_mode = 0;
|
static unsigned fifo_mode;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* "modprobe omap_udc fifo_mode=42", or else as a kernel
|
/* "modprobe omap_udc fifo_mode=42", or else as a kernel
|
||||||
* boot parameter "omap_udc:fifo_mode=42"
|
* boot parameter "omap_udc:fifo_mode=42"
|
||||||
*/
|
*/
|
||||||
module_param (fifo_mode, uint, 0);
|
module_param(fifo_mode, uint, 0);
|
||||||
MODULE_PARM_DESC (fifo_mode, "endpoint configuration");
|
MODULE_PARM_DESC(fifo_mode, "endpoint configuration");
|
||||||
|
|
||||||
#ifdef USE_DMA
|
#ifdef USE_DMA
|
||||||
static bool use_dma = 1;
|
static bool use_dma = 1;
|
||||||
|
@ -103,8 +99,8 @@ static bool use_dma = 1;
|
||||||
/* "modprobe omap_udc use_dma=y", or else as a kernel
|
/* "modprobe omap_udc use_dma=y", or else as a kernel
|
||||||
* boot parameter "omap_udc:use_dma=y"
|
* boot parameter "omap_udc:use_dma=y"
|
||||||
*/
|
*/
|
||||||
module_param (use_dma, bool, 0);
|
module_param(use_dma, bool, 0);
|
||||||
MODULE_PARM_DESC (use_dma, "enable/disable DMA");
|
MODULE_PARM_DESC(use_dma, "enable/disable DMA");
|
||||||
#else /* !USE_DMA */
|
#else /* !USE_DMA */
|
||||||
|
|
||||||
/* save a bit of code */
|
/* save a bit of code */
|
||||||
|
@ -112,8 +108,8 @@ MODULE_PARM_DESC (use_dma, "enable/disable DMA");
|
||||||
#endif /* !USE_DMA */
|
#endif /* !USE_DMA */
|
||||||
|
|
||||||
|
|
||||||
static const char driver_name [] = "omap_udc";
|
static const char driver_name[] = "omap_udc";
|
||||||
static const char driver_desc [] = DRIVER_DESC;
|
static const char driver_desc[] = DRIVER_DESC;
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -251,7 +247,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
|
||||||
|
|
||||||
spin_lock_irqsave(&ep->udc->lock, flags);
|
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||||
ep->ep.desc = NULL;
|
ep->ep.desc = NULL;
|
||||||
nuke (ep, -ESHUTDOWN);
|
nuke(ep, -ESHUTDOWN);
|
||||||
ep->ep.maxpacket = ep->maxpacket;
|
ep->ep.maxpacket = ep->maxpacket;
|
||||||
ep->has_dma = 0;
|
ep->has_dma = 0;
|
||||||
omap_writew(UDC_SET_HALT, UDC_CTRL);
|
omap_writew(UDC_SET_HALT, UDC_CTRL);
|
||||||
|
@ -272,10 +268,11 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||||
struct omap_req *req;
|
struct omap_req *req;
|
||||||
|
|
||||||
req = kzalloc(sizeof(*req), gfp_flags);
|
req = kzalloc(sizeof(*req), gfp_flags);
|
||||||
if (req) {
|
if (!req)
|
||||||
req->req.dma = DMA_ADDR_INVALID;
|
return NULL;
|
||||||
INIT_LIST_HEAD (&req->queue);
|
|
||||||
}
|
INIT_LIST_HEAD(&req->queue);
|
||||||
|
|
||||||
return &req->req;
|
return &req->req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,8 +281,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||||
{
|
{
|
||||||
struct omap_req *req = container_of(_req, struct omap_req, req);
|
struct omap_req *req = container_of(_req, struct omap_req, req);
|
||||||
|
|
||||||
if (_req)
|
kfree(req);
|
||||||
kfree (req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -293,6 +289,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||||
static void
|
static void
|
||||||
done(struct omap_ep *ep, struct omap_req *req, int status)
|
done(struct omap_ep *ep, struct omap_req *req, int status)
|
||||||
{
|
{
|
||||||
|
struct omap_udc *udc = ep->udc;
|
||||||
unsigned stopped = ep->stopped;
|
unsigned stopped = ep->stopped;
|
||||||
|
|
||||||
list_del_init(&req->queue);
|
list_del_init(&req->queue);
|
||||||
|
@ -302,22 +299,9 @@ done(struct omap_ep *ep, struct omap_req *req, int status)
|
||||||
else
|
else
|
||||||
status = req->req.status;
|
status = req->req.status;
|
||||||
|
|
||||||
if (use_dma && ep->has_dma) {
|
if (use_dma && ep->has_dma)
|
||||||
if (req->mapped) {
|
usb_gadget_unmap_request(&udc->gadget, &req->req,
|
||||||
dma_unmap_single(ep->udc->gadget.dev.parent,
|
(ep->bEndpointAddress & USB_DIR_IN));
|
||||||
req->req.dma, req->req.length,
|
|
||||||
(ep->bEndpointAddress & USB_DIR_IN)
|
|
||||||
? DMA_TO_DEVICE
|
|
||||||
: DMA_FROM_DEVICE);
|
|
||||||
req->req.dma = DMA_ADDR_INVALID;
|
|
||||||
req->mapped = 0;
|
|
||||||
} else
|
|
||||||
dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
|
|
||||||
req->req.dma, req->req.length,
|
|
||||||
(ep->bEndpointAddress & USB_DIR_IN)
|
|
||||||
? DMA_TO_DEVICE
|
|
||||||
: DMA_FROM_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef USB_TRACE
|
#ifndef USB_TRACE
|
||||||
if (status && status != -ESHUTDOWN)
|
if (status && status != -ESHUTDOWN)
|
||||||
|
@ -365,10 +349,10 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME change r/w fifo calling convention
|
/* FIXME change r/w fifo calling convention */
|
||||||
|
|
||||||
|
|
||||||
// return: 0 = still running, 1 = completed, negative = errno
|
/* return: 0 = still running, 1 = completed, negative = errno */
|
||||||
static int write_fifo(struct omap_ep *ep, struct omap_req *req)
|
static int write_fifo(struct omap_ep *ep, struct omap_req *req)
|
||||||
{
|
{
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
@ -430,7 +414,7 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return: 0 = still running, 1 = queue empty, negative = errno
|
/* return: 0 = still running, 1 = queue empty, negative = errno */
|
||||||
static int read_fifo(struct omap_ep *ep, struct omap_req *req)
|
static int read_fifo(struct omap_ep *ep, struct omap_req *req)
|
||||||
{
|
{
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
@ -537,12 +521,8 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
|
||||||
: OMAP_DMA_SYNC_ELEMENT;
|
: OMAP_DMA_SYNC_ELEMENT;
|
||||||
int dma_trigger = 0;
|
int dma_trigger = 0;
|
||||||
|
|
||||||
if (cpu_is_omap24xx())
|
|
||||||
dma_trigger = OMAP24XX_DMA(USB_W2FC_TX0, ep->dma_channel);
|
|
||||||
|
|
||||||
/* measure length in either bytes or packets */
|
/* measure length in either bytes or packets */
|
||||||
if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
|
if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
|
||||||
|| (cpu_is_omap24xx() && length < ep->maxpacket)
|
|
||||||
|| (cpu_is_omap15xx() && length < ep->maxpacket)) {
|
|| (cpu_is_omap15xx() && length < ep->maxpacket)) {
|
||||||
txdma_ctrl = UDC_TXN_EOT | length;
|
txdma_ctrl = UDC_TXN_EOT | length;
|
||||||
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
|
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
|
||||||
|
@ -601,28 +581,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
|
||||||
int dma_trigger = 0;
|
int dma_trigger = 0;
|
||||||
u16 w;
|
u16 w;
|
||||||
|
|
||||||
if (cpu_is_omap24xx())
|
/* set up this DMA transfer, enable the fifo, start */
|
||||||
dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel);
|
packets /= ep->ep.maxpacket;
|
||||||
|
packets = min(packets, (unsigned)UDC_RXN_TC + 1);
|
||||||
/* NOTE: we filtered out "short reads" before, so we know
|
req->dma_bytes = packets * ep->ep.maxpacket;
|
||||||
* the buffer has only whole numbers of packets.
|
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
|
||||||
* except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode
|
ep->ep.maxpacket >> 1, packets,
|
||||||
*/
|
OMAP_DMA_SYNC_ELEMENT,
|
||||||
if (cpu_is_omap24xx() && packets < ep->maxpacket) {
|
dma_trigger, 0);
|
||||||
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
|
|
||||||
packets, 1, OMAP_DMA_SYNC_ELEMENT,
|
|
||||||
dma_trigger, 0);
|
|
||||||
req->dma_bytes = packets;
|
|
||||||
} else {
|
|
||||||
/* set up this DMA transfer, enable the fifo, start */
|
|
||||||
packets /= ep->ep.maxpacket;
|
|
||||||
packets = min(packets, (unsigned)UDC_RXN_TC + 1);
|
|
||||||
req->dma_bytes = packets * ep->ep.maxpacket;
|
|
||||||
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
|
|
||||||
ep->ep.maxpacket >> 1, packets,
|
|
||||||
OMAP_DMA_SYNC_ELEMENT,
|
|
||||||
dma_trigger, 0);
|
|
||||||
}
|
|
||||||
omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
|
omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
|
||||||
OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
|
OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
|
||||||
0, 0);
|
0, 0);
|
||||||
|
@ -684,7 +650,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
}
|
}
|
||||||
omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC);
|
omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC);
|
||||||
|
|
||||||
if (!list_empty (&ep->queue)) {
|
if (!list_empty(&ep->queue)) {
|
||||||
req = container_of(ep->queue.next,
|
req = container_of(ep->queue.next,
|
||||||
struct omap_req, queue);
|
struct omap_req, queue);
|
||||||
next_in_dma(ep, req);
|
next_in_dma(ep, req);
|
||||||
|
@ -703,7 +669,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
}
|
}
|
||||||
omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC);
|
omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC);
|
||||||
|
|
||||||
if (!list_empty (&ep->queue)) {
|
if (!list_empty(&ep->queue)) {
|
||||||
req = container_of(ep->queue.next,
|
req = container_of(ep->queue.next,
|
||||||
struct omap_req, queue);
|
struct omap_req, queue);
|
||||||
next_out_dma(ep, req);
|
next_out_dma(ep, req);
|
||||||
|
@ -761,10 +727,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
|
||||||
ep->dma_channel = channel;
|
ep->dma_channel = channel;
|
||||||
|
|
||||||
if (is_in) {
|
if (is_in) {
|
||||||
if (cpu_is_omap24xx())
|
dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
|
||||||
dma_channel = OMAP24XX_DMA(USB_W2FC_TX0, channel);
|
|
||||||
else
|
|
||||||
dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
|
|
||||||
status = omap_request_dma(dma_channel,
|
status = omap_request_dma(dma_channel,
|
||||||
ep->ep.name, dma_error, ep, &ep->lch);
|
ep->ep.name, dma_error, ep, &ep->lch);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
|
@ -781,11 +744,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
|
||||||
0, 0);
|
0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cpu_is_omap24xx())
|
dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
|
||||||
dma_channel = OMAP24XX_DMA(USB_W2FC_RX0, channel);
|
|
||||||
else
|
|
||||||
dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
|
|
||||||
|
|
||||||
status = omap_request_dma(dma_channel,
|
status = omap_request_dma(dma_channel,
|
||||||
ep->ep.name, dma_error, ep, &ep->lch);
|
ep->ep.name, dma_error, ep, &ep->lch);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
|
@ -809,7 +768,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
|
||||||
omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
|
omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
|
||||||
|
|
||||||
/* channel type P: hw synch (fifo) */
|
/* channel type P: hw synch (fifo) */
|
||||||
if (cpu_class_is_omap1() && !cpu_is_omap15xx())
|
if (!cpu_is_omap15xx())
|
||||||
omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
|
omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,13 +888,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||||
|
|
||||||
/* this isn't bogus, but OMAP DMA isn't the only hardware to
|
/* this isn't bogus, but OMAP DMA isn't the only hardware to
|
||||||
* have a hard time with partial packet reads... reject it.
|
* have a hard time with partial packet reads... reject it.
|
||||||
* Except OMAP2 can handle the small packets.
|
|
||||||
*/
|
*/
|
||||||
if (use_dma
|
if (use_dma
|
||||||
&& ep->has_dma
|
&& ep->has_dma
|
||||||
&& ep->bEndpointAddress != 0
|
&& ep->bEndpointAddress != 0
|
||||||
&& (ep->bEndpointAddress & USB_DIR_IN) == 0
|
&& (ep->bEndpointAddress & USB_DIR_IN) == 0
|
||||||
&& !cpu_class_is_omap2()
|
|
||||||
&& (req->req.length % ep->ep.maxpacket) != 0) {
|
&& (req->req.length % ep->ep.maxpacket) != 0) {
|
||||||
DBG("%s, no partial packet OUT reads\n", __func__);
|
DBG("%s, no partial packet OUT reads\n", __func__);
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
@ -945,26 +902,9 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||||
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
|
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||||
return -ESHUTDOWN;
|
return -ESHUTDOWN;
|
||||||
|
|
||||||
if (use_dma && ep->has_dma) {
|
if (use_dma && ep->has_dma)
|
||||||
if (req->req.dma == DMA_ADDR_INVALID) {
|
usb_gadget_map_request(&udc->gadget, &req->req,
|
||||||
req->req.dma = dma_map_single(
|
(ep->bEndpointAddress & USB_DIR_IN));
|
||||||
ep->udc->gadget.dev.parent,
|
|
||||||
req->req.buf,
|
|
||||||
req->req.length,
|
|
||||||
(ep->bEndpointAddress & USB_DIR_IN)
|
|
||||||
? DMA_TO_DEVICE
|
|
||||||
: DMA_FROM_DEVICE);
|
|
||||||
req->mapped = 1;
|
|
||||||
} else {
|
|
||||||
dma_sync_single_for_device(
|
|
||||||
ep->udc->gadget.dev.parent,
|
|
||||||
req->req.dma, req->req.length,
|
|
||||||
(ep->bEndpointAddress & USB_DIR_IN)
|
|
||||||
? DMA_TO_DEVICE
|
|
||||||
: DMA_FROM_DEVICE);
|
|
||||||
req->mapped = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VDBG("%s queue req %p, len %d buf %p\n",
|
VDBG("%s queue req %p, len %d buf %p\n",
|
||||||
ep->ep.name, _req, _req->length, _req->buf);
|
ep->ep.name, _req, _req->length, _req->buf);
|
||||||
|
@ -985,7 +925,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||||
int is_in;
|
int is_in;
|
||||||
|
|
||||||
if (ep->bEndpointAddress == 0) {
|
if (ep->bEndpointAddress == 0) {
|
||||||
if (!udc->ep0_pending || !list_empty (&ep->queue)) {
|
if (!udc->ep0_pending || !list_empty(&ep->queue)) {
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&udc->lock, flags);
|
||||||
return -EL2HLT;
|
return -EL2HLT;
|
||||||
}
|
}
|
||||||
|
@ -1012,7 +952,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||||
* always an IN ... even for IN transfers,
|
* always an IN ... even for IN transfers,
|
||||||
* a weird case which seem to stall OMAP.
|
* a weird case which seem to stall OMAP.
|
||||||
*/
|
*/
|
||||||
omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
|
omap_writew(UDC_EP_SEL | UDC_EP_DIR,
|
||||||
|
UDC_EP_NUM);
|
||||||
omap_writew(UDC_CLR_EP, UDC_CTRL);
|
omap_writew(UDC_CLR_EP, UDC_CTRL);
|
||||||
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
|
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
|
||||||
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
|
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
|
||||||
|
@ -1024,7 +965,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||||
|
|
||||||
/* non-empty DATA stage */
|
/* non-empty DATA stage */
|
||||||
} else if (is_in) {
|
} else if (is_in) {
|
||||||
omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
|
omap_writew(UDC_EP_SEL | UDC_EP_DIR,
|
||||||
|
UDC_EP_NUM);
|
||||||
} else {
|
} else {
|
||||||
if (udc->ep0_setup)
|
if (udc->ep0_setup)
|
||||||
goto irq_wait;
|
goto irq_wait;
|
||||||
|
@ -1072,7 +1014,7 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||||
spin_lock_irqsave(&ep->udc->lock, flags);
|
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||||
|
|
||||||
/* make sure it's actually queued on this endpoint */
|
/* make sure it's actually queued on this endpoint */
|
||||||
list_for_each_entry (req, &ep->queue, queue) {
|
list_for_each_entry(req, &ep->queue, queue) {
|
||||||
if (&req->req == _req)
|
if (&req->req == _req)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1179,8 +1121,8 @@ static struct usb_ep_ops omap_ep_ops = {
|
||||||
.dequeue = omap_ep_dequeue,
|
.dequeue = omap_ep_dequeue,
|
||||||
|
|
||||||
.set_halt = omap_ep_set_halt,
|
.set_halt = omap_ep_set_halt,
|
||||||
// fifo_status ... report bytes in fifo
|
/* fifo_status ... report bytes in fifo */
|
||||||
// fifo_flush ... flush fifo
|
/* fifo_flush ... flush fifo */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -1212,7 +1154,7 @@ static int omap_wakeup(struct usb_gadget *gadget)
|
||||||
|
|
||||||
/* NOTE: non-OTG systems may use SRP TOO... */
|
/* NOTE: non-OTG systems may use SRP TOO... */
|
||||||
} else if (!(udc->devstat & UDC_ATT)) {
|
} else if (!(udc->devstat & UDC_ATT)) {
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
retval = otg_start_srp(udc->transceiver->otg);
|
retval = otg_start_srp(udc->transceiver->otg);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&udc->lock, flags);
|
spin_unlock_irqrestore(&udc->lock, flags);
|
||||||
|
@ -1344,7 +1286,7 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||||
struct omap_udc *udc;
|
struct omap_udc *udc;
|
||||||
|
|
||||||
udc = container_of(gadget, struct omap_udc, gadget);
|
udc = container_of(gadget, struct omap_udc, gadget);
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return usb_phy_set_power(udc->transceiver, mA);
|
return usb_phy_set_power(udc->transceiver, mA);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -1410,7 +1352,7 @@ static void udc_quiesce(struct omap_udc *udc)
|
||||||
|
|
||||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
nuke(&udc->ep[0], -ESHUTDOWN);
|
nuke(&udc->ep[0], -ESHUTDOWN);
|
||||||
list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list)
|
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
|
||||||
nuke(ep, -ESHUTDOWN);
|
nuke(ep, -ESHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1526,7 +1468,8 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
/* read next OUT packet of request, maybe
|
/* read next OUT packet of request, maybe
|
||||||
* reactiviting the fifo; stall on errors.
|
* reactiviting the fifo; stall on errors.
|
||||||
*/
|
*/
|
||||||
if (!req || (stat = read_fifo(ep0, req)) < 0) {
|
stat = read_fifo(ep0, req);
|
||||||
|
if (!req || stat < 0) {
|
||||||
omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
|
omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
|
||||||
udc->ep0_pending = 0;
|
udc->ep0_pending = 0;
|
||||||
stat = 0;
|
stat = 0;
|
||||||
|
@ -1659,7 +1602,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
/* this has rude side-effects (aborts) and
|
/* this has rude side-effects (aborts) and
|
||||||
* can't really work if DMA-IN is active
|
* can't really work if DMA-IN is active
|
||||||
*/
|
*/
|
||||||
DBG("%s host set_halt, NYET \n", ep->name);
|
DBG("%s host set_halt, NYET\n", ep->name);
|
||||||
goto do_stall;
|
goto do_stall;
|
||||||
}
|
}
|
||||||
use_ep(ep, 0);
|
use_ep(ep, 0);
|
||||||
|
@ -1750,7 +1693,7 @@ delegate:
|
||||||
*/
|
*/
|
||||||
udc->ep0_setup = 1;
|
udc->ep0_setup = 1;
|
||||||
spin_unlock(&udc->lock);
|
spin_unlock(&udc->lock);
|
||||||
status = udc->driver->setup (&udc->gadget, &u.r);
|
status = udc->driver->setup(&udc->gadget, &u.r);
|
||||||
spin_lock(&udc->lock);
|
spin_lock(&udc->lock);
|
||||||
udc->ep0_setup = 0;
|
udc->ep0_setup = 0;
|
||||||
}
|
}
|
||||||
|
@ -1793,12 +1736,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
if (devstat & UDC_ATT) {
|
if (devstat & UDC_ATT) {
|
||||||
udc->gadget.speed = USB_SPEED_FULL;
|
udc->gadget.speed = USB_SPEED_FULL;
|
||||||
VDBG("connect\n");
|
VDBG("connect\n");
|
||||||
if (!udc->transceiver)
|
if (IS_ERR_OR_NULL(udc->transceiver))
|
||||||
pullup_enable(udc);
|
pullup_enable(udc);
|
||||||
// if (driver->connect) call it
|
/* if (driver->connect) call it */
|
||||||
} else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
|
} else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
if (!udc->transceiver)
|
if (IS_ERR_OR_NULL(udc->transceiver))
|
||||||
pullup_disable(udc);
|
pullup_disable(udc);
|
||||||
DBG("disconnect, gadget %s\n",
|
DBG("disconnect, gadget %s\n",
|
||||||
udc->driver->driver.name);
|
udc->driver->driver.name);
|
||||||
|
@ -1827,7 +1770,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
}
|
}
|
||||||
if (change & UDC_SUS) {
|
if (change & UDC_SUS) {
|
||||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
|
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||||
// FIXME tell isp1301 to suspend/resume (?)
|
/* FIXME tell isp1301 to suspend/resume (?) */
|
||||||
if (devstat & UDC_SUS) {
|
if (devstat & UDC_SUS) {
|
||||||
VDBG("suspend\n");
|
VDBG("suspend\n");
|
||||||
update_otg(udc);
|
update_otg(udc);
|
||||||
|
@ -1838,12 +1781,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
|
||||||
udc->driver->suspend(&udc->gadget);
|
udc->driver->suspend(&udc->gadget);
|
||||||
spin_lock(&udc->lock);
|
spin_lock(&udc->lock);
|
||||||
}
|
}
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
usb_phy_set_suspend(
|
usb_phy_set_suspend(
|
||||||
udc->transceiver, 1);
|
udc->transceiver, 1);
|
||||||
} else {
|
} else {
|
||||||
VDBG("resume\n");
|
VDBG("resume\n");
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
usb_phy_set_suspend(
|
usb_phy_set_suspend(
|
||||||
udc->transceiver, 0);
|
udc->transceiver, 0);
|
||||||
if (udc->gadget.speed == USB_SPEED_FULL
|
if (udc->gadget.speed == USB_SPEED_FULL
|
||||||
|
@ -2030,7 +1973,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
|
||||||
spin_lock_irqsave(&udc->lock, flags);
|
spin_lock_irqsave(&udc->lock, flags);
|
||||||
|
|
||||||
/* handle all non-DMA ISO transfers */
|
/* handle all non-DMA ISO transfers */
|
||||||
list_for_each_entry (ep, &udc->iso, iso) {
|
list_for_each_entry(ep, &udc->iso, iso) {
|
||||||
u16 stat;
|
u16 stat;
|
||||||
struct omap_req *req;
|
struct omap_req *req;
|
||||||
|
|
||||||
|
@ -2089,15 +2032,11 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
|
||||||
|
|
||||||
static inline int machine_without_vbus_sense(void)
|
static inline int machine_without_vbus_sense(void)
|
||||||
{
|
{
|
||||||
return (machine_is_omap_innovator()
|
return machine_is_omap_innovator()
|
||||||
|| machine_is_omap_osk()
|
|| machine_is_omap_osk()
|
||||||
|| machine_is_omap_apollon()
|
|
||||||
#ifndef CONFIG_MACH_OMAP_H4_OTG
|
|
||||||
|| machine_is_omap_h4()
|
|
||||||
#endif
|
|
||||||
|| machine_is_sx1()
|
|| machine_is_sx1()
|
||||||
|| cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */
|
/* No known omap7xx boards with vbus sense */
|
||||||
);
|
|| cpu_is_omap7xx();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_udc_start(struct usb_gadget_driver *driver,
|
static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||||
|
@ -2111,7 +2050,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||||
if (!udc)
|
if (!udc)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (!driver
|
if (!driver
|
||||||
// FIXME if otg, check: driver->is_otg
|
/* FIXME if otg, check: driver->is_otg */
|
||||||
|| driver->max_speed < USB_SPEED_FULL
|
|| driver->max_speed < USB_SPEED_FULL
|
||||||
|| !bind || !driver->setup)
|
|| !bind || !driver->setup)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -2123,7 +2062,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset state */
|
/* reset state */
|
||||||
list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
|
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
|
||||||
ep->irqs = 0;
|
ep->irqs = 0;
|
||||||
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
|
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
|
||||||
continue;
|
continue;
|
||||||
|
@ -2155,13 +2094,13 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||||
omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
|
omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
|
||||||
|
|
||||||
/* connect to bus through transceiver */
|
/* connect to bus through transceiver */
|
||||||
if (udc->transceiver) {
|
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||||
status = otg_set_peripheral(udc->transceiver->otg,
|
status = otg_set_peripheral(udc->transceiver->otg,
|
||||||
&udc->gadget);
|
&udc->gadget);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
ERR("can't bind to transceiver\n");
|
ERR("can't bind to transceiver\n");
|
||||||
if (driver->unbind) {
|
if (driver->unbind) {
|
||||||
driver->unbind (&udc->gadget);
|
driver->unbind(&udc->gadget);
|
||||||
udc->gadget.dev.driver = NULL;
|
udc->gadget.dev.driver = NULL;
|
||||||
udc->driver = NULL;
|
udc->driver = NULL;
|
||||||
}
|
}
|
||||||
|
@ -2169,9 +2108,9 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (can_pullup(udc))
|
if (can_pullup(udc))
|
||||||
pullup_enable (udc);
|
pullup_enable(udc);
|
||||||
else
|
else
|
||||||
pullup_disable (udc);
|
pullup_disable(udc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* boards that don't have VBUS sensing can't autogate 48MHz;
|
/* boards that don't have VBUS sensing can't autogate 48MHz;
|
||||||
|
@ -2202,7 +2141,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
|
||||||
if (machine_without_vbus_sense())
|
if (machine_without_vbus_sense())
|
||||||
omap_vbus_session(&udc->gadget, 0);
|
omap_vbus_session(&udc->gadget, 0);
|
||||||
|
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
(void) otg_set_peripheral(udc->transceiver->otg, NULL);
|
(void) otg_set_peripheral(udc->transceiver->otg, NULL);
|
||||||
else
|
else
|
||||||
pullup_disable(udc);
|
pullup_disable(udc);
|
||||||
|
@ -2230,7 +2169,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
|
||||||
static const char proc_filename[] = "driver/udc";
|
static const char proc_filename[] = "driver/udc";
|
||||||
|
|
||||||
#define FOURBITS "%s%s%s%s"
|
#define FOURBITS "%s%s%s%s"
|
||||||
#define EIGHTBITS FOURBITS FOURBITS
|
#define EIGHTBITS "%s%s%s%s%s%s%s%s"
|
||||||
|
|
||||||
static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
|
static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
|
||||||
{
|
{
|
||||||
|
@ -2252,12 +2191,21 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
|
||||||
"\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n",
|
"\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n",
|
||||||
ep->name, buf,
|
ep->name, buf,
|
||||||
ep->double_buf ? "dbuf " : "",
|
ep->double_buf ? "dbuf " : "",
|
||||||
({char *s; switch(ep->ackwait){
|
({ char *s;
|
||||||
case 0: s = ""; break;
|
switch (ep->ackwait) {
|
||||||
case 1: s = "(ackw) "; break;
|
case 0:
|
||||||
case 2: s = "(ackw2) "; break;
|
s = "";
|
||||||
default: s = "(?) "; break;
|
break;
|
||||||
} s;}),
|
case 1:
|
||||||
|
s = "(ackw) ";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
s = "(ackw2) ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = "(?) ";
|
||||||
|
break;
|
||||||
|
} s; }),
|
||||||
ep->irqs, stat_flg,
|
ep->irqs, stat_flg,
|
||||||
(stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "",
|
(stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "",
|
||||||
(stat_flg & UDC_MISS_IN) ? "miss_in " : "",
|
(stat_flg & UDC_MISS_IN) ? "miss_in " : "",
|
||||||
|
@ -2273,10 +2221,10 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
|
||||||
(stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "",
|
(stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "",
|
||||||
(stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : "");
|
(stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : "");
|
||||||
|
|
||||||
if (list_empty (&ep->queue))
|
if (list_empty(&ep->queue))
|
||||||
seq_printf(s, "\t(queue empty)\n");
|
seq_printf(s, "\t(queue empty)\n");
|
||||||
else
|
else
|
||||||
list_for_each_entry (req, &ep->queue, queue) {
|
list_for_each_entry(req, &ep->queue, queue) {
|
||||||
unsigned length = req->req.actual;
|
unsigned length = req->req.actual;
|
||||||
|
|
||||||
if (use_dma && buf[0]) {
|
if (use_dma && buf[0]) {
|
||||||
|
@ -2294,11 +2242,16 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
|
||||||
static char *trx_mode(unsigned m, int enabled)
|
static char *trx_mode(unsigned m, int enabled)
|
||||||
{
|
{
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case 0: return enabled ? "*6wire" : "unused";
|
case 0:
|
||||||
case 1: return "4wire";
|
return enabled ? "*6wire" : "unused";
|
||||||
case 2: return "3wire";
|
case 1:
|
||||||
case 3: return "6wire";
|
return "4wire";
|
||||||
default: return "unknown";
|
case 2:
|
||||||
|
return "3wire";
|
||||||
|
case 3:
|
||||||
|
return "6wire";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2308,12 +2261,9 @@ static int proc_otg_show(struct seq_file *s)
|
||||||
u32 trans = 0;
|
u32 trans = 0;
|
||||||
char *ctrl_name = "(UNKNOWN)";
|
char *ctrl_name = "(UNKNOWN)";
|
||||||
|
|
||||||
/* XXX This needs major revision for OMAP2+ */
|
|
||||||
tmp = omap_readl(OTG_REV);
|
tmp = omap_readl(OTG_REV);
|
||||||
if (cpu_class_is_omap1()) {
|
ctrl_name = "tranceiver_ctrl";
|
||||||
ctrl_name = "tranceiver_ctrl";
|
trans = omap_readw(USB_TRANSCEIVER_CTRL);
|
||||||
trans = omap_readw(USB_TRANSCEIVER_CTRL);
|
|
||||||
}
|
|
||||||
seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
|
seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
|
||||||
tmp >> 4, tmp & 0xf, ctrl_name, trans);
|
tmp >> 4, tmp & 0xf, ctrl_name, trans);
|
||||||
tmp = omap_readw(OTG_SYSCON_1);
|
tmp = omap_readw(OTG_SYSCON_1);
|
||||||
|
@ -2333,7 +2283,7 @@ static int proc_otg_show(struct seq_file *s)
|
||||||
" b_ase_brst=%d hmc=%d\n", tmp,
|
" b_ase_brst=%d hmc=%d\n", tmp,
|
||||||
(tmp & OTG_EN) ? " otg_en" : "",
|
(tmp & OTG_EN) ? " otg_en" : "",
|
||||||
(tmp & USBX_SYNCHRO) ? " synchro" : "",
|
(tmp & USBX_SYNCHRO) ? " synchro" : "",
|
||||||
// much more SRP stuff
|
/* much more SRP stuff */
|
||||||
(tmp & SRP_DATA) ? " srp_data" : "",
|
(tmp & SRP_DATA) ? " srp_data" : "",
|
||||||
(tmp & SRP_VBUS) ? " srp_vbus" : "",
|
(tmp & SRP_VBUS) ? " srp_vbus" : "",
|
||||||
(tmp & OTG_PADEN) ? " otg_paden" : "",
|
(tmp & OTG_PADEN) ? " otg_paden" : "",
|
||||||
|
@ -2400,14 +2350,12 @@ static int proc_udc_show(struct seq_file *s, void *_)
|
||||||
HMC,
|
HMC,
|
||||||
udc->transceiver
|
udc->transceiver
|
||||||
? udc->transceiver->label
|
? udc->transceiver->label
|
||||||
: ((cpu_is_omap1710() || cpu_is_omap24xx())
|
: (cpu_is_omap1710()
|
||||||
? "external" : "(none)"));
|
? "external" : "(none)"));
|
||||||
if (cpu_class_is_omap1()) {
|
seq_printf(s, "ULPD control %04x req %04x status %04x\n",
|
||||||
seq_printf(s, "ULPD control %04x req %04x status %04x\n",
|
omap_readw(ULPD_CLOCK_CTRL),
|
||||||
omap_readw(ULPD_CLOCK_CTRL),
|
omap_readw(ULPD_SOFT_REQ),
|
||||||
omap_readw(ULPD_SOFT_REQ),
|
omap_readw(ULPD_STATUS_REQ));
|
||||||
omap_readw(ULPD_STATUS_REQ));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OTG controller registers */
|
/* OTG controller registers */
|
||||||
if (!cpu_is_omap15xx())
|
if (!cpu_is_omap15xx())
|
||||||
|
@ -2423,7 +2371,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
|
||||||
(tmp & UDC_SELF_PWR) ? " self_pwr" : "",
|
(tmp & UDC_SELF_PWR) ? " self_pwr" : "",
|
||||||
(tmp & UDC_SOFF_DIS) ? " soff_dis" : "",
|
(tmp & UDC_SOFF_DIS) ? " soff_dis" : "",
|
||||||
(tmp & UDC_PULLUP_EN) ? " PULLUP" : "");
|
(tmp & UDC_PULLUP_EN) ? " PULLUP" : "");
|
||||||
// syscon2 is write-only
|
/* syscon2 is write-only */
|
||||||
|
|
||||||
/* UDC controller registers */
|
/* UDC controller registers */
|
||||||
if (!(tmp & UDC_PULLUP_EN)) {
|
if (!(tmp & UDC_PULLUP_EN)) {
|
||||||
|
@ -2507,7 +2455,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
|
||||||
if (tmp & UDC_ATT) {
|
if (tmp & UDC_ATT) {
|
||||||
proc_ep_show(s, &udc->ep[0]);
|
proc_ep_show(s, &udc->ep[0]);
|
||||||
if (tmp & UDC_ADD) {
|
if (tmp & UDC_ADD) {
|
||||||
list_for_each_entry (ep, &udc->gadget.ep_list,
|
list_for_each_entry(ep, &udc->gadget.ep_list,
|
||||||
ep.ep_list) {
|
ep.ep_list) {
|
||||||
if (ep->ep.desc)
|
if (ep->ep.desc)
|
||||||
proc_ep_show(s, ep);
|
proc_ep_show(s, ep);
|
||||||
|
@ -2558,7 +2506,7 @@ static inline void remove_proc_file(void) {}
|
||||||
* UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that
|
* UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that
|
||||||
* capability yet though.
|
* capability yet though.
|
||||||
*/
|
*/
|
||||||
static unsigned __init
|
static unsigned __devinit
|
||||||
omap_ep_setup(char *name, u8 addr, u8 type,
|
omap_ep_setup(char *name, u8 addr, u8 type,
|
||||||
unsigned buf, unsigned maxp, int dbuf)
|
unsigned buf, unsigned maxp, int dbuf)
|
||||||
{
|
{
|
||||||
|
@ -2576,14 +2524,29 @@ omap_ep_setup(char *name, u8 addr, u8 type,
|
||||||
/* chip setup ... bit values are same for IN, OUT */
|
/* chip setup ... bit values are same for IN, OUT */
|
||||||
if (type == USB_ENDPOINT_XFER_ISOC) {
|
if (type == USB_ENDPOINT_XFER_ISOC) {
|
||||||
switch (maxp) {
|
switch (maxp) {
|
||||||
case 8: epn_rxtx = 0 << 12; break;
|
case 8:
|
||||||
case 16: epn_rxtx = 1 << 12; break;
|
epn_rxtx = 0 << 12;
|
||||||
case 32: epn_rxtx = 2 << 12; break;
|
break;
|
||||||
case 64: epn_rxtx = 3 << 12; break;
|
case 16:
|
||||||
case 128: epn_rxtx = 4 << 12; break;
|
epn_rxtx = 1 << 12;
|
||||||
case 256: epn_rxtx = 5 << 12; break;
|
break;
|
||||||
case 512: epn_rxtx = 6 << 12; break;
|
case 32:
|
||||||
default: BUG();
|
epn_rxtx = 2 << 12;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
epn_rxtx = 3 << 12;
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
epn_rxtx = 4 << 12;
|
||||||
|
break;
|
||||||
|
case 256:
|
||||||
|
epn_rxtx = 5 << 12;
|
||||||
|
break;
|
||||||
|
case 512:
|
||||||
|
epn_rxtx = 6 << 12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
}
|
}
|
||||||
epn_rxtx |= UDC_EPN_RX_ISO;
|
epn_rxtx |= UDC_EPN_RX_ISO;
|
||||||
dbuf = 1;
|
dbuf = 1;
|
||||||
|
@ -2592,15 +2555,24 @@ omap_ep_setup(char *name, u8 addr, u8 type,
|
||||||
* and ignored for PIO-IN on newer chips
|
* and ignored for PIO-IN on newer chips
|
||||||
* (for more reliable behavior)
|
* (for more reliable behavior)
|
||||||
*/
|
*/
|
||||||
if (!use_dma || cpu_is_omap15xx() || cpu_is_omap24xx())
|
if (!use_dma || cpu_is_omap15xx())
|
||||||
dbuf = 0;
|
dbuf = 0;
|
||||||
|
|
||||||
switch (maxp) {
|
switch (maxp) {
|
||||||
case 8: epn_rxtx = 0 << 12; break;
|
case 8:
|
||||||
case 16: epn_rxtx = 1 << 12; break;
|
epn_rxtx = 0 << 12;
|
||||||
case 32: epn_rxtx = 2 << 12; break;
|
break;
|
||||||
case 64: epn_rxtx = 3 << 12; break;
|
case 16:
|
||||||
default: BUG();
|
epn_rxtx = 1 << 12;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
epn_rxtx = 2 << 12;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
epn_rxtx = 3 << 12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
}
|
}
|
||||||
if (dbuf && addr)
|
if (dbuf && addr)
|
||||||
epn_rxtx |= UDC_EPN_RX_DB;
|
epn_rxtx |= UDC_EPN_RX_DB;
|
||||||
|
@ -2640,7 +2612,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
|
||||||
ep->ep.name = ep->name;
|
ep->ep.name = ep->name;
|
||||||
ep->ep.ops = &omap_ep_ops;
|
ep->ep.ops = &omap_ep_ops;
|
||||||
ep->ep.maxpacket = ep->maxpacket = maxp;
|
ep->ep.maxpacket = ep->maxpacket = maxp;
|
||||||
list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list);
|
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
@ -2648,11 +2620,11 @@ omap_ep_setup(char *name, u8 addr, u8 type,
|
||||||
static void omap_udc_release(struct device *dev)
|
static void omap_udc_release(struct device *dev)
|
||||||
{
|
{
|
||||||
complete(udc->done);
|
complete(udc->done);
|
||||||
kfree (udc);
|
kfree(udc);
|
||||||
udc = NULL;
|
udc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init
|
static int __devinit
|
||||||
omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
||||||
{
|
{
|
||||||
unsigned tmp, buf;
|
unsigned tmp, buf;
|
||||||
|
@ -2666,13 +2638,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
||||||
omap_writew(0, UDC_TXDMA_CFG);
|
omap_writew(0, UDC_TXDMA_CFG);
|
||||||
|
|
||||||
/* UDC_PULLUP_EN gates the chip clock */
|
/* UDC_PULLUP_EN gates the chip clock */
|
||||||
// OTG_SYSCON_1 |= DEV_IDLE_EN;
|
/* OTG_SYSCON_1 |= DEV_IDLE_EN; */
|
||||||
|
|
||||||
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
||||||
if (!udc)
|
if (!udc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_init (&udc->lock);
|
spin_lock_init(&udc->lock);
|
||||||
|
|
||||||
udc->gadget.ops = &omap_gadget_ops;
|
udc->gadget.ops = &omap_gadget_ops;
|
||||||
udc->gadget.ep0 = &udc->ep[0].ep;
|
udc->gadget.ep0 = &udc->ep[0].ep;
|
||||||
|
@ -2702,13 +2674,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
||||||
omap_writew(0, UDC_EP_TX(tmp));
|
omap_writew(0, UDC_EP_TX(tmp));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OMAP_BULK_EP(name,addr) \
|
#define OMAP_BULK_EP(name, addr) \
|
||||||
buf = omap_ep_setup(name "-bulk", addr, \
|
buf = omap_ep_setup(name "-bulk", addr, \
|
||||||
USB_ENDPOINT_XFER_BULK, buf, 64, 1);
|
USB_ENDPOINT_XFER_BULK, buf, 64, 1);
|
||||||
#define OMAP_INT_EP(name,addr, maxp) \
|
#define OMAP_INT_EP(name, addr, maxp) \
|
||||||
buf = omap_ep_setup(name "-int", addr, \
|
buf = omap_ep_setup(name "-int", addr, \
|
||||||
USB_ENDPOINT_XFER_INT, buf, maxp, 0);
|
USB_ENDPOINT_XFER_INT, buf, maxp, 0);
|
||||||
#define OMAP_ISO_EP(name,addr, maxp) \
|
#define OMAP_ISO_EP(name, addr, maxp) \
|
||||||
buf = omap_ep_setup(name "-iso", addr, \
|
buf = omap_ep_setup(name "-iso", addr, \
|
||||||
USB_ENDPOINT_XFER_ISOC, buf, maxp, 1);
|
USB_ENDPOINT_XFER_ISOC, buf, maxp, 1);
|
||||||
|
|
||||||
|
@ -2789,15 +2761,18 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init omap_udc_probe(struct platform_device *pdev)
|
static int __devinit omap_udc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int status = -ENODEV;
|
int status = -ENODEV;
|
||||||
int hmc;
|
int hmc;
|
||||||
struct usb_phy *xceiv = NULL;
|
struct usb_phy *xceiv = NULL;
|
||||||
const char *type = NULL;
|
const char *type = NULL;
|
||||||
struct omap_usb_config *config = pdev->dev.platform_data;
|
struct omap_usb_config *config = pdev->dev.platform_data;
|
||||||
struct clk *dc_clk;
|
struct clk *dc_clk = NULL;
|
||||||
struct clk *hhc_clk;
|
struct clk *hhc_clk = NULL;
|
||||||
|
|
||||||
|
if (cpu_is_omap7xx())
|
||||||
|
use_dma = 0;
|
||||||
|
|
||||||
/* NOTE: "knows" the order of the resources! */
|
/* NOTE: "knows" the order of the resources! */
|
||||||
if (!request_mem_region(pdev->resource[0].start,
|
if (!request_mem_region(pdev->resource[0].start,
|
||||||
|
@ -2817,16 +2792,6 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||||
udelay(100);
|
udelay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpu_is_omap24xx()) {
|
|
||||||
dc_clk = clk_get(&pdev->dev, "usb_fck");
|
|
||||||
hhc_clk = clk_get(&pdev->dev, "usb_l4_ick");
|
|
||||||
BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
|
|
||||||
/* can't use omap_udc_enable_clock yet */
|
|
||||||
clk_enable(dc_clk);
|
|
||||||
clk_enable(hhc_clk);
|
|
||||||
udelay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpu_is_omap7xx()) {
|
if (cpu_is_omap7xx()) {
|
||||||
dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
|
dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
|
||||||
hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
|
hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
|
||||||
|
@ -2866,8 +2831,8 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||||
* use it. Except for OTG, we don't _need_ to talk to one;
|
* use it. Except for OTG, we don't _need_ to talk to one;
|
||||||
* but not having one probably means no VBUS detection.
|
* but not having one probably means no VBUS detection.
|
||||||
*/
|
*/
|
||||||
xceiv = usb_get_transceiver();
|
xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
if (xceiv)
|
if (!IS_ERR_OR_NULL(xceiv))
|
||||||
type = xceiv->label;
|
type = xceiv->label;
|
||||||
else if (config->otg) {
|
else if (config->otg) {
|
||||||
DBG("OTG requires external transceiver!\n");
|
DBG("OTG requires external transceiver!\n");
|
||||||
|
@ -2876,14 +2841,6 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
hmc = HMC_1610;
|
hmc = HMC_1610;
|
||||||
|
|
||||||
if (cpu_is_omap24xx()) {
|
|
||||||
/* this could be transceiverless in one of the
|
|
||||||
* "we don't need to know" modes.
|
|
||||||
*/
|
|
||||||
type = "external";
|
|
||||||
goto known;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (hmc) {
|
switch (hmc) {
|
||||||
case 0: /* POWERUP DEFAULT == 0 */
|
case 0: /* POWERUP DEFAULT == 0 */
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -2899,7 +2856,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||||
case 16:
|
case 16:
|
||||||
case 19:
|
case 19:
|
||||||
case 25:
|
case 25:
|
||||||
if (!xceiv) {
|
if (IS_ERR_OR_NULL(xceiv)) {
|
||||||
DBG("external transceiver not registered!\n");
|
DBG("external transceiver not registered!\n");
|
||||||
type = "unknown";
|
type = "unknown";
|
||||||
}
|
}
|
||||||
|
@ -2922,16 +2879,16 @@ bad_on_1710:
|
||||||
goto cleanup0;
|
goto cleanup0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
known:
|
|
||||||
INFO("hmc mode %d, %s transceiver\n", hmc, type);
|
INFO("hmc mode %d, %s transceiver\n", hmc, type);
|
||||||
|
|
||||||
/* a "gadget" abstracts/virtualizes the controller */
|
/* a "gadget" abstracts/virtualizes the controller */
|
||||||
status = omap_udc_setup(pdev, xceiv);
|
status = omap_udc_setup(pdev, xceiv);
|
||||||
if (status) {
|
if (status)
|
||||||
goto cleanup0;
|
goto cleanup0;
|
||||||
}
|
|
||||||
xceiv = NULL;
|
xceiv = NULL;
|
||||||
// "udc" is now valid
|
/* "udc" is now valid */
|
||||||
pullup_disable(udc);
|
pullup_disable(udc);
|
||||||
#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
|
#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
|
||||||
udc->gadget.is_otg = (config->otg != 0);
|
udc->gadget.is_otg = (config->otg != 0);
|
||||||
|
@ -2945,7 +2902,7 @@ known:
|
||||||
|
|
||||||
/* USB general purpose IRQ: ep0, state changes, dma, etc */
|
/* USB general purpose IRQ: ep0, state changes, dma, etc */
|
||||||
status = request_irq(pdev->resource[1].start, omap_udc_irq,
|
status = request_irq(pdev->resource[1].start, omap_udc_irq,
|
||||||
IRQF_SAMPLE_RANDOM, driver_name, udc);
|
0, driver_name, udc);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
ERR("can't get irq %d, err %d\n",
|
ERR("can't get irq %d, err %d\n",
|
||||||
(int) pdev->resource[1].start, status);
|
(int) pdev->resource[1].start, status);
|
||||||
|
@ -2954,7 +2911,7 @@ known:
|
||||||
|
|
||||||
/* USB "non-iso" IRQ (PIO for all but ep0) */
|
/* USB "non-iso" IRQ (PIO for all but ep0) */
|
||||||
status = request_irq(pdev->resource[2].start, omap_udc_pio_irq,
|
status = request_irq(pdev->resource[2].start, omap_udc_pio_irq,
|
||||||
IRQF_SAMPLE_RANDOM, "omap_udc pio", udc);
|
0, "omap_udc pio", udc);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
ERR("can't get irq %d, err %d\n",
|
ERR("can't get irq %d, err %d\n",
|
||||||
(int) pdev->resource[2].start, status);
|
(int) pdev->resource[2].start, status);
|
||||||
|
@ -2976,16 +2933,6 @@ known:
|
||||||
clk_disable(dc_clk);
|
clk_disable(dc_clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpu_is_omap24xx()) {
|
|
||||||
udc->dc_clk = dc_clk;
|
|
||||||
udc->hhc_clk = hhc_clk;
|
|
||||||
/* FIXME OMAP2 don't release hhc & dc clock */
|
|
||||||
#if 0
|
|
||||||
clk_disable(hhc_clk);
|
|
||||||
clk_disable(dc_clk);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
create_proc_file();
|
create_proc_file();
|
||||||
status = device_add(&udc->gadget.dev);
|
status = device_add(&udc->gadget.dev);
|
||||||
if (status)
|
if (status)
|
||||||
|
@ -3007,14 +2954,14 @@ cleanup2:
|
||||||
free_irq(pdev->resource[1].start, udc);
|
free_irq(pdev->resource[1].start, udc);
|
||||||
|
|
||||||
cleanup1:
|
cleanup1:
|
||||||
kfree (udc);
|
kfree(udc);
|
||||||
udc = NULL;
|
udc = NULL;
|
||||||
|
|
||||||
cleanup0:
|
cleanup0:
|
||||||
if (xceiv)
|
if (!IS_ERR_OR_NULL(xceiv))
|
||||||
usb_put_transceiver(xceiv);
|
usb_put_phy(xceiv);
|
||||||
|
|
||||||
if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) {
|
if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
|
||||||
clk_disable(hhc_clk);
|
clk_disable(hhc_clk);
|
||||||
clk_disable(dc_clk);
|
clk_disable(dc_clk);
|
||||||
clk_put(hhc_clk);
|
clk_put(hhc_clk);
|
||||||
|
@ -3027,7 +2974,7 @@ cleanup0:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __exit omap_udc_remove(struct platform_device *pdev)
|
static int __devexit omap_udc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
DECLARE_COMPLETION_ONSTACK(done);
|
DECLARE_COMPLETION_ONSTACK(done);
|
||||||
|
|
||||||
|
@ -3041,8 +2988,8 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
|
||||||
udc->done = &done;
|
udc->done = &done;
|
||||||
|
|
||||||
pullup_disable(udc);
|
pullup_disable(udc);
|
||||||
if (udc->transceiver) {
|
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||||
usb_put_transceiver(udc->transceiver);
|
usb_put_phy(udc->transceiver);
|
||||||
udc->transceiver = NULL;
|
udc->transceiver = NULL;
|
||||||
}
|
}
|
||||||
omap_writew(0, UDC_SYSCON1);
|
omap_writew(0, UDC_SYSCON1);
|
||||||
|
@ -3112,7 +3059,8 @@ static int omap_udc_resume(struct platform_device *dev)
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static struct platform_driver udc_driver = {
|
static struct platform_driver udc_driver = {
|
||||||
.remove = __exit_p(omap_udc_remove),
|
.probe = omap_udc_probe,
|
||||||
|
.remove = __devexit_p(omap_udc_remove),
|
||||||
.suspend = omap_udc_suspend,
|
.suspend = omap_udc_suspend,
|
||||||
.resume = omap_udc_resume,
|
.resume = omap_udc_resume,
|
||||||
.driver = {
|
.driver = {
|
||||||
|
@ -3121,27 +3069,7 @@ static struct platform_driver udc_driver = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init udc_init(void)
|
module_platform_driver(udc_driver);
|
||||||
{
|
|
||||||
/* Disable DMA for omap7xx -- it doesn't work right. */
|
|
||||||
if (cpu_is_omap7xx())
|
|
||||||
use_dma = 0;
|
|
||||||
|
|
||||||
INFO("%s, version: " DRIVER_VERSION
|
|
||||||
#ifdef USE_ISO
|
|
||||||
" (iso)"
|
|
||||||
#endif
|
|
||||||
"%s\n", driver_desc,
|
|
||||||
use_dma ? " (dma)" : "");
|
|
||||||
return platform_driver_probe(&udc_driver, omap_udc_probe);
|
|
||||||
}
|
|
||||||
module_init(udc_init);
|
|
||||||
|
|
||||||
static void __exit udc_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&udc_driver);
|
|
||||||
}
|
|
||||||
module_exit(udc_exit);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -2208,7 +2208,7 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
|
if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
|
||||||
if (td->status | PCH_UDC_DMA_LAST) {
|
if (td->status & PCH_UDC_DMA_LAST) {
|
||||||
count = td->status & PCH_UDC_RXTX_BYTES;
|
count = td->status & PCH_UDC_RXTX_BYTES;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -993,7 +994,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||||
|
|
||||||
udc = container_of(_gadget, struct pxa25x_udc, gadget);
|
udc = container_of(_gadget, struct pxa25x_udc, gadget);
|
||||||
|
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return usb_phy_set_power(udc->transceiver, mA);
|
return usb_phy_set_power(udc->transceiver, mA);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -1299,7 +1300,7 @@ fail:
|
||||||
DMSG("registered gadget driver '%s'\n", driver->driver.name);
|
DMSG("registered gadget driver '%s'\n", driver->driver.name);
|
||||||
|
|
||||||
/* connect to bus through transceiver */
|
/* connect to bus through transceiver */
|
||||||
if (dev->transceiver) {
|
if (!IS_ERR_OR_NULL(dev->transceiver)) {
|
||||||
retval = otg_set_peripheral(dev->transceiver->otg,
|
retval = otg_set_peripheral(dev->transceiver->otg,
|
||||||
&dev->gadget);
|
&dev->gadget);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
|
@ -1359,7 +1360,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver)
|
||||||
stop_activity(dev, driver);
|
stop_activity(dev, driver);
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
if (dev->transceiver)
|
if (!IS_ERR_OR_NULL(dev->transceiver))
|
||||||
(void) otg_set_peripheral(dev->transceiver->otg, NULL);
|
(void) otg_set_peripheral(dev->transceiver->otg, NULL);
|
||||||
|
|
||||||
driver->unbind(&dev->gadget);
|
driver->unbind(&dev->gadget);
|
||||||
|
@ -2159,7 +2160,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
|
||||||
dev->dev = &pdev->dev;
|
dev->dev = &pdev->dev;
|
||||||
dev->mach = pdev->dev.platform_data;
|
dev->mach = pdev->dev.platform_data;
|
||||||
|
|
||||||
dev->transceiver = usb_get_transceiver();
|
dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
|
|
||||||
if (gpio_is_valid(dev->mach->gpio_pullup)) {
|
if (gpio_is_valid(dev->mach->gpio_pullup)) {
|
||||||
if ((retval = gpio_request(dev->mach->gpio_pullup,
|
if ((retval = gpio_request(dev->mach->gpio_pullup,
|
||||||
|
@ -2237,8 +2238,8 @@ lubbock_fail0:
|
||||||
if (gpio_is_valid(dev->mach->gpio_pullup))
|
if (gpio_is_valid(dev->mach->gpio_pullup))
|
||||||
gpio_free(dev->mach->gpio_pullup);
|
gpio_free(dev->mach->gpio_pullup);
|
||||||
err_gpio_pullup:
|
err_gpio_pullup:
|
||||||
if (dev->transceiver) {
|
if (!IS_ERR_OR_NULL(dev->transceiver)) {
|
||||||
usb_put_transceiver(dev->transceiver);
|
usb_put_phy(dev->transceiver);
|
||||||
dev->transceiver = NULL;
|
dev->transceiver = NULL;
|
||||||
}
|
}
|
||||||
clk_put(dev->clk);
|
clk_put(dev->clk);
|
||||||
|
@ -2279,8 +2280,8 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
clk_put(dev->clk);
|
clk_put(dev->clk);
|
||||||
|
|
||||||
if (dev->transceiver) {
|
if (!IS_ERR_OR_NULL(dev->transceiver)) {
|
||||||
usb_put_transceiver(dev->transceiver);
|
usb_put_phy(dev->transceiver);
|
||||||
dev->transceiver = NULL;
|
dev->transceiver = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
@ -1573,7 +1574,7 @@ static int should_enable_udc(struct pxa_udc *udc)
|
||||||
int put_on;
|
int put_on;
|
||||||
|
|
||||||
put_on = ((udc->pullup_on) && (udc->driver));
|
put_on = ((udc->pullup_on) && (udc->driver));
|
||||||
put_on &= ((udc->vbus_sensed) || (!udc->transceiver));
|
put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver)));
|
||||||
return put_on;
|
return put_on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1594,7 +1595,7 @@ static int should_disable_udc(struct pxa_udc *udc)
|
||||||
int put_off;
|
int put_off;
|
||||||
|
|
||||||
put_off = ((!udc->pullup_on) || (!udc->driver));
|
put_off = ((!udc->pullup_on) || (!udc->driver));
|
||||||
put_off |= ((!udc->vbus_sensed) && (udc->transceiver));
|
put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver)));
|
||||||
return put_off;
|
return put_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1665,7 +1666,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||||
struct pxa_udc *udc;
|
struct pxa_udc *udc;
|
||||||
|
|
||||||
udc = to_gadget_udc(_gadget);
|
udc = to_gadget_udc(_gadget);
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return usb_phy_set_power(udc->transceiver, mA);
|
return usb_phy_set_power(udc->transceiver, mA);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -1834,7 +1835,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||||
dev_dbg(udc->dev, "registered gadget driver '%s'\n",
|
dev_dbg(udc->dev, "registered gadget driver '%s'\n",
|
||||||
driver->driver.name);
|
driver->driver.name);
|
||||||
|
|
||||||
if (udc->transceiver) {
|
if (!IS_ERR_OR_NULL(udc->transceiver)) {
|
||||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||||
&udc->gadget);
|
&udc->gadget);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
|
@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
|
||||||
dev_info(udc->dev, "unregistered gadget driver '%s'\n",
|
dev_info(udc->dev, "unregistered gadget driver '%s'\n",
|
||||||
driver->driver.name);
|
driver->driver.name);
|
||||||
|
|
||||||
if (udc->transceiver)
|
if (!IS_ERR_OR_NULL(udc->transceiver))
|
||||||
return otg_set_peripheral(udc->transceiver->otg, NULL);
|
return otg_set_peripheral(udc->transceiver->otg, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2464,7 +2465,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
udc->dev = &pdev->dev;
|
udc->dev = &pdev->dev;
|
||||||
udc->mach = pdev->dev.platform_data;
|
udc->mach = pdev->dev.platform_data;
|
||||||
udc->transceiver = usb_get_transceiver();
|
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
|
|
||||||
gpio = udc->mach->gpio_pullup;
|
gpio = udc->mach->gpio_pullup;
|
||||||
if (gpio_is_valid(gpio)) {
|
if (gpio_is_valid(gpio)) {
|
||||||
|
@ -2543,7 +2544,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)
|
||||||
if (gpio_is_valid(gpio))
|
if (gpio_is_valid(gpio))
|
||||||
gpio_free(gpio);
|
gpio_free(gpio);
|
||||||
|
|
||||||
usb_put_transceiver(udc->transceiver);
|
usb_put_phy(udc->transceiver);
|
||||||
|
|
||||||
udc->transceiver = NULL;
|
udc->transceiver = NULL;
|
||||||
platform_set_drvdata(_dev, NULL);
|
platform_set_drvdata(_dev, NULL);
|
||||||
|
|
|
@ -112,7 +112,6 @@ struct s3c_hsotg_ep {
|
||||||
struct s3c_hsotg_req *req;
|
struct s3c_hsotg_req *req;
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
|
|
||||||
spinlock_t lock;
|
|
||||||
|
|
||||||
unsigned long total_data;
|
unsigned long total_data;
|
||||||
unsigned int size_loaded;
|
unsigned int size_loaded;
|
||||||
|
@ -136,7 +135,6 @@ struct s3c_hsotg_ep {
|
||||||
* @driver: USB gadget driver
|
* @driver: USB gadget driver
|
||||||
* @plat: The platform specific configuration data.
|
* @plat: The platform specific configuration data.
|
||||||
* @regs: The memory area mapped for accessing registers.
|
* @regs: The memory area mapped for accessing registers.
|
||||||
* @regs_res: The resource that was allocated when claiming register space.
|
|
||||||
* @irq: The IRQ number we are using
|
* @irq: The IRQ number we are using
|
||||||
* @supplies: Definition of USB power supplies
|
* @supplies: Definition of USB power supplies
|
||||||
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
||||||
|
@ -157,8 +155,9 @@ struct s3c_hsotg {
|
||||||
struct usb_gadget_driver *driver;
|
struct usb_gadget_driver *driver;
|
||||||
struct s3c_hsotg_plat *plat;
|
struct s3c_hsotg_plat *plat;
|
||||||
|
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct resource *regs_res;
|
|
||||||
int irq;
|
int irq;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
|
@ -896,7 +895,6 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||||
struct s3c_hsotg *hs = hs_ep->parent;
|
struct s3c_hsotg *hs = hs_ep->parent;
|
||||||
unsigned long irqflags;
|
|
||||||
bool first;
|
bool first;
|
||||||
|
|
||||||
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
|
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
|
||||||
|
@ -915,19 +913,30 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, irqflags);
|
|
||||||
|
|
||||||
first = list_empty(&hs_ep->queue);
|
first = list_empty(&hs_ep->queue);
|
||||||
list_add_tail(&hs_req->queue, &hs_ep->queue);
|
list_add_tail(&hs_req->queue, &hs_ep->queue);
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
s3c_hsotg_start_req(hs, hs_ep, hs_req, false);
|
s3c_hsotg_start_req(hs, hs_ep, hs_req, false);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, irqflags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
|
||||||
|
gfp_t gfp_flags)
|
||||||
|
{
|
||||||
|
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||||
|
struct s3c_hsotg *hs = hs_ep->parent;
|
||||||
|
unsigned long flags = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hs->lock, flags);
|
||||||
|
ret = s3c_hsotg_ep_queue(ep, req, gfp_flags);
|
||||||
|
spin_unlock_irqrestore(&hs->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void s3c_hsotg_ep_free_request(struct usb_ep *ep,
|
static void s3c_hsotg_ep_free_request(struct usb_ep *ep,
|
||||||
struct usb_request *req)
|
struct usb_request *req)
|
||||||
{
|
{
|
||||||
|
@ -1383,9 +1392,9 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (hs_req->req.complete) {
|
if (hs_req->req.complete) {
|
||||||
spin_unlock(&hs_ep->lock);
|
spin_unlock(&hsotg->lock);
|
||||||
hs_req->req.complete(&hs_ep->ep, &hs_req->req);
|
hs_req->req.complete(&hs_ep->ep, &hs_req->req);
|
||||||
spin_lock(&hs_ep->lock);
|
spin_lock(&hsotg->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1403,28 +1412,6 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* s3c_hsotg_complete_request_lock - complete a request given to us (locked)
|
|
||||||
* @hsotg: The device state.
|
|
||||||
* @hs_ep: The endpoint the request was on.
|
|
||||||
* @hs_req: The request to complete.
|
|
||||||
* @result: The result code (0 => Ok, otherwise errno)
|
|
||||||
*
|
|
||||||
* See s3c_hsotg_complete_request(), but called with the endpoint's
|
|
||||||
* lock held.
|
|
||||||
*/
|
|
||||||
static void s3c_hsotg_complete_request_lock(struct s3c_hsotg *hsotg,
|
|
||||||
struct s3c_hsotg_ep *hs_ep,
|
|
||||||
struct s3c_hsotg_req *hs_req,
|
|
||||||
int result)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, flags);
|
|
||||||
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
|
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* s3c_hsotg_rx_data - receive data from the FIFO for an endpoint
|
* s3c_hsotg_rx_data - receive data from the FIFO for an endpoint
|
||||||
* @hsotg: The device state.
|
* @hsotg: The device state.
|
||||||
|
@ -1444,6 +1431,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||||
int max_req;
|
int max_req;
|
||||||
int read_ptr;
|
int read_ptr;
|
||||||
|
|
||||||
|
|
||||||
if (!hs_req) {
|
if (!hs_req) {
|
||||||
u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
|
u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
|
||||||
int ptr;
|
int ptr;
|
||||||
|
@ -1459,8 +1447,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&hs_ep->lock);
|
|
||||||
|
|
||||||
to_read = size;
|
to_read = size;
|
||||||
read_ptr = hs_req->req.actual;
|
read_ptr = hs_req->req.actual;
|
||||||
max_req = hs_req->req.length - read_ptr;
|
max_req = hs_req->req.length - read_ptr;
|
||||||
|
@ -1487,8 +1473,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||||
* alignment of the data.
|
* alignment of the data.
|
||||||
*/
|
*/
|
||||||
readsl(fifo, hs_req->req.buf + read_ptr, to_read);
|
readsl(fifo, hs_req->req.buf + read_ptr, to_read);
|
||||||
|
|
||||||
spin_unlock(&hs_ep->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1609,7 +1593,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||||
s3c_hsotg_send_zlp(hsotg, hs_req);
|
s3c_hsotg_send_zlp(hsotg, hs_req);
|
||||||
}
|
}
|
||||||
|
|
||||||
s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, result);
|
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1864,7 +1848,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||||
/* Finish ZLP handling for IN EP0 transactions */
|
/* Finish ZLP handling for IN EP0 transactions */
|
||||||
if (hsotg->eps[0].sent_zlp) {
|
if (hsotg->eps[0].sent_zlp) {
|
||||||
dev_dbg(hsotg->dev, "zlp packet received\n");
|
dev_dbg(hsotg->dev, "zlp packet received\n");
|
||||||
s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
|
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1915,7 +1899,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||||
dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
|
dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
|
||||||
s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
|
s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
|
||||||
} else
|
} else
|
||||||
s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
|
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2123,9 +2107,6 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||||
int result, bool force)
|
int result, bool force)
|
||||||
{
|
{
|
||||||
struct s3c_hsotg_req *req, *treq;
|
struct s3c_hsotg_req *req, *treq;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ep->lock, flags);
|
|
||||||
|
|
||||||
list_for_each_entry_safe(req, treq, &ep->queue, queue) {
|
list_for_each_entry_safe(req, treq, &ep->queue, queue) {
|
||||||
/*
|
/*
|
||||||
|
@ -2139,14 +2120,15 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||||
s3c_hsotg_complete_request(hsotg, ep, req,
|
s3c_hsotg_complete_request(hsotg, ep, req,
|
||||||
result);
|
result);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ep->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define call_gadget(_hs, _entry) \
|
#define call_gadget(_hs, _entry) \
|
||||||
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
|
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
|
||||||
(_hs)->driver && (_hs)->driver->_entry) \
|
(_hs)->driver && (_hs)->driver->_entry) { \
|
||||||
(_hs)->driver->_entry(&(_hs)->gadget);
|
spin_unlock(&_hs->lock); \
|
||||||
|
(_hs)->driver->_entry(&(_hs)->gadget); \
|
||||||
|
spin_lock(&_hs->lock); \
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* s3c_hsotg_disconnect - disconnect service
|
* s3c_hsotg_disconnect - disconnect service
|
||||||
|
@ -2388,6 +2370,7 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
|
||||||
u32 gintsts;
|
u32 gintsts;
|
||||||
u32 gintmsk;
|
u32 gintmsk;
|
||||||
|
|
||||||
|
spin_lock(&hsotg->lock);
|
||||||
irq_retry:
|
irq_retry:
|
||||||
gintsts = readl(hsotg->regs + GINTSTS);
|
gintsts = readl(hsotg->regs + GINTSTS);
|
||||||
gintmsk = readl(hsotg->regs + GINTMSK);
|
gintmsk = readl(hsotg->regs + GINTMSK);
|
||||||
|
@ -2557,6 +2540,8 @@ irq_retry:
|
||||||
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
|
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
|
||||||
goto irq_retry;
|
goto irq_retry;
|
||||||
|
|
||||||
|
spin_unlock(&hsotg->lock);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2604,7 +2589,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
||||||
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
|
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
|
||||||
__func__, epctrl, epctrl_reg);
|
__func__, epctrl, epctrl_reg);
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, flags);
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
|
|
||||||
epctrl &= ~(DxEPCTL_EPType_MASK | DxEPCTL_MPS_MASK);
|
epctrl &= ~(DxEPCTL_EPType_MASK | DxEPCTL_MPS_MASK);
|
||||||
epctrl |= DxEPCTL_MPS(mps);
|
epctrl |= DxEPCTL_MPS(mps);
|
||||||
|
@ -2683,7 +2668,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
||||||
s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
|
s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, flags);
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2710,10 +2695,10 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||||
|
|
||||||
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
|
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
/* terminate all requests with shutdown */
|
/* terminate all requests with shutdown */
|
||||||
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
|
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, flags);
|
|
||||||
|
|
||||||
ctrl = readl(hsotg->regs + epctrl_reg);
|
ctrl = readl(hsotg->regs + epctrl_reg);
|
||||||
ctrl &= ~DxEPCTL_EPEna;
|
ctrl &= ~DxEPCTL_EPEna;
|
||||||
|
@ -2726,7 +2711,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||||
/* disable endpoint interrupts */
|
/* disable endpoint interrupts */
|
||||||
s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
|
s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, flags);
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2761,15 +2746,15 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||||
|
|
||||||
dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, flags);
|
spin_lock_irqsave(&hs->lock, flags);
|
||||||
|
|
||||||
if (!on_list(hs_ep, hs_req)) {
|
if (!on_list(hs_ep, hs_req)) {
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, flags);
|
spin_unlock_irqrestore(&hs->lock, flags);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
|
s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, flags);
|
spin_unlock_irqrestore(&hs->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2784,15 +2769,12 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||||
struct s3c_hsotg *hs = hs_ep->parent;
|
struct s3c_hsotg *hs = hs_ep->parent;
|
||||||
int index = hs_ep->index;
|
int index = hs_ep->index;
|
||||||
unsigned long irqflags;
|
|
||||||
u32 epreg;
|
u32 epreg;
|
||||||
u32 epctl;
|
u32 epctl;
|
||||||
u32 xfertype;
|
u32 xfertype;
|
||||||
|
|
||||||
dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
|
dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
|
||||||
|
|
||||||
spin_lock_irqsave(&hs_ep->lock, irqflags);
|
|
||||||
|
|
||||||
/* write both IN and OUT control registers */
|
/* write both IN and OUT control registers */
|
||||||
|
|
||||||
epreg = DIEPCTL(index);
|
epreg = DIEPCTL(index);
|
||||||
|
@ -2827,19 +2809,36 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||||
|
|
||||||
writel(epctl, hs->regs + epreg);
|
writel(epctl, hs->regs + epreg);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hs_ep->lock, irqflags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s3c_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held
|
||||||
|
* @ep: The endpoint to set halt.
|
||||||
|
* @value: Set or unset the halt.
|
||||||
|
*/
|
||||||
|
static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
|
||||||
|
{
|
||||||
|
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||||
|
struct s3c_hsotg *hs = hs_ep->parent;
|
||||||
|
unsigned long flags = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hs->lock, flags);
|
||||||
|
ret = s3c_hsotg_ep_sethalt(ep, value);
|
||||||
|
spin_unlock_irqrestore(&hs->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static struct usb_ep_ops s3c_hsotg_ep_ops = {
|
static struct usb_ep_ops s3c_hsotg_ep_ops = {
|
||||||
.enable = s3c_hsotg_ep_enable,
|
.enable = s3c_hsotg_ep_enable,
|
||||||
.disable = s3c_hsotg_ep_disable,
|
.disable = s3c_hsotg_ep_disable,
|
||||||
.alloc_request = s3c_hsotg_ep_alloc_request,
|
.alloc_request = s3c_hsotg_ep_alloc_request,
|
||||||
.free_request = s3c_hsotg_ep_free_request,
|
.free_request = s3c_hsotg_ep_free_request,
|
||||||
.queue = s3c_hsotg_ep_queue,
|
.queue = s3c_hsotg_ep_queue_lock,
|
||||||
.dequeue = s3c_hsotg_ep_dequeue,
|
.dequeue = s3c_hsotg_ep_dequeue,
|
||||||
.set_halt = s3c_hsotg_ep_sethalt,
|
.set_halt = s3c_hsotg_ep_sethalt_lock,
|
||||||
/* note, don't believe we have any call for the fifo routines */
|
/* note, don't believe we have any call for the fifo routines */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2954,6 +2953,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||||
driver->driver.bus = NULL;
|
driver->driver.bus = NULL;
|
||||||
hsotg->driver = driver;
|
hsotg->driver = driver;
|
||||||
hsotg->gadget.dev.driver = &driver->driver;
|
hsotg->gadget.dev.driver = &driver->driver;
|
||||||
|
hsotg->gadget.dev.of_node = hsotg->dev->of_node;
|
||||||
hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask;
|
hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask;
|
||||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
|
|
||||||
|
@ -2964,9 +2964,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
s3c_hsotg_phy_enable(hsotg);
|
|
||||||
|
|
||||||
s3c_hsotg_core_init(hsotg);
|
|
||||||
hsotg->last_rst = jiffies;
|
hsotg->last_rst = jiffies;
|
||||||
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
|
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2988,6 +2985,7 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||||
struct usb_gadget_driver *driver)
|
struct usb_gadget_driver *driver)
|
||||||
{
|
{
|
||||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||||
|
unsigned long flags = 0;
|
||||||
int ep;
|
int ep;
|
||||||
|
|
||||||
if (!hsotg)
|
if (!hsotg)
|
||||||
|
@ -3000,6 +2998,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
|
|
||||||
s3c_hsotg_phy_disable(hsotg);
|
s3c_hsotg_phy_disable(hsotg);
|
||||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||||
|
|
||||||
|
@ -3007,6 +3007,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
hsotg->gadget.dev.driver = NULL;
|
hsotg->gadget.dev.driver = NULL;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
|
|
||||||
dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
|
dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
|
||||||
driver->driver.name);
|
driver->driver.name);
|
||||||
|
|
||||||
|
@ -3024,10 +3026,40 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
|
||||||
return s3c_hsotg_read_frameno(to_hsotg(gadget));
|
return s3c_hsotg_read_frameno(to_hsotg(gadget));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s3c_hsotg_pullup - connect/disconnect the USB PHY
|
||||||
|
* @gadget: The usb gadget state
|
||||||
|
* @is_on: Current state of the USB PHY
|
||||||
|
*
|
||||||
|
* Connect/Disconnect the USB PHY pullup
|
||||||
|
*/
|
||||||
|
static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
|
||||||
|
{
|
||||||
|
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||||
|
unsigned long flags = 0;
|
||||||
|
|
||||||
|
dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
|
if (is_on) {
|
||||||
|
s3c_hsotg_phy_enable(hsotg);
|
||||||
|
s3c_hsotg_core_init(hsotg);
|
||||||
|
} else {
|
||||||
|
s3c_hsotg_disconnect(hsotg);
|
||||||
|
s3c_hsotg_phy_disable(hsotg);
|
||||||
|
}
|
||||||
|
|
||||||
|
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
||||||
.get_frame = s3c_hsotg_gadget_getframe,
|
.get_frame = s3c_hsotg_gadget_getframe,
|
||||||
.udc_start = s3c_hsotg_udc_start,
|
.udc_start = s3c_hsotg_udc_start,
|
||||||
.udc_stop = s3c_hsotg_udc_stop,
|
.udc_stop = s3c_hsotg_udc_stop,
|
||||||
|
.pullup = s3c_hsotg_pullup,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3063,8 +3095,6 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||||
INIT_LIST_HEAD(&hs_ep->queue);
|
INIT_LIST_HEAD(&hs_ep->queue);
|
||||||
INIT_LIST_HEAD(&hs_ep->ep.ep_list);
|
INIT_LIST_HEAD(&hs_ep->ep.ep_list);
|
||||||
|
|
||||||
spin_lock_init(&hs_ep->lock);
|
|
||||||
|
|
||||||
/* add to the list of endpoints known by the gadget driver */
|
/* add to the list of endpoints known by the gadget driver */
|
||||||
if (epnum)
|
if (epnum)
|
||||||
list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
|
list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
|
||||||
|
@ -3342,7 +3372,7 @@ static int ep_show(struct seq_file *seq, void *v)
|
||||||
seq_printf(seq, "request list (%p,%p):\n",
|
seq_printf(seq, "request list (%p,%p):\n",
|
||||||
ep->queue.next, ep->queue.prev);
|
ep->queue.next, ep->queue.prev);
|
||||||
|
|
||||||
spin_lock_irqsave(&ep->lock, flags);
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
|
|
||||||
list_for_each_entry(req, &ep->queue, queue) {
|
list_for_each_entry(req, &ep->queue, queue) {
|
||||||
if (--show_limit < 0) {
|
if (--show_limit < 0) {
|
||||||
|
@ -3357,7 +3387,7 @@ static int ep_show(struct seq_file *seq, void *v)
|
||||||
req->req.actual, req->req.status);
|
req->req.actual, req->req.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ep->lock, flags);
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3477,7 +3507,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
hsotg = kzalloc(sizeof(struct s3c_hsotg), GFP_KERNEL);
|
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
|
||||||
if (!hsotg) {
|
if (!hsotg) {
|
||||||
dev_err(dev, "cannot get memory\n");
|
dev_err(dev, "cannot get memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -3489,46 +3519,35 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||||
hsotg->clk = clk_get(&pdev->dev, "otg");
|
hsotg->clk = clk_get(&pdev->dev, "otg");
|
||||||
if (IS_ERR(hsotg->clk)) {
|
if (IS_ERR(hsotg->clk)) {
|
||||||
dev_err(dev, "cannot get otg clock\n");
|
dev_err(dev, "cannot get otg clock\n");
|
||||||
ret = PTR_ERR(hsotg->clk);
|
return PTR_ERR(hsotg->clk);
|
||||||
goto err_mem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, hsotg);
|
platform_set_drvdata(pdev, hsotg);
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
|
||||||
dev_err(dev, "cannot find register resource 0\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto err_clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
hsotg->regs_res = request_mem_region(res->start, resource_size(res),
|
hsotg->regs = devm_request_and_ioremap(&pdev->dev, res);
|
||||||
dev_name(dev));
|
|
||||||
if (!hsotg->regs_res) {
|
|
||||||
dev_err(dev, "cannot reserve registers\n");
|
|
||||||
ret = -ENOENT;
|
|
||||||
goto err_clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
hsotg->regs = ioremap(res->start, resource_size(res));
|
|
||||||
if (!hsotg->regs) {
|
if (!hsotg->regs) {
|
||||||
dev_err(dev, "cannot map registers\n");
|
dev_err(dev, "cannot map registers\n");
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto err_regs_res;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = platform_get_irq(pdev, 0);
|
ret = platform_get_irq(pdev, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "cannot find IRQ\n");
|
dev_err(dev, "cannot find IRQ\n");
|
||||||
goto err_regs;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&hsotg->lock);
|
||||||
|
|
||||||
hsotg->irq = ret;
|
hsotg->irq = ret;
|
||||||
|
|
||||||
ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg);
|
ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
|
||||||
|
dev_name(dev), hsotg);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "cannot claim IRQ\n");
|
dev_err(dev, "cannot claim IRQ\n");
|
||||||
goto err_regs;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
|
dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
|
||||||
|
@ -3558,7 +3577,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
||||||
hsotg->supplies);
|
hsotg->supplies);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||||
goto err_irq;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||||
|
@ -3642,19 +3661,11 @@ err_ep_mem:
|
||||||
err_supplies:
|
err_supplies:
|
||||||
s3c_hsotg_phy_disable(hsotg);
|
s3c_hsotg_phy_disable(hsotg);
|
||||||
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||||
err_irq:
|
|
||||||
free_irq(hsotg->irq, hsotg);
|
|
||||||
err_regs:
|
|
||||||
iounmap(hsotg->regs);
|
|
||||||
|
|
||||||
err_regs_res:
|
|
||||||
release_resource(hsotg->regs_res);
|
|
||||||
kfree(hsotg->regs_res);
|
|
||||||
err_clk:
|
err_clk:
|
||||||
clk_disable_unprepare(hsotg->clk);
|
clk_disable_unprepare(hsotg->clk);
|
||||||
clk_put(hsotg->clk);
|
clk_put(hsotg->clk);
|
||||||
err_mem:
|
|
||||||
kfree(hsotg);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3675,12 +3686,6 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
|
||||||
usb_gadget_unregister_driver(hsotg->driver);
|
usb_gadget_unregister_driver(hsotg->driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_irq(hsotg->irq, hsotg);
|
|
||||||
iounmap(hsotg->regs);
|
|
||||||
|
|
||||||
release_resource(hsotg->regs_res);
|
|
||||||
kfree(hsotg->regs_res);
|
|
||||||
|
|
||||||
s3c_hsotg_phy_disable(hsotg);
|
s3c_hsotg_phy_disable(hsotg);
|
||||||
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/usb/ch9.h>
|
#include <linux/usb/ch9.h>
|
||||||
#include <linux/usb/gadget.h>
|
#include <linux/usb/gadget.h>
|
||||||
#include <linux/usb/otg.h>
|
#include <linux/usb/otg.h>
|
||||||
|
@ -1165,7 +1166,7 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* connect to bus through transceiver */
|
/* connect to bus through transceiver */
|
||||||
if (hsudc->transceiver) {
|
if (!IS_ERR_OR_NULL(hsudc->transceiver)) {
|
||||||
ret = otg_set_peripheral(hsudc->transceiver->otg,
|
ret = otg_set_peripheral(hsudc->transceiver->otg,
|
||||||
&hsudc->gadget);
|
&hsudc->gadget);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -1220,7 +1221,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||||
s3c_hsudc_stop_activity(hsudc);
|
s3c_hsudc_stop_activity(hsudc);
|
||||||
spin_unlock_irqrestore(&hsudc->lock, flags);
|
spin_unlock_irqrestore(&hsudc->lock, flags);
|
||||||
|
|
||||||
if (hsudc->transceiver)
|
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||||
(void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
|
(void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
|
||||||
|
|
||||||
disable_irq(hsudc->irq);
|
disable_irq(hsudc->irq);
|
||||||
|
@ -1249,7 +1250,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||||
if (!hsudc)
|
if (!hsudc)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (hsudc->transceiver)
|
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||||
return usb_phy_set_power(hsudc->transceiver, mA);
|
return usb_phy_set_power(hsudc->transceiver, mA);
|
||||||
|
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -1282,7 +1283,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
|
||||||
hsudc->dev = dev;
|
hsudc->dev = dev;
|
||||||
hsudc->pd = pdev->dev.platform_data;
|
hsudc->pd = pdev->dev.platform_data;
|
||||||
|
|
||||||
hsudc->transceiver = usb_get_transceiver();
|
hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
|
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
|
||||||
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
|
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
|
||||||
|
@ -1385,8 +1386,8 @@ err_irq:
|
||||||
err_remap:
|
err_remap:
|
||||||
release_mem_region(res->start, resource_size(res));
|
release_mem_region(res->start, resource_size(res));
|
||||||
err_res:
|
err_res:
|
||||||
if (hsudc->transceiver)
|
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||||
usb_put_transceiver(hsudc->transceiver);
|
usb_put_phy(hsudc->transceiver);
|
||||||
|
|
||||||
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||||
err_supplies:
|
err_supplies:
|
||||||
|
|
|
@ -37,12 +37,6 @@
|
||||||
* When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
|
* When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
|
|
||||||
* the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
|
|
||||||
* characters rather then a pointer to void.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
|
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
|
||||||
* sets the number of pipeline buffers (length of the fsg_buffhd array).
|
* sets the number of pipeline buffers (length of the fsg_buffhd array).
|
||||||
|
@ -260,11 +254,7 @@ enum fsg_buffer_state {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fsg_buffhd {
|
struct fsg_buffhd {
|
||||||
#ifdef FSG_BUFFHD_STATIC_BUFFER
|
|
||||||
char buf[FSG_BUFLEN];
|
|
||||||
#else
|
|
||||||
void *buf;
|
void *buf;
|
||||||
#endif
|
|
||||||
enum fsg_buffer_state state;
|
enum fsg_buffer_state state;
|
||||||
struct fsg_buffhd *next;
|
struct fsg_buffhd *next;
|
||||||
|
|
||||||
|
@ -627,6 +617,16 @@ static struct usb_gadget_strings fsg_stringtab = {
|
||||||
* the caller must own fsg->filesem for writing.
|
* the caller must own fsg->filesem for writing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void fsg_lun_close(struct fsg_lun *curlun)
|
||||||
|
{
|
||||||
|
if (curlun->filp) {
|
||||||
|
LDBG(curlun, "close backing file\n");
|
||||||
|
fput(curlun->filp);
|
||||||
|
curlun->filp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||||
{
|
{
|
||||||
int ro;
|
int ro;
|
||||||
|
@ -636,6 +636,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||||
loff_t size;
|
loff_t size;
|
||||||
loff_t num_sectors;
|
loff_t num_sectors;
|
||||||
loff_t min_sectors;
|
loff_t min_sectors;
|
||||||
|
unsigned int blkbits;
|
||||||
|
unsigned int blksize;
|
||||||
|
|
||||||
/* R/W if we can, R/O if we must */
|
/* R/W if we can, R/O if we must */
|
||||||
ro = curlun->initially_ro;
|
ro = curlun->initially_ro;
|
||||||
|
@ -680,17 +682,17 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curlun->cdrom) {
|
if (curlun->cdrom) {
|
||||||
curlun->blksize = 2048;
|
blksize = 2048;
|
||||||
curlun->blkbits = 11;
|
blkbits = 11;
|
||||||
} else if (inode->i_bdev) {
|
} else if (inode->i_bdev) {
|
||||||
curlun->blksize = bdev_logical_block_size(inode->i_bdev);
|
blksize = bdev_logical_block_size(inode->i_bdev);
|
||||||
curlun->blkbits = blksize_bits(curlun->blksize);
|
blkbits = blksize_bits(blksize);
|
||||||
} else {
|
} else {
|
||||||
curlun->blksize = 512;
|
blksize = 512;
|
||||||
curlun->blkbits = 9;
|
blkbits = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */
|
num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
|
||||||
min_sectors = 1;
|
min_sectors = 1;
|
||||||
if (curlun->cdrom) {
|
if (curlun->cdrom) {
|
||||||
min_sectors = 300; /* Smallest track is 300 frames */
|
min_sectors = 300; /* Smallest track is 300 frames */
|
||||||
|
@ -707,7 +709,12 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fsg_lun_is_open(curlun))
|
||||||
|
fsg_lun_close(curlun);
|
||||||
|
|
||||||
get_file(filp);
|
get_file(filp);
|
||||||
|
curlun->blksize = blksize;
|
||||||
|
curlun->blkbits = blkbits;
|
||||||
curlun->ro = ro;
|
curlun->ro = ro;
|
||||||
curlun->filp = filp;
|
curlun->filp = filp;
|
||||||
curlun->file_length = size;
|
curlun->file_length = size;
|
||||||
|
@ -721,16 +728,6 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void fsg_lun_close(struct fsg_lun *curlun)
|
|
||||||
{
|
|
||||||
if (curlun->filp) {
|
|
||||||
LDBG(curlun, "close backing file\n");
|
|
||||||
fput(curlun->filp);
|
|
||||||
curlun->filp = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -881,19 +878,17 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
|
||||||
if (count > 0 && buf[count-1] == '\n')
|
if (count > 0 && buf[count-1] == '\n')
|
||||||
((char *) buf)[count-1] = 0; /* Ugh! */
|
((char *) buf)[count-1] = 0; /* Ugh! */
|
||||||
|
|
||||||
/* Eject current medium */
|
|
||||||
down_write(filesem);
|
|
||||||
if (fsg_lun_is_open(curlun)) {
|
|
||||||
fsg_lun_close(curlun);
|
|
||||||
curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load new medium */
|
/* Load new medium */
|
||||||
|
down_write(filesem);
|
||||||
if (count > 0 && buf[0]) {
|
if (count > 0 && buf[0]) {
|
||||||
|
/* fsg_lun_open() will close existing file if any. */
|
||||||
rc = fsg_lun_open(curlun, buf);
|
rc = fsg_lun_open(curlun, buf);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
curlun->unit_attention_data =
|
curlun->unit_attention_data =
|
||||||
SS_NOT_READY_TO_READY_TRANSITION;
|
SS_NOT_READY_TO_READY_TRANSITION;
|
||||||
|
} else if (fsg_lun_is_open(curlun)) {
|
||||||
|
fsg_lun_close(curlun);
|
||||||
|
curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
|
||||||
}
|
}
|
||||||
up_write(filesem);
|
up_write(filesem);
|
||||||
return (rc < 0 ? rc : count);
|
return (rc < 0 ? rc : count);
|
||||||
|
|
|
@ -798,12 +798,6 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||||
|
|
||||||
SET_ETHTOOL_OPS(net, &ops);
|
SET_ETHTOOL_OPS(net, &ops);
|
||||||
|
|
||||||
/* two kinds of host-initiated state changes:
|
|
||||||
* - iff DATA transfer is active, carrier is "on"
|
|
||||||
* - tx queueing enabled if open *and* carrier is "on"
|
|
||||||
*/
|
|
||||||
netif_carrier_off(net);
|
|
||||||
|
|
||||||
dev->gadget = g;
|
dev->gadget = g;
|
||||||
SET_NETDEV_DEV(net, &g->dev);
|
SET_NETDEV_DEV(net, &g->dev);
|
||||||
SET_NETDEV_DEVTYPE(net, &gadget_type);
|
SET_NETDEV_DEVTYPE(net, &gadget_type);
|
||||||
|
@ -817,6 +811,12 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||||
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
||||||
|
|
||||||
the_dev = dev;
|
the_dev = dev;
|
||||||
|
|
||||||
|
/* two kinds of host-initiated state changes:
|
||||||
|
* - iff DATA transfer is active, carrier is "on"
|
||||||
|
* - tx queueing enabled if open *and* carrier is "on"
|
||||||
|
*/
|
||||||
|
netif_carrier_off(net);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -153,9 +153,11 @@ struct uvc_device
|
||||||
|
|
||||||
/* Descriptors */
|
/* Descriptors */
|
||||||
struct {
|
struct {
|
||||||
const struct uvc_descriptor_header * const *control;
|
const struct uvc_descriptor_header * const *fs_control;
|
||||||
|
const struct uvc_descriptor_header * const *ss_control;
|
||||||
const struct uvc_descriptor_header * const *fs_streaming;
|
const struct uvc_descriptor_header * const *fs_streaming;
|
||||||
const struct uvc_descriptor_header * const *hs_streaming;
|
const struct uvc_descriptor_header * const *hs_streaming;
|
||||||
|
const struct uvc_descriptor_header * const *ss_streaming;
|
||||||
} desc;
|
} desc;
|
||||||
|
|
||||||
unsigned int control_intf;
|
unsigned int control_intf;
|
||||||
|
|
|
@ -272,7 +272,15 @@ static const struct uvc_color_matching_descriptor uvc_color_matching = {
|
||||||
.bMatrixCoefficients = 4,
|
.bMatrixCoefficients = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct uvc_descriptor_header * const uvc_control_cls[] = {
|
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_control_header,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_processing,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_output_terminal,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
|
||||||
(const struct uvc_descriptor_header *) &uvc_control_header,
|
(const struct uvc_descriptor_header *) &uvc_control_header,
|
||||||
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
|
||||||
(const struct uvc_descriptor_header *) &uvc_processing,
|
(const struct uvc_descriptor_header *) &uvc_processing,
|
||||||
|
@ -304,6 +312,18 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_input_header,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_format_yuv,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
|
||||||
|
(const struct uvc_descriptor_header *) &uvc_color_matching,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* USB configuration
|
* USB configuration
|
||||||
*/
|
*/
|
||||||
|
@ -311,8 +331,9 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
|
||||||
static int __init
|
static int __init
|
||||||
webcam_config_bind(struct usb_configuration *c)
|
webcam_config_bind(struct usb_configuration *c)
|
||||||
{
|
{
|
||||||
return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
|
return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,
|
||||||
uvc_hs_streaming_cls);
|
uvc_fs_streaming_cls, uvc_hs_streaming_cls,
|
||||||
|
uvc_ss_streaming_cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_configuration webcam_config_driver = {
|
static struct usb_configuration webcam_config_driver = {
|
||||||
|
@ -373,7 +394,7 @@ static struct usb_composite_driver webcam_driver = {
|
||||||
.name = "g_webcam",
|
.name = "g_webcam",
|
||||||
.dev = &webcam_device_descriptor,
|
.dev = &webcam_device_descriptor,
|
||||||
.strings = webcam_device_strings,
|
.strings = webcam_device_strings,
|
||||||
.max_speed = USB_SPEED_HIGH,
|
.max_speed = USB_SPEED_SUPER,
|
||||||
.unbind = webcam_unbind,
|
.unbind = webcam_unbind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -652,7 +652,7 @@ config USB_HCD_BCMA
|
||||||
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
|
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
|
||||||
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
|
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
|
||||||
help
|
help
|
||||||
Enbale support for the EHCI and OCHI host controller on an bcma bus.
|
Enable support for the EHCI and OCHI host controller on an bcma bus.
|
||||||
It converts the bcma driver into two platform device drivers
|
It converts the bcma driver into two platform device drivers
|
||||||
for ehci and ohci.
|
for ehci and ohci.
|
||||||
|
|
||||||
|
@ -664,7 +664,7 @@ config USB_HCD_SSB
|
||||||
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
|
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
|
||||||
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
|
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
|
||||||
help
|
help
|
||||||
Enbale support for the EHCI and OCHI host controller on an bcma bus.
|
Enable support for the EHCI and OCHI host controller on an bcma bus.
|
||||||
It converts the bcma driver into two platform device drivers
|
It converts the bcma driver into two platform device drivers
|
||||||
for ehci and ohci.
|
for ehci and ohci.
|
||||||
|
|
||||||
|
|
|
@ -53,30 +53,15 @@ static void atmel_stop_ehci(struct platform_device *pdev)
|
||||||
static int ehci_atmel_setup(struct usb_hcd *hcd)
|
static int ehci_atmel_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval = 0;
|
int retval;
|
||||||
|
|
||||||
/* registers start at offset 0x0 */
|
/* registers start at offset 0x0 */
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
retval = ehci_setup(hcd);
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
ehci_port_power(ehci, 0);
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
@ -20,10 +20,12 @@ extern int usb_disabled(void);
|
||||||
static int au1xxx_ehci_setup(struct usb_hcd *hcd)
|
static int au1xxx_ehci_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int ret = ehci_init(hcd);
|
int ret;
|
||||||
|
|
||||||
|
ehci->caps = hcd->regs;
|
||||||
|
ret = ehci_setup(hcd);
|
||||||
|
|
||||||
ehci->need_io_watchdog = 0;
|
ehci->need_io_watchdog = 0;
|
||||||
ehci_reset(ehci);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +80,6 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||||
static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
|
static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd;
|
struct usb_hcd *hcd;
|
||||||
struct ehci_hcd *ehci;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -116,13 +117,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
|
||||||
goto err3;
|
goto err3;
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci = hcd_to_ehci(hcd);
|
|
||||||
ehci->caps = hcd->regs;
|
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
||||||
IRQF_SHARED);
|
IRQF_SHARED);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
@ -158,28 +152,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
|
||||||
static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
|
static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
unsigned long flags;
|
int rc;
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
|
||||||
msleep(10);
|
|
||||||
|
|
||||||
/* Root hub was already suspended. Disable irq emission and
|
|
||||||
* mark HW unaccessible. The PM and USB cores make sure that
|
|
||||||
* the root hub is either suspended or stopped.
|
|
||||||
*/
|
|
||||||
ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
|
||||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
|
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
||||||
|
|
||||||
// could save FLADJ in case of Vaux power loss
|
|
||||||
// ... we'd only use it to handle clock skew
|
|
||||||
|
|
||||||
|
rc = ehci_suspend(hcd, do_wakeup);
|
||||||
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
|
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -188,56 +164,9 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
|
||||||
static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
|
static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
|
|
||||||
alchemy_usb_control(ALCHEMY_USB_EHCI0, 1);
|
alchemy_usb_control(ALCHEMY_USB_EHCI0, 1);
|
||||||
|
ehci_resume(hcd, false);
|
||||||
// maybe restore FLADJ
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
/* Mark hardware accessible again as we are out of D3 state by now */
|
|
||||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
|
|
||||||
/* If CF is still set, we maintained PCI Vaux power.
|
|
||||||
* Just undo the effect of ehci_pci_suspend().
|
|
||||||
*/
|
|
||||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
|
||||||
int mask = INTR_MASK;
|
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(ehci);
|
|
||||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
|
||||||
mask &= ~STS_PCD;
|
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci_dbg(ehci, "lost power, restarting\n");
|
|
||||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
|
||||||
|
|
||||||
/* Else reset, to cope with power loss or flush-to-storage
|
|
||||||
* style "resume" having let BIOS kick in during reboot.
|
|
||||||
*/
|
|
||||||
(void) ehci_halt(ehci);
|
|
||||||
(void) ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
|
||||||
if (ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
ehci_work(ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered */
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
|
|
||||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,14 +33,10 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs
|
|
||||||
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
hcd->has_tt = 0;
|
hcd->has_tt = 0;
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
|
|
@ -404,9 +404,9 @@ struct debug_buffer {
|
||||||
|
|
||||||
#define speed_char(info1) ({ char tmp; \
|
#define speed_char(info1) ({ char tmp; \
|
||||||
switch (info1 & (3 << 12)) { \
|
switch (info1 & (3 << 12)) { \
|
||||||
case 0 << 12: tmp = 'f'; break; \
|
case QH_FULL_SPEED: tmp = 'f'; break; \
|
||||||
case 1 << 12: tmp = 'l'; break; \
|
case QH_LOW_SPEED: tmp = 'l'; break; \
|
||||||
case 2 << 12: tmp = 'h'; break; \
|
case QH_HIGH_SPEED: tmp = 'h'; break; \
|
||||||
default: tmp = '?'; break; \
|
default: tmp = '?'; break; \
|
||||||
}; tmp; })
|
}; tmp; })
|
||||||
|
|
||||||
|
@ -538,12 +538,13 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
||||||
spin_lock_irqsave (&ehci->lock, flags);
|
spin_lock_irqsave (&ehci->lock, flags);
|
||||||
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
|
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
|
||||||
qh_lines (ehci, qh, &next, &size);
|
qh_lines (ehci, qh, &next, &size);
|
||||||
if (ehci->reclaim && size > 0) {
|
if (ehci->async_unlink && size > 0) {
|
||||||
temp = scnprintf (next, size, "\nreclaim =\n");
|
temp = scnprintf(next, size, "\nunlink =\n");
|
||||||
size -= temp;
|
size -= temp;
|
||||||
next += temp;
|
next += temp;
|
||||||
|
|
||||||
for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
|
for (qh = ehci->async_unlink; size > 0 && qh;
|
||||||
|
qh = qh->unlink_next)
|
||||||
qh_lines (ehci, qh, &next, &size);
|
qh_lines (ehci, qh, &next, &size);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
|
@ -705,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci)
|
||||||
return "suspended";
|
return "suspended";
|
||||||
case EHCI_RH_RUNNING:
|
case EHCI_RH_RUNNING:
|
||||||
return "running";
|
return "running";
|
||||||
|
case EHCI_RH_STOPPING:
|
||||||
|
return "stopping";
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
@ -841,16 +844,17 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ehci->reclaim) {
|
if (ehci->async_unlink) {
|
||||||
temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim);
|
temp = scnprintf(next, size, "async unlink qh %p\n",
|
||||||
|
ehci->async_unlink);
|
||||||
size -= temp;
|
size -= temp;
|
||||||
next += temp;
|
next += temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EHCI_STATS
|
#ifdef EHCI_STATS
|
||||||
temp = scnprintf (next, size,
|
temp = scnprintf (next, size,
|
||||||
"irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
"irq normal %ld err %ld iaa %ld (lost %ld)\n",
|
||||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
|
||||||
ehci->stats.lost_iaa);
|
ehci->stats.lost_iaa);
|
||||||
size -= temp;
|
size -= temp;
|
||||||
next += temp;
|
next += temp;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/fsl_devices.h>
|
#include <linux/fsl_devices.h>
|
||||||
|
|
||||||
|
@ -142,19 +143,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||||
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
ehci->transceiver = usb_get_transceiver();
|
hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n",
|
dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n",
|
||||||
hcd, ehci, ehci->transceiver);
|
hcd, ehci, hcd->phy);
|
||||||
|
|
||||||
if (ehci->transceiver) {
|
if (!IS_ERR_OR_NULL(hcd->phy)) {
|
||||||
retval = otg_set_host(ehci->transceiver->otg,
|
retval = otg_set_host(hcd->phy->otg,
|
||||||
&ehci_to_hcd(ehci)->self);
|
&ehci_to_hcd(ehci)->self);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
usb_put_transceiver(ehci->transceiver);
|
usb_put_phy(hcd->phy);
|
||||||
goto err4;
|
goto err4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_err(&pdev->dev, "can't find transceiver\n");
|
dev_err(&pdev->dev, "can't find phy\n");
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
goto err4;
|
goto err4;
|
||||||
}
|
}
|
||||||
|
@ -190,11 +191,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
|
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
|
|
||||||
if (ehci->transceiver) {
|
if (!IS_ERR_OR_NULL(hcd->phy)) {
|
||||||
otg_set_host(ehci->transceiver->otg, NULL);
|
otg_set_host(hcd->phy->otg, NULL);
|
||||||
usb_put_transceiver(ehci->transceiver);
|
usb_put_phy(hcd->phy);
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_remove_hcd(hcd);
|
usb_remove_hcd(hcd);
|
||||||
|
@ -313,7 +313,7 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->have_sysif_regs) {
|
if (pdata->have_sysif_regs) {
|
||||||
#ifdef CONFIG_PPC_85xx
|
#ifdef CONFIG_FSL_SOC_BOOKE
|
||||||
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
|
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
|
||||||
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
|
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
|
||||||
#else
|
#else
|
||||||
|
@ -348,29 +348,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
||||||
|
|
||||||
/* EHCI registers start at offset 0x100 */
|
/* EHCI registers start at offset 0x100 */
|
||||||
ehci->caps = hcd->regs + 0x100;
|
ehci->caps = hcd->regs + 0x100;
|
||||||
ehci->regs = hcd->regs + 0x100 +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
if (of_device_is_compatible(dev->parent->of_node,
|
if (of_device_is_compatible(dev->parent->of_node,
|
||||||
"fsl,mpc5121-usb2-dr")) {
|
"fsl,mpc5121-usb2-dr")) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -40,18 +40,13 @@ static int ehci_grlib_setup(struct usb_hcd *hcd)
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
ehci_port_power(ehci, 1);
|
ehci_port_power(ehci, 1);
|
||||||
|
|
||||||
return ehci_reset(ehci);
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,12 +159,6 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op)
|
||||||
ehci->big_endian_capbase = 1;
|
ehci->big_endian_capbase = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
rv = usb_add_hcd(hcd, irq, 0);
|
rv = usb_add_hcd(hcd, irq, 0);
|
||||||
if (rv)
|
if (rv)
|
||||||
goto err_ehci;
|
goto err_ehci;
|
||||||
|
|
|
@ -30,8 +30,7 @@
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/hrtimer.h>
|
||||||
#include <linux/ktime.h>
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
@ -94,12 +93,6 @@ static const char hcd_name [] = "ehci_hcd";
|
||||||
*/
|
*/
|
||||||
#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
|
#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
|
||||||
|
|
||||||
#define EHCI_IAA_MSECS 10 /* arbitrary */
|
|
||||||
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
|
|
||||||
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
|
|
||||||
#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
|
|
||||||
/* 5-ms async qh unlink delay */
|
|
||||||
|
|
||||||
/* Initial IRQ latency: faster than hw default */
|
/* Initial IRQ latency: faster than hw default */
|
||||||
static int log2_irq_thresh = 0; // 0 to 6
|
static int log2_irq_thresh = 0; // 0 to 6
|
||||||
module_param (log2_irq_thresh, int, S_IRUGO);
|
module_param (log2_irq_thresh, int, S_IRUGO);
|
||||||
|
@ -130,41 +123,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void
|
|
||||||
timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
|
|
||||||
{
|
|
||||||
/* Don't override timeouts which shrink or (later) disable
|
|
||||||
* the async ring; just the I/O watchdog. Note that if a
|
|
||||||
* SHRINK were pending, OFF would never be requested.
|
|
||||||
*/
|
|
||||||
if (timer_pending(&ehci->watchdog)
|
|
||||||
&& ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF))
|
|
||||||
& ehci->actions))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!test_and_set_bit(action, &ehci->actions)) {
|
|
||||||
unsigned long t;
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case TIMER_IO_WATCHDOG:
|
|
||||||
if (!ehci->need_io_watchdog)
|
|
||||||
return;
|
|
||||||
t = EHCI_IO_JIFFIES;
|
|
||||||
break;
|
|
||||||
case TIMER_ASYNC_OFF:
|
|
||||||
t = EHCI_ASYNC_JIFFIES;
|
|
||||||
break;
|
|
||||||
/* case TIMER_ASYNC_SHRINK: */
|
|
||||||
default:
|
|
||||||
t = EHCI_SHRINK_JIFFIES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mod_timer(&ehci->watchdog, t + jiffies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* handshake - spin reading hc until handshake completes or fails
|
* handshake - spin reading hc until handshake completes or fails
|
||||||
* @ptr: address of hc register to be read
|
* @ptr: address of hc register to be read
|
||||||
|
@ -203,29 +161,30 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr,
|
||||||
/* check TDI/ARC silicon is in host mode */
|
/* check TDI/ARC silicon is in host mode */
|
||||||
static int tdi_in_host_mode (struct ehci_hcd *ehci)
|
static int tdi_in_host_mode (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
u32 __iomem *reg_ptr;
|
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
|
tmp = ehci_readl(ehci, &ehci->regs->usbmode);
|
||||||
tmp = ehci_readl(ehci, reg_ptr);
|
|
||||||
return (tmp & 3) == USBMODE_CM_HC;
|
return (tmp & 3) == USBMODE_CM_HC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* force HC to halt state from unknown (EHCI spec section 2.3) */
|
/*
|
||||||
|
* Force HC to halt state from unknown (EHCI spec section 2.3).
|
||||||
|
* Must be called with interrupts enabled and the lock not held.
|
||||||
|
*/
|
||||||
static int ehci_halt (struct ehci_hcd *ehci)
|
static int ehci_halt (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
u32 temp = ehci_readl(ehci, &ehci->regs->status);
|
u32 temp;
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
|
||||||
/* disable any irqs left enabled by previous code */
|
/* disable any irqs left enabled by previous code */
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||||
|
|
||||||
if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) {
|
if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) {
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((temp & STS_HALT) != 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine gets called during probe before ehci->command
|
* This routine gets called during probe before ehci->command
|
||||||
* has been initialized, so we can't rely on its value.
|
* has been initialized, so we can't rely on its value.
|
||||||
|
@ -234,80 +193,20 @@ static int ehci_halt (struct ehci_hcd *ehci)
|
||||||
temp = ehci_readl(ehci, &ehci->regs->command);
|
temp = ehci_readl(ehci, &ehci->regs->command);
|
||||||
temp &= ~(CMD_RUN | CMD_IAAD);
|
temp &= ~(CMD_RUN | CMD_IAAD);
|
||||||
ehci_writel(ehci, temp, &ehci->regs->command);
|
ehci_writel(ehci, temp, &ehci->regs->command);
|
||||||
return handshake (ehci, &ehci->regs->status,
|
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
synchronize_irq(ehci_to_hcd(ehci)->irq);
|
||||||
|
|
||||||
|
return handshake(ehci, &ehci->regs->status,
|
||||||
STS_HALT, STS_HALT, 16 * 125);
|
STS_HALT, STS_HALT, 16 * 125);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The EHCI controller of the Cell Super Companion Chip used in the
|
|
||||||
* PS3 will stop the root hub after all root hub ports are suspended.
|
|
||||||
* When in this condition handshake will return -ETIMEDOUT. The
|
|
||||||
* STS_HLT bit will not be set, so inspection of the frame index is
|
|
||||||
* used here to test for the condition. If the condition is found
|
|
||||||
* return success to allow the USB suspend to complete.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
|
|
||||||
void __iomem *ptr, u32 mask, u32 done,
|
|
||||||
int usec)
|
|
||||||
{
|
|
||||||
unsigned int old_index;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
|
|
||||||
old_index = ehci_read_frame_index(ehci);
|
|
||||||
|
|
||||||
error = handshake(ehci, ptr, mask, done, usec);
|
|
||||||
|
|
||||||
if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
|
|
||||||
void __iomem *ptr, u32 mask, u32 done,
|
|
||||||
int usec)
|
|
||||||
{
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
|
|
||||||
u32 mask, u32 done, int usec)
|
|
||||||
{
|
|
||||||
int error;
|
|
||||||
|
|
||||||
error = handshake(ehci, ptr, mask, done, usec);
|
|
||||||
if (error == -ETIMEDOUT)
|
|
||||||
error = handshake_for_broken_root_hub(ehci, ptr, mask, done,
|
|
||||||
usec);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
ehci_halt(ehci);
|
|
||||||
ehci->rh_state = EHCI_RH_HALTED;
|
|
||||||
ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
|
|
||||||
ptr, mask, done, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* put TDI/ARC silicon into EHCI mode */
|
/* put TDI/ARC silicon into EHCI mode */
|
||||||
static void tdi_reset (struct ehci_hcd *ehci)
|
static void tdi_reset (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
u32 __iomem *reg_ptr;
|
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
|
tmp = ehci_readl(ehci, &ehci->regs->usbmode);
|
||||||
tmp = ehci_readl(ehci, reg_ptr);
|
|
||||||
tmp |= USBMODE_CM_HC;
|
tmp |= USBMODE_CM_HC;
|
||||||
/* The default byte access to MMR space is LE after
|
/* The default byte access to MMR space is LE after
|
||||||
* controller reset. Set the required endian mode
|
* controller reset. Set the required endian mode
|
||||||
|
@ -315,10 +214,13 @@ static void tdi_reset (struct ehci_hcd *ehci)
|
||||||
*/
|
*/
|
||||||
if (ehci_big_endian_mmio(ehci))
|
if (ehci_big_endian_mmio(ehci))
|
||||||
tmp |= USBMODE_BE;
|
tmp |= USBMODE_BE;
|
||||||
ehci_writel(ehci, tmp, reg_ptr);
|
ehci_writel(ehci, tmp, &ehci->regs->usbmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset a non-running (STS_HALT == 1) controller */
|
/*
|
||||||
|
* Reset a non-running (STS_HALT == 1) controller.
|
||||||
|
* Must be called with interrupts enabled and the lock not held.
|
||||||
|
*/
|
||||||
static int ehci_reset (struct ehci_hcd *ehci)
|
static int ehci_reset (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -339,9 +241,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
|
||||||
|
|
||||||
if (ehci->has_hostpc) {
|
if (ehci->has_hostpc) {
|
||||||
ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
|
ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
|
||||||
(u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
|
&ehci->regs->usbmode_ex);
|
||||||
ehci_writel(ehci, TXFIFO_DEFAULT,
|
ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning);
|
||||||
(u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
|
|
||||||
}
|
}
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -357,36 +258,40 @@ static int ehci_reset (struct ehci_hcd *ehci)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* idle the controller (from running) */
|
/*
|
||||||
|
* Idle the controller (turn off the schedules).
|
||||||
|
* Must be called with interrupts enabled and the lock not held.
|
||||||
|
*/
|
||||||
static void ehci_quiesce (struct ehci_hcd *ehci)
|
static void ehci_quiesce (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
u32 temp;
|
u32 temp;
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (ehci->rh_state != EHCI_RH_RUNNING)
|
if (ehci->rh_state != EHCI_RH_RUNNING)
|
||||||
BUG ();
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* wait for any schedule enables/disables to take effect */
|
/* wait for any schedule enables/disables to take effect */
|
||||||
temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
|
temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
|
||||||
if (handshake_on_error_set_halt(ehci, &ehci->regs->status,
|
handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125);
|
||||||
STS_ASS | STS_PSS, temp, 16 * 125))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* then disable anything that's still active */
|
/* then disable anything that's still active */
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
ehci->command &= ~(CMD_ASE | CMD_PSE);
|
ehci->command &= ~(CMD_ASE | CMD_PSE);
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
/* hardware can take 16 microframes to turn off ... */
|
/* hardware can take 16 microframes to turn off ... */
|
||||||
handshake_on_error_set_halt(ehci, &ehci->regs->status,
|
handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125);
|
||||||
STS_ASS | STS_PSS, 0, 16 * 125);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void end_unlink_async(struct ehci_hcd *ehci);
|
static void end_unlink_async(struct ehci_hcd *ehci);
|
||||||
|
static void unlink_empty_async(struct ehci_hcd *ehci);
|
||||||
static void ehci_work(struct ehci_hcd *ehci);
|
static void ehci_work(struct ehci_hcd *ehci);
|
||||||
|
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
|
||||||
|
#include "ehci-timer.c"
|
||||||
#include "ehci-hub.c"
|
#include "ehci-hub.c"
|
||||||
#include "ehci-lpm.c"
|
#include "ehci-lpm.c"
|
||||||
#include "ehci-mem.c"
|
#include "ehci-mem.c"
|
||||||
|
@ -396,68 +301,6 @@ static void ehci_work(struct ehci_hcd *ehci);
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void ehci_iaa_watchdog(unsigned long param)
|
|
||||||
{
|
|
||||||
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave (&ehci->lock, flags);
|
|
||||||
|
|
||||||
/* Lost IAA irqs wedge things badly; seen first with a vt8235.
|
|
||||||
* So we need this watchdog, but must protect it against both
|
|
||||||
* (a) SMP races against real IAA firing and retriggering, and
|
|
||||||
* (b) clean HC shutdown, when IAA watchdog was pending.
|
|
||||||
*/
|
|
||||||
if (ehci->reclaim
|
|
||||||
&& !timer_pending(&ehci->iaa_watchdog)
|
|
||||||
&& ehci->rh_state == EHCI_RH_RUNNING) {
|
|
||||||
u32 cmd, status;
|
|
||||||
|
|
||||||
/* If we get here, IAA is *REALLY* late. It's barely
|
|
||||||
* conceivable that the system is so busy that CMD_IAAD
|
|
||||||
* is still legitimately set, so let's be sure it's
|
|
||||||
* clear before we read STS_IAA. (The HC should clear
|
|
||||||
* CMD_IAAD when it sets STS_IAA.)
|
|
||||||
*/
|
|
||||||
cmd = ehci_readl(ehci, &ehci->regs->command);
|
|
||||||
|
|
||||||
/* If IAA is set here it either legitimately triggered
|
|
||||||
* before we cleared IAAD above (but _way_ late, so we'll
|
|
||||||
* still count it as lost) ... or a silicon erratum:
|
|
||||||
* - VIA seems to set IAA without triggering the IRQ;
|
|
||||||
* - IAAD potentially cleared without setting IAA.
|
|
||||||
*/
|
|
||||||
status = ehci_readl(ehci, &ehci->regs->status);
|
|
||||||
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
|
|
||||||
COUNT (ehci->stats.lost_iaa);
|
|
||||||
ehci_writel(ehci, STS_IAA, &ehci->regs->status);
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
|
|
||||||
status, cmd);
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ehci_watchdog(unsigned long param)
|
|
||||||
{
|
|
||||||
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
|
|
||||||
/* stop async processing after it's idled a bit */
|
|
||||||
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
|
|
||||||
start_unlink_async (ehci, ehci->async);
|
|
||||||
|
|
||||||
/* ehci could run by timer, without IRQs ... */
|
|
||||||
ehci_work (ehci);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On some systems, leaving remote wakeup enabled prevents system shutdown.
|
/* On some systems, leaving remote wakeup enabled prevents system shutdown.
|
||||||
* The firmware seems to think that powering off is a wakeup event!
|
* The firmware seems to think that powering off is a wakeup event!
|
||||||
* This routine turns off remote wakeup and everything else, on all ports.
|
* This routine turns off remote wakeup and everything else, on all ports.
|
||||||
|
@ -473,11 +316,14 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Halt HC, turn off all ports, and let the BIOS use the companion controllers.
|
* Halt HC, turn off all ports, and let the BIOS use the companion controllers.
|
||||||
* Should be called with ehci->lock held.
|
* Must be called with interrupts enabled and the lock not held.
|
||||||
*/
|
*/
|
||||||
static void ehci_silence_controller(struct ehci_hcd *ehci)
|
static void ehci_silence_controller(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
ehci_halt(ehci);
|
ehci_halt(ehci);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
ehci->rh_state = EHCI_RH_HALTED;
|
||||||
ehci_turn_off_all_ports(ehci);
|
ehci_turn_off_all_ports(ehci);
|
||||||
|
|
||||||
/* make BIOS/etc use companion controller during reboot */
|
/* make BIOS/etc use companion controller during reboot */
|
||||||
|
@ -485,6 +331,7 @@ static void ehci_silence_controller(struct ehci_hcd *ehci)
|
||||||
|
|
||||||
/* unblock posted writes */
|
/* unblock posted writes */
|
||||||
ehci_readl(ehci, &ehci->regs->configured_flag);
|
ehci_readl(ehci, &ehci->regs->configured_flag);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
|
/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
|
||||||
|
@ -495,12 +342,15 @@ static void ehci_shutdown(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
del_timer_sync(&ehci->watchdog);
|
|
||||||
del_timer_sync(&ehci->iaa_watchdog);
|
|
||||||
|
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
ehci_silence_controller(ehci);
|
ehci->shutdown = true;
|
||||||
|
ehci->rh_state = EHCI_RH_STOPPING;
|
||||||
|
ehci->enabled_hrtimer_events = 0;
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
ehci_silence_controller(ehci);
|
||||||
|
|
||||||
|
hrtimer_cancel(&ehci->hrtimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
||||||
|
@ -529,28 +379,33 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
||||||
*/
|
*/
|
||||||
static void ehci_work (struct ehci_hcd *ehci)
|
static void ehci_work (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
timer_action_done (ehci, TIMER_IO_WATCHDOG);
|
|
||||||
|
|
||||||
/* another CPU may drop ehci->lock during a schedule scan while
|
/* another CPU may drop ehci->lock during a schedule scan while
|
||||||
* it reports urb completions. this flag guards against bogus
|
* it reports urb completions. this flag guards against bogus
|
||||||
* attempts at re-entrant schedule scanning.
|
* attempts at re-entrant schedule scanning.
|
||||||
*/
|
*/
|
||||||
if (ehci->scanning)
|
if (ehci->scanning) {
|
||||||
|
ehci->need_rescan = true;
|
||||||
return;
|
return;
|
||||||
ehci->scanning = 1;
|
}
|
||||||
scan_async (ehci);
|
ehci->scanning = true;
|
||||||
if (ehci->next_uframe != -1)
|
|
||||||
scan_periodic (ehci);
|
rescan:
|
||||||
ehci->scanning = 0;
|
ehci->need_rescan = false;
|
||||||
|
if (ehci->async_count)
|
||||||
|
scan_async(ehci);
|
||||||
|
if (ehci->intr_count > 0)
|
||||||
|
scan_intr(ehci);
|
||||||
|
if (ehci->isoc_count > 0)
|
||||||
|
scan_isoc(ehci);
|
||||||
|
if (ehci->need_rescan)
|
||||||
|
goto rescan;
|
||||||
|
ehci->scanning = false;
|
||||||
|
|
||||||
/* the IO watchdog guards against hardware or driver bugs that
|
/* the IO watchdog guards against hardware or driver bugs that
|
||||||
* misplace IRQs, and should let us run completely without IRQs.
|
* misplace IRQs, and should let us run completely without IRQs.
|
||||||
* such lossage has been observed on both VT6202 and VT8235.
|
* such lossage has been observed on both VT6202 and VT8235.
|
||||||
*/
|
*/
|
||||||
if (ehci->rh_state == EHCI_RH_RUNNING &&
|
turn_on_io_watchdog(ehci);
|
||||||
(ehci->async->qh_next.ptr != NULL ||
|
|
||||||
ehci->periodic_sched != 0))
|
|
||||||
timer_action (ehci, TIMER_IO_WATCHDOG);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -563,24 +418,22 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||||
ehci_dbg (ehci, "stop\n");
|
ehci_dbg (ehci, "stop\n");
|
||||||
|
|
||||||
/* no more interrupts ... */
|
/* no more interrupts ... */
|
||||||
del_timer_sync (&ehci->watchdog);
|
|
||||||
del_timer_sync(&ehci->iaa_watchdog);
|
|
||||||
|
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
if (ehci->rh_state == EHCI_RH_RUNNING)
|
ehci->enabled_hrtimer_events = 0;
|
||||||
ehci_quiesce (ehci);
|
|
||||||
|
|
||||||
ehci_silence_controller(ehci);
|
|
||||||
ehci_reset (ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
ehci_quiesce(ehci);
|
||||||
|
ehci_silence_controller(ehci);
|
||||||
|
ehci_reset (ehci);
|
||||||
|
|
||||||
|
hrtimer_cancel(&ehci->hrtimer);
|
||||||
remove_sysfs_files(ehci);
|
remove_sysfs_files(ehci);
|
||||||
remove_debug_files (ehci);
|
remove_debug_files (ehci);
|
||||||
|
|
||||||
/* root hub is shut down separately (first, when possible) */
|
/* root hub is shut down separately (first, when possible) */
|
||||||
spin_lock_irq (&ehci->lock);
|
spin_lock_irq (&ehci->lock);
|
||||||
if (ehci->async)
|
end_free_itds(ehci);
|
||||||
ehci_work (ehci);
|
|
||||||
spin_unlock_irq (&ehci->lock);
|
spin_unlock_irq (&ehci->lock);
|
||||||
ehci_mem_cleanup (ehci);
|
ehci_mem_cleanup (ehci);
|
||||||
|
|
||||||
|
@ -588,8 +441,8 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||||
usb_amd_dev_put();
|
usb_amd_dev_put();
|
||||||
|
|
||||||
#ifdef EHCI_STATS
|
#ifdef EHCI_STATS
|
||||||
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
ehci_dbg(ehci, "irq normal %ld err %ld iaa %ld (lost %ld)\n",
|
||||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
|
||||||
ehci->stats.lost_iaa);
|
ehci->stats.lost_iaa);
|
||||||
ehci_dbg (ehci, "complete %ld unlink %ld\n",
|
ehci_dbg (ehci, "complete %ld unlink %ld\n",
|
||||||
ehci->stats.complete, ehci->stats.unlink);
|
ehci->stats.complete, ehci->stats.unlink);
|
||||||
|
@ -614,13 +467,10 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||||
* keep io watchdog by default, those good HCDs could turn off it later
|
* keep io watchdog by default, those good HCDs could turn off it later
|
||||||
*/
|
*/
|
||||||
ehci->need_io_watchdog = 1;
|
ehci->need_io_watchdog = 1;
|
||||||
init_timer(&ehci->watchdog);
|
|
||||||
ehci->watchdog.function = ehci_watchdog;
|
|
||||||
ehci->watchdog.data = (unsigned long) ehci;
|
|
||||||
|
|
||||||
init_timer(&ehci->iaa_watchdog);
|
hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||||
ehci->iaa_watchdog.function = ehci_iaa_watchdog;
|
ehci->hrtimer.function = ehci_hrtimer_func;
|
||||||
ehci->iaa_watchdog.data = (unsigned long) ehci;
|
ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
|
||||||
|
|
||||||
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||||
|
|
||||||
|
@ -635,6 +485,7 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||||
* periodic_size can shrink by USBCMD update if hcc_params allows.
|
* periodic_size can shrink by USBCMD update if hcc_params allows.
|
||||||
*/
|
*/
|
||||||
ehci->periodic_size = DEFAULT_I_TDPS;
|
ehci->periodic_size = DEFAULT_I_TDPS;
|
||||||
|
INIT_LIST_HEAD(&ehci->intr_qh_list);
|
||||||
INIT_LIST_HEAD(&ehci->cached_itd_list);
|
INIT_LIST_HEAD(&ehci->cached_itd_list);
|
||||||
INIT_LIST_HEAD(&ehci->cached_sitd_list);
|
INIT_LIST_HEAD(&ehci->cached_sitd_list);
|
||||||
|
|
||||||
|
@ -656,10 +507,6 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||||
else // N microframes cached
|
else // N microframes cached
|
||||||
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
|
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
|
||||||
|
|
||||||
ehci->reclaim = NULL;
|
|
||||||
ehci->next_uframe = -1;
|
|
||||||
ehci->clock_frame = -1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dedicate a qh for the async ring head, since we couldn't unlink
|
* dedicate a qh for the async ring head, since we couldn't unlink
|
||||||
* a 'real' qh without stopping the async schedule [4.8]. use it
|
* a 'real' qh without stopping the async schedule [4.8]. use it
|
||||||
|
@ -672,7 +519,7 @@ static int ehci_init(struct usb_hcd *hcd)
|
||||||
hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
|
hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
|
||||||
hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
|
hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
|
||||||
#if defined(CONFIG_PPC_PS3)
|
#if defined(CONFIG_PPC_PS3)
|
||||||
hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */
|
hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE);
|
||||||
#endif
|
#endif
|
||||||
hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
|
hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
|
||||||
hw->hw_qtd_next = EHCI_LIST_END(ehci);
|
hw->hw_qtd_next = EHCI_LIST_END(ehci);
|
||||||
|
@ -813,7 +660,7 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused ehci_setup (struct usb_hcd *hcd)
|
static int ehci_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -828,15 +675,18 @@ static int __maybe_unused ehci_setup (struct usb_hcd *hcd)
|
||||||
|
|
||||||
ehci->sbrn = HCD_USB2;
|
ehci->sbrn = HCD_USB2;
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
/* data structure init */
|
/* data structure init */
|
||||||
retval = ehci_init(hcd);
|
retval = ehci_init(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
retval = ehci_halt(ehci);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (ehci_is_TDI(ehci))
|
||||||
|
tdi_reset(ehci);
|
||||||
|
|
||||||
ehci_reset(ehci);
|
ehci_reset(ehci);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -895,14 +745,28 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||||
|
|
||||||
/* complete the unlinking of some qh [4.15.2.3] */
|
/* complete the unlinking of some qh [4.15.2.3] */
|
||||||
if (status & STS_IAA) {
|
if (status & STS_IAA) {
|
||||||
|
|
||||||
|
/* Turn off the IAA watchdog */
|
||||||
|
ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mild optimization: Allow another IAAD to reset the
|
||||||
|
* hrtimer, if one occurs before the next expiration.
|
||||||
|
* In theory we could always cancel the hrtimer, but
|
||||||
|
* tests show that about half the time it will be reset
|
||||||
|
* for some other event anyway.
|
||||||
|
*/
|
||||||
|
if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG)
|
||||||
|
++ehci->next_hrtimer_event;
|
||||||
|
|
||||||
/* guard against (alleged) silicon errata */
|
/* guard against (alleged) silicon errata */
|
||||||
if (cmd & CMD_IAAD)
|
if (cmd & CMD_IAAD)
|
||||||
ehci_dbg(ehci, "IAA with IAAD still set?\n");
|
ehci_dbg(ehci, "IAA with IAAD still set?\n");
|
||||||
if (ehci->reclaim) {
|
if (ehci->async_iaa) {
|
||||||
COUNT(ehci->stats.reclaim);
|
COUNT(ehci->stats.iaa);
|
||||||
end_unlink_async(ehci);
|
end_unlink_async(ehci);
|
||||||
} else
|
} else
|
||||||
ehci_dbg(ehci, "IAA with nothing to reclaim?\n");
|
ehci_dbg(ehci, "IAA with nothing unlinked?\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remote wakeup [4.3.1] */
|
/* remote wakeup [4.3.1] */
|
||||||
|
@ -956,15 +820,19 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||||
ehci_err(ehci, "fatal error\n");
|
ehci_err(ehci, "fatal error\n");
|
||||||
dbg_cmd(ehci, "fatal", cmd);
|
dbg_cmd(ehci, "fatal", cmd);
|
||||||
dbg_status(ehci, "fatal", status);
|
dbg_status(ehci, "fatal", status);
|
||||||
ehci_halt(ehci);
|
|
||||||
dead:
|
dead:
|
||||||
ehci_reset(ehci);
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
|
||||||
usb_hc_died(hcd);
|
usb_hc_died(hcd);
|
||||||
/* generic layer kills/unlinks all urbs, then
|
|
||||||
* uses ehci_stop to clean up the rest
|
/* Don't let the controller do anything more */
|
||||||
*/
|
ehci->shutdown = true;
|
||||||
bh = 1;
|
ehci->rh_state = EHCI_RH_STOPPING;
|
||||||
|
ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
|
||||||
|
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||||
|
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||||
|
ehci_handle_controller_death(ehci);
|
||||||
|
|
||||||
|
/* Handle completions when the controller stops */
|
||||||
|
bh = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bh)
|
if (bh)
|
||||||
|
@ -1026,38 +894,6 @@ static int ehci_urb_enqueue (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
||||||
{
|
|
||||||
/* failfast */
|
|
||||||
if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
|
|
||||||
/* If the QH isn't linked then there's nothing we can do
|
|
||||||
* unless we were called during a giveback, in which case
|
|
||||||
* qh_completions() has to deal with it.
|
|
||||||
*/
|
|
||||||
if (qh->qh_state != QH_STATE_LINKED) {
|
|
||||||
if (qh->qh_state == QH_STATE_COMPLETING)
|
|
||||||
qh->needs_rescan = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* defer till later if busy */
|
|
||||||
if (ehci->reclaim) {
|
|
||||||
struct ehci_qh *last;
|
|
||||||
|
|
||||||
for (last = ehci->reclaim;
|
|
||||||
last->reclaim;
|
|
||||||
last = last->reclaim)
|
|
||||||
continue;
|
|
||||||
qh->qh_state = QH_STATE_UNLINK_WAIT;
|
|
||||||
last->reclaim = qh;
|
|
||||||
|
|
||||||
/* start IAA cycle */
|
|
||||||
} else
|
|
||||||
start_unlink_async (ehci, qh);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove from hardware lists
|
/* remove from hardware lists
|
||||||
* completions normally happen asynchronously
|
* completions normally happen asynchronously
|
||||||
*/
|
*/
|
||||||
|
@ -1084,7 +920,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||||
switch (qh->qh_state) {
|
switch (qh->qh_state) {
|
||||||
case QH_STATE_LINKED:
|
case QH_STATE_LINKED:
|
||||||
case QH_STATE_COMPLETING:
|
case QH_STATE_COMPLETING:
|
||||||
unlink_async(ehci, qh);
|
start_unlink_async(ehci, qh);
|
||||||
break;
|
break;
|
||||||
case QH_STATE_UNLINK:
|
case QH_STATE_UNLINK:
|
||||||
case QH_STATE_UNLINK_WAIT:
|
case QH_STATE_UNLINK_WAIT:
|
||||||
|
@ -1104,7 +940,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||||
switch (qh->qh_state) {
|
switch (qh->qh_state) {
|
||||||
case QH_STATE_LINKED:
|
case QH_STATE_LINKED:
|
||||||
case QH_STATE_COMPLETING:
|
case QH_STATE_COMPLETING:
|
||||||
intr_deschedule (ehci, qh);
|
start_unlink_intr(ehci, qh);
|
||||||
break;
|
break;
|
||||||
case QH_STATE_IDLE:
|
case QH_STATE_IDLE:
|
||||||
qh_completions (ehci, qh);
|
qh_completions (ehci, qh);
|
||||||
|
@ -1152,11 +988,17 @@ rescan:
|
||||||
* accelerate iso completions ... so spin a while.
|
* accelerate iso completions ... so spin a while.
|
||||||
*/
|
*/
|
||||||
if (qh->hw == NULL) {
|
if (qh->hw == NULL) {
|
||||||
ehci_vdbg (ehci, "iso delay\n");
|
struct ehci_iso_stream *stream = ep->hcpriv;
|
||||||
goto idle_timeout;
|
|
||||||
|
if (!list_empty(&stream->td_list))
|
||||||
|
goto idle_timeout;
|
||||||
|
|
||||||
|
/* BUG_ON(!list_empty(&stream->free_list)); */
|
||||||
|
kfree(stream);
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ehci->rh_state != EHCI_RH_RUNNING)
|
if (ehci->rh_state < EHCI_RH_RUNNING)
|
||||||
qh->qh_state = QH_STATE_IDLE;
|
qh->qh_state = QH_STATE_IDLE;
|
||||||
switch (qh->qh_state) {
|
switch (qh->qh_state) {
|
||||||
case QH_STATE_LINKED:
|
case QH_STATE_LINKED:
|
||||||
|
@ -1169,7 +1011,7 @@ rescan:
|
||||||
* may already be unlinked.
|
* may already be unlinked.
|
||||||
*/
|
*/
|
||||||
if (tmp)
|
if (tmp)
|
||||||
unlink_async(ehci, qh);
|
start_unlink_async(ehci, qh);
|
||||||
/* FALL THROUGH */
|
/* FALL THROUGH */
|
||||||
case QH_STATE_UNLINK: /* wait for hw to finish? */
|
case QH_STATE_UNLINK: /* wait for hw to finish? */
|
||||||
case QH_STATE_UNLINK_WAIT:
|
case QH_STATE_UNLINK_WAIT:
|
||||||
|
@ -1181,7 +1023,7 @@ idle_timeout:
|
||||||
if (qh->clearing_tt)
|
if (qh->clearing_tt)
|
||||||
goto idle_timeout;
|
goto idle_timeout;
|
||||||
if (list_empty (&qh->qtd_list)) {
|
if (list_empty (&qh->qtd_list)) {
|
||||||
qh_put (qh);
|
qh_destroy(ehci, qh);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* else FALL THROUGH */
|
/* else FALL THROUGH */
|
||||||
|
@ -1194,8 +1036,8 @@ idle_timeout:
|
||||||
list_empty (&qh->qtd_list) ? "" : "(has tds)");
|
list_empty (&qh->qtd_list) ? "" : "(has tds)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
done:
|
||||||
ep->hcpriv = NULL;
|
ep->hcpriv = NULL;
|
||||||
done:
|
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1232,9 +1074,9 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
||||||
* re-linking will call qh_refresh().
|
* re-linking will call qh_refresh().
|
||||||
*/
|
*/
|
||||||
if (eptype == USB_ENDPOINT_XFER_BULK)
|
if (eptype == USB_ENDPOINT_XFER_BULK)
|
||||||
unlink_async(ehci, qh);
|
start_unlink_async(ehci, qh);
|
||||||
else
|
else
|
||||||
intr_deschedule(ehci, qh);
|
start_unlink_intr(ehci, qh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
|
@ -1247,6 +1089,104 @@ static int ehci_get_frame (struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
/* suspend/resume, section 4.3 */
|
||||||
|
|
||||||
|
/* These routines handle the generic parts of controller suspend/resume */
|
||||||
|
|
||||||
|
static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
|
if (time_before(jiffies, ehci->next_statechange))
|
||||||
|
msleep(10);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Root hub was already suspended. Disable IRQ emission and
|
||||||
|
* mark HW unaccessible. The PM and USB cores make sure that
|
||||||
|
* the root hub is either suspended or stopped.
|
||||||
|
*/
|
||||||
|
ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||||
|
(void) ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||||
|
|
||||||
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 0 if power was preserved, 1 if power was lost */
|
||||||
|
static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
|
if (time_before(jiffies, ehci->next_statechange))
|
||||||
|
msleep(100);
|
||||||
|
|
||||||
|
/* Mark hardware accessible again as we are back to full power by now */
|
||||||
|
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
|
|
||||||
|
if (ehci->shutdown)
|
||||||
|
return 0; /* Controller is dead */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If CF is still set and we aren't resuming from hibernation
|
||||||
|
* then we maintained suspend power.
|
||||||
|
* Just undo the effect of ehci_suspend().
|
||||||
|
*/
|
||||||
|
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
|
||||||
|
!hibernated) {
|
||||||
|
int mask = INTR_MASK;
|
||||||
|
|
||||||
|
ehci_prepare_ports_for_controller_resume(ehci);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
if (!hcd->self.root_hub->do_remote_wakeup)
|
||||||
|
mask &= ~STS_PCD;
|
||||||
|
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
||||||
|
ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||||
|
skip:
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Else reset, to cope with power loss or resume from hibernation
|
||||||
|
* having let the firmware kick in during reboot.
|
||||||
|
*/
|
||||||
|
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||||
|
(void) ehci_halt(ehci);
|
||||||
|
(void) ehci_reset(ehci);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||||
|
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||||
|
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||||
|
|
||||||
|
ehci->rh_state = EHCI_RH_SUSPENDED;
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
/* here we "know" root ports should always stay powered */
|
||||||
|
ehci_port_power(ehci, 1);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The EHCI in ChipIdea HDRC cannot be a separate module or device,
|
* The EHCI in ChipIdea HDRC cannot be a separate module or device,
|
||||||
* because its registers (and irq) are shared between host/gadget/otg
|
* because its registers (and irq) are shared between host/gadget/otg
|
||||||
|
|
|
@ -59,6 +59,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||||
/* Give the connections some time to appear */
|
/* Give the connections some time to appear */
|
||||||
msleep(20);
|
msleep(20);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
if (test_bit(port, &ehci->owned_ports)) {
|
if (test_bit(port, &ehci->owned_ports)) {
|
||||||
|
@ -70,23 +71,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||||
clear_bit(port, &ehci->owned_ports);
|
clear_bit(port, &ehci->owned_ports);
|
||||||
else if (test_bit(port, &ehci->companion_ports))
|
else if (test_bit(port, &ehci->companion_ports))
|
||||||
ehci_writel(ehci, status & ~PORT_PE, reg);
|
ehci_writel(ehci, status & ~PORT_PE, reg);
|
||||||
else
|
else {
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
ehci_hub_control(hcd, SetPortFeature,
|
ehci_hub_control(hcd, SetPortFeature,
|
||||||
USB_PORT_FEAT_RESET, port + 1,
|
USB_PORT_FEAT_RESET, port + 1,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
if (!ehci->owned_ports)
|
if (!ehci->owned_ports)
|
||||||
return;
|
return;
|
||||||
msleep(90); /* Wait for resets to complete */
|
msleep(90); /* Wait for resets to complete */
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
if (test_bit(port, &ehci->owned_ports)) {
|
if (test_bit(port, &ehci->owned_ports)) {
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
ehci_hub_control(hcd, GetPortStatus,
|
ehci_hub_control(hcd, GetPortStatus,
|
||||||
0, port + 1,
|
0, port + 1,
|
||||||
(char *) &buf, sizeof(buf));
|
(char *) &buf, sizeof(buf));
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
|
||||||
/* The companion should now own the port,
|
/* The companion should now own the port,
|
||||||
* but if something went wrong the port must not
|
* but if something went wrong the port must not
|
||||||
|
@ -105,9 +113,10 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci->owned_ports = 0;
|
ehci->owned_ports = 0;
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
|
static int ehci_port_change(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int i = HCS_N_PORTS(ehci->hcs_params);
|
int i = HCS_N_PORTS(ehci->hcs_params);
|
||||||
|
|
||||||
|
@ -128,12 +137,11 @@ static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||||
bool suspending, bool do_wakeup)
|
bool suspending, bool do_wakeup)
|
||||||
{
|
{
|
||||||
int port;
|
int port;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/* If remote wakeup is enabled for the root hub but disabled
|
/* If remote wakeup is enabled for the root hub but disabled
|
||||||
* for the controller, we must adjust all the port wakeup flags
|
* for the controller, we must adjust all the port wakeup flags
|
||||||
|
@ -143,22 +151,20 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||||
if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
|
if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
|
||||||
/* clear phy low-power mode before changing wakeup flags */
|
/* clear phy low-power mode before changing wakeup flags */
|
||||||
if (ehci->has_hostpc) {
|
if (ehci->has_hostpc) {
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
u32 __iomem *hostpc_reg;
|
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
|
||||||
|
|
||||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
|
||||||
+ HOSTPC0 + 4 * port);
|
|
||||||
temp = ehci_readl(ehci, hostpc_reg);
|
temp = ehci_readl(ehci, hostpc_reg);
|
||||||
ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
|
ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irq(&ehci->lock);
|
||||||
msleep(5);
|
msleep(5);
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
spin_lock_irq(&ehci->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
|
@ -185,10 +191,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||||
if (ehci->has_hostpc) {
|
if (ehci->has_hostpc) {
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
u32 __iomem *hostpc_reg;
|
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
|
||||||
|
|
||||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
|
||||||
+ HOSTPC0 + 4 * port);
|
|
||||||
temp = ehci_readl(ehci, hostpc_reg);
|
temp = ehci_readl(ehci, hostpc_reg);
|
||||||
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
|
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +202,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||||
if (!suspending && ehci_port_change(ehci))
|
if (!suspending && ehci_port_change(ehci))
|
||||||
usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
|
usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irq(&ehci->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
|
@ -212,10 +216,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
|
|
||||||
if (time_before (jiffies, ehci->next_statechange))
|
if (time_before (jiffies, ehci->next_statechange))
|
||||||
msleep(5);
|
msleep(5);
|
||||||
del_timer_sync(&ehci->watchdog);
|
|
||||||
del_timer_sync(&ehci->iaa_watchdog);
|
/* stop the schedules */
|
||||||
|
ehci_quiesce(ehci);
|
||||||
|
|
||||||
spin_lock_irq (&ehci->lock);
|
spin_lock_irq (&ehci->lock);
|
||||||
|
if (ehci->rh_state < EHCI_RH_RUNNING)
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* Once the controller is stopped, port resumes that are already
|
/* Once the controller is stopped, port resumes that are already
|
||||||
* in progress won't complete. Hence if remote wakeup is enabled
|
* in progress won't complete. Hence if remote wakeup is enabled
|
||||||
|
@ -230,11 +237,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stop schedules, clean any completed work */
|
|
||||||
if (ehci->rh_state == EHCI_RH_RUNNING)
|
|
||||||
ehci_quiesce (ehci);
|
|
||||||
ehci_work(ehci);
|
|
||||||
|
|
||||||
/* Unlike other USB host controller types, EHCI doesn't have
|
/* Unlike other USB host controller types, EHCI doesn't have
|
||||||
* any notion of "global" or bus-wide suspend. The driver has
|
* any notion of "global" or bus-wide suspend. The driver has
|
||||||
* to manually suspend all the active unsuspended ports, and
|
* to manually suspend all the active unsuspended ports, and
|
||||||
|
@ -285,11 +287,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
|
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
u32 __iomem *hostpc_reg;
|
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
|
||||||
u32 t3;
|
u32 t3;
|
||||||
|
|
||||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
|
||||||
+ HOSTPC0 + 4 * port);
|
|
||||||
t3 = ehci_readl(ehci, hostpc_reg);
|
t3 = ehci_readl(ehci, hostpc_reg);
|
||||||
ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
|
ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
|
||||||
t3 = ehci_readl(ehci, hostpc_reg);
|
t3 = ehci_readl(ehci, hostpc_reg);
|
||||||
|
@ -298,6 +298,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
"succeeded" : "failed");
|
"succeeded" : "failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
/* Apparently some devices need a >= 1-uframe delay here */
|
/* Apparently some devices need a >= 1-uframe delay here */
|
||||||
if (ehci->bus_suspended)
|
if (ehci->bus_suspended)
|
||||||
|
@ -305,10 +306,18 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
|
|
||||||
/* turn off now-idle HC */
|
/* turn off now-idle HC */
|
||||||
ehci_halt (ehci);
|
ehci_halt (ehci);
|
||||||
|
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD))
|
||||||
|
ehci_handle_controller_death(ehci);
|
||||||
|
if (ehci->rh_state != EHCI_RH_RUNNING)
|
||||||
|
goto done;
|
||||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
ehci->rh_state = EHCI_RH_SUSPENDED;
|
||||||
|
|
||||||
if (ehci->reclaim)
|
end_unlink_async(ehci);
|
||||||
end_unlink_async(ehci);
|
unlink_empty_async(ehci);
|
||||||
|
ehci_handle_intr_unlinks(ehci);
|
||||||
|
end_free_itds(ehci);
|
||||||
|
|
||||||
/* allow remote wakeup */
|
/* allow remote wakeup */
|
||||||
mask = INTR_MASK;
|
mask = INTR_MASK;
|
||||||
|
@ -317,13 +326,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||||
|
|
||||||
|
done:
|
||||||
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
|
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
|
||||||
|
ehci->enabled_hrtimer_events = 0;
|
||||||
|
ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
|
||||||
spin_unlock_irq (&ehci->lock);
|
spin_unlock_irq (&ehci->lock);
|
||||||
|
|
||||||
/* ehci_work() may have re-enabled the watchdog timer, which we do not
|
hrtimer_cancel(&ehci->hrtimer);
|
||||||
* want, and so we must delete any pending watchdog timer events.
|
|
||||||
*/
|
|
||||||
del_timer_sync(&ehci->watchdog);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,10 +349,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||||
if (time_before (jiffies, ehci->next_statechange))
|
if (time_before (jiffies, ehci->next_statechange))
|
||||||
msleep(5);
|
msleep(5);
|
||||||
spin_lock_irq (&ehci->lock);
|
spin_lock_irq (&ehci->lock);
|
||||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown)
|
||||||
spin_unlock_irq(&ehci->lock);
|
goto shutdown;
|
||||||
return -ESHUTDOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(ehci->debug)) {
|
if (unlikely(ehci->debug)) {
|
||||||
if (!dbgp_reset_prep())
|
if (!dbgp_reset_prep())
|
||||||
|
@ -382,16 +389,17 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
msleep(8);
|
msleep(8);
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
/* clear phy low-power mode before resume */
|
/* clear phy low-power mode before resume */
|
||||||
if (ehci->bus_suspended && ehci->has_hostpc) {
|
if (ehci->bus_suspended && ehci->has_hostpc) {
|
||||||
i = HCS_N_PORTS(ehci->hcs_params);
|
i = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (i--) {
|
while (i--) {
|
||||||
if (test_bit(i, &ehci->bus_suspended)) {
|
if (test_bit(i, &ehci->bus_suspended)) {
|
||||||
u32 __iomem *hostpc_reg;
|
u32 __iomem *hostpc_reg =
|
||||||
|
&ehci->regs->hostpc[i];
|
||||||
|
|
||||||
hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
|
|
||||||
+ HOSTPC0 + 4 * i);
|
|
||||||
temp = ehci_readl(ehci, hostpc_reg);
|
temp = ehci_readl(ehci, hostpc_reg);
|
||||||
ehci_writel(ehci, temp & ~HOSTPC_PHCD,
|
ehci_writel(ehci, temp & ~HOSTPC_PHCD,
|
||||||
hostpc_reg);
|
hostpc_reg);
|
||||||
|
@ -400,6 +408,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
msleep(5);
|
msleep(5);
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* manually resume the ports we suspended during bus_suspend() */
|
/* manually resume the ports we suspended during bus_suspend() */
|
||||||
|
@ -420,6 +430,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
msleep(20);
|
msleep(20);
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = HCS_N_PORTS (ehci->hcs_params);
|
i = HCS_N_PORTS (ehci->hcs_params);
|
||||||
|
@ -431,27 +443,25 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||||
ehci_vdbg (ehci, "resumed port %d\n", i + 1);
|
ehci_vdbg (ehci, "resumed port %d\n", i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) ehci_readl(ehci, &ehci->regs->command);
|
|
||||||
|
|
||||||
/* maybe re-activate the schedule(s) */
|
|
||||||
temp = 0;
|
|
||||||
if (ehci->async->qh_next.qh)
|
|
||||||
temp |= CMD_ASE;
|
|
||||||
if (ehci->periodic_sched)
|
|
||||||
temp |= CMD_PSE;
|
|
||||||
if (temp) {
|
|
||||||
ehci->command |= temp;
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
|
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
ehci_handover_companion_ports(ehci);
|
||||||
|
|
||||||
/* Now we can safely re-enable irqs */
|
/* Now we can safely re-enable irqs */
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
if (ehci->shutdown)
|
||||||
|
goto shutdown;
|
||||||
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
|
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
|
||||||
|
(void) ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
spin_unlock_irq (&ehci->lock);
|
|
||||||
ehci_handover_companion_ports(ehci);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
return -ESHUTDOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -667,7 +677,7 @@ static int ehci_hub_control (
|
||||||
int ports = HCS_N_PORTS (ehci->hcs_params);
|
int ports = HCS_N_PORTS (ehci->hcs_params);
|
||||||
u32 __iomem *status_reg = &ehci->regs->port_status[
|
u32 __iomem *status_reg = &ehci->regs->port_status[
|
||||||
(wIndex & 0xff) - 1];
|
(wIndex & 0xff) - 1];
|
||||||
u32 __iomem *hostpc_reg = NULL;
|
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];
|
||||||
u32 temp, temp1, status;
|
u32 temp, temp1, status;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
@ -680,9 +690,6 @@ static int ehci_hub_control (
|
||||||
* power, "this is the one", etc. EHCI spec supports this.
|
* power, "this is the one", etc. EHCI spec supports this.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (ehci->has_hostpc)
|
|
||||||
hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
|
|
||||||
+ HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
|
|
||||||
spin_lock_irqsave (&ehci->lock, flags);
|
spin_lock_irqsave (&ehci->lock, flags);
|
||||||
switch (typeReq) {
|
switch (typeReq) {
|
||||||
case ClearHubFeature:
|
case ClearHubFeature:
|
||||||
|
@ -724,7 +731,7 @@ static int ehci_hub_control (
|
||||||
#ifdef CONFIG_USB_OTG
|
#ifdef CONFIG_USB_OTG
|
||||||
if ((hcd->self.otg_port == (wIndex + 1))
|
if ((hcd->self.otg_port == (wIndex + 1))
|
||||||
&& hcd->self.b_hnp_enable) {
|
&& hcd->self.b_hnp_enable) {
|
||||||
otg_start_hnp(ehci->transceiver->otg);
|
otg_start_hnp(hcd->phy->otg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -734,7 +741,7 @@ static int ehci_hub_control (
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* clear phy low-power mode before resume */
|
/* clear phy low-power mode before resume */
|
||||||
if (hostpc_reg) {
|
if (ehci->has_hostpc) {
|
||||||
temp1 = ehci_readl(ehci, hostpc_reg);
|
temp1 = ehci_readl(ehci, hostpc_reg);
|
||||||
ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
|
ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
|
||||||
hostpc_reg);
|
hostpc_reg);
|
||||||
|
@ -984,7 +991,7 @@ static int ehci_hub_control (
|
||||||
temp &= ~PORT_WKCONN_E;
|
temp &= ~PORT_WKCONN_E;
|
||||||
temp |= PORT_WKDISC_E | PORT_WKOC_E;
|
temp |= PORT_WKDISC_E | PORT_WKOC_E;
|
||||||
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
|
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
|
||||||
if (hostpc_reg) {
|
if (ehci->has_hostpc) {
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
msleep(5);/* 5ms for HCD enter low pwr mode */
|
msleep(5);/* 5ms for HCD enter low pwr mode */
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
@ -1041,7 +1048,9 @@ static int ehci_hub_control (
|
||||||
case USB_PORT_FEAT_TEST:
|
case USB_PORT_FEAT_TEST:
|
||||||
if (!selector || selector > 5)
|
if (!selector || selector > 5)
|
||||||
goto error;
|
goto error;
|
||||||
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
ehci_quiesce(ehci);
|
ehci_quiesce(ehci);
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
|
||||||
/* Put all enabled ports into suspend */
|
/* Put all enabled ports into suspend */
|
||||||
while (ports--) {
|
while (ports--) {
|
||||||
|
@ -1053,7 +1062,11 @@ static int ehci_hub_control (
|
||||||
ehci_writel(ehci, temp | PORT_SUSPEND,
|
ehci_writel(ehci, temp | PORT_SUSPEND,
|
||||||
sreg);
|
sreg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
ehci_halt(ehci);
|
ehci_halt(ehci);
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
|
||||||
temp = ehci_readl(ehci, status_reg);
|
temp = ehci_readl(ehci, status_reg);
|
||||||
temp |= selector << 16;
|
temp |= selector << 16;
|
||||||
ehci_writel(ehci, temp, status_reg);
|
ehci_writel(ehci, temp, status_reg);
|
||||||
|
|
|
@ -22,14 +22,10 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd)
|
||||||
ehci->big_endian_mmio = 1;
|
ehci->big_endian_mmio = 1;
|
||||||
|
|
||||||
ehci->caps = hcd->regs + 0x100;
|
ehci->caps = hcd->regs + 0x100;
|
||||||
ehci->regs = hcd->regs + 0x100
|
|
||||||
+ HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qh_destroy(struct ehci_qh *qh)
|
static void qh_destroy(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = qh->ehci;
|
|
||||||
|
|
||||||
/* clean qtds first, and know this is not linked */
|
/* clean qtds first, and know this is not linked */
|
||||||
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
|
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
|
||||||
ehci_dbg (ehci, "unused qh not empty!\n");
|
ehci_dbg (ehci, "unused qh not empty!\n");
|
||||||
|
@ -92,8 +90,6 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
|
||||||
if (!qh->hw)
|
if (!qh->hw)
|
||||||
goto fail;
|
goto fail;
|
||||||
memset(qh->hw, 0, sizeof *qh->hw);
|
memset(qh->hw, 0, sizeof *qh->hw);
|
||||||
qh->refcount = 1;
|
|
||||||
qh->ehci = ehci;
|
|
||||||
qh->qh_dma = dma;
|
qh->qh_dma = dma;
|
||||||
// INIT_LIST_HEAD (&qh->qh_list);
|
// INIT_LIST_HEAD (&qh->qh_list);
|
||||||
INIT_LIST_HEAD (&qh->qtd_list);
|
INIT_LIST_HEAD (&qh->qtd_list);
|
||||||
|
@ -113,20 +109,6 @@ fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* to share a qh (cpu threads, or hc) */
|
|
||||||
static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
|
|
||||||
{
|
|
||||||
WARN_ON(!qh->refcount);
|
|
||||||
qh->refcount++;
|
|
||||||
return qh;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void qh_put (struct ehci_qh *qh)
|
|
||||||
{
|
|
||||||
if (!--qh->refcount)
|
|
||||||
qh_destroy(qh);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* The queue heads and transfer descriptors are managed from pools tied
|
/* The queue heads and transfer descriptors are managed from pools tied
|
||||||
|
@ -136,13 +118,12 @@ static inline void qh_put (struct ehci_qh *qh)
|
||||||
|
|
||||||
static void ehci_mem_cleanup (struct ehci_hcd *ehci)
|
static void ehci_mem_cleanup (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
free_cached_lists(ehci);
|
|
||||||
if (ehci->async)
|
if (ehci->async)
|
||||||
qh_put (ehci->async);
|
qh_destroy(ehci, ehci->async);
|
||||||
ehci->async = NULL;
|
ehci->async = NULL;
|
||||||
|
|
||||||
if (ehci->dummy)
|
if (ehci->dummy)
|
||||||
qh_put(ehci->dummy);
|
qh_destroy(ehci, ehci->dummy);
|
||||||
ehci->dummy = NULL;
|
ehci->dummy = NULL;
|
||||||
|
|
||||||
/* DMA consistent memory and pools */
|
/* DMA consistent memory and pools */
|
||||||
|
|
|
@ -145,8 +145,8 @@ static int ehci_msm_probe(struct platform_device *pdev)
|
||||||
* powering up VBUS, mapping of registers address space and power
|
* powering up VBUS, mapping of registers address space and power
|
||||||
* management.
|
* management.
|
||||||
*/
|
*/
|
||||||
phy = usb_get_transceiver();
|
phy = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
if (!phy) {
|
if (IS_ERR_OR_NULL(phy)) {
|
||||||
dev_err(&pdev->dev, "unable to find transceiver\n");
|
dev_err(&pdev->dev, "unable to find transceiver\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto unmap;
|
goto unmap;
|
||||||
|
@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
put_transceiver:
|
put_transceiver:
|
||||||
usb_put_transceiver(phy);
|
usb_put_phy(phy);
|
||||||
unmap:
|
unmap:
|
||||||
iounmap(hcd->regs);
|
iounmap(hcd->regs);
|
||||||
put_hcd:
|
put_hcd:
|
||||||
|
@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
|
||||||
pm_runtime_set_suspended(&pdev->dev);
|
pm_runtime_set_suspended(&pdev->dev);
|
||||||
|
|
||||||
otg_set_host(phy->otg, NULL);
|
otg_set_host(phy->otg, NULL);
|
||||||
usb_put_transceiver(phy);
|
usb_put_phy(phy);
|
||||||
|
|
||||||
usb_put_hcd(hcd);
|
usb_put_hcd(hcd);
|
||||||
|
|
||||||
|
@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
|
||||||
static int ehci_msm_pm_suspend(struct device *dev)
|
static int ehci_msm_pm_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
bool wakeup = device_may_wakeup(dev);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "ehci-msm PM suspend\n");
|
dev_dbg(dev, "ehci-msm PM suspend\n");
|
||||||
|
|
||||||
/*
|
return ehci_suspend(hcd, do_wakeup);
|
||||||
* EHCI helper function has also the same check before manipulating
|
|
||||||
* port wakeup flags. We do check here the same condition before
|
|
||||||
* calling the same helper function to avoid bringing hardware
|
|
||||||
* from Low power mode when there is no need for adjusting port
|
|
||||||
* wakeup flags.
|
|
||||||
*/
|
|
||||||
if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
|
|
||||||
pm_runtime_resume(dev);
|
|
||||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
|
|
||||||
wakeup);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_msm_pm_resume(struct device *dev)
|
static int ehci_msm_pm_resume(struct device *dev)
|
||||||
|
@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct device *dev)
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "ehci-msm PM resume\n");
|
dev_dbg(dev, "ehci-msm PM resume\n");
|
||||||
ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
|
ehci_resume(hcd, false);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/usb/otg.h>
|
#include <linux/usb/otg.h>
|
||||||
#include <linux/platform_data/mv_usb.h>
|
#include <linux/platform_data/mv_usb.h>
|
||||||
|
|
||||||
|
@ -76,7 +77,6 @@ static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
|
||||||
|
|
||||||
static int mv_ehci_reset(struct usb_hcd *hcd)
|
static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
struct device *dev = hcd->self.controller;
|
struct device *dev = hcd->self.controller;
|
||||||
struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
|
struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -86,25 +86,13 @@ static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* data structure init
|
|
||||||
*/
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval) {
|
|
||||||
dev_err(dev, "ehci_init failed %d\n", retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
retval = ehci_reset(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval) {
|
if (retval)
|
||||||
dev_err(dev, "ehci_reset failed %d\n", retval);
|
dev_err(dev, "ehci_setup failed %d\n", retval);
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct hc_driver mv_ehci_hc_driver = {
|
static const struct hc_driver mv_ehci_hc_driver = {
|
||||||
|
@ -247,14 +235,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ehci = hcd_to_ehci(hcd);
|
ehci = hcd_to_ehci(hcd);
|
||||||
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
|
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
|
||||||
ehci->regs = (struct ehci_regs *) ehci_mv->op_regs;
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ehci_mv->mode = pdata->mode;
|
ehci_mv->mode = pdata->mode;
|
||||||
if (ehci_mv->mode == MV_USB_MODE_OTG) {
|
if (ehci_mv->mode == MV_USB_MODE_OTG) {
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
ehci_mv->otg = usb_get_transceiver();
|
ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
if (!ehci_mv->otg) {
|
if (IS_ERR_OR_NULL(ehci_mv->otg)) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"unable to find transceiver\n");
|
"unable to find transceiver\n");
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
|
@ -302,8 +288,8 @@ err_set_vbus:
|
||||||
pdata->set_vbus(0);
|
pdata->set_vbus(0);
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
err_put_transceiver:
|
err_put_transceiver:
|
||||||
if (ehci_mv->otg)
|
if (!IS_ERR_OR_NULL(ehci_mv->otg))
|
||||||
usb_put_transceiver(ehci_mv->otg);
|
usb_put_phy(ehci_mv->otg);
|
||||||
#endif
|
#endif
|
||||||
err_disable_clk:
|
err_disable_clk:
|
||||||
mv_ehci_disable(ehci_mv);
|
mv_ehci_disable(ehci_mv);
|
||||||
|
@ -331,9 +317,9 @@ static int mv_ehci_remove(struct platform_device *pdev)
|
||||||
if (hcd->rh_registered)
|
if (hcd->rh_registered)
|
||||||
usb_remove_hcd(hcd);
|
usb_remove_hcd(hcd);
|
||||||
|
|
||||||
if (ehci_mv->otg) {
|
if (!IS_ERR_OR_NULL(ehci_mv->otg)) {
|
||||||
otg_set_host(ehci_mv->otg->otg, NULL);
|
otg_set_host(ehci_mv->otg->otg, NULL);
|
||||||
usb_put_transceiver(ehci_mv->otg);
|
usb_put_phy(ehci_mv->otg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ehci_mv->mode == MV_USB_MODE_HOST) {
|
if (ehci_mv->mode == MV_USB_MODE_HOST) {
|
||||||
|
|
|
@ -42,27 +42,12 @@ static int ehci_mxc_setup(struct usb_hcd *hcd)
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
ehci_port_power(ehci, 0);
|
ehci_port_power(ehci, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ static const struct hc_driver ehci_octeon_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_init,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
@ -150,12 +150,6 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -115,9 +115,8 @@ static void omap_ehci_erratum_i693(struct ehci_hcd *ehci)
|
||||||
clk_disable(usbhost_p2_fck);
|
clk_disable(usbhost_p2_fck);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
|
static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
|
|
||||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||||
unsigned reg = 0;
|
unsigned reg = 0;
|
||||||
|
|
||||||
|
@ -139,12 +138,63 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
|
|
||||||
if (time_after(jiffies, timeout)) {
|
if (time_after(jiffies, timeout)) {
|
||||||
dev_dbg(&pdev->dev, "phy reset operation timed out\n");
|
dev_dbg(hcd->self.controller,
|
||||||
|
"phy reset operation timed out\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int omap_ehci_init(struct usb_hcd *hcd)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
int rc;
|
||||||
|
struct ehci_hcd_omap_platform_data *pdata;
|
||||||
|
|
||||||
|
pdata = hcd->self.controller->platform_data;
|
||||||
|
if (pdata->phy_reset) {
|
||||||
|
if (gpio_is_valid(pdata->reset_gpio_port[0]))
|
||||||
|
gpio_request_one(pdata->reset_gpio_port[0],
|
||||||
|
GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
|
||||||
|
|
||||||
|
if (gpio_is_valid(pdata->reset_gpio_port[1]))
|
||||||
|
gpio_request_one(pdata->reset_gpio_port[1],
|
||||||
|
GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
|
||||||
|
|
||||||
|
/* Hold the PHY in RESET for enough time till DIR is high */
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Soft reset the PHY using PHY reset command over ULPI */
|
||||||
|
if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
|
||||||
|
omap_ehci_soft_phy_reset(hcd, 0);
|
||||||
|
if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
|
||||||
|
omap_ehci_soft_phy_reset(hcd, 1);
|
||||||
|
|
||||||
|
/* we know this is the memory we want, no need to ioremap again */
|
||||||
|
ehci->caps = hcd->regs;
|
||||||
|
|
||||||
|
rc = ehci_setup(hcd);
|
||||||
|
|
||||||
|
if (pdata->phy_reset) {
|
||||||
|
/* Hold the PHY in RESET for enough time till
|
||||||
|
* PHY is settled and ready
|
||||||
|
*/
|
||||||
|
udelay(10);
|
||||||
|
|
||||||
|
if (gpio_is_valid(pdata->reset_gpio_port[0]))
|
||||||
|
gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
|
||||||
|
|
||||||
|
if (gpio_is_valid(pdata->reset_gpio_port[1]))
|
||||||
|
gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* root ports should always stay powered */
|
||||||
|
ehci_port_power(ehci, 1);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int omap_ehci_hub_control(
|
static int omap_ehci_hub_control(
|
||||||
struct usb_hcd *hcd,
|
struct usb_hcd *hcd,
|
||||||
u16 typeReq,
|
u16 typeReq,
|
||||||
|
@ -219,7 +269,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct usb_hcd *hcd;
|
struct usb_hcd *hcd;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct ehci_hcd *omap_ehci;
|
|
||||||
int ret = -ENODEV;
|
int ret = -ENODEV;
|
||||||
int irq;
|
int irq;
|
||||||
int i;
|
int i;
|
||||||
|
@ -281,18 +330,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hold PHYs in reset while initializing EHCI controller */
|
|
||||||
if (pdata->phy_reset) {
|
|
||||||
if (gpio_is_valid(pdata->reset_gpio_port[0]))
|
|
||||||
gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
|
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->reset_gpio_port[1]))
|
|
||||||
gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
|
|
||||||
|
|
||||||
/* Hold the PHY in RESET for enough time till DIR is high */
|
|
||||||
udelay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
pm_runtime_get_sync(dev);
|
pm_runtime_get_sync(dev);
|
||||||
|
|
||||||
|
@ -308,49 +345,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
||||||
ehci_write(regs, EHCI_INSNREG04,
|
ehci_write(regs, EHCI_INSNREG04,
|
||||||
EHCI_INSNREG04_DISABLE_UNSUSPEND);
|
EHCI_INSNREG04_DISABLE_UNSUSPEND);
|
||||||
|
|
||||||
/* Soft reset the PHY using PHY reset command over ULPI */
|
|
||||||
if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
|
|
||||||
omap_ehci_soft_phy_reset(pdev, 0);
|
|
||||||
if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
|
|
||||||
omap_ehci_soft_phy_reset(pdev, 1);
|
|
||||||
|
|
||||||
omap_ehci = hcd_to_ehci(hcd);
|
|
||||||
omap_ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
/* we know this is the memory we want, no need to ioremap again */
|
|
||||||
omap_ehci->caps = hcd->regs;
|
|
||||||
omap_ehci->regs = hcd->regs
|
|
||||||
+ HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(omap_ehci, "reset");
|
|
||||||
dbg_hcc_params(omap_ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ehci_reset(omap_ehci);
|
|
||||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to add hcd with err %d\n", ret);
|
dev_err(dev, "failed to add hcd with err %d\n", ret);
|
||||||
goto err_add_hcd;
|
goto err_pm_runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->phy_reset) {
|
|
||||||
/* Hold the PHY in RESET for enough time till
|
|
||||||
* PHY is settled and ready
|
|
||||||
*/
|
|
||||||
udelay(10);
|
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->reset_gpio_port[0]))
|
|
||||||
gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1);
|
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->reset_gpio_port[1]))
|
|
||||||
gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* root ports should always stay powered */
|
|
||||||
ehci_port_power(omap_ehci, 1);
|
|
||||||
|
|
||||||
/* get clocks */
|
/* get clocks */
|
||||||
utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
|
utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
|
||||||
if (IS_ERR(utmi_p1_fck)) {
|
if (IS_ERR(utmi_p1_fck)) {
|
||||||
|
@ -422,8 +422,12 @@ err_utmi_p1_fck:
|
||||||
clk_put(utmi_p1_fck);
|
clk_put(utmi_p1_fck);
|
||||||
|
|
||||||
err_add_hcd:
|
err_add_hcd:
|
||||||
|
usb_remove_hcd(hcd);
|
||||||
|
|
||||||
|
err_pm_runtime:
|
||||||
disable_put_regulator(pdata);
|
disable_put_regulator(pdata);
|
||||||
pm_runtime_put_sync(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
usb_put_hcd(hcd);
|
||||||
|
|
||||||
err_io:
|
err_io:
|
||||||
iounmap(regs);
|
iounmap(regs);
|
||||||
|
@ -506,7 +510,7 @@ static const struct hc_driver ehci_omap_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_init,
|
.reset = omap_ehci_init,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
|
|
@ -106,21 +106,10 @@ static int ehci_orion_setup(struct usb_hcd *hcd)
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
hcd->has_tt = 1;
|
retval = ehci_setup(hcd);
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/*
|
|
||||||
* data structure init
|
|
||||||
*/
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
ehci_port_power(ehci, 0);
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -261,11 +250,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ehci = hcd_to_ehci(hcd);
|
ehci = hcd_to_ehci(hcd);
|
||||||
ehci->caps = hcd->regs + 0x100;
|
ehci->caps = hcd->regs + 0x100;
|
||||||
ehci->regs = hcd->regs + 0x100 +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (Re-)program MBUS remapping windows if we are asked to.
|
* (Re-)program MBUS remapping windows if we are asked to.
|
||||||
|
|
|
@ -54,6 +54,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
u32 temp;
|
u32 temp;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
ehci->caps = hcd->regs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ehci_init() causes memory for DMA transfers to be
|
||||||
|
* allocated. Thus, any vendor-specific workarounds based on
|
||||||
|
* limiting the type of memory used for DMA transfers must
|
||||||
|
* happen before ehci_setup() is called.
|
||||||
|
*
|
||||||
|
* Most other workarounds can be done either before or after
|
||||||
|
* init and reset; they are located here too.
|
||||||
|
*/
|
||||||
switch (pdev->vendor) {
|
switch (pdev->vendor) {
|
||||||
case PCI_VENDOR_ID_TOSHIBA_2:
|
case PCI_VENDOR_ID_TOSHIBA_2:
|
||||||
/* celleb's companion chip */
|
/* celleb's companion chip */
|
||||||
|
@ -66,20 +77,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* ehci_init() causes memory for DMA transfers to be
|
|
||||||
* allocated. Thus, any vendor-specific workarounds based on
|
|
||||||
* limiting the type of memory used for DMA transfers must
|
|
||||||
* happen before ehci_init() is called. */
|
|
||||||
switch (pdev->vendor) {
|
|
||||||
case PCI_VENDOR_ID_NVIDIA:
|
case PCI_VENDOR_ID_NVIDIA:
|
||||||
/* NVidia reports that certain chips don't handle
|
/* NVidia reports that certain chips don't handle
|
||||||
* QH, ITD, or SITD addresses above 2GB. (But TD,
|
* QH, ITD, or SITD addresses above 2GB. (But TD,
|
||||||
|
@ -95,61 +92,24 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
ehci_warn(ehci, "can't enable NVidia "
|
ehci_warn(ehci, "can't enable NVidia "
|
||||||
"workaround for >2GB RAM\n");
|
"workaround for >2GB RAM\n");
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
/* Some NForce2 chips have problems with selective suspend;
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
* fixed in newer silicon.
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) ||
|
|
||||||
(pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) {
|
|
||||||
/* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
|
|
||||||
* read/write memory space which does not belong to it when
|
|
||||||
* there is NULL pointer with T-bit set to 1 in the frame list
|
|
||||||
* table. To avoid the issue, the frame list link pointer
|
|
||||||
* should always contain a valid pointer to a inactive qh.
|
|
||||||
*/
|
*/
|
||||||
ehci->use_dummy_qh = 1;
|
case 0x0068:
|
||||||
ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI "
|
if (pdev->revision < 0xa4)
|
||||||
"dummy qh workaround\n");
|
ehci->no_selective_suspend = 1;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
switch (pdev->vendor) {
|
|
||||||
case PCI_VENDOR_ID_NEC:
|
|
||||||
ehci->need_io_watchdog = 0;
|
|
||||||
break;
|
break;
|
||||||
case PCI_VENDOR_ID_INTEL:
|
case PCI_VENDOR_ID_INTEL:
|
||||||
ehci->need_io_watchdog = 0;
|
|
||||||
ehci->fs_i_thresh = 1;
|
ehci->fs_i_thresh = 1;
|
||||||
if (pdev->device == 0x27cc) {
|
if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB)
|
||||||
ehci->broken_periodic = 1;
|
|
||||||
ehci_info(ehci, "using broken periodic workaround\n");
|
|
||||||
}
|
|
||||||
if (pdev->device == 0x0806 || pdev->device == 0x0811
|
|
||||||
|| pdev->device == 0x0829) {
|
|
||||||
ehci_info(ehci, "disable lpm for langwell/penwell\n");
|
|
||||||
ehci->has_lpm = 0;
|
|
||||||
}
|
|
||||||
if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) {
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
tdi_reset(ehci);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PCI_VENDOR_ID_TDI:
|
case PCI_VENDOR_ID_TDI:
|
||||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI)
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
tdi_reset(ehci);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PCI_VENDOR_ID_AMD:
|
case PCI_VENDOR_ID_AMD:
|
||||||
/* AMD PLL quirk */
|
/* AMD PLL quirk */
|
||||||
|
@ -161,28 +121,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
retval = -EIO;
|
retval = -EIO;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case PCI_VENDOR_ID_NVIDIA:
|
|
||||||
switch (pdev->device) {
|
|
||||||
/* Some NForce2 chips have problems with selective suspend;
|
|
||||||
* fixed in newer silicon.
|
|
||||||
*/
|
|
||||||
case 0x0068:
|
|
||||||
if (pdev->revision < 0xa4)
|
|
||||||
ehci->no_selective_suspend = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* MCP89 chips on the MacBookAir3,1 give EPROTO when
|
/*
|
||||||
* fetching device descriptors unless LPM is disabled.
|
* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
|
||||||
* There are also intermittent problems enumerating
|
* read/write memory space which does not belong to it when
|
||||||
* devices with PPCD enabled.
|
* there is NULL pointer with T-bit set to 1 in the frame list
|
||||||
|
* table. To avoid the issue, the frame list link pointer
|
||||||
|
* should always contain a valid pointer to a inactive qh.
|
||||||
*/
|
*/
|
||||||
case 0x0d9d:
|
if (pdev->device == 0x7808) {
|
||||||
ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89");
|
ehci->use_dummy_qh = 1;
|
||||||
ehci->has_lpm = 0;
|
ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
|
||||||
ehci->has_ppcd = 0;
|
|
||||||
ehci->command &= ~CMD_PPCEE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PCI_VENDOR_ID_VIA:
|
case PCI_VENDOR_ID_VIA:
|
||||||
|
@ -203,6 +152,18 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
/* AMD PLL quirk */
|
/* AMD PLL quirk */
|
||||||
if (usb_amd_find_chipset_info())
|
if (usb_amd_find_chipset_info())
|
||||||
ehci->amd_pll_fix = 1;
|
ehci->amd_pll_fix = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may
|
||||||
|
* read/write memory space which does not belong to it when
|
||||||
|
* there is NULL pointer with T-bit set to 1 in the frame list
|
||||||
|
* table. To avoid the issue, the frame list link pointer
|
||||||
|
* should always contain a valid pointer to a inactive qh.
|
||||||
|
*/
|
||||||
|
if (pdev->device == 0x4396) {
|
||||||
|
ehci->use_dummy_qh = 1;
|
||||||
|
ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n");
|
||||||
|
}
|
||||||
/* SB600 and old version of SB700 have a bug in EHCI controller,
|
/* SB600 and old version of SB700 have a bug in EHCI controller,
|
||||||
* which causes usb devices lose response in some cases.
|
* which causes usb devices lose response in some cases.
|
||||||
*/
|
*/
|
||||||
|
@ -231,6 +192,40 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retval = ehci_setup(hcd);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* These workarounds need to be applied after ehci_setup() */
|
||||||
|
switch (pdev->vendor) {
|
||||||
|
case PCI_VENDOR_ID_NEC:
|
||||||
|
ehci->need_io_watchdog = 0;
|
||||||
|
break;
|
||||||
|
case PCI_VENDOR_ID_INTEL:
|
||||||
|
ehci->need_io_watchdog = 0;
|
||||||
|
if (pdev->device == 0x0806 || pdev->device == 0x0811
|
||||||
|
|| pdev->device == 0x0829) {
|
||||||
|
ehci_info(ehci, "disable lpm for langwell/penwell\n");
|
||||||
|
ehci->has_lpm = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PCI_VENDOR_ID_NVIDIA:
|
||||||
|
switch (pdev->device) {
|
||||||
|
/* MCP89 chips on the MacBookAir3,1 give EPROTO when
|
||||||
|
* fetching device descriptors unless LPM is disabled.
|
||||||
|
* There are also intermittent problems enumerating
|
||||||
|
* devices with PPCD enabled.
|
||||||
|
*/
|
||||||
|
case 0x0d9d:
|
||||||
|
ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89");
|
||||||
|
ehci->has_lpm = 0;
|
||||||
|
ehci->has_ppcd = 0;
|
||||||
|
ehci->command &= ~CMD_PPCEE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* optional debug port, normally in the first BAR */
|
/* optional debug port, normally in the first BAR */
|
||||||
temp = pci_find_capability(pdev, 0x0a);
|
temp = pci_find_capability(pdev, 0x0a);
|
||||||
if (temp) {
|
if (temp) {
|
||||||
|
@ -238,7 +233,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
temp >>= 16;
|
temp >>= 16;
|
||||||
if ((temp & (3 << 13)) == (1 << 13)) {
|
if ((temp & (3 << 13)) == (1 << 13)) {
|
||||||
temp &= 0x1fff;
|
temp &= 0x1fff;
|
||||||
ehci->debug = ehci_to_hcd(ehci)->regs + temp;
|
ehci->debug = hcd->regs + temp;
|
||||||
temp = ehci_readl(ehci, &ehci->debug->control);
|
temp = ehci_readl(ehci, &ehci->debug->control);
|
||||||
ehci_info(ehci, "debug port %d%s\n",
|
ehci_info(ehci, "debug port %d%s\n",
|
||||||
HCS_DEBUG_PORT(ehci->hcs_params),
|
HCS_DEBUG_PORT(ehci->hcs_params),
|
||||||
|
@ -250,8 +245,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* at least the Genesys GL880S needs fixup here */
|
/* at least the Genesys GL880S needs fixup here */
|
||||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||||
temp &= 0x0f;
|
temp &= 0x0f;
|
||||||
|
@ -275,10 +268,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Serial Bus Release Number is at PCI 0x60 offset */
|
/* Serial Bus Release Number is at PCI 0x60 offset */
|
||||||
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
|
||||||
if (pdev->vendor == PCI_VENDOR_ID_STMICRO
|
if (pdev->vendor == PCI_VENDOR_ID_STMICRO
|
||||||
&& pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST)
|
&& pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST)
|
||||||
ehci->sbrn = 0x20; /* ConneXT has no sbrn register */
|
; /* ConneXT has no sbrn register */
|
||||||
|
else
|
||||||
|
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
||||||
|
|
||||||
/* Keep this around for a while just in case some EHCI
|
/* Keep this around for a while just in case some EHCI
|
||||||
* implementation uses legacy PCI PM support. This test
|
* implementation uses legacy PCI PM support. This test
|
||||||
|
@ -331,29 +325,7 @@ done:
|
||||||
|
|
||||||
static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
return ehci_suspend(hcd, do_wakeup);
|
||||||
unsigned long flags;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
|
||||||
msleep(10);
|
|
||||||
|
|
||||||
/* Root hub was already suspended. Disable irq emission and
|
|
||||||
* mark HW unaccessible. The PM and USB cores make sure that
|
|
||||||
* the root hub is either suspended or stopped.
|
|
||||||
*/
|
|
||||||
ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
|
|
||||||
spin_lock_irqsave (&ehci->lock, flags);
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
|
||||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
|
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
|
||||||
|
|
||||||
// could save FLADJ in case of Vaux power loss
|
|
||||||
// ... we'd only use it to handle clock skew
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
|
static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
|
||||||
|
@ -402,54 +374,8 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||||
if (usb_is_intel_switchable_ehci(pdev))
|
if (usb_is_intel_switchable_ehci(pdev))
|
||||||
ehci_enable_xhci_companion();
|
ehci_enable_xhci_companion();
|
||||||
|
|
||||||
// maybe restore FLADJ
|
if (ehci_resume(hcd, hibernated) != 0)
|
||||||
|
(void) ehci_pci_reinit(ehci, pdev);
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
/* Mark hardware accessible again as we are out of D3 state by now */
|
|
||||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
|
|
||||||
/* If CF is still set and we aren't resuming from hibernation
|
|
||||||
* then we maintained PCI Vaux power.
|
|
||||||
* Just undo the effect of ehci_pci_suspend().
|
|
||||||
*/
|
|
||||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
|
|
||||||
!hibernated) {
|
|
||||||
int mask = INTR_MASK;
|
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(ehci);
|
|
||||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
|
||||||
mask &= ~STS_PCD;
|
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
|
||||||
|
|
||||||
/* Else reset, to cope with power loss or flush-to-storage
|
|
||||||
* style "resume" having let BIOS kick in during reboot.
|
|
||||||
*/
|
|
||||||
(void) ehci_halt(ehci);
|
|
||||||
(void) ehci_reset(ehci);
|
|
||||||
(void) ehci_pci_reinit(ehci, pdev);
|
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
|
||||||
if (ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
ehci_work(ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered */
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
|
|
||||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -153,17 +153,16 @@ static int __devexit ehci_platform_remove(struct platform_device *dev)
|
||||||
static int ehci_platform_suspend(struct device *dev)
|
static int ehci_platform_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
bool wakeup = device_may_wakeup(dev);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup);
|
return ehci_suspend(hcd, do_wakeup);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_platform_resume(struct device *dev)
|
static int ehci_platform_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
|
ehci_resume(hcd, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,27 +78,14 @@ static int ehci_msp_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
ehci->big_endian_mmio = 1;
|
ehci->big_endian_mmio = 1;
|
||||||
ehci->big_endian_desc = 1;
|
ehci->big_endian_desc = 1;
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
|
|
@ -17,24 +17,6 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
/* called during probe() after chip reset completes */
|
|
||||||
static int ehci_ppc_of_setup(struct usb_hcd *hcd)
|
|
||||||
{
|
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
return ehci_reset(ehci);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const struct hc_driver ehci_ppc_of_hc_driver = {
|
static const struct hc_driver ehci_ppc_of_hc_driver = {
|
||||||
.description = hcd_name,
|
.description = hcd_name,
|
||||||
|
@ -50,7 +32,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_ppc_of_setup,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
@ -178,11 +160,6 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
|
||||||
ehci->big_endian_desc = 1;
|
ehci->big_endian_desc = 1;
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
|
if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
|
||||||
rv = ppc44x_enable_bmt(dn);
|
rv = ppc44x_enable_bmt(dn);
|
||||||
|
|
|
@ -55,28 +55,12 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
ehci->big_endian_mmio = 1;
|
ehci->big_endian_mmio = 1;
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
|
|
||||||
&ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
result = ehci_halt(ehci);
|
|
||||||
|
|
||||||
|
result = ehci_setup(hcd);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result = ehci_init(hcd);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
ps3_ehci_setup_insnreg(ehci);
|
ps3_ehci_setup_insnreg(ehci);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -100,7 +100,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
|
||||||
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
|
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
|
||||||
* ever clear it.
|
* ever clear it.
|
||||||
*/
|
*/
|
||||||
if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
|
if (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) {
|
||||||
unsigned is_out, epnum;
|
unsigned is_out, epnum;
|
||||||
|
|
||||||
is_out = qh->is_out;
|
is_out = qh->is_out;
|
||||||
|
@ -265,7 +265,6 @@ __acquires(ehci->lock)
|
||||||
/* ... update hc-wide periodic stats (for usbfs) */
|
/* ... update hc-wide periodic stats (for usbfs) */
|
||||||
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
|
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
|
||||||
}
|
}
|
||||||
qh_put (qh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(urb->unlinked)) {
|
if (unlikely(urb->unlinked)) {
|
||||||
|
@ -294,9 +293,6 @@ __acquires(ehci->lock)
|
||||||
spin_lock (&ehci->lock);
|
spin_lock (&ehci->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
|
||||||
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
|
||||||
|
|
||||||
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -326,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
*
|
*
|
||||||
* It's a bug for qh->qh_state to be anything other than
|
* It's a bug for qh->qh_state to be anything other than
|
||||||
* QH_STATE_IDLE, unless our caller is scan_async() or
|
* QH_STATE_IDLE, unless our caller is scan_async() or
|
||||||
* scan_periodic().
|
* scan_intr().
|
||||||
*/
|
*/
|
||||||
state = qh->qh_state;
|
state = qh->qh_state;
|
||||||
qh->qh_state = QH_STATE_COMPLETING;
|
qh->qh_state = QH_STATE_COMPLETING;
|
||||||
|
@ -434,7 +430,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
|
||||||
/* stop scanning when we reach qtds the hc is using */
|
/* stop scanning when we reach qtds the hc is using */
|
||||||
} else if (likely (!stopped
|
} else if (likely (!stopped
|
||||||
&& ehci->rh_state == EHCI_RH_RUNNING)) {
|
&& ehci->rh_state >= EHCI_RH_RUNNING)) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* scan the whole queue for unlinks whenever it stops */
|
/* scan the whole queue for unlinks whenever it stops */
|
||||||
|
@ -442,7 +438,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
stopped = 1;
|
stopped = 1;
|
||||||
|
|
||||||
/* cancel everything if we halt, suspend, etc */
|
/* cancel everything if we halt, suspend, etc */
|
||||||
if (ehci->rh_state != EHCI_RH_RUNNING)
|
if (ehci->rh_state < EHCI_RH_RUNNING)
|
||||||
last_status = -ESHUTDOWN;
|
last_status = -ESHUTDOWN;
|
||||||
|
|
||||||
/* this qtd is active; skip it unless a previous qtd
|
/* this qtd is active; skip it unless a previous qtd
|
||||||
|
@ -836,7 +832,6 @@ qh_make (
|
||||||
is_input, 0,
|
is_input, 0,
|
||||||
hb_mult(maxp) * max_packet(maxp)));
|
hb_mult(maxp) * max_packet(maxp)));
|
||||||
qh->start = NO_FRAME;
|
qh->start = NO_FRAME;
|
||||||
qh->stamp = ehci->periodic_stamp;
|
|
||||||
|
|
||||||
if (urb->dev->speed == USB_SPEED_HIGH) {
|
if (urb->dev->speed == USB_SPEED_HIGH) {
|
||||||
qh->c_usecs = 0;
|
qh->c_usecs = 0;
|
||||||
|
@ -887,7 +882,7 @@ qh_make (
|
||||||
/* using TT? */
|
/* using TT? */
|
||||||
switch (urb->dev->speed) {
|
switch (urb->dev->speed) {
|
||||||
case USB_SPEED_LOW:
|
case USB_SPEED_LOW:
|
||||||
info1 |= (1 << 12); /* EPS "low" */
|
info1 |= QH_LOW_SPEED;
|
||||||
/* FALL THROUGH */
|
/* FALL THROUGH */
|
||||||
|
|
||||||
case USB_SPEED_FULL:
|
case USB_SPEED_FULL:
|
||||||
|
@ -895,8 +890,8 @@ qh_make (
|
||||||
if (type != PIPE_INTERRUPT)
|
if (type != PIPE_INTERRUPT)
|
||||||
info1 |= (EHCI_TUNE_RL_TT << 28);
|
info1 |= (EHCI_TUNE_RL_TT << 28);
|
||||||
if (type == PIPE_CONTROL) {
|
if (type == PIPE_CONTROL) {
|
||||||
info1 |= (1 << 27); /* for TT */
|
info1 |= QH_CONTROL_EP; /* for TT */
|
||||||
info1 |= 1 << 14; /* toggle from qtd */
|
info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
|
||||||
}
|
}
|
||||||
info1 |= maxp << 16;
|
info1 |= maxp << 16;
|
||||||
|
|
||||||
|
@ -921,11 +916,11 @@ qh_make (
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_SPEED_HIGH: /* no TT involved */
|
case USB_SPEED_HIGH: /* no TT involved */
|
||||||
info1 |= (2 << 12); /* EPS "high" */
|
info1 |= QH_HIGH_SPEED;
|
||||||
if (type == PIPE_CONTROL) {
|
if (type == PIPE_CONTROL) {
|
||||||
info1 |= (EHCI_TUNE_RL_HS << 28);
|
info1 |= (EHCI_TUNE_RL_HS << 28);
|
||||||
info1 |= 64 << 16; /* usb2 fixed maxpacket */
|
info1 |= 64 << 16; /* usb2 fixed maxpacket */
|
||||||
info1 |= 1 << 14; /* toggle from qtd */
|
info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
|
||||||
info2 |= (EHCI_TUNE_MULT_HS << 30);
|
info2 |= (EHCI_TUNE_MULT_HS << 30);
|
||||||
} else if (type == PIPE_BULK) {
|
} else if (type == PIPE_BULK) {
|
||||||
info1 |= (EHCI_TUNE_RL_HS << 28);
|
info1 |= (EHCI_TUNE_RL_HS << 28);
|
||||||
|
@ -946,7 +941,7 @@ qh_make (
|
||||||
ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev,
|
ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev,
|
||||||
urb->dev->speed);
|
urb->dev->speed);
|
||||||
done:
|
done:
|
||||||
qh_put (qh);
|
qh_destroy(ehci, qh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -965,6 +960,31 @@ done:
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void enable_async(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
if (ehci->async_count++)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Stop waiting to turn off the async schedule */
|
||||||
|
ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
|
||||||
|
|
||||||
|
/* Don't start the schedule until ASS is 0 */
|
||||||
|
ehci_poll_ASS(ehci);
|
||||||
|
turn_on_io_watchdog(ehci);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disable_async(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
if (--ehci->async_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* The async schedule and async_unlink list are supposed to be empty */
|
||||||
|
WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink);
|
||||||
|
|
||||||
|
/* Don't turn off the schedule until ASS is 1 */
|
||||||
|
ehci_poll_ASS(ehci);
|
||||||
|
}
|
||||||
|
|
||||||
/* move qh (and its qtds) onto async queue; maybe enable queue. */
|
/* move qh (and its qtds) onto async queue; maybe enable queue. */
|
||||||
|
|
||||||
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
@ -978,24 +998,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
|
||||||
WARN_ON(qh->qh_state != QH_STATE_IDLE);
|
WARN_ON(qh->qh_state != QH_STATE_IDLE);
|
||||||
|
|
||||||
/* (re)start the async schedule? */
|
|
||||||
head = ehci->async;
|
|
||||||
timer_action_done (ehci, TIMER_ASYNC_OFF);
|
|
||||||
if (!head->qh_next.qh) {
|
|
||||||
if (!(ehci->command & CMD_ASE)) {
|
|
||||||
/* in case a clear of CMD_ASE didn't take yet */
|
|
||||||
(void)handshake(ehci, &ehci->regs->status,
|
|
||||||
STS_ASS, 0, 150);
|
|
||||||
ehci->command |= CMD_ASE;
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
/* posted write need not be known to HC yet ... */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear halt and/or toggle; and maybe recover from silicon quirk */
|
/* clear halt and/or toggle; and maybe recover from silicon quirk */
|
||||||
qh_refresh(ehci, qh);
|
qh_refresh(ehci, qh);
|
||||||
|
|
||||||
/* splice right after start */
|
/* splice right after start */
|
||||||
|
head = ehci->async;
|
||||||
qh->qh_next = head->qh_next;
|
qh->qh_next = head->qh_next;
|
||||||
qh->hw->hw_next = head->hw->hw_next;
|
qh->hw->hw_next = head->hw->hw_next;
|
||||||
wmb ();
|
wmb ();
|
||||||
|
@ -1003,10 +1010,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
head->qh_next.qh = qh;
|
head->qh_next.qh = qh;
|
||||||
head->hw->hw_next = dma;
|
head->hw->hw_next = dma;
|
||||||
|
|
||||||
qh_get(qh);
|
|
||||||
qh->xacterrs = 0;
|
qh->xacterrs = 0;
|
||||||
qh->qh_state = QH_STATE_LINKED;
|
qh->qh_state = QH_STATE_LINKED;
|
||||||
/* qtd completions reported later by interrupt */
|
/* qtd completions reported later by interrupt */
|
||||||
|
|
||||||
|
enable_async(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -1090,7 +1098,7 @@ static struct ehci_qh *qh_append_tds (
|
||||||
wmb ();
|
wmb ();
|
||||||
dummy->hw_token = token;
|
dummy->hw_token = token;
|
||||||
|
|
||||||
urb->hcpriv = qh_get (qh);
|
urb->hcpriv = qh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return qh;
|
return qh;
|
||||||
|
@ -1155,83 +1163,19 @@ submit_async (
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* the async qh for the qtds being reclaimed are now unlinked from the HC */
|
static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
|
||||||
static void end_unlink_async (struct ehci_hcd *ehci)
|
|
||||||
{
|
{
|
||||||
struct ehci_qh *qh = ehci->reclaim;
|
struct ehci_qh *prev;
|
||||||
struct ehci_qh *next;
|
|
||||||
|
|
||||||
iaa_watchdog_done(ehci);
|
|
||||||
|
|
||||||
// qh->hw_next = cpu_to_hc32(qh->qh_dma);
|
|
||||||
qh->qh_state = QH_STATE_IDLE;
|
|
||||||
qh->qh_next.qh = NULL;
|
|
||||||
qh_put (qh); // refcount from reclaim
|
|
||||||
|
|
||||||
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
|
|
||||||
next = qh->reclaim;
|
|
||||||
ehci->reclaim = next;
|
|
||||||
qh->reclaim = NULL;
|
|
||||||
|
|
||||||
qh_completions (ehci, qh);
|
|
||||||
|
|
||||||
if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
|
|
||||||
qh_link_async (ehci, qh);
|
|
||||||
} else {
|
|
||||||
/* it's not free to turn the async schedule on/off; leave it
|
|
||||||
* active but idle for a while once it empties.
|
|
||||||
*/
|
|
||||||
if (ehci->rh_state == EHCI_RH_RUNNING
|
|
||||||
&& ehci->async->qh_next.qh == NULL)
|
|
||||||
timer_action (ehci, TIMER_ASYNC_OFF);
|
|
||||||
}
|
|
||||||
qh_put(qh); /* refcount from async list */
|
|
||||||
|
|
||||||
if (next) {
|
|
||||||
ehci->reclaim = NULL;
|
|
||||||
start_unlink_async (ehci, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ehci->has_synopsys_hc_bug)
|
|
||||||
ehci_writel(ehci, (u32) ehci->async->qh_dma,
|
|
||||||
&ehci->regs->async_next);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* makes sure the async qh will become idle */
|
|
||||||
/* caller must own ehci->lock */
|
|
||||||
|
|
||||||
static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
||||||
{
|
|
||||||
struct ehci_qh *prev;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
assert_spin_locked(&ehci->lock);
|
|
||||||
if (ehci->reclaim
|
|
||||||
|| (qh->qh_state != QH_STATE_LINKED
|
|
||||||
&& qh->qh_state != QH_STATE_UNLINK_WAIT)
|
|
||||||
)
|
|
||||||
BUG ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* stop async schedule right now? */
|
|
||||||
if (unlikely (qh == ehci->async)) {
|
|
||||||
/* can't get here without STS_ASS set */
|
|
||||||
if (ehci->rh_state != EHCI_RH_HALTED
|
|
||||||
&& !ehci->reclaim) {
|
|
||||||
/* ... and CMD_IAAD clear */
|
|
||||||
ehci->command &= ~CMD_ASE;
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
wmb ();
|
|
||||||
// handshake later, if we need to
|
|
||||||
timer_action_done (ehci, TIMER_ASYNC_OFF);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Add to the end of the list of QHs waiting for the next IAAD */
|
||||||
qh->qh_state = QH_STATE_UNLINK;
|
qh->qh_state = QH_STATE_UNLINK;
|
||||||
ehci->reclaim = qh = qh_get (qh);
|
if (ehci->async_unlink)
|
||||||
|
ehci->async_unlink_last->unlink_next = qh;
|
||||||
|
else
|
||||||
|
ehci->async_unlink = qh;
|
||||||
|
ehci->async_unlink_last = qh;
|
||||||
|
|
||||||
|
/* Unlink it from the schedule */
|
||||||
prev = ehci->async;
|
prev = ehci->async;
|
||||||
while (prev->qh_next.qh != qh)
|
while (prev->qh_next.qh != qh)
|
||||||
prev = prev->qh_next.qh;
|
prev = prev->qh_next.qh;
|
||||||
|
@ -1240,32 +1184,134 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
prev->qh_next = qh->qh_next;
|
prev->qh_next = qh->qh_next;
|
||||||
if (ehci->qh_scan_next == qh)
|
if (ehci->qh_scan_next == qh)
|
||||||
ehci->qh_scan_next = qh->qh_next.qh;
|
ehci->qh_scan_next = qh->qh_next.qh;
|
||||||
wmb ();
|
}
|
||||||
|
|
||||||
|
static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Do nothing if an IAA cycle is already running or
|
||||||
|
* if one will be started shortly.
|
||||||
|
*/
|
||||||
|
if (ehci->async_iaa || ehci->async_unlinking)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Do all the waiting QHs at once */
|
||||||
|
ehci->async_iaa = ehci->async_unlink;
|
||||||
|
ehci->async_unlink = NULL;
|
||||||
|
|
||||||
/* If the controller isn't running, we don't have to wait for it */
|
/* If the controller isn't running, we don't have to wait for it */
|
||||||
if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) {
|
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
|
||||||
/* if (unlikely (qh->reclaim != 0))
|
if (!nested) /* Avoid recursion */
|
||||||
* this will recurse, probably not much
|
end_unlink_async(ehci);
|
||||||
*/
|
|
||||||
end_unlink_async (ehci);
|
/* Otherwise start a new IAA cycle */
|
||||||
|
} else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
|
||||||
|
/* Make sure the unlinks are all visible to the hardware */
|
||||||
|
wmb();
|
||||||
|
|
||||||
|
ehci_writel(ehci, ehci->command | CMD_IAAD,
|
||||||
|
&ehci->regs->command);
|
||||||
|
ehci_readl(ehci, &ehci->regs->command);
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the async qh for the qtds being unlinked are now gone from the HC */
|
||||||
|
|
||||||
|
static void end_unlink_async(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
struct ehci_qh *qh;
|
||||||
|
|
||||||
|
if (ehci->has_synopsys_hc_bug)
|
||||||
|
ehci_writel(ehci, (u32) ehci->async->qh_dma,
|
||||||
|
&ehci->regs->async_next);
|
||||||
|
|
||||||
|
/* Process the idle QHs */
|
||||||
|
restart:
|
||||||
|
ehci->async_unlinking = true;
|
||||||
|
while (ehci->async_iaa) {
|
||||||
|
qh = ehci->async_iaa;
|
||||||
|
ehci->async_iaa = qh->unlink_next;
|
||||||
|
qh->unlink_next = NULL;
|
||||||
|
|
||||||
|
qh->qh_state = QH_STATE_IDLE;
|
||||||
|
qh->qh_next.qh = NULL;
|
||||||
|
|
||||||
|
qh_completions(ehci, qh);
|
||||||
|
if (!list_empty(&qh->qtd_list) &&
|
||||||
|
ehci->rh_state == EHCI_RH_RUNNING)
|
||||||
|
qh_link_async(ehci, qh);
|
||||||
|
disable_async(ehci);
|
||||||
|
}
|
||||||
|
ehci->async_unlinking = false;
|
||||||
|
|
||||||
|
/* Start a new IAA cycle if any QHs are waiting for it */
|
||||||
|
if (ehci->async_unlink) {
|
||||||
|
start_iaa_cycle(ehci, true);
|
||||||
|
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING))
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlink_empty_async(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
struct ehci_qh *qh, *next;
|
||||||
|
bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
|
||||||
|
bool check_unlinks_later = false;
|
||||||
|
|
||||||
|
/* Unlink all the async QHs that have been empty for a timer cycle */
|
||||||
|
next = ehci->async->qh_next.qh;
|
||||||
|
while (next) {
|
||||||
|
qh = next;
|
||||||
|
next = qh->qh_next.qh;
|
||||||
|
|
||||||
|
if (list_empty(&qh->qtd_list) &&
|
||||||
|
qh->qh_state == QH_STATE_LINKED) {
|
||||||
|
if (!stopped && qh->unlink_cycle ==
|
||||||
|
ehci->async_unlink_cycle)
|
||||||
|
check_unlinks_later = true;
|
||||||
|
else
|
||||||
|
single_unlink_async(ehci, qh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start a new IAA cycle if any QHs are waiting for it */
|
||||||
|
if (ehci->async_unlink)
|
||||||
|
start_iaa_cycle(ehci, false);
|
||||||
|
|
||||||
|
/* QHs that haven't been empty for long enough will be handled later */
|
||||||
|
if (check_unlinks_later) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
|
||||||
|
++ehci->async_unlink_cycle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* makes sure the async qh will become idle */
|
||||||
|
/* caller must own ehci->lock */
|
||||||
|
|
||||||
|
static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the QH isn't linked then there's nothing we can do
|
||||||
|
* unless we were called during a giveback, in which case
|
||||||
|
* qh_completions() has to deal with it.
|
||||||
|
*/
|
||||||
|
if (qh->qh_state != QH_STATE_LINKED) {
|
||||||
|
if (qh->qh_state == QH_STATE_COMPLETING)
|
||||||
|
qh->needs_rescan = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
|
single_unlink_async(ehci, qh);
|
||||||
(void)ehci_readl(ehci, &ehci->regs->command);
|
start_iaa_cycle(ehci, false);
|
||||||
iaa_watchdog_start(ehci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void scan_async (struct ehci_hcd *ehci)
|
static void scan_async (struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
bool stopped;
|
|
||||||
struct ehci_qh *qh;
|
struct ehci_qh *qh;
|
||||||
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
|
bool check_unlinks_later = false;
|
||||||
|
|
||||||
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
|
|
||||||
stopped = (ehci->rh_state != EHCI_RH_RUNNING);
|
|
||||||
|
|
||||||
ehci->qh_scan_next = ehci->async->qh_next.qh;
|
ehci->qh_scan_next = ehci->async->qh_next.qh;
|
||||||
while (ehci->qh_scan_next) {
|
while (ehci->qh_scan_next) {
|
||||||
|
@ -1281,33 +1327,30 @@ static void scan_async (struct ehci_hcd *ehci)
|
||||||
* drops the lock. That's why ehci->qh_scan_next
|
* drops the lock. That's why ehci->qh_scan_next
|
||||||
* always holds the next qh to scan; if the next qh
|
* always holds the next qh to scan; if the next qh
|
||||||
* gets unlinked then ehci->qh_scan_next is adjusted
|
* gets unlinked then ehci->qh_scan_next is adjusted
|
||||||
* in start_unlink_async().
|
* in single_unlink_async().
|
||||||
*/
|
*/
|
||||||
qh = qh_get(qh);
|
|
||||||
temp = qh_completions(ehci, qh);
|
temp = qh_completions(ehci, qh);
|
||||||
if (qh->needs_rescan)
|
if (qh->needs_rescan) {
|
||||||
unlink_async(ehci, qh);
|
start_unlink_async(ehci, qh);
|
||||||
qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
|
} else if (list_empty(&qh->qtd_list)
|
||||||
qh_put(qh);
|
&& qh->qh_state == QH_STATE_LINKED) {
|
||||||
if (temp != 0)
|
qh->unlink_cycle = ehci->async_unlink_cycle;
|
||||||
|
check_unlinks_later = true;
|
||||||
|
} else if (temp != 0)
|
||||||
goto rescan;
|
goto rescan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlink idle entries, reducing DMA usage as well
|
|
||||||
* as HCD schedule-scanning costs. delay for any qh
|
|
||||||
* we just scanned, there's a not-unusual case that it
|
|
||||||
* doesn't stay idle for long.
|
|
||||||
* (plus, avoids some kind of re-activation race.)
|
|
||||||
*/
|
|
||||||
if (list_empty(&qh->qtd_list)
|
|
||||||
&& qh->qh_state == QH_STATE_LINKED) {
|
|
||||||
if (!ehci->reclaim && (stopped ||
|
|
||||||
time_after_eq(jiffies, qh->unlink_time)))
|
|
||||||
start_unlink_async(ehci, qh);
|
|
||||||
else
|
|
||||||
action = TIMER_ASYNC_SHRINK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (action == TIMER_ASYNC_SHRINK)
|
|
||||||
timer_action (ehci, TIMER_ASYNC_SHRINK);
|
/*
|
||||||
|
* Unlink empty entries, reducing DMA usage as well
|
||||||
|
* as HCD schedule-scanning costs. Delay for any qh
|
||||||
|
* we just scanned, there's a not-unusual case that it
|
||||||
|
* doesn't stay idle for long.
|
||||||
|
*/
|
||||||
|
if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
|
||||||
|
!(ehci->enabled_hrtimer_events &
|
||||||
|
BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
|
||||||
|
++ehci->async_unlink_cycle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <plat/ehci.h>
|
#include <plat/ehci.h>
|
||||||
#include <plat/usb-phy.h>
|
#include <plat/usb-phy.h>
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ static const struct hc_driver s5p_ehci_hc_driver = {
|
||||||
.irq = ehci_irq,
|
.irq = ehci_irq,
|
||||||
.flags = HCD_MEMORY | HCD_USB2,
|
.flags = HCD_MEMORY | HCD_USB2,
|
||||||
|
|
||||||
.reset = ehci_init,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
@ -63,6 +65,26 @@ static const struct hc_driver s5p_ehci_hc_driver = {
|
||||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void s5p_setup_vbus_gpio(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int gpio;
|
||||||
|
|
||||||
|
if (!pdev->dev.of_node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gpio = of_get_named_gpio(pdev->dev.of_node,
|
||||||
|
"samsung,vbus-gpio", 0);
|
||||||
|
if (!gpio_is_valid(gpio))
|
||||||
|
return;
|
||||||
|
|
||||||
|
err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio");
|
||||||
|
if (err)
|
||||||
|
dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32);
|
||||||
|
|
||||||
static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct s5p_ehci_platdata *pdata;
|
struct s5p_ehci_platdata *pdata;
|
||||||
|
@ -79,7 +101,20 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL);
|
/*
|
||||||
|
* Right now device-tree probed devices don't get dma_mask set.
|
||||||
|
* Since shared usb code relies on it, set it here for now.
|
||||||
|
* Once we move to full device tree support this will vanish off.
|
||||||
|
*/
|
||||||
|
if (!pdev->dev.dma_mask)
|
||||||
|
pdev->dev.dma_mask = &ehci_s5p_dma_mask;
|
||||||
|
if (!pdev->dev.coherent_dma_mask)
|
||||||
|
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||||
|
|
||||||
|
s5p_setup_vbus_gpio(pdev);
|
||||||
|
|
||||||
|
s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd),
|
||||||
|
GFP_KERNEL);
|
||||||
if (!s5p_ehci)
|
if (!s5p_ehci)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -89,8 +124,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
dev_name(&pdev->dev));
|
dev_name(&pdev->dev));
|
||||||
if (!hcd) {
|
if (!hcd) {
|
||||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto fail_hcd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s5p_ehci->hcd = hcd;
|
s5p_ehci->hcd = hcd;
|
||||||
|
@ -115,7 +149,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
hcd->rsrc_start = res->start;
|
hcd->rsrc_start = res->start;
|
||||||
hcd->rsrc_len = resource_size(res);
|
hcd->rsrc_len = resource_size(res);
|
||||||
hcd->regs = ioremap(res->start, resource_size(res));
|
hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
|
||||||
if (!hcd->regs) {
|
if (!hcd->regs) {
|
||||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -126,7 +160,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
if (!irq) {
|
if (!irq) {
|
||||||
dev_err(&pdev->dev, "Failed to get IRQ\n");
|
dev_err(&pdev->dev, "Failed to get IRQ\n");
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto fail;
|
goto fail_io;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->phy_init)
|
if (pdata->phy_init)
|
||||||
|
@ -134,40 +168,26 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ehci = hcd_to_ehci(hcd);
|
ehci = hcd_to_ehci(hcd);
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
/* DMA burst Enable */
|
/* DMA burst Enable */
|
||||||
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
|
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "Failed to add USB HCD\n");
|
dev_err(&pdev->dev, "Failed to add USB HCD\n");
|
||||||
goto fail;
|
goto fail_io;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, s5p_ehci);
|
platform_set_drvdata(pdev, s5p_ehci);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
|
||||||
iounmap(hcd->regs);
|
|
||||||
fail_io:
|
fail_io:
|
||||||
clk_disable(s5p_ehci->clk);
|
clk_disable(s5p_ehci->clk);
|
||||||
fail_clken:
|
fail_clken:
|
||||||
clk_put(s5p_ehci->clk);
|
clk_put(s5p_ehci->clk);
|
||||||
fail_clk:
|
fail_clk:
|
||||||
usb_put_hcd(hcd);
|
usb_put_hcd(hcd);
|
||||||
fail_hcd:
|
|
||||||
kfree(s5p_ehci);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,13 +202,10 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev)
|
||||||
if (pdata && pdata->phy_exit)
|
if (pdata && pdata->phy_exit)
|
||||||
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
|
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
|
||||||
|
|
||||||
iounmap(hcd->regs);
|
|
||||||
|
|
||||||
clk_disable(s5p_ehci->clk);
|
clk_disable(s5p_ehci->clk);
|
||||||
clk_put(s5p_ehci->clk);
|
clk_put(s5p_ehci->clk);
|
||||||
|
|
||||||
usb_put_hcd(hcd);
|
usb_put_hcd(hcd);
|
||||||
kfree(s5p_ehci);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -207,27 +224,12 @@ static int s5p_ehci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
|
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
|
||||||
struct usb_hcd *hcd = s5p_ehci->hcd;
|
struct usb_hcd *hcd = s5p_ehci->hcd;
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
|
struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
|
||||||
unsigned long flags;
|
int rc;
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
rc = ehci_suspend(hcd, do_wakeup);
|
||||||
msleep(20);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Root hub was already suspended. Disable irq emission and
|
|
||||||
* mark HW unaccessible. The PM and USB cores make sure that
|
|
||||||
* the root hub is either suspended or stopped.
|
|
||||||
*/
|
|
||||||
ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
|
||||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
|
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
||||||
|
|
||||||
if (pdata && pdata->phy_exit)
|
if (pdata && pdata->phy_exit)
|
||||||
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
|
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
|
||||||
|
@ -241,7 +243,6 @@ static int s5p_ehci_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
|
struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
|
||||||
struct usb_hcd *hcd = s5p_ehci->hcd;
|
struct usb_hcd *hcd = s5p_ehci->hcd;
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
|
struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
|
||||||
|
|
||||||
|
@ -253,44 +254,7 @@ static int s5p_ehci_resume(struct device *dev)
|
||||||
/* DMA burst Enable */
|
/* DMA burst Enable */
|
||||||
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
|
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
ehci_resume(hcd, false);
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
/* Mark hardware accessible again as we are out of D3 state by now */
|
|
||||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
|
|
||||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
|
||||||
int mask = INTR_MASK;
|
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(ehci);
|
|
||||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
|
||||||
mask &= ~STS_PCD;
|
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
|
||||||
|
|
||||||
(void) ehci_halt(ehci);
|
|
||||||
(void) ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
|
||||||
if (ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
ehci_work(ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered */
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
|
|
||||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -303,6 +267,14 @@ static const struct dev_pm_ops s5p_ehci_pm_ops = {
|
||||||
.resume = s5p_ehci_resume,
|
.resume = s5p_ehci_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id exynos_ehci_match[] = {
|
||||||
|
{ .compatible = "samsung,exynos-ehci" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, exynos_ehci_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct platform_driver s5p_ehci_driver = {
|
static struct platform_driver s5p_ehci_driver = {
|
||||||
.probe = s5p_ehci_probe,
|
.probe = s5p_ehci_probe,
|
||||||
.remove = __devexit_p(s5p_ehci_remove),
|
.remove = __devexit_p(s5p_ehci_remove),
|
||||||
|
@ -311,6 +283,7 @@ static struct platform_driver s5p_ehci_driver = {
|
||||||
.name = "s5p-ehci",
|
.name = "s5p-ehci",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = &s5p_ehci_pm_ops,
|
.pm = &s5p_ehci_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(exynos_ehci_match),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -479,70 +479,26 @@ static int tt_no_collision (
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static int enable_periodic (struct ehci_hcd *ehci)
|
static void enable_periodic(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int status;
|
if (ehci->periodic_count++)
|
||||||
|
return;
|
||||||
|
|
||||||
if (ehci->periodic_sched++)
|
/* Stop waiting to turn off the periodic schedule */
|
||||||
return 0;
|
ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
|
||||||
|
|
||||||
/* did clearing PSE did take effect yet?
|
/* Don't start the schedule until PSS is 0 */
|
||||||
* takes effect only at frame boundaries...
|
ehci_poll_PSS(ehci);
|
||||||
*/
|
turn_on_io_watchdog(ehci);
|
||||||
status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
|
|
||||||
STS_PSS, 0, 9 * 125);
|
|
||||||
if (status) {
|
|
||||||
usb_hc_died(ehci_to_hcd(ehci));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci->command |= CMD_PSE;
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
/* posted write ... PSS happens later */
|
|
||||||
|
|
||||||
/* make sure ehci_work scans these */
|
|
||||||
ehci->next_uframe = ehci_read_frame_index(ehci)
|
|
||||||
% (ehci->periodic_size << 3);
|
|
||||||
if (unlikely(ehci->broken_periodic))
|
|
||||||
ehci->last_periodic_enable = ktime_get_real();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int disable_periodic (struct ehci_hcd *ehci)
|
static void disable_periodic(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int status;
|
if (--ehci->periodic_count)
|
||||||
|
return;
|
||||||
|
|
||||||
if (--ehci->periodic_sched)
|
/* Don't turn off the schedule until PSS is 1 */
|
||||||
return 0;
|
ehci_poll_PSS(ehci);
|
||||||
|
|
||||||
if (unlikely(ehci->broken_periodic)) {
|
|
||||||
/* delay experimentally determined */
|
|
||||||
ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000);
|
|
||||||
ktime_t now = ktime_get_real();
|
|
||||||
s64 delay = ktime_us_delta(safe, now);
|
|
||||||
|
|
||||||
if (unlikely(delay > 0))
|
|
||||||
udelay(delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* did setting PSE not take effect yet?
|
|
||||||
* takes effect only at frame boundaries...
|
|
||||||
*/
|
|
||||||
status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
|
|
||||||
STS_PSS, STS_PSS, 9 * 125);
|
|
||||||
if (status) {
|
|
||||||
usb_hc_died(ehci_to_hcd(ehci));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci->command &= ~CMD_PSE;
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
/* posted write ... */
|
|
||||||
|
|
||||||
free_cached_lists(ehci);
|
|
||||||
|
|
||||||
ehci->next_uframe = -1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -553,7 +509,7 @@ static int disable_periodic (struct ehci_hcd *ehci)
|
||||||
* this just links in a qh; caller guarantees uframe masks are set right.
|
* this just links in a qh; caller guarantees uframe masks are set right.
|
||||||
* no FSTN support (yet; ehci 0.96+)
|
* no FSTN support (yet; ehci 0.96+)
|
||||||
*/
|
*/
|
||||||
static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
unsigned period = qh->period;
|
unsigned period = qh->period;
|
||||||
|
@ -606,28 +562,38 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
}
|
}
|
||||||
qh->qh_state = QH_STATE_LINKED;
|
qh->qh_state = QH_STATE_LINKED;
|
||||||
qh->xacterrs = 0;
|
qh->xacterrs = 0;
|
||||||
qh_get (qh);
|
|
||||||
|
|
||||||
/* update per-qh bandwidth for usbfs */
|
/* update per-qh bandwidth for usbfs */
|
||||||
ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
|
ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
|
||||||
? ((qh->usecs + qh->c_usecs) / qh->period)
|
? ((qh->usecs + qh->c_usecs) / qh->period)
|
||||||
: (qh->usecs * 8);
|
: (qh->usecs * 8);
|
||||||
|
|
||||||
|
list_add(&qh->intr_node, &ehci->intr_qh_list);
|
||||||
|
|
||||||
/* maybe enable periodic schedule processing */
|
/* maybe enable periodic schedule processing */
|
||||||
return enable_periodic(ehci);
|
++ehci->intr_count;
|
||||||
|
enable_periodic(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
unsigned period;
|
unsigned period;
|
||||||
|
|
||||||
// FIXME:
|
/*
|
||||||
// IF this isn't high speed
|
* If qh is for a low/full-speed device, simply unlinking it
|
||||||
// and this qh is active in the current uframe
|
* could interfere with an ongoing split transaction. To unlink
|
||||||
// (and overlay token SplitXstate is false?)
|
* it safely would require setting the QH_INACTIVATE bit and
|
||||||
// THEN
|
* waiting at least one frame, as described in EHCI 4.12.2.5.
|
||||||
// qh->hw_info1 |= cpu_to_hc32(1 << 7 /* "ignore" */);
|
*
|
||||||
|
* We won't bother with any of this. Instead, we assume that the
|
||||||
|
* only reason for unlinking an interrupt QH while the current URB
|
||||||
|
* is still active is to dequeue all the URBs (flush the whole
|
||||||
|
* endpoint queue).
|
||||||
|
*
|
||||||
|
* If rebalancing the periodic schedule is ever implemented, this
|
||||||
|
* approach will no longer be valid.
|
||||||
|
*/
|
||||||
|
|
||||||
/* high bandwidth, or otherwise part of every microframe */
|
/* high bandwidth, or otherwise part of every microframe */
|
||||||
if ((period = qh->period) == 0)
|
if ((period = qh->period) == 0)
|
||||||
|
@ -650,18 +616,15 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
/* qh->qh_next still "live" to HC */
|
/* qh->qh_next still "live" to HC */
|
||||||
qh->qh_state = QH_STATE_UNLINK;
|
qh->qh_state = QH_STATE_UNLINK;
|
||||||
qh->qh_next.ptr = NULL;
|
qh->qh_next.ptr = NULL;
|
||||||
qh_put (qh);
|
|
||||||
|
|
||||||
/* maybe turn off periodic schedule */
|
if (ehci->qh_scan_next == qh)
|
||||||
return disable_periodic(ehci);
|
ehci->qh_scan_next = list_entry(qh->intr_node.next,
|
||||||
|
struct ehci_qh, intr_node);
|
||||||
|
list_del(&qh->intr_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
{
|
{
|
||||||
unsigned wait;
|
|
||||||
struct ehci_qh_hw *hw = qh->hw;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* If the QH isn't linked then there's nothing we can do
|
/* If the QH isn't linked then there's nothing we can do
|
||||||
* unless we were called during a giveback, in which case
|
* unless we were called during a giveback, in which case
|
||||||
* qh_completions() has to deal with it.
|
* qh_completions() has to deal with it.
|
||||||
|
@ -674,28 +637,45 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
|
||||||
qh_unlink_periodic (ehci, qh);
|
qh_unlink_periodic (ehci, qh);
|
||||||
|
|
||||||
/* simple/paranoid: always delay, expecting the HC needs to read
|
/* Make sure the unlinks are visible before starting the timer */
|
||||||
* qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and
|
wmb();
|
||||||
* expect khubd to clean up after any CSPLITs we won't issue.
|
|
||||||
* active high speed queues may need bigger delays...
|
/*
|
||||||
*/
|
* The EHCI spec doesn't say how long it takes the controller to
|
||||||
if (list_empty (&qh->qtd_list)
|
* stop accessing an unlinked interrupt QH. The timer delay is
|
||||||
|| (cpu_to_hc32(ehci, QH_CMASK)
|
* 9 uframes; presumably that will be long enough.
|
||||||
& hw->hw_info2) != 0)
|
*/
|
||||||
wait = 2;
|
qh->unlink_cycle = ehci->intr_unlink_cycle;
|
||||||
else
|
|
||||||
wait = 55; /* worst case: 3 * 1024 */
|
/* New entries go at the end of the intr_unlink list */
|
||||||
|
if (ehci->intr_unlink)
|
||||||
|
ehci->intr_unlink_last->unlink_next = qh;
|
||||||
|
else
|
||||||
|
ehci->intr_unlink = qh;
|
||||||
|
ehci->intr_unlink_last = qh;
|
||||||
|
|
||||||
|
if (ehci->intr_unlinking)
|
||||||
|
; /* Avoid recursive calls */
|
||||||
|
else if (ehci->rh_state < EHCI_RH_RUNNING)
|
||||||
|
ehci_handle_intr_unlinks(ehci);
|
||||||
|
else if (ehci->intr_unlink == qh) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
|
||||||
|
++ehci->intr_unlink_cycle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
|
{
|
||||||
|
struct ehci_qh_hw *hw = qh->hw;
|
||||||
|
int rc;
|
||||||
|
|
||||||
udelay (wait);
|
|
||||||
qh->qh_state = QH_STATE_IDLE;
|
qh->qh_state = QH_STATE_IDLE;
|
||||||
hw->hw_next = EHCI_LIST_END(ehci);
|
hw->hw_next = EHCI_LIST_END(ehci);
|
||||||
wmb ();
|
|
||||||
|
|
||||||
qh_completions(ehci, qh);
|
qh_completions(ehci, qh);
|
||||||
|
|
||||||
/* reschedule QH iff another request is queued */
|
/* reschedule QH iff another request is queued */
|
||||||
if (!list_empty(&qh->qtd_list) &&
|
if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
|
||||||
ehci->rh_state == EHCI_RH_RUNNING) {
|
|
||||||
rc = qh_schedule(ehci, qh);
|
rc = qh_schedule(ehci, qh);
|
||||||
|
|
||||||
/* An error here likely indicates handshake failure
|
/* An error here likely indicates handshake failure
|
||||||
|
@ -708,6 +688,10 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
ehci_err(ehci, "can't reschedule qh %p, err %d\n",
|
ehci_err(ehci, "can't reschedule qh %p, err %d\n",
|
||||||
qh, rc);
|
qh, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* maybe turn off periodic schedule */
|
||||||
|
--ehci->intr_count;
|
||||||
|
disable_periodic(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -884,7 +868,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
|
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
|
||||||
|
|
||||||
/* stuff into the periodic schedule */
|
/* stuff into the periodic schedule */
|
||||||
status = qh_link_periodic (ehci, qh);
|
qh_link_periodic(ehci, qh);
|
||||||
done:
|
done:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -944,6 +928,35 @@ done_not_linked:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void scan_intr(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
struct ehci_qh *qh;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list,
|
||||||
|
intr_node) {
|
||||||
|
rescan:
|
||||||
|
/* clean any finished work for this qh */
|
||||||
|
if (!list_empty(&qh->qtd_list)) {
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlinks could happen here; completion reporting
|
||||||
|
* drops the lock. That's why ehci->qh_scan_next
|
||||||
|
* always holds the next qh to scan; if the next qh
|
||||||
|
* gets unlinked then ehci->qh_scan_next is adjusted
|
||||||
|
* in qh_unlink_periodic().
|
||||||
|
*/
|
||||||
|
temp = qh_completions(ehci, qh);
|
||||||
|
if (unlikely(qh->needs_rescan ||
|
||||||
|
(list_empty(&qh->qtd_list) &&
|
||||||
|
qh->qh_state == QH_STATE_LINKED)))
|
||||||
|
start_unlink_intr(ehci, qh);
|
||||||
|
else if (temp != 0)
|
||||||
|
goto rescan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* ehci_iso_stream ops work with both ITD and SITD */
|
/* ehci_iso_stream ops work with both ITD and SITD */
|
||||||
|
@ -958,7 +971,6 @@ iso_stream_alloc (gfp_t mem_flags)
|
||||||
INIT_LIST_HEAD(&stream->td_list);
|
INIT_LIST_HEAD(&stream->td_list);
|
||||||
INIT_LIST_HEAD(&stream->free_list);
|
INIT_LIST_HEAD(&stream->free_list);
|
||||||
stream->next_uframe = -1;
|
stream->next_uframe = -1;
|
||||||
stream->refcount = 1;
|
|
||||||
}
|
}
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -1058,57 +1070,6 @@ iso_stream_init (
|
||||||
stream->maxp = maxp;
|
stream->maxp = maxp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
|
|
||||||
{
|
|
||||||
stream->refcount--;
|
|
||||||
|
|
||||||
/* free whenever just a dev->ep reference remains.
|
|
||||||
* not like a QH -- no persistent state (toggle, halt)
|
|
||||||
*/
|
|
||||||
if (stream->refcount == 1) {
|
|
||||||
// BUG_ON (!list_empty(&stream->td_list));
|
|
||||||
|
|
||||||
while (!list_empty (&stream->free_list)) {
|
|
||||||
struct list_head *entry;
|
|
||||||
|
|
||||||
entry = stream->free_list.next;
|
|
||||||
list_del (entry);
|
|
||||||
|
|
||||||
/* knows about ITD vs SITD */
|
|
||||||
if (stream->highspeed) {
|
|
||||||
struct ehci_itd *itd;
|
|
||||||
|
|
||||||
itd = list_entry (entry, struct ehci_itd,
|
|
||||||
itd_list);
|
|
||||||
dma_pool_free (ehci->itd_pool, itd,
|
|
||||||
itd->itd_dma);
|
|
||||||
} else {
|
|
||||||
struct ehci_sitd *sitd;
|
|
||||||
|
|
||||||
sitd = list_entry (entry, struct ehci_sitd,
|
|
||||||
sitd_list);
|
|
||||||
dma_pool_free (ehci->sitd_pool, sitd,
|
|
||||||
sitd->sitd_dma);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->bEndpointAddress &= 0x0f;
|
|
||||||
if (stream->ep)
|
|
||||||
stream->ep->hcpriv = NULL;
|
|
||||||
|
|
||||||
kfree(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct ehci_iso_stream *
|
|
||||||
iso_stream_get (struct ehci_iso_stream *stream)
|
|
||||||
{
|
|
||||||
if (likely (stream != NULL))
|
|
||||||
stream->refcount++;
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ehci_iso_stream *
|
static struct ehci_iso_stream *
|
||||||
iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
|
iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
|
||||||
{
|
{
|
||||||
|
@ -1129,7 +1090,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
|
||||||
if (unlikely (stream == NULL)) {
|
if (unlikely (stream == NULL)) {
|
||||||
stream = iso_stream_alloc(GFP_ATOMIC);
|
stream = iso_stream_alloc(GFP_ATOMIC);
|
||||||
if (likely (stream != NULL)) {
|
if (likely (stream != NULL)) {
|
||||||
/* dev->ep owns the initial refcount */
|
|
||||||
ep->hcpriv = stream;
|
ep->hcpriv = stream;
|
||||||
stream->ep = ep;
|
stream->ep = ep;
|
||||||
iso_stream_init(ehci, stream, urb->dev, urb->pipe,
|
iso_stream_init(ehci, stream, urb->dev, urb->pipe,
|
||||||
|
@ -1144,9 +1104,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
|
||||||
stream = NULL;
|
stream = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caller guarantees an eventual matching iso_stream_put */
|
|
||||||
stream = iso_stream_get (stream);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -1254,17 +1211,19 @@ itd_urb_transaction (
|
||||||
spin_lock_irqsave (&ehci->lock, flags);
|
spin_lock_irqsave (&ehci->lock, flags);
|
||||||
for (i = 0; i < num_itds; i++) {
|
for (i = 0; i < num_itds; i++) {
|
||||||
|
|
||||||
/* free_list.next might be cache-hot ... but maybe
|
/*
|
||||||
* the HC caches it too. avoid that issue for now.
|
* Use iTDs from the free list, but not iTDs that may
|
||||||
|
* still be in use by the hardware.
|
||||||
*/
|
*/
|
||||||
|
if (likely(!list_empty(&stream->free_list))) {
|
||||||
/* prefer previously-allocated itds */
|
itd = list_first_entry(&stream->free_list,
|
||||||
if (likely (!list_empty(&stream->free_list))) {
|
|
||||||
itd = list_entry (stream->free_list.prev,
|
|
||||||
struct ehci_itd, itd_list);
|
struct ehci_itd, itd_list);
|
||||||
|
if (itd->frame == ehci->now_frame)
|
||||||
|
goto alloc_itd;
|
||||||
list_del (&itd->itd_list);
|
list_del (&itd->itd_list);
|
||||||
itd_dma = itd->itd_dma;
|
itd_dma = itd->itd_dma;
|
||||||
} else {
|
} else {
|
||||||
|
alloc_itd:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
|
itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
|
||||||
&itd_dma);
|
&itd_dma);
|
||||||
|
@ -1528,6 +1487,10 @@ iso_stream_schedule (
|
||||||
urb->start_frame = stream->next_uframe;
|
urb->start_frame = stream->next_uframe;
|
||||||
if (!stream->highspeed)
|
if (!stream->highspeed)
|
||||||
urb->start_frame >>= 3;
|
urb->start_frame >>= 3;
|
||||||
|
|
||||||
|
/* Make sure scan_isoc() sees these */
|
||||||
|
if (ehci->isoc_count == 0)
|
||||||
|
ehci->next_frame = now >> 3;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -1615,8 +1578,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fit urb's itds into the selected schedule slot; activate as needed */
|
/* fit urb's itds into the selected schedule slot; activate as needed */
|
||||||
static int
|
static void itd_link_urb(
|
||||||
itd_link_urb (
|
|
||||||
struct ehci_hcd *ehci,
|
struct ehci_hcd *ehci,
|
||||||
struct urb *urb,
|
struct urb *urb,
|
||||||
unsigned mod,
|
unsigned mod,
|
||||||
|
@ -1659,7 +1621,7 @@ itd_link_urb (
|
||||||
itd = list_entry (iso_sched->td_list.next,
|
itd = list_entry (iso_sched->td_list.next,
|
||||||
struct ehci_itd, itd_list);
|
struct ehci_itd, itd_list);
|
||||||
list_move_tail (&itd->itd_list, &stream->td_list);
|
list_move_tail (&itd->itd_list, &stream->td_list);
|
||||||
itd->stream = iso_stream_get (stream);
|
itd->stream = stream;
|
||||||
itd->urb = urb;
|
itd->urb = urb;
|
||||||
itd_init (ehci, stream, itd);
|
itd_init (ehci, stream, itd);
|
||||||
}
|
}
|
||||||
|
@ -1686,8 +1648,8 @@ itd_link_urb (
|
||||||
iso_sched_free (stream, iso_sched);
|
iso_sched_free (stream, iso_sched);
|
||||||
urb->hcpriv = NULL;
|
urb->hcpriv = NULL;
|
||||||
|
|
||||||
timer_action (ehci, TIMER_IO_WATCHDOG);
|
++ehci->isoc_count;
|
||||||
return enable_periodic(ehci);
|
enable_periodic(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
|
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
|
||||||
|
@ -1702,11 +1664,8 @@ itd_link_urb (
|
||||||
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
||||||
* corrupts things if you reuse completed descriptors very quickly...
|
* corrupts things if you reuse completed descriptors very quickly...
|
||||||
*/
|
*/
|
||||||
static unsigned
|
static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||||
itd_complete (
|
{
|
||||||
struct ehci_hcd *ehci,
|
|
||||||
struct ehci_itd *itd
|
|
||||||
) {
|
|
||||||
struct urb *urb = itd->urb;
|
struct urb *urb = itd->urb;
|
||||||
struct usb_iso_packet_descriptor *desc;
|
struct usb_iso_packet_descriptor *desc;
|
||||||
u32 t;
|
u32 t;
|
||||||
|
@ -1714,7 +1673,7 @@ itd_complete (
|
||||||
int urb_index = -1;
|
int urb_index = -1;
|
||||||
struct ehci_iso_stream *stream = itd->stream;
|
struct ehci_iso_stream *stream = itd->stream;
|
||||||
struct usb_device *dev;
|
struct usb_device *dev;
|
||||||
unsigned retval = false;
|
bool retval = false;
|
||||||
|
|
||||||
/* for each uframe with a packet */
|
/* for each uframe with a packet */
|
||||||
for (uframe = 0; uframe < 8; uframe++) {
|
for (uframe = 0; uframe < 8; uframe++) {
|
||||||
|
@ -1767,9 +1726,11 @@ itd_complete (
|
||||||
ehci_urb_done(ehci, urb, 0);
|
ehci_urb_done(ehci, urb, 0);
|
||||||
retval = true;
|
retval = true;
|
||||||
urb = NULL;
|
urb = NULL;
|
||||||
(void) disable_periodic(ehci);
|
|
||||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
|
||||||
|
|
||||||
|
--ehci->isoc_count;
|
||||||
|
disable_periodic(ehci);
|
||||||
|
|
||||||
|
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||||
if (ehci->amd_pll_fix == 1)
|
if (ehci->amd_pll_fix == 1)
|
||||||
usb_amd_quirk_pll_enable();
|
usb_amd_quirk_pll_enable();
|
||||||
|
@ -1783,28 +1744,20 @@ itd_complete (
|
||||||
dev->devpath, stream->bEndpointAddress & 0x0f,
|
dev->devpath, stream->bEndpointAddress & 0x0f,
|
||||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
||||||
}
|
}
|
||||||
iso_stream_put (ehci, stream);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
itd->urb = NULL;
|
itd->urb = NULL;
|
||||||
if (ehci->clock_frame != itd->frame || itd->index[7] != -1) {
|
|
||||||
/* OK to recycle this ITD now. */
|
/* Add to the end of the free list for later reuse */
|
||||||
itd->stream = NULL;
|
list_move_tail(&itd->itd_list, &stream->free_list);
|
||||||
list_move(&itd->itd_list, &stream->free_list);
|
|
||||||
iso_stream_put(ehci, stream);
|
/* Recycle the iTDs when the pipeline is empty (ep no longer in use) */
|
||||||
} else {
|
if (list_empty(&stream->td_list)) {
|
||||||
/* HW might remember this ITD, so we can't recycle it yet.
|
list_splice_tail_init(&stream->free_list,
|
||||||
* Move it to a safe place until a new frame starts.
|
&ehci->cached_itd_list);
|
||||||
*/
|
start_free_itds(ehci);
|
||||||
list_move(&itd->itd_list, &ehci->cached_itd_list);
|
|
||||||
if (stream->refcount == 2) {
|
|
||||||
/* If iso_stream_put() were called here, stream
|
|
||||||
* would be freed. Instead, just prevent reuse.
|
|
||||||
*/
|
|
||||||
stream->ep->hcpriv = NULL;
|
|
||||||
stream->ep = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1861,12 +1814,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
|
||||||
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
||||||
else
|
else
|
||||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||||
done_not_linked:
|
done_not_linked:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
|
done:
|
||||||
done:
|
|
||||||
if (unlikely (status < 0))
|
|
||||||
iso_stream_put (ehci, stream);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1955,17 +1905,19 @@ sitd_urb_transaction (
|
||||||
* means we never need two sitds for full speed packets.
|
* means we never need two sitds for full speed packets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* free_list.next might be cache-hot ... but maybe
|
/*
|
||||||
* the HC caches it too. avoid that issue for now.
|
* Use siTDs from the free list, but not siTDs that may
|
||||||
|
* still be in use by the hardware.
|
||||||
*/
|
*/
|
||||||
|
if (likely(!list_empty(&stream->free_list))) {
|
||||||
/* prefer previously-allocated sitds */
|
sitd = list_first_entry(&stream->free_list,
|
||||||
if (!list_empty(&stream->free_list)) {
|
|
||||||
sitd = list_entry (stream->free_list.prev,
|
|
||||||
struct ehci_sitd, sitd_list);
|
struct ehci_sitd, sitd_list);
|
||||||
|
if (sitd->frame == ehci->now_frame)
|
||||||
|
goto alloc_sitd;
|
||||||
list_del (&sitd->sitd_list);
|
list_del (&sitd->sitd_list);
|
||||||
sitd_dma = sitd->sitd_dma;
|
sitd_dma = sitd->sitd_dma;
|
||||||
} else {
|
} else {
|
||||||
|
alloc_sitd:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
|
sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
|
||||||
&sitd_dma);
|
&sitd_dma);
|
||||||
|
@ -2034,8 +1986,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fit urb's sitds into the selected schedule slot; activate as needed */
|
/* fit urb's sitds into the selected schedule slot; activate as needed */
|
||||||
static int
|
static void sitd_link_urb(
|
||||||
sitd_link_urb (
|
|
||||||
struct ehci_hcd *ehci,
|
struct ehci_hcd *ehci,
|
||||||
struct urb *urb,
|
struct urb *urb,
|
||||||
unsigned mod,
|
unsigned mod,
|
||||||
|
@ -2081,7 +2032,7 @@ sitd_link_urb (
|
||||||
sitd = list_entry (sched->td_list.next,
|
sitd = list_entry (sched->td_list.next,
|
||||||
struct ehci_sitd, sitd_list);
|
struct ehci_sitd, sitd_list);
|
||||||
list_move_tail (&sitd->sitd_list, &stream->td_list);
|
list_move_tail (&sitd->sitd_list, &stream->td_list);
|
||||||
sitd->stream = iso_stream_get (stream);
|
sitd->stream = stream;
|
||||||
sitd->urb = urb;
|
sitd->urb = urb;
|
||||||
|
|
||||||
sitd_patch(ehci, stream, sitd, sched, packet);
|
sitd_patch(ehci, stream, sitd, sched, packet);
|
||||||
|
@ -2096,8 +2047,8 @@ sitd_link_urb (
|
||||||
iso_sched_free (stream, sched);
|
iso_sched_free (stream, sched);
|
||||||
urb->hcpriv = NULL;
|
urb->hcpriv = NULL;
|
||||||
|
|
||||||
timer_action (ehci, TIMER_IO_WATCHDOG);
|
++ehci->isoc_count;
|
||||||
return enable_periodic(ehci);
|
enable_periodic(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -2115,18 +2066,15 @@ sitd_link_urb (
|
||||||
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
||||||
* corrupts things if you reuse completed descriptors very quickly...
|
* corrupts things if you reuse completed descriptors very quickly...
|
||||||
*/
|
*/
|
||||||
static unsigned
|
static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||||
sitd_complete (
|
{
|
||||||
struct ehci_hcd *ehci,
|
|
||||||
struct ehci_sitd *sitd
|
|
||||||
) {
|
|
||||||
struct urb *urb = sitd->urb;
|
struct urb *urb = sitd->urb;
|
||||||
struct usb_iso_packet_descriptor *desc;
|
struct usb_iso_packet_descriptor *desc;
|
||||||
u32 t;
|
u32 t;
|
||||||
int urb_index = -1;
|
int urb_index = -1;
|
||||||
struct ehci_iso_stream *stream = sitd->stream;
|
struct ehci_iso_stream *stream = sitd->stream;
|
||||||
struct usb_device *dev;
|
struct usb_device *dev;
|
||||||
unsigned retval = false;
|
bool retval = false;
|
||||||
|
|
||||||
urb_index = sitd->index;
|
urb_index = sitd->index;
|
||||||
desc = &urb->iso_frame_desc [urb_index];
|
desc = &urb->iso_frame_desc [urb_index];
|
||||||
|
@ -2163,9 +2111,11 @@ sitd_complete (
|
||||||
ehci_urb_done(ehci, urb, 0);
|
ehci_urb_done(ehci, urb, 0);
|
||||||
retval = true;
|
retval = true;
|
||||||
urb = NULL;
|
urb = NULL;
|
||||||
(void) disable_periodic(ehci);
|
|
||||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
|
||||||
|
|
||||||
|
--ehci->isoc_count;
|
||||||
|
disable_periodic(ehci);
|
||||||
|
|
||||||
|
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||||
if (ehci->amd_pll_fix == 1)
|
if (ehci->amd_pll_fix == 1)
|
||||||
usb_amd_quirk_pll_enable();
|
usb_amd_quirk_pll_enable();
|
||||||
|
@ -2179,28 +2129,20 @@ sitd_complete (
|
||||||
dev->devpath, stream->bEndpointAddress & 0x0f,
|
dev->devpath, stream->bEndpointAddress & 0x0f,
|
||||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
||||||
}
|
}
|
||||||
iso_stream_put (ehci, stream);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
sitd->urb = NULL;
|
sitd->urb = NULL;
|
||||||
if (ehci->clock_frame != sitd->frame) {
|
|
||||||
/* OK to recycle this SITD now. */
|
/* Add to the end of the free list for later reuse */
|
||||||
sitd->stream = NULL;
|
list_move_tail(&sitd->sitd_list, &stream->free_list);
|
||||||
list_move(&sitd->sitd_list, &stream->free_list);
|
|
||||||
iso_stream_put(ehci, stream);
|
/* Recycle the siTDs when the pipeline is empty (ep no longer in use) */
|
||||||
} else {
|
if (list_empty(&stream->td_list)) {
|
||||||
/* HW might remember this SITD, so we can't recycle it yet.
|
list_splice_tail_init(&stream->free_list,
|
||||||
* Move it to a safe place until a new frame starts.
|
&ehci->cached_sitd_list);
|
||||||
*/
|
start_free_itds(ehci);
|
||||||
list_move(&sitd->sitd_list, &ehci->cached_sitd_list);
|
|
||||||
if (stream->refcount == 2) {
|
|
||||||
/* If iso_stream_put() were called here, stream
|
|
||||||
* would be freed. Instead, just prevent reuse.
|
|
||||||
*/
|
|
||||||
stream->ep->hcpriv = NULL;
|
|
||||||
stream->ep = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,74 +2196,39 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
|
||||||
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
|
||||||
else
|
else
|
||||||
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
|
||||||
done_not_linked:
|
done_not_linked:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
|
done:
|
||||||
done:
|
|
||||||
if (status < 0)
|
|
||||||
iso_stream_put (ehci, stream);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void free_cached_lists(struct ehci_hcd *ehci)
|
static void scan_isoc(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
struct ehci_itd *itd, *n;
|
unsigned uf, now_frame, frame;
|
||||||
struct ehci_sitd *sitd, *sn;
|
unsigned fmask = ehci->periodic_size - 1;
|
||||||
|
bool modified, live;
|
||||||
list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
|
|
||||||
struct ehci_iso_stream *stream = itd->stream;
|
|
||||||
itd->stream = NULL;
|
|
||||||
list_move(&itd->itd_list, &stream->free_list);
|
|
||||||
iso_stream_put(ehci, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
|
|
||||||
struct ehci_iso_stream *stream = sitd->stream;
|
|
||||||
sitd->stream = NULL;
|
|
||||||
list_move(&sitd->sitd_list, &stream->free_list);
|
|
||||||
iso_stream_put(ehci, stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
scan_periodic (struct ehci_hcd *ehci)
|
|
||||||
{
|
|
||||||
unsigned now_uframe, frame, clock, clock_frame, mod;
|
|
||||||
unsigned modified;
|
|
||||||
|
|
||||||
mod = ehci->periodic_size << 3;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When running, scan from last scan point up to "now"
|
* When running, scan from last scan point up to "now"
|
||||||
* else clean up by scanning everything that's left.
|
* else clean up by scanning everything that's left.
|
||||||
* Touches as few pages as possible: cache-friendly.
|
* Touches as few pages as possible: cache-friendly.
|
||||||
*/
|
*/
|
||||||
now_uframe = ehci->next_uframe;
|
if (ehci->rh_state >= EHCI_RH_RUNNING) {
|
||||||
if (ehci->rh_state == EHCI_RH_RUNNING) {
|
uf = ehci_read_frame_index(ehci);
|
||||||
clock = ehci_read_frame_index(ehci);
|
now_frame = (uf >> 3) & fmask;
|
||||||
clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
|
live = true;
|
||||||
} else {
|
} else {
|
||||||
clock = now_uframe + mod - 1;
|
now_frame = (ehci->next_frame - 1) & fmask;
|
||||||
clock_frame = -1;
|
live = false;
|
||||||
}
|
}
|
||||||
if (ehci->clock_frame != clock_frame) {
|
ehci->now_frame = now_frame;
|
||||||
free_cached_lists(ehci);
|
|
||||||
ehci->clock_frame = clock_frame;
|
|
||||||
}
|
|
||||||
clock &= mod - 1;
|
|
||||||
clock_frame = clock >> 3;
|
|
||||||
++ehci->periodic_stamp;
|
|
||||||
|
|
||||||
|
frame = ehci->next_frame;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
union ehci_shadow q, *q_p;
|
union ehci_shadow q, *q_p;
|
||||||
__hc32 type, *hw_p;
|
__hc32 type, *hw_p;
|
||||||
unsigned incomplete = false;
|
|
||||||
|
|
||||||
frame = now_uframe >> 3;
|
|
||||||
|
|
||||||
restart:
|
restart:
|
||||||
/* scan each element in frame's queue for completions */
|
/* scan each element in frame's queue for completions */
|
||||||
|
@ -2329,48 +2236,17 @@ restart:
|
||||||
hw_p = &ehci->periodic [frame];
|
hw_p = &ehci->periodic [frame];
|
||||||
q.ptr = q_p->ptr;
|
q.ptr = q_p->ptr;
|
||||||
type = Q_NEXT_TYPE(ehci, *hw_p);
|
type = Q_NEXT_TYPE(ehci, *hw_p);
|
||||||
modified = 0;
|
modified = false;
|
||||||
|
|
||||||
while (q.ptr != NULL) {
|
while (q.ptr != NULL) {
|
||||||
unsigned uf;
|
|
||||||
union ehci_shadow temp;
|
|
||||||
int live;
|
|
||||||
|
|
||||||
live = (ehci->rh_state == EHCI_RH_RUNNING);
|
|
||||||
switch (hc32_to_cpu(ehci, type)) {
|
switch (hc32_to_cpu(ehci, type)) {
|
||||||
case Q_TYPE_QH:
|
|
||||||
/* handle any completions */
|
|
||||||
temp.qh = qh_get (q.qh);
|
|
||||||
type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
|
|
||||||
q = q.qh->qh_next;
|
|
||||||
if (temp.qh->stamp != ehci->periodic_stamp) {
|
|
||||||
modified = qh_completions(ehci, temp.qh);
|
|
||||||
if (!modified)
|
|
||||||
temp.qh->stamp = ehci->periodic_stamp;
|
|
||||||
if (unlikely(list_empty(&temp.qh->qtd_list) ||
|
|
||||||
temp.qh->needs_rescan))
|
|
||||||
intr_deschedule(ehci, temp.qh);
|
|
||||||
}
|
|
||||||
qh_put (temp.qh);
|
|
||||||
break;
|
|
||||||
case Q_TYPE_FSTN:
|
|
||||||
/* for "save place" FSTNs, look at QH entries
|
|
||||||
* in the previous frame for completions.
|
|
||||||
*/
|
|
||||||
if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
|
|
||||||
ehci_dbg(ehci,
|
|
||||||
"ignoring completions from FSTNs\n");
|
|
||||||
}
|
|
||||||
type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
|
|
||||||
q = q.fstn->fstn_next;
|
|
||||||
break;
|
|
||||||
case Q_TYPE_ITD:
|
case Q_TYPE_ITD:
|
||||||
/* If this ITD is still active, leave it for
|
/* If this ITD is still active, leave it for
|
||||||
* later processing ... check the next entry.
|
* later processing ... check the next entry.
|
||||||
* No need to check for activity unless the
|
* No need to check for activity unless the
|
||||||
* frame is current.
|
* frame is current.
|
||||||
*/
|
*/
|
||||||
if (frame == clock_frame && live) {
|
if (frame == now_frame && live) {
|
||||||
rmb();
|
rmb();
|
||||||
for (uf = 0; uf < 8; uf++) {
|
for (uf = 0; uf < 8; uf++) {
|
||||||
if (q.itd->hw_transaction[uf] &
|
if (q.itd->hw_transaction[uf] &
|
||||||
|
@ -2378,7 +2254,6 @@ restart:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (uf < 8) {
|
if (uf < 8) {
|
||||||
incomplete = true;
|
|
||||||
q_p = &q.itd->itd_next;
|
q_p = &q.itd->itd_next;
|
||||||
hw_p = &q.itd->hw_next;
|
hw_p = &q.itd->hw_next;
|
||||||
type = Q_NEXT_TYPE(ehci,
|
type = Q_NEXT_TYPE(ehci,
|
||||||
|
@ -2410,14 +2285,12 @@ restart:
|
||||||
* No need to check for activity unless the
|
* No need to check for activity unless the
|
||||||
* frame is current.
|
* frame is current.
|
||||||
*/
|
*/
|
||||||
if (((frame == clock_frame) ||
|
if (((frame == now_frame) ||
|
||||||
(((frame + 1) & (ehci->periodic_size - 1))
|
(((frame + 1) & fmask) == now_frame))
|
||||||
== clock_frame))
|
|
||||||
&& live
|
&& live
|
||||||
&& (q.sitd->hw_results &
|
&& (q.sitd->hw_results &
|
||||||
SITD_ACTIVE(ehci))) {
|
SITD_ACTIVE(ehci))) {
|
||||||
|
|
||||||
incomplete = true;
|
|
||||||
q_p = &q.sitd->sitd_next;
|
q_p = &q.sitd->sitd_next;
|
||||||
hw_p = &q.sitd->hw_next;
|
hw_p = &q.sitd->hw_next;
|
||||||
type = Q_NEXT_TYPE(ehci,
|
type = Q_NEXT_TYPE(ehci,
|
||||||
|
@ -2445,58 +2318,23 @@ restart:
|
||||||
ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
|
ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
|
||||||
type, frame, q.ptr);
|
type, frame, q.ptr);
|
||||||
// BUG ();
|
// BUG ();
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case Q_TYPE_QH:
|
||||||
|
case Q_TYPE_FSTN:
|
||||||
|
/* End of the iTDs and siTDs */
|
||||||
q.ptr = NULL;
|
q.ptr = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* assume completion callbacks modify the queue */
|
/* assume completion callbacks modify the queue */
|
||||||
if (unlikely (modified)) {
|
if (unlikely(modified && ehci->isoc_count > 0))
|
||||||
if (likely(ehci->periodic_sched > 0))
|
goto restart;
|
||||||
goto restart;
|
|
||||||
/* short-circuit this scan */
|
|
||||||
now_uframe = clock;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we can tell we caught up to the hardware, stop now.
|
/* Stop when we have reached the current frame */
|
||||||
* We can't advance our scan without collecting the ISO
|
if (frame == now_frame)
|
||||||
* transfers that are still pending in this frame.
|
|
||||||
*/
|
|
||||||
if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
|
|
||||||
ehci->next_uframe = now_uframe;
|
|
||||||
break;
|
break;
|
||||||
}
|
frame = (frame + 1) & fmask;
|
||||||
|
|
||||||
// FIXME: this assumes we won't get lapped when
|
|
||||||
// latencies climb; that should be rare, but...
|
|
||||||
// detect it, and just go all the way around.
|
|
||||||
// FLR might help detect this case, so long as latencies
|
|
||||||
// don't exceed periodic_size msec (default 1.024 sec).
|
|
||||||
|
|
||||||
// FIXME: likewise assumes HC doesn't halt mid-scan
|
|
||||||
|
|
||||||
if (now_uframe == clock) {
|
|
||||||
unsigned now;
|
|
||||||
|
|
||||||
if (ehci->rh_state != EHCI_RH_RUNNING
|
|
||||||
|| ehci->periodic_sched == 0)
|
|
||||||
break;
|
|
||||||
ehci->next_uframe = now_uframe;
|
|
||||||
now = ehci_read_frame_index(ehci) & (mod - 1);
|
|
||||||
if (now_uframe == now)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* rescan the rest of this frame, then ... */
|
|
||||||
clock = now;
|
|
||||||
clock_frame = clock >> 3;
|
|
||||||
if (ehci->clock_frame != clock_frame) {
|
|
||||||
free_cached_lists(ehci);
|
|
||||||
ehci->clock_frame = clock_frame;
|
|
||||||
++ehci->periodic_stamp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
now_uframe++;
|
|
||||||
now_uframe &= mod - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ehci->next_frame = now_frame;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,84 +160,16 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
|
||||||
static int ehci_hcd_sead3_drv_suspend(struct device *dev)
|
static int ehci_hcd_sead3_drv_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
unsigned long flags;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
return ehci_suspend(hcd, do_wakeup);
|
||||||
msleep(20);
|
|
||||||
|
|
||||||
/* Root hub was already suspended. Disable irq emission and
|
|
||||||
* mark HW unaccessible. The PM and USB cores make sure that
|
|
||||||
* the root hub is either suspended or stopped.
|
|
||||||
*/
|
|
||||||
ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
|
||||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
|
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
||||||
|
|
||||||
/* could save FLADJ in case of Vaux power loss
|
|
||||||
* ... we'd only use it to handle clock skew
|
|
||||||
*/
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_hcd_sead3_drv_resume(struct device *dev)
|
static int ehci_hcd_sead3_drv_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
|
|
||||||
/* maybe restore FLADJ. */
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
/* Mark hardware accessible again as we are out of D3 state by now */
|
|
||||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
|
|
||||||
/* If CF is still set, we maintained PCI Vaux power.
|
|
||||||
* Just undo the effect of ehci_pci_suspend().
|
|
||||||
*/
|
|
||||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
|
||||||
int mask = INTR_MASK;
|
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(ehci);
|
|
||||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
|
||||||
mask &= ~STS_PCD;
|
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ehci_dbg(ehci, "lost power, restarting\n");
|
|
||||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
|
||||||
|
|
||||||
/* Else reset, to cope with power loss or flush-to-storage
|
|
||||||
* style "resume" having let BIOS kick in during reboot.
|
|
||||||
*/
|
|
||||||
(void) ehci_halt(ehci);
|
|
||||||
(void) ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
|
||||||
if (ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
ehci_work(ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered */
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
|
|
||||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
|
||||||
|
|
||||||
|
ehci_resume(hcd, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,25 +24,11 @@ static int ehci_sh_reset(struct usb_hcd *hcd)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
|
|
||||||
&ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
ret = ehci_setup(hcd);
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ret = ehci_halt(ehci);
|
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = ehci_init(hcd);
|
|
||||||
if (unlikely(ret))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
ehci_port_power(ehci, 0);
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -41,19 +41,11 @@ static int ehci_spear_setup(struct usb_hcd *hcd)
|
||||||
|
|
||||||
/* registers start at offset 0x0 */
|
/* registers start at offset 0x0 */
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci,
|
|
||||||
&ehci->caps->hc_capbase));
|
retval = ehci_setup(hcd);
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
ehci_port_power(ehci, 0);
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -97,71 +89,16 @@ static const struct hc_driver ehci_spear_hc_driver = {
|
||||||
static int ehci_spear_drv_suspend(struct device *dev)
|
static int ehci_spear_drv_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
bool do_wakeup = device_may_wakeup(dev);
|
||||||
unsigned long flags;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
return ehci_suspend(hcd, do_wakeup);
|
||||||
msleep(10);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Root hub was already suspended. Disable irq emission and mark HW
|
|
||||||
* unaccessible. The PM and USB cores make sure that the root hub is
|
|
||||||
* either suspended or stopped.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
|
|
||||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_spear_drv_resume(struct device *dev)
|
static int ehci_spear_drv_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
|
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
ehci_resume(hcd, false);
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
|
||||||
int mask = INTR_MASK;
|
|
||||||
|
|
||||||
ehci_prepare_ports_for_controller_resume(ehci);
|
|
||||||
|
|
||||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
|
||||||
mask &= ~STS_PCD;
|
|
||||||
|
|
||||||
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
|
|
||||||
ehci_readl(ehci, &ehci->regs->intr_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Else reset, to cope with power loss or flush-to-storage style
|
|
||||||
* "resume" having let BIOS kick in during reboot.
|
|
||||||
*/
|
|
||||||
ehci_halt(ehci);
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
/* emptying the schedule aborts any urbs */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
|
||||||
if (ehci->reclaim)
|
|
||||||
end_unlink_async(ehci);
|
|
||||||
|
|
||||||
ehci_work(ehci);
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
|
|
||||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
|
||||||
|
|
||||||
/* here we "know" root ports should always stay powered */
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/platform_data/tegra_usb.h>
|
#include <linux/platform_data/tegra_usb.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -280,30 +281,14 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
|
||||||
|
|
||||||
/* EHCI registers start at offset 0x100 */
|
/* EHCI registers start at offset 0x100 */
|
||||||
ehci->caps = hcd->regs + 0x100;
|
ehci->caps = hcd->regs + 0x100;
|
||||||
ehci->regs = hcd->regs + 0x100 +
|
|
||||||
HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
/* switch to host mode */
|
/* switch to host mode */
|
||||||
hcd->has_tt = 1;
|
hcd->has_tt = 1;
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
retval = ehci_setup(hcd);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
ehci_port_power(ehci, 1);
|
ehci_port_power(ehci, 1);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -460,12 +445,11 @@ static int controller_suspend(struct device *dev)
|
||||||
if (time_before(jiffies, ehci->next_statechange))
|
if (time_before(jiffies, ehci->next_statechange))
|
||||||
msleep(10);
|
msleep(10);
|
||||||
|
|
||||||
spin_lock_irqsave(&ehci->lock, flags);
|
|
||||||
|
|
||||||
tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
|
|
||||||
ehci_halt(ehci);
|
ehci_halt(ehci);
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
|
||||||
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
|
|
||||||
tegra_ehci_power_down(hcd);
|
tegra_ehci_power_down(hcd);
|
||||||
|
@ -749,8 +733,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
if (pdata->operating_mode == TEGRA_USB_OTG) {
|
if (pdata->operating_mode == TEGRA_USB_OTG) {
|
||||||
tegra->transceiver = usb_get_transceiver();
|
tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||||
if (tegra->transceiver)
|
if (!IS_ERR_OR_NULL(tegra->transceiver))
|
||||||
otg_set_host(tegra->transceiver->otg, &hcd->self);
|
otg_set_host(tegra->transceiver->otg, &hcd->self);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -773,9 +757,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
if (tegra->transceiver) {
|
if (!IS_ERR_OR_NULL(tegra->transceiver)) {
|
||||||
otg_set_host(tegra->transceiver->otg, NULL);
|
otg_set_host(tegra->transceiver->otg, NULL);
|
||||||
usb_put_transceiver(tegra->transceiver);
|
usb_put_phy(tegra->transceiver);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
tegra_usb_phy_close(tegra->phy);
|
tegra_usb_phy_close(tegra->phy);
|
||||||
|
@ -808,9 +792,9 @@ static int tegra_ehci_remove(struct platform_device *pdev)
|
||||||
pm_runtime_put_noidle(&pdev->dev);
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
#ifdef CONFIG_USB_OTG_UTILS
|
#ifdef CONFIG_USB_OTG_UTILS
|
||||||
if (tegra->transceiver) {
|
if (!IS_ERR_OR_NULL(tegra->transceiver)) {
|
||||||
otg_set_host(tegra->transceiver->otg, NULL);
|
otg_set_host(tegra->transceiver->otg, NULL);
|
||||||
usb_put_transceiver(tegra->transceiver);
|
usb_put_phy(tegra->transceiver);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,401 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 by Alan Stern
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file is part of ehci-hcd.c */
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Set a bit in the USBCMD register */
|
||||||
|
static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit)
|
||||||
|
{
|
||||||
|
ehci->command |= bit;
|
||||||
|
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||||
|
|
||||||
|
/* unblock posted write */
|
||||||
|
ehci_readl(ehci, &ehci->regs->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear a bit in the USBCMD register */
|
||||||
|
static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
|
||||||
|
{
|
||||||
|
ehci->command &= ~bit;
|
||||||
|
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||||
|
|
||||||
|
/* unblock posted write */
|
||||||
|
ehci_readl(ehci, &ehci->regs->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EHCI timer support... Now using hrtimers.
|
||||||
|
*
|
||||||
|
* Lots of different events are triggered from ehci->hrtimer. Whenever
|
||||||
|
* the timer routine runs, it checks each possible event; events that are
|
||||||
|
* currently enabled and whose expiration time has passed get handled.
|
||||||
|
* The set of enabled events is stored as a collection of bitflags in
|
||||||
|
* ehci->enabled_hrtimer_events, and they are numbered in order of
|
||||||
|
* increasing delay values (ranging between 1 ms and 100 ms).
|
||||||
|
*
|
||||||
|
* Rather than implementing a sorted list or tree of all pending events,
|
||||||
|
* we keep track only of the lowest-numbered pending event, in
|
||||||
|
* ehci->next_hrtimer_event. Whenever ehci->hrtimer gets restarted, its
|
||||||
|
* expiration time is set to the timeout value for this event.
|
||||||
|
*
|
||||||
|
* As a result, events might not get handled right away; the actual delay
|
||||||
|
* could be anywhere up to twice the requested delay. This doesn't
|
||||||
|
* matter, because none of the events are especially time-critical. The
|
||||||
|
* ones that matter most all have a delay of 1 ms, so they will be
|
||||||
|
* handled after 2 ms at most, which is okay. In addition to this, we
|
||||||
|
* allow for an expiration range of 1 ms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delay lengths for the hrtimer event types.
|
||||||
|
* Keep this list sorted by delay length, in the same order as
|
||||||
|
* the event types indexed by enum ehci_hrtimer_event in ehci.h.
|
||||||
|
*/
|
||||||
|
static unsigned event_delays_ns[] = {
|
||||||
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
|
||||||
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
|
||||||
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
|
||||||
|
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||||
|
2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
|
||||||
|
6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
|
||||||
|
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
|
||||||
|
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||||
|
15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
||||||
|
100 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IO_WATCHDOG */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enable a pending hrtimer event */
|
||||||
|
static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
|
||||||
|
bool resched)
|
||||||
|
{
|
||||||
|
ktime_t *timeout = &ehci->hr_timeouts[event];
|
||||||
|
|
||||||
|
if (resched)
|
||||||
|
*timeout = ktime_add(ktime_get(),
|
||||||
|
ktime_set(0, event_delays_ns[event]));
|
||||||
|
ehci->enabled_hrtimer_events |= (1 << event);
|
||||||
|
|
||||||
|
/* Track only the lowest-numbered pending event */
|
||||||
|
if (event < ehci->next_hrtimer_event) {
|
||||||
|
ehci->next_hrtimer_event = event;
|
||||||
|
hrtimer_start_range_ns(&ehci->hrtimer, *timeout,
|
||||||
|
NSEC_PER_MSEC, HRTIMER_MODE_ABS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
|
||||||
|
static void ehci_poll_ASS(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
unsigned actual, want;
|
||||||
|
|
||||||
|
/* Don't enable anything if the controller isn't running (e.g., died) */
|
||||||
|
if (ehci->rh_state != EHCI_RH_RUNNING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
want = (ehci->command & CMD_ASE) ? STS_ASS : 0;
|
||||||
|
actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS;
|
||||||
|
|
||||||
|
if (want != actual) {
|
||||||
|
|
||||||
|
/* Poll again later, but give up after about 20 ms */
|
||||||
|
if (ehci->ASS_poll_count++ < 20) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n");
|
||||||
|
}
|
||||||
|
ehci->ASS_poll_count = 0;
|
||||||
|
|
||||||
|
/* The status is up-to-date; restart or stop the schedule as needed */
|
||||||
|
if (want == 0) { /* Stopped */
|
||||||
|
if (ehci->async_count > 0)
|
||||||
|
ehci_set_command_bit(ehci, CMD_ASE);
|
||||||
|
|
||||||
|
} else { /* Running */
|
||||||
|
if (ehci->async_count == 0) {
|
||||||
|
|
||||||
|
/* Turn off the schedule after a while */
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn off the async schedule after a brief delay */
|
||||||
|
static void ehci_disable_ASE(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
ehci_clear_command_bit(ehci, CMD_ASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
|
||||||
|
static void ehci_poll_PSS(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
unsigned actual, want;
|
||||||
|
|
||||||
|
/* Don't do anything if the controller isn't running (e.g., died) */
|
||||||
|
if (ehci->rh_state != EHCI_RH_RUNNING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
want = (ehci->command & CMD_PSE) ? STS_PSS : 0;
|
||||||
|
actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS;
|
||||||
|
|
||||||
|
if (want != actual) {
|
||||||
|
|
||||||
|
/* Poll again later, but give up after about 20 ms */
|
||||||
|
if (ehci->PSS_poll_count++ < 20) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n");
|
||||||
|
}
|
||||||
|
ehci->PSS_poll_count = 0;
|
||||||
|
|
||||||
|
/* The status is up-to-date; restart or stop the schedule as needed */
|
||||||
|
if (want == 0) { /* Stopped */
|
||||||
|
if (ehci->periodic_count > 0)
|
||||||
|
ehci_set_command_bit(ehci, CMD_PSE);
|
||||||
|
|
||||||
|
} else { /* Running */
|
||||||
|
if (ehci->periodic_count == 0) {
|
||||||
|
|
||||||
|
/* Turn off the schedule after a while */
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn off the periodic schedule after a brief delay */
|
||||||
|
static void ehci_disable_PSE(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
ehci_clear_command_bit(ehci, CMD_PSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Poll the STS_HALT status bit; see when a dead controller stops */
|
||||||
|
static void ehci_handle_controller_death(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {
|
||||||
|
|
||||||
|
/* Give up after a few milliseconds */
|
||||||
|
if (ehci->died_poll_count++ < 5) {
|
||||||
|
/* Try again later */
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up the mess */
|
||||||
|
ehci->rh_state = EHCI_RH_HALTED;
|
||||||
|
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||||
|
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||||
|
ehci_work(ehci);
|
||||||
|
end_unlink_async(ehci);
|
||||||
|
|
||||||
|
/* Not in process context, so don't try to reset the controller */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle unlinked interrupt QHs once they are gone from the hardware */
|
||||||
|
static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process all the QHs on the intr_unlink list that were added
|
||||||
|
* before the current unlink cycle began. The list is in
|
||||||
|
* temporal order, so stop when we reach the first entry in the
|
||||||
|
* current cycle. But if the root hub isn't running then
|
||||||
|
* process all the QHs on the list.
|
||||||
|
*/
|
||||||
|
ehci->intr_unlinking = true;
|
||||||
|
while (ehci->intr_unlink) {
|
||||||
|
struct ehci_qh *qh = ehci->intr_unlink;
|
||||||
|
|
||||||
|
if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle)
|
||||||
|
break;
|
||||||
|
ehci->intr_unlink = qh->unlink_next;
|
||||||
|
qh->unlink_next = NULL;
|
||||||
|
end_unlink_intr(ehci, qh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle remaining entries later */
|
||||||
|
if (ehci->intr_unlink) {
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true);
|
||||||
|
++ehci->intr_unlink_cycle;
|
||||||
|
}
|
||||||
|
ehci->intr_unlinking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Start another free-iTDs/siTDs cycle */
|
||||||
|
static void start_free_itds(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) {
|
||||||
|
ehci->last_itd_to_free = list_entry(
|
||||||
|
ehci->cached_itd_list.prev,
|
||||||
|
struct ehci_itd, itd_list);
|
||||||
|
ehci->last_sitd_to_free = list_entry(
|
||||||
|
ehci->cached_sitd_list.prev,
|
||||||
|
struct ehci_sitd, sitd_list);
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for controller to stop using old iTDs and siTDs */
|
||||||
|
static void end_free_itds(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
struct ehci_itd *itd, *n;
|
||||||
|
struct ehci_sitd *sitd, *sn;
|
||||||
|
|
||||||
|
if (ehci->rh_state < EHCI_RH_RUNNING) {
|
||||||
|
ehci->last_itd_to_free = NULL;
|
||||||
|
ehci->last_sitd_to_free = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
|
||||||
|
list_del(&itd->itd_list);
|
||||||
|
dma_pool_free(ehci->itd_pool, itd, itd->itd_dma);
|
||||||
|
if (itd == ehci->last_itd_to_free)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
|
||||||
|
list_del(&sitd->sitd_list);
|
||||||
|
dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma);
|
||||||
|
if (sitd == ehci->last_sitd_to_free)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list_empty(&ehci->cached_itd_list) ||
|
||||||
|
!list_empty(&ehci->cached_sitd_list))
|
||||||
|
start_free_itds(ehci);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle lost (or very late) IAA interrupts */
|
||||||
|
static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
if (ehci->rh_state != EHCI_RH_RUNNING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lost IAA irqs wedge things badly; seen first with a vt8235.
|
||||||
|
* So we need this watchdog, but must protect it against both
|
||||||
|
* (a) SMP races against real IAA firing and retriggering, and
|
||||||
|
* (b) clean HC shutdown, when IAA watchdog was pending.
|
||||||
|
*/
|
||||||
|
if (ehci->async_iaa) {
|
||||||
|
u32 cmd, status;
|
||||||
|
|
||||||
|
/* If we get here, IAA is *REALLY* late. It's barely
|
||||||
|
* conceivable that the system is so busy that CMD_IAAD
|
||||||
|
* is still legitimately set, so let's be sure it's
|
||||||
|
* clear before we read STS_IAA. (The HC should clear
|
||||||
|
* CMD_IAAD when it sets STS_IAA.)
|
||||||
|
*/
|
||||||
|
cmd = ehci_readl(ehci, &ehci->regs->command);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If IAA is set here it either legitimately triggered
|
||||||
|
* after the watchdog timer expired (_way_ late, so we'll
|
||||||
|
* still count it as lost) ... or a silicon erratum:
|
||||||
|
* - VIA seems to set IAA without triggering the IRQ;
|
||||||
|
* - IAAD potentially cleared without setting IAA.
|
||||||
|
*/
|
||||||
|
status = ehci_readl(ehci, &ehci->regs->status);
|
||||||
|
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
|
||||||
|
COUNT(ehci->stats.lost_iaa);
|
||||||
|
ehci_writel(ehci, STS_IAA, &ehci->regs->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
|
||||||
|
status, cmd);
|
||||||
|
end_unlink_async(ehci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Enable the I/O watchdog, if appropriate */
|
||||||
|
static void turn_on_io_watchdog(struct ehci_hcd *ehci)
|
||||||
|
{
|
||||||
|
/* Not needed if the controller isn't running or it's already enabled */
|
||||||
|
if (ehci->rh_state != EHCI_RH_RUNNING ||
|
||||||
|
(ehci->enabled_hrtimer_events &
|
||||||
|
BIT(EHCI_HRTIMER_IO_WATCHDOG)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Isochronous transfers always need the watchdog.
|
||||||
|
* For other sorts we use it only if the flag is set.
|
||||||
|
*/
|
||||||
|
if (ehci->isoc_count > 0 || (ehci->need_io_watchdog &&
|
||||||
|
ehci->async_count + ehci->intr_count > 0))
|
||||||
|
ehci_enable_event(ehci, EHCI_HRTIMER_IO_WATCHDOG, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler functions for the hrtimer event types.
|
||||||
|
* Keep this array in the same order as the event types indexed by
|
||||||
|
* enum ehci_hrtimer_event in ehci.h.
|
||||||
|
*/
|
||||||
|
static void (*event_handlers[])(struct ehci_hcd *) = {
|
||||||
|
ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
|
||||||
|
ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
|
||||||
|
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
|
||||||
|
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||||
|
end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
|
||||||
|
unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
|
||||||
|
ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
|
||||||
|
ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||||
|
ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
||||||
|
ehci_work, /* EHCI_HRTIMER_IO_WATCHDOG */
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer);
|
||||||
|
ktime_t now;
|
||||||
|
unsigned long events;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned e;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
|
||||||
|
events = ehci->enabled_hrtimer_events;
|
||||||
|
ehci->enabled_hrtimer_events = 0;
|
||||||
|
ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check each pending event. If its time has expired, handle
|
||||||
|
* the event; otherwise re-enable it.
|
||||||
|
*/
|
||||||
|
now = ktime_get();
|
||||||
|
for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) {
|
||||||
|
if (now.tv64 >= ehci->hr_timeouts[e].tv64)
|
||||||
|
event_handlers[e](ehci);
|
||||||
|
else
|
||||||
|
ehci_enable_event(ehci, e, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
|
return HRTIMER_NORESTART;
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ static const struct hc_driver vt8500_ehci_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_init,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
@ -121,18 +121,6 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ehci = hcd_to_ehci(hcd);
|
ehci = hcd_to_ehci(hcd);
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
ehci_port_power(ehci, 1);
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
||||||
IRQF_SHARED);
|
IRQF_SHARED);
|
||||||
|
|
|
@ -71,21 +71,14 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver,
|
||||||
val |= ENPHY;
|
val |= ENPHY;
|
||||||
__raw_writel(val, ehci->regs+PHY1_CTR);
|
__raw_writel(val, ehci->regs+PHY1_CTR);
|
||||||
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
goto err4;
|
goto err4;
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||||
if (retval != 0)
|
if (retval != 0)
|
||||||
goto err4;
|
goto err4;
|
||||||
|
|
||||||
ehci_writel(ehci, 1, &ehci->regs->configured_flag);
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
err4:
|
err4:
|
||||||
iounmap(hcd->regs);
|
iounmap(hcd->regs);
|
||||||
|
@ -120,7 +113,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_init,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
|
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
|
|
|
@ -31,30 +31,6 @@
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* ehci_xilinx_of_setup - Initialize the device for ehci_reset()
|
|
||||||
* @hcd: Pointer to the usb_hcd device to which the host controller bound
|
|
||||||
*
|
|
||||||
* called during probe() after chip reset completes.
|
|
||||||
*/
|
|
||||||
static int ehci_xilinx_of_setup(struct usb_hcd *hcd)
|
|
||||||
{
|
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci->sbrn = 0x20;
|
|
||||||
|
|
||||||
return ehci_reset(ehci);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ehci_xilinx_port_handed_over - hand the port out if failed to enable it
|
* ehci_xilinx_port_handed_over - hand the port out if failed to enable it
|
||||||
* @hcd: Pointer to the usb_hcd device to which the host controller bound
|
* @hcd: Pointer to the usb_hcd device to which the host controller bound
|
||||||
|
@ -107,7 +83,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
|
||||||
/*
|
/*
|
||||||
* basic lifecycle operations
|
* basic lifecycle operations
|
||||||
*/
|
*/
|
||||||
.reset = ehci_xilinx_of_setup,
|
.reset = ehci_setup,
|
||||||
.start = ehci_run,
|
.start = ehci_run,
|
||||||
.stop = ehci_stop,
|
.stop = ehci_stop,
|
||||||
.shutdown = ehci_shutdown,
|
.shutdown = ehci_shutdown,
|
||||||
|
@ -219,11 +195,6 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op)
|
||||||
/* Debug registers are at the first 0x100 region
|
/* Debug registers are at the first 0x100 region
|
||||||
*/
|
*/
|
||||||
ehci->caps = hcd->regs + 0x100;
|
ehci->caps = hcd->regs + 0x100;
|
||||||
ehci->regs = hcd->regs + 0x100 +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
rv = usb_add_hcd(hcd, irq, 0);
|
rv = usb_add_hcd(hcd, irq, 0);
|
||||||
if (rv == 0)
|
if (rv == 0)
|
||||||
|
|
|
@ -14,30 +14,11 @@
|
||||||
|
|
||||||
static int ehci_xls_setup(struct usb_hcd *hcd)
|
static int ehci_xls_setup(struct usb_hcd *hcd)
|
||||||
{
|
{
|
||||||
int retval;
|
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
ehci->caps = hcd->regs;
|
ehci->caps = hcd->regs;
|
||||||
ehci->regs = hcd->regs +
|
|
||||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
|
||||||
dbg_hcs_params(ehci, "reset");
|
|
||||||
dbg_hcc_params(ehci, "reset");
|
|
||||||
|
|
||||||
/* cache this readonly data; minimize chip reads */
|
return ehci_setup(hcd);
|
||||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
|
||||||
|
|
||||||
retval = ehci_halt(ehci);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
/* data structure init */
|
|
||||||
retval = ehci_init(hcd);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
ehci_reset(ehci);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ehci_xls_probe_internal(const struct hc_driver *driver,
|
int ehci_xls_probe_internal(const struct hc_driver *driver,
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct ehci_stats {
|
||||||
/* irq usage */
|
/* irq usage */
|
||||||
unsigned long normal;
|
unsigned long normal;
|
||||||
unsigned long error;
|
unsigned long error;
|
||||||
unsigned long reclaim;
|
unsigned long iaa;
|
||||||
unsigned long lost_iaa;
|
unsigned long lost_iaa;
|
||||||
|
|
||||||
/* termination of urbs from core */
|
/* termination of urbs from core */
|
||||||
|
@ -51,7 +51,7 @@ struct ehci_stats {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ehci_hcd->lock guards shared data against other CPUs:
|
/* ehci_hcd->lock guards shared data against other CPUs:
|
||||||
* ehci_hcd: async, reclaim, periodic (and shadow), ...
|
* ehci_hcd: async, unlink, periodic (and shadow), ...
|
||||||
* usb_host_endpoint: hcpriv
|
* usb_host_endpoint: hcpriv
|
||||||
* ehci_qh: qh_next, qtd_list
|
* ehci_qh: qh_next, qtd_list
|
||||||
* ehci_qtd: qtd_list
|
* ehci_qtd: qtd_list
|
||||||
|
@ -62,13 +62,48 @@ struct ehci_stats {
|
||||||
|
|
||||||
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
|
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
|
||||||
|
* controller may be doing DMA. Lower values mean there's no DMA.
|
||||||
|
*/
|
||||||
enum ehci_rh_state {
|
enum ehci_rh_state {
|
||||||
EHCI_RH_HALTED,
|
EHCI_RH_HALTED,
|
||||||
EHCI_RH_SUSPENDED,
|
EHCI_RH_SUSPENDED,
|
||||||
EHCI_RH_RUNNING
|
EHCI_RH_RUNNING,
|
||||||
|
EHCI_RH_STOPPING
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timer events, ordered by increasing delay length.
|
||||||
|
* Always update event_delays_ns[] and event_handlers[] (defined in
|
||||||
|
* ehci-timer.c) in parallel with this list.
|
||||||
|
*/
|
||||||
|
enum ehci_hrtimer_event {
|
||||||
|
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
|
||||||
|
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
|
||||||
|
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
|
||||||
|
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
|
||||||
|
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
|
||||||
|
EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
|
||||||
|
EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
|
||||||
|
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
|
||||||
|
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
|
||||||
|
EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
|
||||||
|
EHCI_HRTIMER_NUM_EVENTS /* Must come last */
|
||||||
|
};
|
||||||
|
#define EHCI_HRTIMER_NO_EVENT 99
|
||||||
|
|
||||||
struct ehci_hcd { /* one per controller */
|
struct ehci_hcd { /* one per controller */
|
||||||
|
/* timing support */
|
||||||
|
enum ehci_hrtimer_event next_hrtimer_event;
|
||||||
|
unsigned enabled_hrtimer_events;
|
||||||
|
ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
|
||||||
|
struct hrtimer hrtimer;
|
||||||
|
|
||||||
|
int PSS_poll_count;
|
||||||
|
int ASS_poll_count;
|
||||||
|
int died_poll_count;
|
||||||
|
|
||||||
/* glue to PCI and HCD framework */
|
/* glue to PCI and HCD framework */
|
||||||
struct ehci_caps __iomem *caps;
|
struct ehci_caps __iomem *caps;
|
||||||
struct ehci_regs __iomem *regs;
|
struct ehci_regs __iomem *regs;
|
||||||
|
@ -78,30 +113,48 @@ struct ehci_hcd { /* one per controller */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
enum ehci_rh_state rh_state;
|
enum ehci_rh_state rh_state;
|
||||||
|
|
||||||
|
/* general schedule support */
|
||||||
|
bool scanning:1;
|
||||||
|
bool need_rescan:1;
|
||||||
|
bool intr_unlinking:1;
|
||||||
|
bool async_unlinking:1;
|
||||||
|
bool shutdown:1;
|
||||||
|
struct ehci_qh *qh_scan_next;
|
||||||
|
|
||||||
/* async schedule support */
|
/* async schedule support */
|
||||||
struct ehci_qh *async;
|
struct ehci_qh *async;
|
||||||
struct ehci_qh *dummy; /* For AMD quirk use */
|
struct ehci_qh *dummy; /* For AMD quirk use */
|
||||||
struct ehci_qh *reclaim;
|
struct ehci_qh *async_unlink;
|
||||||
struct ehci_qh *qh_scan_next;
|
struct ehci_qh *async_unlink_last;
|
||||||
unsigned scanning : 1;
|
struct ehci_qh *async_iaa;
|
||||||
|
unsigned async_unlink_cycle;
|
||||||
|
unsigned async_count; /* async activity count */
|
||||||
|
|
||||||
/* periodic schedule support */
|
/* periodic schedule support */
|
||||||
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
|
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
|
||||||
unsigned periodic_size;
|
unsigned periodic_size;
|
||||||
__hc32 *periodic; /* hw periodic table */
|
__hc32 *periodic; /* hw periodic table */
|
||||||
dma_addr_t periodic_dma;
|
dma_addr_t periodic_dma;
|
||||||
|
struct list_head intr_qh_list;
|
||||||
unsigned i_thresh; /* uframes HC might cache */
|
unsigned i_thresh; /* uframes HC might cache */
|
||||||
|
|
||||||
union ehci_shadow *pshadow; /* mirror hw periodic table */
|
union ehci_shadow *pshadow; /* mirror hw periodic table */
|
||||||
int next_uframe; /* scan periodic, start here */
|
struct ehci_qh *intr_unlink;
|
||||||
unsigned periodic_sched; /* periodic activity count */
|
struct ehci_qh *intr_unlink_last;
|
||||||
|
unsigned intr_unlink_cycle;
|
||||||
|
unsigned now_frame; /* frame from HC hardware */
|
||||||
|
unsigned next_frame; /* scan periodic, start here */
|
||||||
|
unsigned intr_count; /* intr activity count */
|
||||||
|
unsigned isoc_count; /* isoc activity count */
|
||||||
|
unsigned periodic_count; /* periodic activity count */
|
||||||
unsigned uframe_periodic_max; /* max periodic time per uframe */
|
unsigned uframe_periodic_max; /* max periodic time per uframe */
|
||||||
|
|
||||||
|
|
||||||
/* list of itds & sitds completed while clock_frame was still active */
|
/* list of itds & sitds completed while now_frame was still active */
|
||||||
struct list_head cached_itd_list;
|
struct list_head cached_itd_list;
|
||||||
|
struct ehci_itd *last_itd_to_free;
|
||||||
struct list_head cached_sitd_list;
|
struct list_head cached_sitd_list;
|
||||||
unsigned clock_frame;
|
struct ehci_sitd *last_sitd_to_free;
|
||||||
|
|
||||||
/* per root hub port */
|
/* per root hub port */
|
||||||
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
||||||
|
@ -126,10 +179,6 @@ struct ehci_hcd { /* one per controller */
|
||||||
struct dma_pool *itd_pool; /* itd per iso urb */
|
struct dma_pool *itd_pool; /* itd per iso urb */
|
||||||
struct dma_pool *sitd_pool; /* sitd per split iso urb */
|
struct dma_pool *sitd_pool; /* sitd per split iso urb */
|
||||||
|
|
||||||
struct timer_list iaa_watchdog;
|
|
||||||
struct timer_list watchdog;
|
|
||||||
unsigned long actions;
|
|
||||||
unsigned periodic_stamp;
|
|
||||||
unsigned random_frame;
|
unsigned random_frame;
|
||||||
unsigned long next_statechange;
|
unsigned long next_statechange;
|
||||||
ktime_t last_periodic_enable;
|
ktime_t last_periodic_enable;
|
||||||
|
@ -143,7 +192,6 @@ struct ehci_hcd { /* one per controller */
|
||||||
unsigned big_endian_capbase:1;
|
unsigned big_endian_capbase:1;
|
||||||
unsigned has_amcc_usb23:1;
|
unsigned has_amcc_usb23:1;
|
||||||
unsigned need_io_watchdog:1;
|
unsigned need_io_watchdog:1;
|
||||||
unsigned broken_periodic:1;
|
|
||||||
unsigned amd_pll_fix:1;
|
unsigned amd_pll_fix:1;
|
||||||
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
||||||
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
|
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
|
||||||
|
@ -175,10 +223,6 @@ struct ehci_hcd { /* one per controller */
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
struct dentry *debug_dir;
|
struct dentry *debug_dir;
|
||||||
#endif
|
#endif
|
||||||
/*
|
|
||||||
* OTG controllers and transceivers need software interaction
|
|
||||||
*/
|
|
||||||
struct usb_phy *transceiver;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
||||||
|
@ -191,34 +235,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
|
||||||
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
|
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
iaa_watchdog_start(struct ehci_hcd *ehci)
|
|
||||||
{
|
|
||||||
WARN_ON(timer_pending(&ehci->iaa_watchdog));
|
|
||||||
mod_timer(&ehci->iaa_watchdog,
|
|
||||||
jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
|
|
||||||
{
|
|
||||||
del_timer(&ehci->iaa_watchdog);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ehci_timer_action {
|
|
||||||
TIMER_IO_WATCHDOG,
|
|
||||||
TIMER_ASYNC_SHRINK,
|
|
||||||
TIMER_ASYNC_OFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
|
|
||||||
{
|
|
||||||
clear_bit (action, &ehci->actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_cached_lists(struct ehci_hcd *ehci);
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include <linux/usb/ehci_def.h>
|
#include <linux/usb/ehci_def.h>
|
||||||
|
@ -328,7 +344,13 @@ union ehci_shadow {
|
||||||
struct ehci_qh_hw {
|
struct ehci_qh_hw {
|
||||||
__hc32 hw_next; /* see EHCI 3.6.1 */
|
__hc32 hw_next; /* see EHCI 3.6.1 */
|
||||||
__hc32 hw_info1; /* see EHCI 3.6.2 */
|
__hc32 hw_info1; /* see EHCI 3.6.2 */
|
||||||
#define QH_HEAD 0x00008000
|
#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
|
||||||
|
#define QH_HEAD (1 << 15) /* Head of async reclamation list */
|
||||||
|
#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
|
||||||
|
#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */
|
||||||
|
#define QH_LOW_SPEED (1 << 12)
|
||||||
|
#define QH_FULL_SPEED (0 << 12)
|
||||||
|
#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
|
||||||
__hc32 hw_info2; /* see EHCI 3.6.2 */
|
__hc32 hw_info2; /* see EHCI 3.6.2 */
|
||||||
#define QH_SMASK 0x000000ff
|
#define QH_SMASK 0x000000ff
|
||||||
#define QH_CMASK 0x0000ff00
|
#define QH_CMASK 0x0000ff00
|
||||||
|
@ -346,32 +368,23 @@ struct ehci_qh_hw {
|
||||||
} __attribute__ ((aligned(32)));
|
} __attribute__ ((aligned(32)));
|
||||||
|
|
||||||
struct ehci_qh {
|
struct ehci_qh {
|
||||||
struct ehci_qh_hw *hw;
|
struct ehci_qh_hw *hw; /* Must come first */
|
||||||
/* the rest is HCD-private */
|
/* the rest is HCD-private */
|
||||||
dma_addr_t qh_dma; /* address of qh */
|
dma_addr_t qh_dma; /* address of qh */
|
||||||
union ehci_shadow qh_next; /* ptr to qh; or periodic */
|
union ehci_shadow qh_next; /* ptr to qh; or periodic */
|
||||||
struct list_head qtd_list; /* sw qtd list */
|
struct list_head qtd_list; /* sw qtd list */
|
||||||
|
struct list_head intr_node; /* list of intr QHs */
|
||||||
struct ehci_qtd *dummy;
|
struct ehci_qtd *dummy;
|
||||||
struct ehci_qh *reclaim; /* next to reclaim */
|
struct ehci_qh *unlink_next; /* next on unlink list */
|
||||||
|
|
||||||
struct ehci_hcd *ehci;
|
unsigned unlink_cycle;
|
||||||
unsigned long unlink_time;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do NOT use atomic operations for QH refcounting. On some CPUs
|
|
||||||
* (PPC7448 for example), atomic operations cannot be performed on
|
|
||||||
* memory that is cache-inhibited (i.e. being used for DMA).
|
|
||||||
* Spinlocks are used to protect all QH fields.
|
|
||||||
*/
|
|
||||||
u32 refcount;
|
|
||||||
unsigned stamp;
|
|
||||||
|
|
||||||
u8 needs_rescan; /* Dequeue during giveback */
|
u8 needs_rescan; /* Dequeue during giveback */
|
||||||
u8 qh_state;
|
u8 qh_state;
|
||||||
#define QH_STATE_LINKED 1 /* HC sees this */
|
#define QH_STATE_LINKED 1 /* HC sees this */
|
||||||
#define QH_STATE_UNLINK 2 /* HC may still see this */
|
#define QH_STATE_UNLINK 2 /* HC may still see this */
|
||||||
#define QH_STATE_IDLE 3 /* HC doesn't see this */
|
#define QH_STATE_IDLE 3 /* HC doesn't see this */
|
||||||
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
|
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
|
||||||
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
|
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
|
||||||
|
|
||||||
u8 xacterrs; /* XactErr retry counter */
|
u8 xacterrs; /* XactErr retry counter */
|
||||||
|
@ -421,7 +434,6 @@ struct ehci_iso_stream {
|
||||||
/* first field matches ehci_hq, but is NULL */
|
/* first field matches ehci_hq, but is NULL */
|
||||||
struct ehci_qh_hw *hw;
|
struct ehci_qh_hw *hw;
|
||||||
|
|
||||||
u32 refcount;
|
|
||||||
u8 bEndpointAddress;
|
u8 bEndpointAddress;
|
||||||
u8 highspeed;
|
u8 highspeed;
|
||||||
struct list_head td_list; /* queued itds/sitds */
|
struct list_head td_list; /* queued itds/sitds */
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue