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:
Linus Torvalds 2012-07-26 10:23:47 -07:00
commit 9fc377799b
159 changed files with 8063 additions and 4146 deletions

View File

@ -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.

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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.

View File

@ -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),

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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);
} }

View File

@ -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>");

View File

@ -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");

View File

@ -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 */ }
}; };

View File

@ -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;

View File

@ -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;
} }

View File

@ -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

View File

@ -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:

View File

@ -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! */
},
{ } { }
}; };

View File

@ -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)

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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;
} }

View File

@ -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()

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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);
} }
} }

View File

@ -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(&params, 0x00, sizeof(params)); memset(&params, 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, &params); DWC3_DEPCMD_SETSTALL, &params);
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(&params, 0, sizeof(params)); cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params); memset(&params, 0, sizeof(params));
WARN_ON_ONCE(ret); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
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");

View File

@ -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);

View File

@ -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 */

View File

@ -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
# #

View File

@ -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

View File

@ -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),
}; };

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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;

View File

@ -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);

View File

@ -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);
} }

View File

@ -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";

View File

@ -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_ */

View File

@ -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);
}
} }

View File

@ -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 */

View File

@ -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));

View File

@ -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);
} }

320
drivers/usb/gadget/mv_u3d.h Normal file
View File

@ -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

View File

@ -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) {

View File

@ -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");

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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,
}; };

View File

@ -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.

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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")) {
/* /*

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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;
} }

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
} }

View File

@ -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),
} }
}; };

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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