summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig16
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/class/cdc-acm.c11
-rw-r--r--drivers/usb/class/cdc-wdm.c8
-rw-r--r--drivers/usb/class/usbtmc.c5
-rw-r--r--drivers/usb/core/config.c111
-rw-r--r--drivers/usb/core/devices.c4
-rw-r--r--drivers/usb/core/devio.c57
-rw-r--r--drivers/usb/core/driver.c25
-rw-r--r--drivers/usb/core/endpoint.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c36
-rw-r--r--drivers/usb/core/hub.c326
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/core/quirks.c24
-rw-r--r--drivers/usb/core/sysfs.c59
-rw-r--r--drivers/usb/core/urb.c2
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h7
-rw-r--r--drivers/usb/dwc3/Kconfig25
-rw-r--r--drivers/usb/dwc3/Makefile36
-rw-r--r--drivers/usb/dwc3/core.c484
-rw-r--r--drivers/usb/dwc3/core.h768
-rw-r--r--drivers/usb/dwc3/debug.h50
-rw-r--r--drivers/usb/dwc3/debugfs.c441
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c401
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c219
-rw-r--r--drivers/usb/dwc3/ep0.c804
-rw-r--r--drivers/usb/dwc3/gadget.c2104
-rw-r--r--drivers/usb/dwc3/gadget.h211
-rw-r--r--drivers/usb/dwc3/io.h54
-rw-r--r--drivers/usb/gadget/Kconfig47
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/acm_ms.c256
-rw-r--r--drivers/usb/gadget/amd5536udc.c20
-rw-r--r--drivers/usb/gadget/amd5536udc.h9
-rw-r--r--drivers/usb/gadget/at91_udc.c32
-rw-r--r--drivers/usb/gadget/at91_udc.h10
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c13
-rw-r--r--drivers/usb/gadget/cdc2.c9
-rw-r--r--drivers/usb/gadget/ci13xxx_msm.c11
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c2
-rw-r--r--drivers/usb/gadget/composite.c37
-rw-r--r--drivers/usb/gadget/config.c9
-rw-r--r--drivers/usb/gadget/dbgp.c1
-rw-r--r--drivers/usb/gadget/dummy_hcd.c13
-rw-r--r--drivers/usb/gadget/epautoconf.c12
-rw-r--r--drivers/usb/gadget/ether.c9
-rw-r--r--drivers/usb/gadget/f_audio.c15
-rw-r--r--drivers/usb/gadget/f_ecm.c9
-rw-r--r--drivers/usb/gadget/f_eem.c9
-rw-r--r--drivers/usb/gadget/f_fs.c9
-rw-r--r--drivers/usb/gadget/f_hid.c9
-rw-r--r--drivers/usb/gadget/f_loopback.c9
-rw-r--r--drivers/usb/gadget/f_mass_storage.c196
-rw-r--r--drivers/usb/gadget/f_midi.c998
-rw-r--r--drivers/usb/gadget/f_ncm.c9
-rw-r--r--drivers/usb/gadget/f_obex.c9
-rw-r--r--drivers/usb/gadget/f_phonet.c10
-rw-r--r--drivers/usb/gadget/f_rndis.c9
-rw-r--r--drivers/usb/gadget/f_sourcesink.c9
-rw-r--r--drivers/usb/gadget/f_subset.c9
-rw-r--r--drivers/usb/gadget/f_uvc.c1
-rw-r--r--drivers/usb/gadget/f_uvc.h1
-rw-r--r--drivers/usb/gadget/file_storage.c254
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c55
-rw-r--r--drivers/usb/gadget/fusb300_udc.c16
-rw-r--r--drivers/usb/gadget/fusb300_udc.h10
-rw-r--r--drivers/usb/gadget/g_ffs.c9
-rw-r--r--drivers/usb/gadget/gadget_chips.h3
-rw-r--r--drivers/usb/gadget/gmidi.c1292
-rw-r--r--drivers/usb/gadget/hid.c9
-rw-r--r--drivers/usb/gadget/imx_udc.c4
-rw-r--r--drivers/usb/gadget/imx_udc.h5
-rw-r--r--drivers/usb/gadget/inode.c9
-rw-r--r--drivers/usb/gadget/langwell_udc.c149
-rw-r--r--drivers/usb/gadget/langwell_udc.h12
-rw-r--r--drivers/usb/gadget/m66592-udc.c16
-rw-r--r--drivers/usb/gadget/m66592-udc.h10
-rw-r--r--drivers/usb/gadget/mass_storage.c11
-rw-r--r--drivers/usb/gadget/multi.c9
-rw-r--r--drivers/usb/gadget/mv_udc.h26
-rw-r--r--drivers/usb/gadget/mv_udc_core.c564
-rw-r--r--drivers/usb/gadget/mv_udc_phy.c214
-rw-r--r--drivers/usb/gadget/ncm.c9
-rw-r--r--drivers/usb/gadget/ndis.h6
-rw-r--r--drivers/usb/gadget/net2272.c78
-rw-r--r--drivers/usb/gadget/net2280.c89
-rw-r--r--drivers/usb/gadget/net2280.h9
-rw-r--r--drivers/usb/gadget/omap_udc.c18
-rw-r--r--drivers/usb/gadget/pch_udc.c15
-rw-r--r--drivers/usb/gadget/printer.c25
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c30
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h17
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c12
-rw-r--r--drivers/usb/gadget/pxa27x_udc.h9
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c516
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h73
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c10
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c69
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c16
-rw-r--r--drivers/usb/gadget/s3c2410_udc.h10
-rw-r--r--drivers/usb/gadget/storage_common.c185
-rw-r--r--drivers/usb/gadget/u_ether.c9
-rw-r--r--drivers/usb/gadget/u_ether.h9
-rw-r--r--drivers/usb/gadget/u_serial.c3
-rw-r--r--drivers/usb/gadget/udc-core.c21
-rw-r--r--drivers/usb/gadget/uvc.h2
-rw-r--r--drivers/usb/gadget/uvc_queue.c1
-rw-r--r--drivers/usb/gadget/uvc_v4l2.c2
-rw-r--r--drivers/usb/gadget/uvc_video.c1
-rw-r--r--drivers/usb/gadget/webcam.c2
-rw-r--r--drivers/usb/gadget/zero.c9
-rw-r--r--drivers/usb/host/Kconfig28
-rw-r--r--drivers/usb/host/Makefile3
-rw-r--r--drivers/usb/host/ehci-ath79.c2
-rw-r--r--drivers/usb/host/ehci-au1xxx.c4
-rw-r--r--drivers/usb/host/ehci-dbg.c19
-rw-r--r--drivers/usb/host/ehci-fsl.c6
-rw-r--r--drivers/usb/host/ehci-hcd.c64
-rw-r--r--drivers/usb/host/ehci-hub.c10
-rw-r--r--drivers/usb/host/ehci-mxc.c2
-rw-r--r--drivers/usb/host/ehci-octeon.c2
-rw-r--r--drivers/usb/host/ehci-omap.c2
-rw-r--r--drivers/usb/host/ehci-orion.c2
-rw-r--r--drivers/usb/host/ehci-pci.c7
-rw-r--r--drivers/usb/host/ehci-ps3.c2
-rw-r--r--drivers/usb/host/ehci-pxa168.c363
-rw-r--r--drivers/usb/host/ehci-q.c31
-rw-r--r--drivers/usb/host/ehci-s5p.c4
-rw-r--r--drivers/usb/host/ehci-sched.c41
-rw-r--r--drivers/usb/host/ehci-sh.c2
-rw-r--r--drivers/usb/host/ehci-spear.c2
-rw-r--r--drivers/usb/host/ehci-tegra.c2
-rw-r--r--drivers/usb/host/ehci-vt8500.c2
-rw-r--r--drivers/usb/host/ehci-xls.c161
-rw-r--r--drivers/usb/host/ehci.h24
-rw-r--r--drivers/usb/host/fhci-hcd.c9
-rw-r--r--drivers/usb/host/fhci-sched.c19
-rw-r--r--drivers/usb/host/imx21-hcd.c2
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c8
-rw-r--r--drivers/usb/host/isp1760-hcd.c418
-rw-r--r--drivers/usb/host/isp1760-hcd.h5
-rw-r--r--drivers/usb/host/isp1760-if.c80
-rw-r--r--drivers/usb/host/ohci-at91.c239
-rw-r--r--drivers/usb/host/ohci-ath79.c2
-rw-r--r--drivers/usb/host/ohci-au1xxx.c2
-rw-r--r--drivers/usb/host/ohci-da8xx.c2
-rw-r--r--drivers/usb/host/ohci-ep93xx.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/ohci-hub.c5
-rw-r--r--drivers/usb/host/ohci-octeon.c2
-rw-r--r--drivers/usb/host/ohci-omap.c4
-rw-r--r--drivers/usb/host/ohci-omap3.c4
-rw-r--r--drivers/usb/host/ohci-pnx4008.c4
-rw-r--r--drivers/usb/host/ohci-pnx8550.c2
-rw-r--r--drivers/usb/host/ohci-ppc-of.c2
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c2
-rw-r--r--drivers/usb/host/ohci-ps3.c2
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/usb/host/ohci-q.c4
-rw-r--r--drivers/usb/host/ohci-s3c2410.c2
-rw-r--r--drivers/usb/host/ohci-sa1111.c2
-rw-r--r--drivers/usb/host/ohci-sh.c2
-rw-r--r--drivers/usb/host/ohci-sm501.c2
-rw-r--r--drivers/usb/host/ohci-spear.c2
-rw-r--r--drivers/usb/host/ohci-ssb.c2
-rw-r--r--drivers/usb/host/ohci-tmio.c2
-rw-r--r--drivers/usb/host/ohci-xls.c151
-rw-r--r--drivers/usb/host/r8a66597-hcd.c4
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c66
-rw-r--r--drivers/usb/host/uhci-q.c6
-rw-r--r--drivers/usb/host/xhci-ext-caps.h6
-rw-r--r--drivers/usb/host/xhci-hub.c139
-rw-r--r--drivers/usb/host/xhci-mem.c371
-rw-r--r--drivers/usb/host/xhci-pci.c107
-rw-r--r--drivers/usb/host/xhci-ring.c80
-rw-r--r--drivers/usb/host/xhci.c1151
-rw-r--r--drivers/usb/host/xhci.h187
-rw-r--r--drivers/usb/misc/adutux.c14
-rw-r--r--drivers/usb/misc/ftdi-elan.c2
-rw-r--r--drivers/usb/misc/idmouse.c2
-rw-r--r--drivers/usb/misc/iowarrior.c2
-rw-r--r--drivers/usb/misc/ldusb.c4
-rw-r--r--drivers/usb/misc/legousbtower.c4
-rw-r--r--drivers/usb/misc/usblcd.c129
-rw-r--r--drivers/usb/misc/usbled.c25
-rw-r--r--drivers/usb/misc/usbtest.c31
-rw-r--r--drivers/usb/mon/mon_bin.c2
-rw-r--r--drivers/usb/musb/davinci.c2
-rw-r--r--drivers/usb/musb/musb_debugfs.c6
-rw-r--r--drivers/usb/musb/musb_gadget.c73
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c8
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c2
-rw-r--r--drivers/usb/otg/isp1301_omap.c2
-rw-r--r--drivers/usb/otg/twl6030-usb.c16
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig2
-rw-r--r--drivers/usb/renesas_usbhs/Makefile8
-rw-r--r--drivers/usb/renesas_usbhs/common.c236
-rw-r--r--drivers/usb/renesas_usbhs/common.h60
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c222
-rw-r--r--drivers/usb/renesas_usbhs/fifo.h22
-rw-r--r--drivers/usb/renesas_usbhs/mod.c81
-rw-r--r--drivers/usb/renesas_usbhs/mod.h57
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c130
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c1312
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c200
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h27
-rw-r--r--drivers/usb/serial/Kconfig1
-rw-r--r--drivers/usb/serial/digi_acceleport.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c91
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h7
-rw-r--r--drivers/usb/serial/io_edgeport.c4
-rw-r--r--drivers/usb/serial/ipw.c33
-rw-r--r--drivers/usb/serial/mos7720.c1
-rw-r--r--drivers/usb/serial/mos7840.c1
-rw-r--r--drivers/usb/serial/opticon.c2
-rw-r--r--drivers/usb/serial/option.c170
-rw-r--r--drivers/usb/serial/pl2303.c4
-rw-r--r--drivers/usb/serial/pl2303.h5
-rw-r--r--drivers/usb/serial/qcserial.c2
-rw-r--r--drivers/usb/serial/sierra.c2
-rw-r--r--drivers/usb/serial/symbolserial.c2
-rw-r--r--drivers/usb/serial/usb-serial.c8
-rw-r--r--drivers/usb/serial/usb_wwan.c2
-rw-r--r--drivers/usb/storage/Kconfig2
-rw-r--r--drivers/usb/storage/realtek_cr.c117
-rw-r--r--drivers/usb/storage/transport.c34
-rw-r--r--drivers/usb/storage/usb.c16
-rw-r--r--drivers/usb/usb-common.c35
-rw-r--r--drivers/usb/usb-skeleton.c2
-rw-r--r--drivers/usb/wusbcore/wa-hc.c2
236 files changed, 14947 insertions, 4216 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 85d5a011dca6..791f11bed606 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -12,6 +12,11 @@ menuconfig USB_SUPPORT
if USB_SUPPORT
+config USB_COMMON
+ tristate
+ default y
+ depends on USB || USB_GADGET
+
# Host-side USB depends on having a host controller
# NOTE: dummy_hcd is always an option, but it's ignored here ...
# NOTE: SL-811 option should be board-specific ...
@@ -19,6 +24,7 @@ config USB_ARCH_HAS_HCD
boolean
default y if USB_ARCH_HAS_OHCI
default y if USB_ARCH_HAS_EHCI
+ default y if USB_ARCH_HAS_XHCI
default y if PCMCIA && !M32R # sl811_cs
default y if ARM # SL-811
default y if BLACKFIN # SL-811
@@ -54,7 +60,7 @@ config USB_ARCH_HAS_OHCI
# some non-PCI hcds implement EHCI
config USB_ARCH_HAS_EHCI
boolean
- default y if PPC_83xx
+ default y if FSL_SOC
default y if PPC_MPC512x
default y if ARCH_IXP4XX
default y if ARCH_W90X900
@@ -68,6 +74,12 @@ config USB_ARCH_HAS_EHCI
default y if ARCH_MSM
default y if MICROBLAZE
default y if SPARC_LEON
+ default y if ARCH_MMP
+ default PCI
+
+# some non-PCI HCDs implement xHCI
+config USB_ARCH_HAS_XHCI
+ boolean
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
@@ -109,6 +121,8 @@ config USB
source "drivers/usb/core/Kconfig"
+source "drivers/usb/dwc3/Kconfig"
+
source "drivers/usb/mon/Kconfig"
source "drivers/usb/wusbcore/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 30ddf8dc4f72..75eca7645227 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -6,6 +6,8 @@
obj-$(CONFIG_USB) += core/
+obj-$(CONFIG_USB_DWC3) += dwc3/
+
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_PCI) += host/
@@ -51,3 +53,5 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb/
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
obj-$(CONFIG_USB_OTG_UTILS) += otg/
obj-$(CONFIG_USB_GADGET) += gadget/
+
+obj-$(CONFIG_USB_COMMON) += usb-common.o
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index dac7676ce21b..6960715c5063 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1058,11 +1058,11 @@ made_compressed_probe:
goto alloc_fail;
}
- ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
- readsize = le16_to_cpu(epread->wMaxPacketSize) *
+ ctrlsize = usb_endpoint_maxp(epctrl);
+ readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 2);
acm->combined_interfaces = combined_interfaces;
- acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
+ acm->writesize = usb_endpoint_maxp(epwrite) * 20;
acm->control = control_interface;
acm->data = data_interface;
acm->minor = minor;
@@ -1305,7 +1305,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
struct acm *acm = usb_get_intfdata(intf);
int cnt;
- if (message.event & PM_EVENT_AUTO) {
+ if (PMSG_IS_AUTO(message)) {
int b;
spin_lock_irq(&acm->write_lock);
@@ -1534,6 +1534,9 @@ static const struct usb_device_id acm_ids[] = {
{ NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
+ /* Support for Owen devices */
+ { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
+
/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
/* Support Lego NXT using pbLua firmware */
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 2b9ff518b509..efe684908c1f 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -682,7 +682,7 @@ next_desc:
if (!ep || !usb_endpoint_is_int_in(ep))
goto err;
- desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+ desc->wMaxPacketSize = usb_endpoint_maxp(ep);
desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
@@ -798,11 +798,11 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
/* if this is an autosuspend the caller does the locking */
- if (!(message.event & PM_EVENT_AUTO))
+ if (!PMSG_IS_AUTO(message))
mutex_lock(&desc->lock);
spin_lock_irq(&desc->iuspin);
- if ((message.event & PM_EVENT_AUTO) &&
+ if (PMSG_IS_AUTO(message) &&
(test_bit(WDM_IN_USE, &desc->flags)
|| test_bit(WDM_RESPONDING, &desc->flags))) {
spin_unlock_irq(&desc->iuspin);
@@ -815,7 +815,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
}
- if (!(message.event & PM_EVENT_AUTO))
+ if (!PMSG_IS_AUTO(message))
mutex_unlock(&desc->lock);
return rv;
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 3f94ac34dce3..12cf5e7395a8 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -186,8 +186,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++)
if (current_setting->endpoint[n].desc.bEndpointAddress ==
data->bulk_in)
- max_size = le16_to_cpu(current_setting->endpoint[n].
- desc.wMaxPacketSize);
+ max_size = usb_endpoint_maxp(&current_setting->endpoint[n].desc);
if (max_size == 0) {
dev_err(dev, "Couldn't get wMaxPacketSize\n");
@@ -636,7 +635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {
desc = &current_setting->endpoint[n].desc;
if (desc->bEndpointAddress == data->bulk_in)
- max_size = le16_to_cpu(desc->wMaxPacketSize);
+ max_size = usb_endpoint_maxp(desc);
}
if (max_size == 0) {
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 26678cadfb21..f4bdd0ce8d56 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
if (usb_endpoint_xfer_isoc(&ep->desc))
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
- le16_to_cpu(ep->desc.wMaxPacketSize);
+ usb_endpoint_maxp(&ep->desc);
else if (usb_endpoint_xfer_int(&ep->desc))
- max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) *
+ max_tx = usb_endpoint_maxp(&ep->desc) *
(desc->bMaxBurst + 1);
else
max_tx = 999999;
@@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
cfgno, inum, asnum, d->bEndpointAddress);
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
endpoint->desc.bInterval = 1;
- if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
+ if (usb_endpoint_maxp(&endpoint->desc) > 8)
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
}
@@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
&& usb_endpoint_xfer_bulk(d)) {
unsigned maxp;
- maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff;
+ maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;
if (maxp != 512)
dev_warn(ddev, "config %d interface %d altsetting %d "
"bulk endpoint 0x%X has invalid maxpacket %d\n",
@@ -755,3 +755,106 @@ err2:
dev_err(ddev, "out of memory\n");
return result;
}
+
+void usb_release_bos_descriptor(struct usb_device *dev)
+{
+ if (dev->bos) {
+ kfree(dev->bos->desc);
+ kfree(dev->bos);
+ dev->bos = NULL;
+ }
+}
+
+/* Get BOS descriptor set */
+int usb_get_bos_descriptor(struct usb_device *dev)
+{
+ struct device *ddev = &dev->dev;
+ struct usb_bos_descriptor *bos;
+ struct usb_dev_cap_header *cap;
+ unsigned char *buffer;
+ int length, total_len, num, i;
+ int ret;
+
+ bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
+ if (!bos)
+ return -ENOMEM;
+
+ /* Get BOS descriptor */
+ ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
+ if (ret < USB_DT_BOS_SIZE) {
+ dev_err(ddev, "unable to get BOS descriptor\n");
+ if (ret >= 0)
+ ret = -ENOMSG;
+ kfree(bos);
+ return ret;
+ }
+
+ length = bos->bLength;
+ total_len = le16_to_cpu(bos->wTotalLength);
+ num = bos->bNumDeviceCaps;
+ kfree(bos);
+ if (total_len < length)
+ return -EINVAL;
+
+ dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL);
+ if (!dev->bos)
+ return -ENOMEM;
+
+ /* Now let's get the whole BOS descriptor set */
+ buffer = kzalloc(total_len, GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev->bos->desc = (struct usb_bos_descriptor *)buffer;
+
+ ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
+ if (ret < total_len) {
+ dev_err(ddev, "unable to get BOS descriptor set\n");
+ if (ret >= 0)
+ ret = -ENOMSG;
+ goto err;
+ }
+ total_len -= length;
+
+ for (i = 0; i < num; i++) {
+ buffer += length;
+ cap = (struct usb_dev_cap_header *)buffer;
+ length = cap->bLength;
+
+ if (total_len < length)
+ break;
+ total_len -= length;
+
+ if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
+ dev_warn(ddev, "descriptor type invalid, skip\n");
+ continue;
+ }
+
+ switch (cap->bDevCapabilityType) {
+ case USB_CAP_TYPE_WIRELESS_USB:
+ /* Wireless USB cap descriptor is handled by wusb */
+ break;
+ case USB_CAP_TYPE_EXT:
+ dev->bos->ext_cap =
+ (struct usb_ext_cap_descriptor *)buffer;
+ break;
+ case USB_SS_CAP_TYPE:
+ dev->bos->ss_cap =
+ (struct usb_ss_cap_descriptor *)buffer;
+ break;
+ case CONTAINER_ID_TYPE:
+ dev->bos->ss_id =
+ (struct usb_ss_container_id_descriptor *)buffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+
+err:
+ usb_release_bos_descriptor(dev);
+ return ret;
+}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 0149c0976e9c..d95696584762 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
dir = usb_endpoint_dir_in(desc) ? 'I' : 'O';
if (speed == USB_SPEED_HIGH) {
- switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
+ switch (usb_endpoint_maxp(desc) & (0x03 << 11)) {
case 1 << 11:
bandwidth = 2; break;
case 2 << 11:
@@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
- (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) *
+ (usb_endpoint_maxp(desc) & 0x07ff) *
bandwidth,
interval, unit);
return start;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 37518dfdeb98..e3beaf229ee3 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -46,6 +46,7 @@
#include <linux/cdev.h>
#include <linux/notifier.h>
#include <linux/security.h>
+#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <linux/moduleparam.h>
@@ -68,7 +69,7 @@ struct dev_state {
wait_queue_head_t wait; /* wake up if a request completed */
unsigned int discsignr;
struct pid *disc_pid;
- uid_t disc_uid, disc_euid;
+ const struct cred *cred;
void __user *disccontext;
unsigned long ifclaimed;
u32 secid;
@@ -79,7 +80,7 @@ struct async {
struct list_head asynclist;
struct dev_state *ps;
struct pid *pid;
- uid_t uid, euid;
+ const struct cred *cred;
unsigned int signr;
unsigned int ifnum;
void __user *userbuffer;
@@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as)
{
put_pid(as->pid);
+ put_cred(as->cred);
kfree(as->urb->transfer_buffer);
kfree(as->urb->setup_packet);
usb_free_urb(as->urb);
@@ -393,9 +395,8 @@ static void async_completed(struct urb *urb)
struct dev_state *ps = as->ps;
struct siginfo sinfo;
struct pid *pid = NULL;
- uid_t uid = 0;
- uid_t euid = 0;
u32 secid = 0;
+ const struct cred *cred = NULL;
int signr;
spin_lock(&ps->lock);
@@ -407,9 +408,8 @@ static void async_completed(struct urb *urb)
sinfo.si_errno = as->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb;
- pid = as->pid;
- uid = as->uid;
- euid = as->euid;
+ pid = get_pid(as->pid);
+ cred = get_cred(as->cred);
secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
@@ -422,9 +422,11 @@ static void async_completed(struct urb *urb)
cancel_bulk_urbs(ps, as->bulk_addr);
spin_unlock(&ps->lock);
- if (signr)
- kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
- euid, secid);
+ if (signr) {
+ kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid);
+ put_pid(pid);
+ put_cred(cred);
+ }
wake_up(&ps->wait);
}
@@ -607,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep)
}
static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
- unsigned int index)
+ unsigned int request, unsigned int index)
{
int ret = 0;
+ struct usb_host_interface *alt_setting;
if (ps->dev->state != USB_STATE_UNAUTHENTICATED
&& ps->dev->state != USB_STATE_ADDRESS
@@ -618,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
return 0;
+ /*
+ * check for the special corner case 'get_device_id' in the printer
+ * class specification, where wIndex is (interface << 8 | altsetting)
+ * instead of just interface
+ */
+ if (requesttype == 0xa1 && request == 0) {
+ alt_setting = usb_find_alt_setting(ps->dev->actconfig,
+ index >> 8, index & 0xff);
+ if (alt_setting
+ && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
+ index >>= 8;
+ }
+
index &= 0xff;
switch (requesttype & USB_RECIP_MASK) {
case USB_RECIP_ENDPOINT:
@@ -656,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_device *dev = NULL;
struct dev_state *ps;
- const struct cred *cred = current_cred();
int ret;
ret = -ENOMEM;
@@ -706,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ps->wait);
ps->discsignr = 0;
ps->disc_pid = get_pid(task_pid(current));
- ps->disc_uid = cred->uid;
- ps->disc_euid = cred->euid;
+ ps->cred = get_current_cred();
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
@@ -749,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
usb_unlock_device(dev);
usb_put_dev(dev);
put_pid(ps->disc_pid);
+ put_cred(ps->cred);
as = async_getcompleted(ps);
while (as) {
@@ -770,7 +785,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex);
+ ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest,
+ ctrl.wIndex);
if (ret)
return ret;
wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */
@@ -1048,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct usb_host_endpoint *ep;
struct async *as;
struct usb_ctrlrequest *dr = NULL;
- const struct cred *cred = current_cred();
unsigned int u, totlen, isofrmlen;
int ret, ifnum = -1;
int is_in;
@@ -1100,7 +1115,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
kfree(dr);
return -EINVAL;
}
- ret = check_ctrlrecip(ps, dr->bRequestType,
+ ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
le16_to_cpup(&dr->wIndex));
if (ret) {
kfree(dr);
@@ -1262,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->signr = uurb->signr;
as->ifnum = ifnum;
as->pid = get_pid(task_pid(current));
- as->uid = cred->uid;
- as->euid = cred->euid;
+ as->cred = get_current_cred();
security_task_getsecid(current, &as->secid);
if (!is_in && uurb->buffer_length > 0) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
@@ -1981,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev)
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ps->disccontext;
- kill_pid_info_as_uid(ps->discsignr, &sinfo,
- ps->disc_pid, ps->disc_uid,
- ps->disc_euid, ps->secid);
+ kill_pid_info_as_cred(ps->discsignr, &sinfo,
+ ps->disc_pid, ps->cred, ps->secid);
}
}
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 34e3da5aa72a..3b029a0a4787 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1046,8 +1046,7 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
/* Non-root devices on a full/low-speed bus must wait for their
* companion high-speed root hub, in case a handoff is needed.
*/
- if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
- udev->bus->hs_companion)
+ if (!PMSG_IS_AUTO(msg) && udev->parent && udev->bus->hs_companion)
device_pm_wait_for_dev(&udev->dev,
&udev->bus->hs_companion->root_hub->dev);
@@ -1075,7 +1074,7 @@ static int usb_suspend_interface(struct usb_device *udev,
if (driver->suspend) {
status = driver->suspend(intf, msg);
- if (status && !(msg.event & PM_EVENT_AUTO))
+ if (status && !PMSG_IS_AUTO(msg))
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
@@ -1189,7 +1188,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
status = usb_suspend_interface(udev, intf, msg);
/* Ignore errors during system sleep transitions */
- if (!(msg.event & PM_EVENT_AUTO))
+ if (!PMSG_IS_AUTO(msg))
status = 0;
if (status != 0)
break;
@@ -1199,7 +1198,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
status = usb_suspend_device(udev, msg);
/* Again, ignore errors during system sleep transitions */
- if (!(msg.event & PM_EVENT_AUTO))
+ if (!PMSG_IS_AUTO(msg))
status = 0;
}
@@ -1583,7 +1582,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf)
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&intf->dev.power.usage_count),
status);
- if (status > 0)
+ if (status > 0 || status == -EINPROGRESS)
status = 0;
return status;
}
@@ -1700,6 +1699,20 @@ int usb_runtime_idle(struct device *dev)
return 0;
}
+int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ int ret = -EPERM;
+
+ if (hcd->driver->set_usb2_hw_lpm) {
+ ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
+ if (!ret)
+ udev->usb2_hw_lpm_enabled = enable;
+ }
+
+ return ret;
+}
+
#endif /* CONFIG_USB_SUSPEND */
struct bus_type usb_bus_type = {
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index df502a98d0df..db7fe50c23d4 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev,
{
struct ep_device *ep = to_ep_device(dev);
return sprintf(buf, "%04x\n",
- le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
+ usb_endpoint_maxp(ep->desc) & 0x07ff);
}
static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index ce22f4a84ed0..a004db35f6d0 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -242,7 +242,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_set_master(dev);
- retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
if (retval != 0)
goto unmap_registers;
set_hs_companion(dev, hcd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 73cbbd85219f..13222d352a61 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ /*
+ * tbuf should be as big as the BOS descriptor and
+ * the USB hub descriptor.
+ */
+ u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE]
__attribute__((aligned(4)));
const u8 *bufp = tbuf;
unsigned len = 0;
@@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
else /* unsupported IDs --> "protocol stall" */
goto error;
break;
+ case USB_DT_BOS << 8:
+ goto nongeneric;
default:
goto error;
}
@@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* CLASS REQUESTS (and errors) */
default:
+nongeneric:
/* non-generic request */
switch (typeReq) {
case GetHubStatus:
@@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
case GetHubDescriptor:
len = sizeof (struct usb_hub_descriptor);
break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ /* len is returned by hub_control */
+ break;
}
status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex,
@@ -615,7 +625,7 @@ error:
status = -EPIPE;
}
- if (status) {
+ if (status < 0) {
len = 0;
if (status != -EPIPE) {
dev_dbg (hcd->self.controller,
@@ -624,6 +634,10 @@ error:
typeReq, wValue, wIndex,
wLength, status);
}
+ } else if (status > 0) {
+ /* hub_control may return the length of data copied. */
+ len = status;
+ status = 0;
}
if (len) {
if (urb->transfer_buffer_length < len)
@@ -1961,8 +1975,9 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
int status;
int old_state = hcd->state;
- dev_dbg(&rhdev->dev, "bus %s%s\n",
- (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
+ dev_dbg(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
+ (PMSG_IS_AUTO(msg) ? "auto-" : ""),
+ rhdev->do_remote_wakeup);
if (HCD_DEAD(hcd)) {
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
return 0;
@@ -1997,8 +2012,8 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
int status;
int old_state = hcd->state;
- dev_dbg(&rhdev->dev, "usb %s%s\n",
- (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
+ dev_dbg(&rhdev->dev, "usb %sresume\n",
+ (PMSG_IS_AUTO(msg) ? "auto-" : ""));
if (HCD_DEAD(hcd)) {
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
return 0;
@@ -2429,7 +2444,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
* but drivers can override it in reset() if needed, along with
* recording the overall controller's system wakeup capability.
*/
- device_init_wakeup(&rhdev->dev, 1);
+ device_set_wakeup_capable(&rhdev->dev, 1);
/* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is
* registered. But since the controller can die at any time,
@@ -2478,6 +2493,13 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
+
+ /*
+ * Host controllers don't generate their own wakeup requests;
+ * they only forward requests from the root hub. Therefore
+ * controllers should always be enabled for remote wakeup.
+ */
+ device_wakeup_enable(hcd->self.controller);
return retval;
error_create_attr_group:
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a428aa080a36..96f05b29c9ad 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev)
int i;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- if (!udev) {
- pr_debug ("%s nodev\n", __func__);
- return;
- }
-
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
* this quiesces everything except pending urbs.
@@ -2030,11 +2025,23 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define HUB_ROOT_RESET_TIME 50 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
+#define HUB_BH_RESET_TIME 50
#define HUB_LONG_RESET_TIME 200
#define HUB_RESET_TIMEOUT 500
+static int hub_port_reset(struct usb_hub *hub, int port1,
+ struct usb_device *udev, unsigned int delay, bool warm);
+
+/* Is a USB 3.0 port in the Inactive state? */
+static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+{
+ return hub_is_superspeed(hub->hdev) &&
+ (portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_SS_INACTIVE;
+}
+
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
- struct usb_device *udev, unsigned int delay)
+ struct usb_device *udev, unsigned int delay, bool warm)
{
int delay_time, ret;
u16 portstatus;
@@ -2051,28 +2058,71 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /* Device went away? */
- if (!(portstatus & USB_PORT_STAT_CONNECTION))
- return -ENOTCONN;
-
- /* bomb out completely if the connection bounced */
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- return -ENOTCONN;
-
- /* if we`ve finished resetting, then break out of the loop */
- if (!(portstatus & USB_PORT_STAT_RESET) &&
- (portstatus & USB_PORT_STAT_ENABLE)) {
- if (hub_is_wusb(hub))
- udev->speed = USB_SPEED_WIRELESS;
- else if (hub_is_superspeed(hub->hdev))
- udev->speed = USB_SPEED_SUPER;
- else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- udev->speed = USB_SPEED_HIGH;
- else if (portstatus & USB_PORT_STAT_LOW_SPEED)
- udev->speed = USB_SPEED_LOW;
- else
- udev->speed = USB_SPEED_FULL;
- return 0;
+ /*
+ * Some buggy devices require a warm reset to be issued even
+ * when the port appears not to be connected.
+ */
+ if (!warm) {
+ /*
+ * Some buggy devices can cause an NEC host controller
+ * to transition to the "Error" state after a hot port
+ * reset. This will show up as the port state in
+ * "Inactive", and the port may also report a
+ * disconnect. Forcing a warm port reset seems to make
+ * the device work.
+ *
+ * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+ */
+ if (hub_port_inactive(hub, portstatus)) {
+ int ret;
+
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ if (portchange & USB_PORT_STAT_C_LINK_STATE)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ if (portchange & USB_PORT_STAT_C_RESET)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_RESET);
+ dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+ port1);
+ ret = hub_port_reset(hub, port1,
+ udev, HUB_BH_RESET_TIME,
+ true);
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ return ret;
+ }
+ /* Device went away? */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return -ENOTCONN;
+
+ /* bomb out completely if the connection bounced */
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ return -ENOTCONN;
+
+ /* if we`ve finished resetting, then break out of
+ * the loop
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_ENABLE)) {
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_WIRELESS;
+ else if (hub_is_superspeed(hub->hdev))
+ udev->speed = USB_SPEED_SUPER;
+ else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ udev->speed = USB_SPEED_HIGH;
+ else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ udev->speed = USB_SPEED_LOW;
+ else
+ udev->speed = USB_SPEED_FULL;
+ return 0;
+ }
+ } else {
+ if (portchange & USB_PORT_STAT_C_BH_RESET)
+ return 0;
}
/* switch to the long delay after two short delay failures */
@@ -2080,35 +2130,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
delay = HUB_LONG_RESET_TIME;
dev_dbg (hub->intfdev,
- "port %d not reset yet, waiting %dms\n",
- port1, delay);
+ "port %d not %sreset yet, waiting %dms\n",
+ port1, warm ? "warm " : "", delay);
}
return -EBUSY;
}
+static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+ struct usb_device *udev, int *status, bool warm)
+{
+ switch (*status) {
+ case 0:
+ if (!warm) {
+ struct usb_hcd *hcd;
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+ update_devnum(udev, 0);
+ hcd = bus_to_hcd(udev->bus);
+ if (hcd->driver->reset_device) {
+ *status = hcd->driver->reset_device(hcd, udev);
+ if (*status < 0) {
+ dev_err(&udev->dev, "Cannot reset "
+ "HCD device state\n");
+ break;
+ }
+ }
+ }
+ /* FALL THROUGH */
+ case -ENOTCONN:
+ case -ENODEV:
+ clear_port_feature(hub->hdev,
+ port1, USB_PORT_FEAT_C_RESET);
+ /* FIXME need disconnect() for NOTATTACHED device */
+ if (warm) {
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ } else {
+ usb_set_device_state(udev, *status
+ ? USB_STATE_NOTATTACHED
+ : USB_STATE_DEFAULT);
+ }
+ break;
+ }
+}
+
+/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
static int hub_port_reset(struct usb_hub *hub, int port1,
- struct usb_device *udev, unsigned int delay)
+ struct usb_device *udev, unsigned int delay, bool warm)
{
int i, status;
- struct usb_hcd *hcd;
- hcd = bus_to_hcd(udev->bus);
- /* Block EHCI CF initialization during the port reset.
- * Some companion controllers don't like it when they mix.
- */
- down_read(&ehci_cf_port_reset_rwsem);
+ if (!warm) {
+ /* Block EHCI CF initialization during the port reset.
+ * Some companion controllers don't like it when they mix.
+ */
+ down_read(&ehci_cf_port_reset_rwsem);
+ } else {
+ if (!hub_is_superspeed(hub->hdev)) {
+ dev_err(hub->intfdev, "only USB3 hub support "
+ "warm reset\n");
+ return -EINVAL;
+ }
+ }
/* Reset the port */
for (i = 0; i < PORT_RESET_TRIES; i++) {
- status = set_port_feature(hub->hdev,
- port1, USB_PORT_FEAT_RESET);
- if (status)
+ status = set_port_feature(hub->hdev, port1, (warm ?
+ USB_PORT_FEAT_BH_PORT_RESET :
+ USB_PORT_FEAT_RESET));
+ if (status) {
dev_err(hub->intfdev,
- "cannot reset port %d (err = %d)\n",
- port1, status);
- else {
- status = hub_port_wait_reset(hub, port1, udev, delay);
+ "cannot %sreset port %d (err = %d)\n",
+ warm ? "warm " : "", port1, status);
+ } else {
+ status = hub_port_wait_reset(hub, port1, udev, delay,
+ warm);
if (status && status != -ENOTCONN)
dev_dbg(hub->intfdev,
"port_wait_reset: err = %d\n",
@@ -2116,34 +2215,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
}
/* return on disconnect or reset */
- switch (status) {
- case 0:
- /* TRSTRCY = 10 ms; plus some extra */
- msleep(10 + 40);
- update_devnum(udev, 0);
- if (hcd->driver->reset_device) {
- status = hcd->driver->reset_device(hcd, udev);
- if (status < 0) {
- dev_err(&udev->dev, "Cannot reset "
- "HCD device state\n");
- break;
- }
- }
- /* FALL THROUGH */
- case -ENOTCONN:
- case -ENODEV:
- clear_port_feature(hub->hdev,
- port1, USB_PORT_FEAT_C_RESET);
- /* FIXME need disconnect() for NOTATTACHED device */
- usb_set_device_state(udev, status
- ? USB_STATE_NOTATTACHED
- : USB_STATE_DEFAULT);
+ if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+ hub_port_finish_reset(hub, port1, udev, &status, warm);
goto done;
}
dev_dbg (hub->intfdev,
- "port %d not enabled, trying reset again...\n",
- port1);
+ "port %d not enabled, trying %sreset again...\n",
+ port1, warm ? "warm " : "");
delay = HUB_LONG_RESET_TIME;
}
@@ -2151,45 +2230,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
"Cannot enable port %i. Maybe the USB cable is bad?\n",
port1);
- done:
- up_read(&ehci_cf_port_reset_rwsem);
- return status;
-}
-
-/* Warm reset a USB3 protocol port */
-static int hub_port_warm_reset(struct usb_hub *hub, int port)
-{
- int ret;
- u16 portstatus, portchange;
-
- if (!hub_is_superspeed(hub->hdev)) {
- dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
- return -EINVAL;
- }
-
- /* Warm reset the port */
- ret = set_port_feature(hub->hdev,
- port, USB_PORT_FEAT_BH_PORT_RESET);
- if (ret) {
- dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
- return ret;
- }
-
- msleep(20);
- ret = hub_port_status(hub, port, &portstatus, &portchange);
-
- if (portchange & USB_PORT_STAT_C_RESET)
- clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
-
- if (portchange & USB_PORT_STAT_C_BH_RESET)
- clear_port_feature(hub->hdev, port,
- USB_PORT_FEAT_C_BH_PORT_RESET);
-
- if (portchange & USB_PORT_STAT_C_LINK_STATE)
- clear_port_feature(hub->hdev, port,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
+done:
+ if (!warm)
+ up_read(&ehci_cf_port_reset_rwsem);
- return ret;
+ return status;
}
/* Check if a port is power on */
@@ -2324,8 +2369,6 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
int port1 = udev->portnum;
int status;
- // dev_dbg(hub->intfdev, "suspend port %d\n", port1);
-
/* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
*
@@ -2342,11 +2385,15 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
/* bail if autosuspend is requested */
- if (msg.event & PM_EVENT_AUTO)
+ if (PMSG_IS_AUTO(msg))
return status;
}
}
+ /* disable USB2 hardware LPM */
+ if (udev->usb2_hw_lpm_enabled == 1)
+ usb_set_usb2_hardware_lpm(udev, 0);
+
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
@@ -2367,12 +2414,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
USB_CTRL_SET_TIMEOUT);
/* System sleep transitions should never fail */
- if (!(msg.event & PM_EVENT_AUTO))
+ if (!PMSG_IS_AUTO(msg))
status = 0;
} else {
/* device has up to 10 msec to fully suspend */
- dev_dbg(&udev->dev, "usb %ssuspend\n",
- (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+ dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
+ (PMSG_IS_AUTO(msg) ? "auto-" : ""),
+ udev->do_remote_wakeup);
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
@@ -2523,7 +2571,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
} else {
/* drive resume for at least 20 msec */
dev_dbg(&udev->dev, "usb %sresume\n",
- (msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+ (PMSG_IS_AUTO(msg) ? "auto-" : ""));
msleep(25);
/* Virtual root hubs can trigger on GET_PORT_STATUS to
@@ -2558,7 +2606,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status < 0) {
dev_dbg(&udev->dev, "can't resume, status %d\n", status);
hub_port_logical_disconnect(hub, port1);
+ } else {
+ /* Try to enable USB2 hardware LPM */
+ if (udev->usb2_hw_lpm_capable == 1)
+ usb_set_usb2_hardware_lpm(udev, 1);
}
+
return status;
}
@@ -2625,7 +2678,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
udev = hdev->children [port1-1];
if (udev && udev->can_submit) {
dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
- if (msg.event & PM_EVENT_AUTO)
+ if (PMSG_IS_AUTO(msg))
return -EBUSY;
}
}
@@ -2798,7 +2851,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
- char *speed, *type;
+ const char *speed;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
@@ -2819,7 +2872,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
- retval = hub_port_reset(hub, port1, udev, delay);
+ retval = hub_port_reset(hub, port1, udev, delay, false);
if (retval < 0) /* error or disconnect */
goto fail;
/* success, speed is known */
@@ -2858,25 +2911,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
default:
goto fail;
}
-
- type = "";
- switch (udev->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- case USB_SPEED_SUPER:
- speed = "super";
- break;
- case USB_SPEED_WIRELESS:
- speed = "variable";
- type = "Wireless ";
- break;
- default: speed = "?"; break;
- }
+
+ if (udev->speed == USB_SPEED_WIRELESS)
+ speed = "variable speed Wireless";
+ else
+ speed = usb_speed_string(udev->speed);
+
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
- "%s %s speed %sUSB device number %d using %s\n",
- (udev->config) ? "reset" : "new", speed, type,
+ "%s %s USB device number %d using %s\n",
+ (udev->config) ? "reset" : "new", speed,
devnum, udev->bus->controller->driver->name);
/* Set up TT records, if needed */
@@ -2949,7 +2993,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
buf->bMaxPacketSize0;
kfree(buf);
- retval = hub_port_reset(hub, port1, udev, delay);
+ retval = hub_port_reset(hub, port1, udev, delay, false);
if (retval < 0) /* error or disconnect */
goto fail;
if (oldspeed != udev->speed) {
@@ -3023,7 +3067,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
- if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
+ if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
@@ -3047,6 +3091,15 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
+ if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+ retval = usb_get_bos_descriptor(udev);
+ if (!retval) {
+ if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
+ le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
+ udev->lpm_capable = 1;
+ }
+ }
+
retval = 0;
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
@@ -3570,7 +3623,8 @@ static void hub_events(void)
(portstatus & USB_PORT_STAT_LINK_STATE)
== USB_SS_PORT_LS_SS_INACTIVE) {
dev_dbg(hub_dev, "warm reset port %d\n", i);
- hub_port_warm_reset(hub, i);
+ hub_port_reset(hub, i, NULL,
+ HUB_BH_RESET_TIME, true);
}
if (connect_change)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 0b5ec234c787..b3bdfede45e6 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -435,7 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
len = sg->length;
if (length) {
- len = min_t(unsigned, len, length);
+ len = min_t(size_t, len, length);
length -= len;
if (length == 0)
io->entries = i + 1;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 81ce6a8e1d94..d6a8d8269bfb 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -38,6 +38,27 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Webcam C200 */
+ { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C250 */
+ { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C300 */
+ { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam B/C500 */
+ { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam Pro 9000 */
+ { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C310 */
+ { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C270 */
+ { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Logitech Harmony 700-series */
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -69,6 +90,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x06a3, 0x0006), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Guillemot Webcam Hercules Dualpix Exchange*/
+ { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index cf05b97693ea..662c0cf3a3e1 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+static ssize_t
+show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ const char *p;
+
+ if (udev->usb2_hw_lpm_enabled == 1)
+ p = "enabled";
+ else
+ p = "disabled";
+
+ return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ bool value;
+ int ret;
+
+ usb_lock_device(udev);
+
+ ret = strtobool(buf, &value);
+
+ if (!ret)
+ ret = usb_set_usb2_hardware_lpm(udev, value);
+
+ usb_unlock_device(udev);
+
+ if (!ret)
+ return count;
+
+ return ret;
+}
+
+static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
+ set_usb2_hardware_lpm);
+
+static struct attribute *usb2_hardware_lpm_attr[] = {
+ &dev_attr_usb2_hardware_lpm.attr,
+ NULL,
+};
+static struct attribute_group usb2_hardware_lpm_attr_group = {
+ .name = power_group_name,
+ .attrs = usb2_hardware_lpm_attr,
+};
+
static struct attribute *power_attrs[] = {
&dev_attr_autosuspend.attr,
&dev_attr_level.attr,
@@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev)
{
int rc = 0;
- if (is_usb_device(dev))
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
rc = sysfs_merge_group(&dev->kobj, &power_attr_group);
+ if (udev->usb2_hw_lpm_capable == 1)
+ rc = sysfs_merge_group(&dev->kobj,
+ &usb2_hardware_lpm_attr_group);
+ }
+
return rc;
}
static void remove_power_attributes(struct device *dev)
{
+ sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group);
sysfs_unmerge_group(&dev->kobj, &power_attr_group);
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index ae334b067c13..909625b91eb3 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
- max = le16_to_cpu(ep->desc.wMaxPacketSize);
+ max = usb_endpoint_maxp(&ep->desc);
if (max <= 0) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8706fc97e60f..73cd90012ec5 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev)
hcd = bus_to_hcd(udev->bus);
usb_destroy_configuration(udev);
+ usb_release_bos_descriptor(udev);
usb_put_hcd(hcd);
kfree(udev->product);
kfree(udev->manufacturer);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index d44d4b7bbf17..3888778582c4 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
+extern int usb_get_bos_descriptor(struct usb_device *dev);
+extern void usb_release_bos_descriptor(struct usb_device *dev);
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
@@ -80,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev);
extern int usb_runtime_suspend(struct device *dev);
extern int usb_runtime_resume(struct device *dev);
extern int usb_runtime_idle(struct device *dev);
+extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable);
#else
@@ -94,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev)
return 0;
}
+static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
+{
+ return 0;
+}
#endif
extern struct bus_type usb_bus_type;
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
new file mode 100644
index 000000000000..3c1d67d324fd
--- /dev/null
+++ b/drivers/usb/dwc3/Kconfig
@@ -0,0 +1,25 @@
+config USB_DWC3
+ tristate "DesignWare USB3 DRD Core Support"
+ depends on (USB || USB_GADGET)
+ select USB_OTG_UTILS
+ help
+ Say Y or M here if your system has a Dual Role SuperSpeed
+ USB controller based on the DesignWare USB3 IP Core.
+
+ If you choose to build this driver is a dynamically linked
+ module, the module will be called dwc3.ko.
+
+if USB_DWC3
+
+config USB_DWC3_DEBUG
+ bool "Enable Debugging Messages"
+ help
+ Say Y here to enable debugging messages on DWC3 Driver.
+
+config USB_DWC3_VERBOSE
+ bool "Enable Verbose Debugging Messages"
+ depends on USB_DWC3_DEBUG
+ help
+ Say Y here to enable verbose debugging messages on DWC3 Driver.
+
+endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
new file mode 100644
index 000000000000..593d1dbc465b
--- /dev/null
+++ b/drivers/usb/dwc3/Makefile
@@ -0,0 +1,36 @@
+ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
+
+obj-$(CONFIG_USB_DWC3) += dwc3.o
+
+dwc3-y := core.o
+
+ifneq ($(CONFIG_USB_GADGET_DWC3),)
+ dwc3-y += gadget.o ep0.o
+endif
+
+ifneq ($(CONFIG_DEBUG_FS),)
+ dwc3-y += debugfs.o
+endif
+
+##
+# Platform-specific glue layers go here
+#
+# NOTICE: Make sure your glue layer doesn't depend on anything
+# which is arch-specific and that it compiles on all situations.
+#
+# We want to keep this requirement in order to be able to compile
+# the entire driver (with all its glue layers) on several architectures
+# and make sure it compiles fine. This will also help with allmodconfig
+# and allyesconfig builds.
+#
+# The only exception is the PCI glue layer, but that's only because
+# PCI doesn't provide nops if CONFIG_PCI isn't enabled.
+##
+
+obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
+
+ifneq ($(CONFIG_PCI),)
+ obj-$(CONFIG_USB_DWC3) += dwc3-pci.o
+endif
+
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
new file mode 100644
index 000000000000..717ebc9ff941
--- /dev/null
+++ b/drivers/usb/dwc3/core.c
@@ -0,0 +1,484 @@
+/**
+ * core.c - DesignWare USB3 DRD Controller Core file
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/module.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+
+#include "debug.h"
+
+/**
+ * dwc3_core_soft_reset - Issues core soft reset and PHY reset
+ * @dwc: pointer to our context structure
+ */
+static void dwc3_core_soft_reset(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* Before Resetting PHY, put Core in Reset */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /* Assert USB3 PHY reset */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
+ /* Assert USB2 PHY reset */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ mdelay(100);
+
+ /* Clear USB3 PHY reset */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
+ /* Clear USB2 PHY reset */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* After PHYs are stable we can take Core out of reset state */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+/**
+ * dwc3_free_one_event_buffer - Frees one event buffer
+ * @dwc: Pointer to our controller context structure
+ * @evt: Pointer to event buffer to be freed
+ */
+static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
+ struct dwc3_event_buffer *evt)
+{
+ dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma);
+ kfree(evt);
+}
+
+/**
+ * dwc3_alloc_one_event_buffer - Allocated one event buffer structure
+ * @dwc: Pointer to our controller context structure
+ * @length: size of the event buffer
+ *
+ * Returns a pointer to the allocated event buffer structure on succes
+ * otherwise ERR_PTR(errno).
+ */
+static struct dwc3_event_buffer *__devinit
+dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length)
+{
+ struct dwc3_event_buffer *evt;
+
+ evt = kzalloc(sizeof(*evt), GFP_KERNEL);
+ if (!evt)
+ return ERR_PTR(-ENOMEM);
+
+ evt->dwc = dwc;
+ evt->length = length;
+ evt->buf = dma_alloc_coherent(dwc->dev, length,
+ &evt->dma, GFP_KERNEL);
+ if (!evt->buf) {
+ kfree(evt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return evt;
+}
+
+/**
+ * dwc3_free_event_buffers - frees all allocated event buffers
+ * @dwc: Pointer to our controller context structure
+ */
+static void dwc3_free_event_buffers(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+ int i;
+
+ for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
+ evt = dwc->ev_buffs[i];
+ if (evt) {
+ dwc3_free_one_event_buffer(dwc, evt);
+ dwc->ev_buffs[i] = NULL;
+ }
+ }
+}
+
+/**
+ * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
+ * @dwc: Pointer to out controller context structure
+ * @num: number of event buffers to allocate
+ * @length: size of event buffer
+ *
+ * Returns 0 on success otherwise negative errno. In error the case, dwc
+ * may contain some buffers allocated but not all which were requested.
+ */
+static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num,
+ unsigned length)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct dwc3_event_buffer *evt;
+
+ evt = dwc3_alloc_one_event_buffer(dwc, length);
+ if (IS_ERR(evt)) {
+ dev_err(dwc->dev, "can't allocate event buffer\n");
+ return PTR_ERR(evt);
+ }
+ dwc->ev_buffs[i] = evt;
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_event_buffers_setup - setup our allocated event buffers
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+ int n;
+
+ for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
+ evt = dwc->ev_buffs[n];
+ dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
+ evt->buf, (unsigned long long) evt->dma,
+ evt->length);
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
+ evt->length & 0xffff);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
+ }
+
+ return 0;
+}
+
+static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+{
+ struct dwc3_event_buffer *evt;
+ int n;
+
+ for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
+ evt = dwc->ev_buffs[n];
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
+ }
+}
+
+static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc)
+{
+ struct dwc3_hwparams *parms = &dwc->hwparams;
+
+ parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
+ parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1);
+ parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2);
+ parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3);
+ parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4);
+ parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5);
+ parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+ parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
+ parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+}
+
+/**
+ * dwc3_core_init - Low-level initialization of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int __devinit dwc3_core_init(struct dwc3 *dwc)
+{
+ unsigned long timeout;
+ u32 reg;
+ int ret;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ /* This should read as U3 followed by revision number */
+ if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+ dwc->revision = reg & DWC3_GSNPSREV_MASK;
+
+ dwc3_core_soft_reset(dwc);
+
+ /* issue device SoftReset too */
+ timeout = jiffies + msecs_to_jiffies(500);
+ dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(dwc->dev, "Reset Timed Out\n");
+ ret = -ETIMEDOUT;
+ goto err0;
+ }
+
+ cpu_relax();
+ } while (true);
+
+ ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM,
+ DWC3_EVENT_BUFFERS_SIZE);
+ if (ret) {
+ dev_err(dwc->dev, "failed to allocate event buffers\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ ret = dwc3_event_buffers_setup(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to setup event buffers\n");
+ goto err1;
+ }
+
+ dwc3_cache_hwparams(dwc);
+
+ return 0;
+
+err1:
+ dwc3_free_event_buffers(dwc);
+
+err0:
+ return ret;
+}
+
+static void dwc3_core_exit(struct dwc3 *dwc)
+{
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_free_event_buffers(dwc);
+}
+
+#define DWC3_ALIGN_MASK (16 - 1)
+
+static int __devinit dwc3_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct resource *res;
+ struct dwc3 *dwc;
+ void __iomem *regs;
+ unsigned int features = id->driver_data;
+ int ret = -ENOMEM;
+ int irq;
+ void *mem;
+
+ mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
+ if (!mem) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ goto err0;
+ }
+ dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
+ dwc->mem = mem;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing resource\n");
+ goto err1;
+ }
+
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "can't request mem region\n");
+ goto err1;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ goto err2;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing IRQ\n");
+ goto err3;
+ }
+
+ spin_lock_init(&dwc->lock);
+ platform_set_drvdata(pdev, dwc);
+
+ dwc->regs = regs;
+ dwc->regs_size = resource_size(res);
+ dwc->dev = &pdev->dev;
+ dwc->irq = irq;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_forbid(&pdev->dev);
+
+ ret = dwc3_core_init(dwc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize core\n");
+ goto err3;
+ }
+
+ if (features & DWC3_HAS_PERIPHERAL) {
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialized gadget\n");
+ goto err4;
+ }
+ }
+
+ ret = dwc3_debugfs_init(dwc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize debugfs\n");
+ goto err5;
+ }
+
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+
+err5:
+ if (features & DWC3_HAS_PERIPHERAL)
+ dwc3_gadget_exit(dwc);
+
+err4:
+ dwc3_core_exit(dwc);
+
+err3:
+ iounmap(regs);
+
+err2:
+ release_mem_region(res->start, resource_size(res));
+
+err1:
+ kfree(dwc->mem);
+
+err0:
+ return ret;
+}
+
+static int __devexit dwc3_remove(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct resource *res;
+ unsigned int features = id->driver_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ dwc3_debugfs_exit(dwc);
+
+ if (features & DWC3_HAS_PERIPHERAL)
+ dwc3_gadget_exit(dwc);
+
+ dwc3_core_exit(dwc);
+ release_mem_region(res->start, resource_size(res));
+ iounmap(dwc->regs);
+ kfree(dwc->mem);
+
+ return 0;
+}
+
+static const struct platform_device_id dwc3_id_table[] __devinitconst = {
+ {
+ .name = "dwc3-omap",
+ .driver_data = (DWC3_HAS_PERIPHERAL
+ | DWC3_HAS_XHCI
+ | DWC3_HAS_OTG),
+ },
+ {
+ .name = "dwc3-pci",
+ .driver_data = DWC3_HAS_PERIPHERAL,
+ },
+ { }, /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, dwc3_id_table);
+
+static struct platform_driver dwc3_driver = {
+ .probe = dwc3_probe,
+ .remove = __devexit_p(dwc3_remove),
+ .driver = {
+ .name = "dwc3",
+ },
+ .id_table = dwc3_id_table,
+};
+
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
+
+static int __devinit dwc3_init(void)
+{
+ return platform_driver_register(&dwc3_driver);
+}
+module_init(dwc3_init);
+
+static void __exit dwc3_exit(void)
+{
+ platform_driver_unregister(&dwc3_driver);
+}
+module_exit(dwc3_exit);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
new file mode 100644
index 000000000000..29a8e1679e12
--- /dev/null
+++ b/drivers/usb/dwc3/core.h
@@ -0,0 +1,768 @@
+/**
+ * core.h - DesignWare USB3 DRD Core Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DRIVERS_USB_DWC3_CORE_H
+#define __DRIVERS_USB_DWC3_CORE_H
+
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Global constants */
+#define DWC3_ENDPOINTS_NUM 32
+
+#define DWC3_EVENT_BUFFERS_NUM 2
+#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
+#define DWC3_EVENT_TYPE_MASK 0xfe
+
+#define DWC3_EVENT_TYPE_DEV 0
+#define DWC3_EVENT_TYPE_CARKIT 3
+#define DWC3_EVENT_TYPE_I2C 4
+
+#define DWC3_DEVICE_EVENT_DISCONNECT 0
+#define DWC3_DEVICE_EVENT_RESET 1
+#define DWC3_DEVICE_EVENT_CONNECT_DONE 2
+#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
+#define DWC3_DEVICE_EVENT_WAKEUP 4
+#define DWC3_DEVICE_EVENT_EOPF 6
+#define DWC3_DEVICE_EVENT_SOF 7
+#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
+#define DWC3_DEVICE_EVENT_CMD_CMPL 10
+#define DWC3_DEVICE_EVENT_OVERFLOW 11
+
+#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GSNPSID_MASK 0xffff0000
+#define DWC3_GSNPSREV_MASK 0xffff
+
+/* Global Registers */
+#define DWC3_GSBUSCFG0 0xc100
+#define DWC3_GSBUSCFG1 0xc104
+#define DWC3_GTXTHRCFG 0xc108
+#define DWC3_GRXTHRCFG 0xc10c
+#define DWC3_GCTL 0xc110
+#define DWC3_GEVTEN 0xc114
+#define DWC3_GSTS 0xc118
+#define DWC3_GSNPSID 0xc120
+#define DWC3_GGPIO 0xc124
+#define DWC3_GUID 0xc128
+#define DWC3_GUCTL 0xc12c
+#define DWC3_GBUSERRADDR0 0xc130
+#define DWC3_GBUSERRADDR1 0xc134
+#define DWC3_GPRTBIMAP0 0xc138
+#define DWC3_GPRTBIMAP1 0xc13c
+#define DWC3_GHWPARAMS0 0xc140
+#define DWC3_GHWPARAMS1 0xc144
+#define DWC3_GHWPARAMS2 0xc148
+#define DWC3_GHWPARAMS3 0xc14c
+#define DWC3_GHWPARAMS4 0xc150
+#define DWC3_GHWPARAMS5 0xc154
+#define DWC3_GHWPARAMS6 0xc158
+#define DWC3_GHWPARAMS7 0xc15c
+#define DWC3_GDBGFIFOSPACE 0xc160
+#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GPRTBIMAP_HS0 0xc180
+#define DWC3_GPRTBIMAP_HS1 0xc184
+#define DWC3_GPRTBIMAP_FS0 0xc188
+#define DWC3_GPRTBIMAP_FS1 0xc18c
+
+#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
+#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
+
+#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04))
+
+#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
+
+#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04))
+#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04))
+
+#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10))
+#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10))
+#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10))
+#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
+
+#define DWC3_GHWPARAMS8 0xc600
+
+/* Device Registers */
+#define DWC3_DCFG 0xc700
+#define DWC3_DCTL 0xc704
+#define DWC3_DEVTEN 0xc708
+#define DWC3_DSTS 0xc70c
+#define DWC3_DGCMDPAR 0xc710
+#define DWC3_DGCMD 0xc714
+#define DWC3_DALEPENA 0xc720
+#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10))
+#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10))
+#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
+#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
+
+/* OTG Registers */
+#define DWC3_OCFG 0xcc00
+#define DWC3_OCTL 0xcc04
+#define DWC3_OEVTEN 0xcc08
+#define DWC3_OSTS 0xcc0C
+
+/* Bit fields */
+
+/* Global Configuration Register */
+#define DWC3_GCTL_PWRDNSCALE(n) (n << 19)
+#define DWC3_GCTL_U2RSTECN (1 << 16)
+#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6)
+#define DWC3_GCTL_CLK_BUS (0)
+#define DWC3_GCTL_CLK_PIPE (1)
+#define DWC3_GCTL_CLK_PIPEHALF (2)
+#define DWC3_GCTL_CLK_MASK (3)
+
+#define DWC3_GCTL_PRTCAPDIR(n) (n << 12)
+#define DWC3_GCTL_PRTCAP_HOST 1
+#define DWC3_GCTL_PRTCAP_DEVICE 2
+#define DWC3_GCTL_PRTCAP_OTG 3
+
+#define DWC3_GCTL_CORESOFTRESET (1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n) (n << 4)
+#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
+#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+
+/* Global USB2 PHY Configuration Register */
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
+
+/* Global USB3 PIPE Control Register */
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+
+/* Global HWPARAMS1 Register */
+#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24)
+#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
+#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+
+/* Device Configuration Register */
+#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
+#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
+
+#define DWC3_DCFG_SPEED_MASK (7 << 0)
+#define DWC3_DCFG_SUPERSPEED (4 << 0)
+#define DWC3_DCFG_HIGHSPEED (0 << 0)
+#define DWC3_DCFG_FULLSPEED2 (1 << 0)
+#define DWC3_DCFG_LOWSPEED (2 << 0)
+#define DWC3_DCFG_FULLSPEED1 (3 << 0)
+
+/* Device Control Register */
+#define DWC3_DCTL_RUN_STOP (1 << 31)
+#define DWC3_DCTL_CSFTRST (1 << 30)
+#define DWC3_DCTL_LSFTRST (1 << 29)
+
+#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
+#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24)
+
+#define DWC3_DCTL_APPL1RES (1 << 23)
+
+#define DWC3_DCTL_INITU2ENA (1 << 12)
+#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
+#define DWC3_DCTL_INITU1ENA (1 << 10)
+#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
+#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
+
+#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
+#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
+
+#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0))
+#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4))
+#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5))
+#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6))
+#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8))
+#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10))
+#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
+
+/* Device Event Enable Register */
+#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12)
+#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11)
+#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10)
+#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
+#define DWC3_DEVTEN_SOFEN (1 << 7)
+#define DWC3_DEVTEN_EOPFEN (1 << 6)
+#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
+#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
+#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
+#define DWC3_DEVTEN_USBRSTEN (1 << 1)
+#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
+
+/* Device Status Register */
+#define DWC3_DSTS_PWRUPREQ (1 << 24)
+#define DWC3_DSTS_COREIDLE (1 << 23)
+#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
+
+#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
+#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18)
+
+#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
+
+#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3)
+#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
+
+#define DWC3_DSTS_CONNECTSPD (7 << 0)
+
+#define DWC3_DSTS_SUPERSPEED (4 << 0)
+#define DWC3_DSTS_HIGHSPEED (0 << 0)
+#define DWC3_DSTS_FULLSPEED2 (1 << 0)
+#define DWC3_DSTS_LOWSPEED (2 << 0)
+#define DWC3_DSTS_FULLSPEED1 (3 << 0)
+
+/* Device Generic Command Register */
+#define DWC3_DGCMD_SET_LMP 0x01
+#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
+#define DWC3_DGCMD_XMIT_FUNCTION 0x03
+#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
+#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
+#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
+
+/* Device Endpoint Command Register */
+#define DWC3_DEPCMD_PARAM_SHIFT 16
+#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT)
+#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
+#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12)
+#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12)
+#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
+#define DWC3_DEPCMD_CMDACT (1 << 10)
+#define DWC3_DEPCMD_CMDIOC (1 << 8)
+
+#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
+#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
+#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0)
+#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
+#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
+#define DWC3_DEPCMD_SETSTALL (0x04 << 0)
+#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
+#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
+#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
+
+/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
+#define DWC3_DALEPENA_EP(n) (1 << n)
+
+#define DWC3_DEPCMD_TYPE_CONTROL 0
+#define DWC3_DEPCMD_TYPE_ISOC 1
+#define DWC3_DEPCMD_TYPE_BULK 2
+#define DWC3_DEPCMD_TYPE_INTR 3
+
+/* Structures */
+
+struct dwc3_trb_hw;
+
+/**
+ * struct dwc3_event_buffer - Software event buffer representation
+ * @list: a list of event buffers
+ * @buf: _THE_ buffer
+ * @length: size of this buffer
+ * @dma: dma_addr_t
+ * @dwc: pointer to DWC controller
+ */
+struct dwc3_event_buffer {
+ void *buf;
+ unsigned length;
+ unsigned int lpos;
+
+ dma_addr_t dma;
+
+ struct dwc3 *dwc;
+};
+
+#define DWC3_EP_FLAG_STALLED (1 << 0)
+#define DWC3_EP_FLAG_WEDGED (1 << 1)
+
+#define DWC3_EP_DIRECTION_TX true
+#define DWC3_EP_DIRECTION_RX false
+
+#define DWC3_TRB_NUM 32
+#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
+
+/**
+ * struct dwc3_ep - device side endpoint representation
+ * @endpoint: usb endpoint
+ * @request_list: list of requests for this endpoint
+ * @req_queued: list of requests on this ep which have TRBs setup
+ * @trb_pool: array of transaction buffers
+ * @trb_pool_dma: dma address of @trb_pool
+ * @free_slot: next slot which is going to be used
+ * @busy_slot: first slot which is owned by HW
+ * @desc: usb_endpoint_descriptor pointer
+ * @dwc: pointer to DWC controller
+ * @flags: endpoint flags (wedged, stalled, ...)
+ * @current_trb: index of current used trb
+ * @number: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @res_trans_idx: Resource transfer index
+ * @interval: the intervall on which the ISOC transfer is started
+ * @name: a human readable name e.g. ep1out-bulk
+ * @direction: true for TX, false for RX
+ * @stream_capable: true when streams are enabled
+ */
+struct dwc3_ep {
+ struct usb_ep endpoint;
+ struct list_head request_list;
+ struct list_head req_queued;
+
+ struct dwc3_trb_hw *trb_pool;
+ dma_addr_t trb_pool_dma;
+ u32 free_slot;
+ u32 busy_slot;
+ const struct usb_endpoint_descriptor *desc;
+ struct dwc3 *dwc;
+
+ unsigned flags;
+#define DWC3_EP_ENABLED (1 << 0)
+#define DWC3_EP_STALL (1 << 1)
+#define DWC3_EP_WEDGE (1 << 2)
+#define DWC3_EP_BUSY (1 << 4)
+#define DWC3_EP_PENDING_REQUEST (1 << 5)
+
+ /* This last one is specific to EP0 */
+#define DWC3_EP0_DIR_IN (1 << 31)
+
+ unsigned current_trb;
+
+ u8 number;
+ u8 type;
+ u8 res_trans_idx;
+ u32 interval;
+
+ char name[20];
+
+ unsigned direction:1;
+ unsigned stream_capable:1;
+};
+
+enum dwc3_phy {
+ DWC3_PHY_UNKNOWN = 0,
+ DWC3_PHY_USB3,
+ DWC3_PHY_USB2,
+};
+
+enum dwc3_ep0_next {
+ DWC3_EP0_UNKNOWN = 0,
+ DWC3_EP0_COMPLETE,
+ DWC3_EP0_NRDY_SETUP,
+ DWC3_EP0_NRDY_DATA,
+ DWC3_EP0_NRDY_STATUS,
+};
+
+enum dwc3_ep0_state {
+ EP0_UNCONNECTED = 0,
+ EP0_SETUP_PHASE,
+ EP0_DATA_PHASE,
+ EP0_STATUS_PHASE,
+};
+
+enum dwc3_link_state {
+ /* In SuperSpeed */
+ DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */
+ DWC3_LINK_STATE_U1 = 0x01,
+ DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */
+ DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */
+ DWC3_LINK_STATE_SS_DIS = 0x04,
+ DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */
+ DWC3_LINK_STATE_SS_INACT = 0x06,
+ DWC3_LINK_STATE_POLL = 0x07,
+ DWC3_LINK_STATE_RECOV = 0x08,
+ DWC3_LINK_STATE_HRESET = 0x09,
+ DWC3_LINK_STATE_CMPLY = 0x0a,
+ DWC3_LINK_STATE_LPBK = 0x0b,
+ DWC3_LINK_STATE_MASK = 0x0f,
+};
+
+enum dwc3_device_state {
+ DWC3_DEFAULT_STATE,
+ DWC3_ADDRESS_STATE,
+ DWC3_CONFIGURED_STATE,
+};
+
+/**
+ * struct dwc3_trb - transfer request block
+ * @bpl: lower 32bit of the buffer
+ * @bph: higher 32bit of the buffer
+ * @length: buffer size (up to 16mb - 1)
+ * @pcm1: packet count m1
+ * @trbsts: trb status
+ * 0 = ok
+ * 1 = missed isoc
+ * 2 = setup pending
+ * @hwo: hardware owner of descriptor
+ * @lst: last trb
+ * @chn: chain buffers
+ * @csp: continue on short packets (only supported on isoc eps)
+ * @trbctl: trb control
+ * 1 = normal
+ * 2 = control-setup
+ * 3 = control-status-2
+ * 4 = control-status-3
+ * 5 = control-data (first trb of data stage)
+ * 6 = isochronous-first (first trb of service interval)
+ * 7 = isochronous
+ * 8 = link trb
+ * others = reserved
+ * @isp_imi: interrupt on short packet / interrupt on missed isoc
+ * @ioc: interrupt on complete
+ * @sid_sofn: Stream ID / SOF Number
+ */
+struct dwc3_trb {
+ u64 bplh;
+
+ union {
+ struct {
+ u32 length:24;
+ u32 pcm1:2;
+ u32 reserved27_26:2;
+ u32 trbsts:4;
+#define DWC3_TRB_STS_OKAY 0
+#define DWC3_TRB_STS_MISSED_ISOC 1
+#define DWC3_TRB_STS_SETUP_PENDING 2
+ };
+ u32 len_pcm;
+ };
+
+ union {
+ struct {
+ u32 hwo:1;
+ u32 lst:1;
+ u32 chn:1;
+ u32 csp:1;
+ u32 trbctl:6;
+ u32 isp_imi:1;
+ u32 ioc:1;
+ u32 reserved13_12:2;
+ u32 sid_sofn:16;
+ u32 reserved31_30:2;
+ };
+ u32 control;
+ };
+} __packed;
+
+/**
+ * struct dwc3_trb_hw - transfer request block (hw format)
+ * @bpl: DW0-3
+ * @bph: DW4-7
+ * @size: DW8-B
+ * @trl: DWC-F
+ */
+struct dwc3_trb_hw {
+ __le32 bpl;
+ __le32 bph;
+ __le32 size;
+ __le32 ctrl;
+} __packed;
+
+static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw)
+{
+ hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh));
+ hw->bph = cpu_to_le32(upper_32_bits(nat->bplh));
+ hw->size = cpu_to_le32p(&nat->len_pcm);
+ /* HWO is written last */
+ hw->ctrl = cpu_to_le32p(&nat->control);
+}
+
+static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
+{
+ u64 bplh;
+
+ bplh = le32_to_cpup(&hw->bpl);
+ bplh |= (u64) le32_to_cpup(&hw->bph) << 32;
+ nat->bplh = bplh;
+
+ nat->len_pcm = le32_to_cpup(&hw->size);
+ nat->control = le32_to_cpup(&hw->ctrl);
+}
+
+/**
+ * dwc3_hwparams - copy of HWPARAMS registers
+ * @hwparams0 - GHWPARAMS0
+ * @hwparams1 - GHWPARAMS1
+ * @hwparams2 - GHWPARAMS2
+ * @hwparams3 - GHWPARAMS3
+ * @hwparams4 - GHWPARAMS4
+ * @hwparams5 - GHWPARAMS5
+ * @hwparams6 - GHWPARAMS6
+ * @hwparams7 - GHWPARAMS7
+ * @hwparams8 - GHWPARAMS8
+ */
+struct dwc3_hwparams {
+ u32 hwparams0;
+ u32 hwparams1;
+ u32 hwparams2;
+ u32 hwparams3;
+ u32 hwparams4;
+ u32 hwparams5;
+ u32 hwparams6;
+ u32 hwparams7;
+ u32 hwparams8;
+};
+
+/**
+ * struct dwc3 - representation of our controller
+ * @ctrl_req: usb control request which is used for ep0
+ * @ep0_trb: trb which is used for the ctrl_req
+ * @ep0_bounce: bounce buffer for ep0
+ * @setup_buf: used while precessing STD USB requests
+ * @ctrl_req_addr: dma address of ctrl_req
+ * @ep0_trb: dma address of ep0_trb
+ * @ep0_usb_req: dummy req used while handling STD USB requests
+ * @setup_buf_addr: dma address of setup_buf
+ * @ep0_bounce_addr: dma address of ep0_bounce
+ * @lock: for synchronizing
+ * @dev: pointer to our struct device
+ * @event_buffer_list: a list of event buffers
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @regs: base address for our registers
+ * @regs_size: address space size
+ * @irq: IRQ number
+ * @revision: revision register contents
+ * @is_selfpowered: true when we are selfpowered
+ * @three_stage_setup: set if we perform a three phase setup
+ * @ep0_status_pending: ep0 status response without a req is pending
+ * @ep0_bounced: true when we used bounce buffer
+ * @ep0_expect_in: true when we expect a DATA IN transfer
+ * @start_config_issued: true when StartConfig command has been issued
+ * @ep0_next_event: hold the next expected event
+ * @ep0state: state of endpoint zero
+ * @link_state: link state
+ * @speed: device speed (super, high, full, low)
+ * @mem: points to start of memory which is used for this struct.
+ * @hwparams: copy of hwparams registers
+ * @root: debugfs root folder pointer
+ */
+struct dwc3 {
+ struct usb_ctrlrequest *ctrl_req;
+ struct dwc3_trb_hw *ep0_trb;
+ void *ep0_bounce;
+ u8 *setup_buf;
+ dma_addr_t ctrl_req_addr;
+ dma_addr_t ep0_trb_addr;
+ dma_addr_t setup_buf_addr;
+ dma_addr_t ep0_bounce_addr;
+ struct usb_request ep0_usb_req;
+ /* device lock */
+ spinlock_t lock;
+ struct device *dev;
+
+ struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM];
+ struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
+ void __iomem *regs;
+ size_t regs_size;
+
+ int irq;
+
+ u32 revision;
+
+#define DWC3_REVISION_173A 0x5533173a
+#define DWC3_REVISION_175A 0x5533175a
+#define DWC3_REVISION_180A 0x5533180a
+#define DWC3_REVISION_183A 0x5533183a
+#define DWC3_REVISION_185A 0x5533185a
+#define DWC3_REVISION_188A 0x5533188a
+#define DWC3_REVISION_190A 0x5533190a
+
+ unsigned is_selfpowered:1;
+ unsigned three_stage_setup:1;
+ unsigned ep0_status_pending:1;
+ unsigned ep0_bounced:1;
+ unsigned ep0_expect_in:1;
+ unsigned start_config_issued:1;
+
+ enum dwc3_ep0_next ep0_next_event;
+ enum dwc3_ep0_state ep0state;
+ enum dwc3_link_state link_state;
+ enum dwc3_device_state dev_state;
+
+ u8 speed;
+ void *mem;
+
+ struct dwc3_hwparams hwparams;
+ struct dentry *root;
+};
+
+/* -------------------------------------------------------------------------- */
+
+#define DWC3_TRBSTS_OK 0
+#define DWC3_TRBSTS_MISSED_ISOC 1
+#define DWC3_TRBSTS_SETUP_PENDING 2
+
+#define DWC3_TRBCTL_NORMAL 1
+#define DWC3_TRBCTL_CONTROL_SETUP 2
+#define DWC3_TRBCTL_CONTROL_STATUS2 3
+#define DWC3_TRBCTL_CONTROL_STATUS3 4
+#define DWC3_TRBCTL_CONTROL_DATA 5
+#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6
+#define DWC3_TRBCTL_ISOCHRONOUS 7
+#define DWC3_TRBCTL_LINK_TRB 8
+
+/* -------------------------------------------------------------------------- */
+
+struct dwc3_event_type {
+ u32 is_devspec:1;
+ u32 type:6;
+ u32 reserved8_31:25;
+} __packed;
+
+#define DWC3_DEPEVT_XFERCOMPLETE 0x01
+#define DWC3_DEPEVT_XFERINPROGRESS 0x02
+#define DWC3_DEPEVT_XFERNOTREADY 0x03
+#define DWC3_DEPEVT_RXTXFIFOEVT 0x04
+#define DWC3_DEPEVT_STREAMEVT 0x06
+#define DWC3_DEPEVT_EPCMDCMPLT 0x07
+
+/**
+ * struct dwc3_event_depvt - Device Endpoint Events
+ * @one_bit: indicates this is an endpoint event (not used)
+ * @endpoint_number: number of the endpoint
+ * @endpoint_event: The event we have:
+ * 0x00 - Reserved
+ * 0x01 - XferComplete
+ * 0x02 - XferInProgress
+ * 0x03 - XferNotReady
+ * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun)
+ * 0x05 - Reserved
+ * 0x06 - StreamEvt
+ * 0x07 - EPCmdCmplt
+ * @reserved11_10: Reserved, don't use.
+ * @status: Indicates the status of the event. Refer to databook for
+ * more information.
+ * @parameters: Parameters of the current event. Refer to databook for
+ * more information.
+ */
+struct dwc3_event_depevt {
+ u32 one_bit:1;
+ u32 endpoint_number:5;
+ u32 endpoint_event:4;
+ u32 reserved11_10:2;
+ u32 status:4;
+#define DEPEVT_STATUS_BUSERR (1 << 0)
+#define DEPEVT_STATUS_SHORT (1 << 1)
+#define DEPEVT_STATUS_IOC (1 << 2)
+#define DEPEVT_STATUS_LST (1 << 3)
+
+/* Stream event only */
+#define DEPEVT_STREAMEVT_FOUND 1
+#define DEPEVT_STREAMEVT_NOTFOUND 2
+
+/* Control-only Status */
+#define DEPEVT_STATUS_CONTROL_SETUP 0
+#define DEPEVT_STATUS_CONTROL_DATA 1
+#define DEPEVT_STATUS_CONTROL_STATUS 2
+
+ u32 parameters:16;
+} __packed;
+
+/**
+ * struct dwc3_event_devt - Device Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's a device event. Should read as 0x00
+ * @type: indicates the type of device event.
+ * 0 - DisconnEvt
+ * 1 - USBRst
+ * 2 - ConnectDone
+ * 3 - ULStChng
+ * 4 - WkUpEvt
+ * 5 - Reserved
+ * 6 - EOPF
+ * 7 - SOF
+ * 8 - Reserved
+ * 9 - ErrticErr
+ * 10 - CmdCmplt
+ * 11 - EvntOverflow
+ * 12 - VndrDevTstRcved
+ * @reserved15_12: Reserved, not used
+ * @event_info: Information about this event
+ * @reserved31_24: Reserved, not used
+ */
+struct dwc3_event_devt {
+ u32 one_bit:1;
+ u32 device_event:7;
+ u32 type:4;
+ u32 reserved15_12:4;
+ u32 event_info:8;
+ u32 reserved31_24:8;
+} __packed;
+
+/**
+ * struct dwc3_event_gevt - Other Core Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event.
+ * @phy_port_number: self-explanatory
+ * @reserved31_12: Reserved, not used.
+ */
+struct dwc3_event_gevt {
+ u32 one_bit:1;
+ u32 device_event:7;
+ u32 phy_port_number:4;
+ u32 reserved31_12:20;
+} __packed;
+
+/**
+ * union dwc3_event - representation of Event Buffer contents
+ * @raw: raw 32-bit event
+ * @type: the type of the event
+ * @depevt: Device Endpoint Event
+ * @devt: Device Event
+ * @gevt: Global Event
+ */
+union dwc3_event {
+ u32 raw;
+ struct dwc3_event_type type;
+ struct dwc3_event_depevt depevt;
+ struct dwc3_event_devt devt;
+ struct dwc3_event_gevt gevt;
+};
+
+/*
+ * DWC3 Features to be used as Driver Data
+ */
+
+#define DWC3_HAS_PERIPHERAL BIT(0)
+#define DWC3_HAS_XHCI BIT(1)
+#define DWC3_HAS_OTG BIT(3)
+
+#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
new file mode 100644
index 000000000000..5894ee8222af
--- /dev/null
+++ b/drivers/usb/dwc3/debug.h
@@ -0,0 +1,50 @@
+/**
+ * debug.h - DesignWare USB3 DRD Controller Debug Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "core.h"
+
+#ifdef CONFIG_DEBUG_FS
+extern int dwc3_debugfs_init(struct dwc3 *);
+extern void dwc3_debugfs_exit(struct dwc3 *);
+#else
+static inline int dwc3_debugfs_init(struct dwc3 *d)
+{ return 0; }
+static inline void dwc3_debugfs_exit(struct dwc3 *d)
+{ }
+#endif
+
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
new file mode 100644
index 000000000000..da1ad77d8d51
--- /dev/null
+++ b/drivers/usb/dwc3/debugfs.c
@@ -0,0 +1,441 @@
+/**
+ * debugfs.c - DesignWare USB3 DRD Controller DebugFS file
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+
+#include <asm/uaccess.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+
+struct dwc3_register {
+ const char *name;
+ u32 offset;
+};
+
+#define dump_register(nm) \
+{ \
+ .name = __stringify(nm), \
+ .offset = DWC3_ ##nm, \
+}
+
+static const struct dwc3_register dwc3_regs[] = {
+ dump_register(GSBUSCFG0),
+ dump_register(GSBUSCFG1),
+ dump_register(GTXTHRCFG),
+ dump_register(GRXTHRCFG),
+ dump_register(GCTL),
+ dump_register(GEVTEN),
+ dump_register(GSTS),
+ dump_register(GSNPSID),
+ dump_register(GGPIO),
+ dump_register(GUID),
+ dump_register(GUCTL),
+ dump_register(GBUSERRADDR0),
+ dump_register(GBUSERRADDR1),
+ dump_register(GPRTBIMAP0),
+ dump_register(GPRTBIMAP1),
+ dump_register(GHWPARAMS0),
+ dump_register(GHWPARAMS1),
+ dump_register(GHWPARAMS2),
+ dump_register(GHWPARAMS3),
+ dump_register(GHWPARAMS4),
+ dump_register(GHWPARAMS5),
+ dump_register(GHWPARAMS6),
+ dump_register(GHWPARAMS7),
+ dump_register(GDBGFIFOSPACE),
+ dump_register(GDBGLTSSM),
+ dump_register(GPRTBIMAP_HS0),
+ dump_register(GPRTBIMAP_HS1),
+ dump_register(GPRTBIMAP_FS0),
+ dump_register(GPRTBIMAP_FS1),
+
+ dump_register(GUSB2PHYCFG(0)),
+ dump_register(GUSB2PHYCFG(1)),
+ dump_register(GUSB2PHYCFG(2)),
+ dump_register(GUSB2PHYCFG(3)),
+ dump_register(GUSB2PHYCFG(4)),
+ dump_register(GUSB2PHYCFG(5)),
+ dump_register(GUSB2PHYCFG(6)),
+ dump_register(GUSB2PHYCFG(7)),
+ dump_register(GUSB2PHYCFG(8)),
+ dump_register(GUSB2PHYCFG(9)),
+ dump_register(GUSB2PHYCFG(10)),
+ dump_register(GUSB2PHYCFG(11)),
+ dump_register(GUSB2PHYCFG(12)),
+ dump_register(GUSB2PHYCFG(13)),
+ dump_register(GUSB2PHYCFG(14)),
+ dump_register(GUSB2PHYCFG(15)),
+
+ dump_register(GUSB2I2CCTL(0)),
+ dump_register(GUSB2I2CCTL(1)),
+ dump_register(GUSB2I2CCTL(2)),
+ dump_register(GUSB2I2CCTL(3)),
+ dump_register(GUSB2I2CCTL(4)),
+ dump_register(GUSB2I2CCTL(5)),
+ dump_register(GUSB2I2CCTL(6)),
+ dump_register(GUSB2I2CCTL(7)),
+ dump_register(GUSB2I2CCTL(8)),
+ dump_register(GUSB2I2CCTL(9)),
+ dump_register(GUSB2I2CCTL(10)),
+ dump_register(GUSB2I2CCTL(11)),
+ dump_register(GUSB2I2CCTL(12)),
+ dump_register(GUSB2I2CCTL(13)),
+ dump_register(GUSB2I2CCTL(14)),
+ dump_register(GUSB2I2CCTL(15)),
+
+ dump_register(GUSB2PHYACC(0)),
+ dump_register(GUSB2PHYACC(1)),
+ dump_register(GUSB2PHYACC(2)),
+ dump_register(GUSB2PHYACC(3)),
+ dump_register(GUSB2PHYACC(4)),
+ dump_register(GUSB2PHYACC(5)),
+ dump_register(GUSB2PHYACC(6)),
+ dump_register(GUSB2PHYACC(7)),
+ dump_register(GUSB2PHYACC(8)),
+ dump_register(GUSB2PHYACC(9)),
+ dump_register(GUSB2PHYACC(10)),
+ dump_register(GUSB2PHYACC(11)),
+ dump_register(GUSB2PHYACC(12)),
+ dump_register(GUSB2PHYACC(13)),
+ dump_register(GUSB2PHYACC(14)),
+ dump_register(GUSB2PHYACC(15)),
+
+ dump_register(GUSB3PIPECTL(0)),
+ dump_register(GUSB3PIPECTL(1)),
+ dump_register(GUSB3PIPECTL(2)),
+ dump_register(GUSB3PIPECTL(3)),
+ dump_register(GUSB3PIPECTL(4)),
+ dump_register(GUSB3PIPECTL(5)),
+ dump_register(GUSB3PIPECTL(6)),
+ dump_register(GUSB3PIPECTL(7)),
+ dump_register(GUSB3PIPECTL(8)),
+ dump_register(GUSB3PIPECTL(9)),
+ dump_register(GUSB3PIPECTL(10)),
+ dump_register(GUSB3PIPECTL(11)),
+ dump_register(GUSB3PIPECTL(12)),
+ dump_register(GUSB3PIPECTL(13)),
+ dump_register(GUSB3PIPECTL(14)),
+ dump_register(GUSB3PIPECTL(15)),
+
+ dump_register(GTXFIFOSIZ(0)),
+ dump_register(GTXFIFOSIZ(1)),
+ dump_register(GTXFIFOSIZ(2)),
+ dump_register(GTXFIFOSIZ(3)),
+ dump_register(GTXFIFOSIZ(4)),
+ dump_register(GTXFIFOSIZ(5)),
+ dump_register(GTXFIFOSIZ(6)),
+ dump_register(GTXFIFOSIZ(7)),
+ dump_register(GTXFIFOSIZ(8)),
+ dump_register(GTXFIFOSIZ(9)),
+ dump_register(GTXFIFOSIZ(10)),
+ dump_register(GTXFIFOSIZ(11)),
+ dump_register(GTXFIFOSIZ(12)),
+ dump_register(GTXFIFOSIZ(13)),
+ dump_register(GTXFIFOSIZ(14)),
+ dump_register(GTXFIFOSIZ(15)),
+ dump_register(GTXFIFOSIZ(16)),
+ dump_register(GTXFIFOSIZ(17)),
+ dump_register(GTXFIFOSIZ(18)),
+ dump_register(GTXFIFOSIZ(19)),
+ dump_register(GTXFIFOSIZ(20)),
+ dump_register(GTXFIFOSIZ(21)),
+ dump_register(GTXFIFOSIZ(22)),
+ dump_register(GTXFIFOSIZ(23)),
+ dump_register(GTXFIFOSIZ(24)),
+ dump_register(GTXFIFOSIZ(25)),
+ dump_register(GTXFIFOSIZ(26)),
+ dump_register(GTXFIFOSIZ(27)),
+ dump_register(GTXFIFOSIZ(28)),
+ dump_register(GTXFIFOSIZ(29)),
+ dump_register(GTXFIFOSIZ(30)),
+ dump_register(GTXFIFOSIZ(31)),
+
+ dump_register(GRXFIFOSIZ(0)),
+ dump_register(GRXFIFOSIZ(1)),
+ dump_register(GRXFIFOSIZ(2)),
+ dump_register(GRXFIFOSIZ(3)),
+ dump_register(GRXFIFOSIZ(4)),
+ dump_register(GRXFIFOSIZ(5)),
+ dump_register(GRXFIFOSIZ(6)),
+ dump_register(GRXFIFOSIZ(7)),
+ dump_register(GRXFIFOSIZ(8)),
+ dump_register(GRXFIFOSIZ(9)),
+ dump_register(GRXFIFOSIZ(10)),
+ dump_register(GRXFIFOSIZ(11)),
+ dump_register(GRXFIFOSIZ(12)),
+ dump_register(GRXFIFOSIZ(13)),
+ dump_register(GRXFIFOSIZ(14)),
+ dump_register(GRXFIFOSIZ(15)),
+ dump_register(GRXFIFOSIZ(16)),
+ dump_register(GRXFIFOSIZ(17)),
+ dump_register(GRXFIFOSIZ(18)),
+ dump_register(GRXFIFOSIZ(19)),
+ dump_register(GRXFIFOSIZ(20)),
+ dump_register(GRXFIFOSIZ(21)),
+ dump_register(GRXFIFOSIZ(22)),
+ dump_register(GRXFIFOSIZ(23)),
+ dump_register(GRXFIFOSIZ(24)),
+ dump_register(GRXFIFOSIZ(25)),
+ dump_register(GRXFIFOSIZ(26)),
+ dump_register(GRXFIFOSIZ(27)),
+ dump_register(GRXFIFOSIZ(28)),
+ dump_register(GRXFIFOSIZ(29)),
+ dump_register(GRXFIFOSIZ(30)),
+ dump_register(GRXFIFOSIZ(31)),
+
+ dump_register(GEVNTADRLO(0)),
+ dump_register(GEVNTADRHI(0)),
+ dump_register(GEVNTSIZ(0)),
+ dump_register(GEVNTCOUNT(0)),
+
+ dump_register(GHWPARAMS8),
+ dump_register(DCFG),
+ dump_register(DCTL),
+ dump_register(DEVTEN),
+ dump_register(DSTS),
+ dump_register(DGCMDPAR),
+ dump_register(DGCMD),
+ dump_register(DALEPENA),
+
+ dump_register(DEPCMDPAR2(0)),
+ dump_register(DEPCMDPAR2(1)),
+ dump_register(DEPCMDPAR2(2)),
+ dump_register(DEPCMDPAR2(3)),
+ dump_register(DEPCMDPAR2(4)),
+ dump_register(DEPCMDPAR2(5)),
+ dump_register(DEPCMDPAR2(6)),
+ dump_register(DEPCMDPAR2(7)),
+ dump_register(DEPCMDPAR2(8)),
+ dump_register(DEPCMDPAR2(9)),
+ dump_register(DEPCMDPAR2(10)),
+ dump_register(DEPCMDPAR2(11)),
+ dump_register(DEPCMDPAR2(12)),
+ dump_register(DEPCMDPAR2(13)),
+ dump_register(DEPCMDPAR2(14)),
+ dump_register(DEPCMDPAR2(15)),
+ dump_register(DEPCMDPAR2(16)),
+ dump_register(DEPCMDPAR2(17)),
+ dump_register(DEPCMDPAR2(18)),
+ dump_register(DEPCMDPAR2(19)),
+ dump_register(DEPCMDPAR2(20)),
+ dump_register(DEPCMDPAR2(21)),
+ dump_register(DEPCMDPAR2(22)),
+ dump_register(DEPCMDPAR2(23)),
+ dump_register(DEPCMDPAR2(24)),
+ dump_register(DEPCMDPAR2(25)),
+ dump_register(DEPCMDPAR2(26)),
+ dump_register(DEPCMDPAR2(27)),
+ dump_register(DEPCMDPAR2(28)),
+ dump_register(DEPCMDPAR2(29)),
+ dump_register(DEPCMDPAR2(30)),
+ dump_register(DEPCMDPAR2(31)),
+
+ dump_register(DEPCMDPAR1(0)),
+ dump_register(DEPCMDPAR1(1)),
+ dump_register(DEPCMDPAR1(2)),
+ dump_register(DEPCMDPAR1(3)),
+ dump_register(DEPCMDPAR1(4)),
+ dump_register(DEPCMDPAR1(5)),
+ dump_register(DEPCMDPAR1(6)),
+ dump_register(DEPCMDPAR1(7)),
+ dump_register(DEPCMDPAR1(8)),
+ dump_register(DEPCMDPAR1(9)),
+ dump_register(DEPCMDPAR1(10)),
+ dump_register(DEPCMDPAR1(11)),
+ dump_register(DEPCMDPAR1(12)),
+ dump_register(DEPCMDPAR1(13)),
+ dump_register(DEPCMDPAR1(14)),
+ dump_register(DEPCMDPAR1(15)),
+ dump_register(DEPCMDPAR1(16)),
+ dump_register(DEPCMDPAR1(17)),
+ dump_register(DEPCMDPAR1(18)),
+ dump_register(DEPCMDPAR1(19)),
+ dump_register(DEPCMDPAR1(20)),
+ dump_register(DEPCMDPAR1(21)),
+ dump_register(DEPCMDPAR1(22)),
+ dump_register(DEPCMDPAR1(23)),
+ dump_register(DEPCMDPAR1(24)),
+ dump_register(DEPCMDPAR1(25)),
+ dump_register(DEPCMDPAR1(26)),
+ dump_register(DEPCMDPAR1(27)),
+ dump_register(DEPCMDPAR1(28)),
+ dump_register(DEPCMDPAR1(29)),
+ dump_register(DEPCMDPAR1(30)),
+ dump_register(DEPCMDPAR1(31)),
+
+ dump_register(DEPCMDPAR0(0)),
+ dump_register(DEPCMDPAR0(1)),
+ dump_register(DEPCMDPAR0(2)),
+ dump_register(DEPCMDPAR0(3)),
+ dump_register(DEPCMDPAR0(4)),
+ dump_register(DEPCMDPAR0(5)),
+ dump_register(DEPCMDPAR0(6)),
+ dump_register(DEPCMDPAR0(7)),
+ dump_register(DEPCMDPAR0(8)),
+ dump_register(DEPCMDPAR0(9)),
+ dump_register(DEPCMDPAR0(10)),
+ dump_register(DEPCMDPAR0(11)),
+ dump_register(DEPCMDPAR0(12)),
+ dump_register(DEPCMDPAR0(13)),
+ dump_register(DEPCMDPAR0(14)),
+ dump_register(DEPCMDPAR0(15)),
+ dump_register(DEPCMDPAR0(16)),
+ dump_register(DEPCMDPAR0(17)),
+ dump_register(DEPCMDPAR0(18)),
+ dump_register(DEPCMDPAR0(19)),
+ dump_register(DEPCMDPAR0(20)),
+ dump_register(DEPCMDPAR0(21)),
+ dump_register(DEPCMDPAR0(22)),
+ dump_register(DEPCMDPAR0(23)),
+ dump_register(DEPCMDPAR0(24)),
+ dump_register(DEPCMDPAR0(25)),
+ dump_register(DEPCMDPAR0(26)),
+ dump_register(DEPCMDPAR0(27)),
+ dump_register(DEPCMDPAR0(28)),
+ dump_register(DEPCMDPAR0(29)),
+ dump_register(DEPCMDPAR0(30)),
+ dump_register(DEPCMDPAR0(31)),
+
+ dump_register(DEPCMD(0)),
+ dump_register(DEPCMD(1)),
+ dump_register(DEPCMD(2)),
+ dump_register(DEPCMD(3)),
+ dump_register(DEPCMD(4)),
+ dump_register(DEPCMD(5)),
+ dump_register(DEPCMD(6)),
+ dump_register(DEPCMD(7)),
+ dump_register(DEPCMD(8)),
+ dump_register(DEPCMD(9)),
+ dump_register(DEPCMD(10)),
+ dump_register(DEPCMD(11)),
+ dump_register(DEPCMD(12)),
+ dump_register(DEPCMD(13)),
+ dump_register(DEPCMD(14)),
+ dump_register(DEPCMD(15)),
+ dump_register(DEPCMD(16)),
+ dump_register(DEPCMD(17)),
+ dump_register(DEPCMD(18)),
+ dump_register(DEPCMD(19)),
+ dump_register(DEPCMD(20)),
+ dump_register(DEPCMD(21)),
+ dump_register(DEPCMD(22)),
+ dump_register(DEPCMD(23)),
+ dump_register(DEPCMD(24)),
+ dump_register(DEPCMD(25)),
+ dump_register(DEPCMD(26)),
+ dump_register(DEPCMD(27)),
+ dump_register(DEPCMD(28)),
+ dump_register(DEPCMD(29)),
+ dump_register(DEPCMD(30)),
+ dump_register(DEPCMD(31)),
+
+ dump_register(OCFG),
+ dump_register(OCTL),
+ dump_register(OEVTEN),
+ dump_register(OSTS),
+};
+
+static int dwc3_regdump_show(struct seq_file *s, void *unused)
+{
+ struct dwc3 *dwc = s->private;
+ int i;
+
+ seq_printf(s, "DesignWare USB3 Core Register Dump\n");
+
+ for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) {
+ seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name,
+ dwc3_readl(dwc->regs, dwc3_regs[i].offset));
+ }
+
+ return 0;
+}
+
+static int dwc3_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_regdump_show, inode->i_private);
+}
+
+static const struct file_operations dwc3_regdump_fops = {
+ .open = dwc3_regdump_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
+{
+ struct dentry *root;
+ struct dentry *file;
+ int ret;
+
+ root = debugfs_create_dir(dev_name(dwc->dev), NULL);
+ if (IS_ERR(root)){
+ ret = PTR_ERR(root);
+ goto err0;
+ }
+
+ dwc->root = root;
+
+ file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
+ &dwc3_regdump_fops);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err1;
+ }
+ return 0;
+
+err1:
+ debugfs_remove_recursive(root);
+
+err0:
+ return ret;
+}
+
+void __devexit dwc3_debugfs_exit(struct dwc3 *dwc)
+{
+ debugfs_remove_recursive(dwc->root);
+ dwc->root = NULL;
+}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
new file mode 100644
index 000000000000..062552b5fc8a
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -0,0 +1,401 @@
+/**
+ * dwc3-omap.c - OMAP Specific Glue layer
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/dwc3-omap.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include "io.h"
+
+/*
+ * All these registers belong to OMAP's Wrapper around the
+ * DesignWare USB3 Core.
+ */
+
+#define USBOTGSS_REVISION 0x0000
+#define USBOTGSS_SYSCONFIG 0x0010
+#define USBOTGSS_IRQ_EOI 0x0020
+#define USBOTGSS_IRQSTATUS_RAW_0 0x0024
+#define USBOTGSS_IRQSTATUS_0 0x0028
+#define USBOTGSS_IRQENABLE_SET_0 0x002c
+#define USBOTGSS_IRQENABLE_CLR_0 0x0030
+#define USBOTGSS_IRQSTATUS_RAW_1 0x0034
+#define USBOTGSS_IRQSTATUS_1 0x0038
+#define USBOTGSS_IRQENABLE_SET_1 0x003c
+#define USBOTGSS_IRQENABLE_CLR_1 0x0040
+#define USBOTGSS_UTMI_OTG_CTRL 0x0080
+#define USBOTGSS_UTMI_OTG_STATUS 0x0084
+#define USBOTGSS_MMRAM_OFFSET 0x0100
+#define USBOTGSS_FLADJ 0x0104
+#define USBOTGSS_DEBUG_CFG 0x0108
+#define USBOTGSS_DEBUG_DATA 0x010c
+
+/* SYSCONFIG REGISTER */
+#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
+#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4)
+
+#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0
+#define USBOTGSS_STANDBYMODE_NO_STANDBY 1
+#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2
+#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3
+
+#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4)
+
+#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2)
+
+#define USBOTGSS_IDLEMODE_FORCE_IDLE 0
+#define USBOTGSS_IDLEMODE_NO_IDLE 1
+#define USBOTGSS_IDLEMODE_SMART_IDLE 2
+#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3
+
+#define USBOTGSS_IDLEMODE_MASK (0x03 << 2)
+
+/* IRQ_EOI REGISTER */
+#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
+
+/* IRQS0 BITS */
+#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
+
+/* IRQ1 BITS */
+#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17)
+#define USBOTGSS_IRQ1_OEVT (1 << 16)
+#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13)
+#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12)
+#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11)
+#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8)
+#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5)
+#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4)
+#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3)
+#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0)
+
+/* UTMI_OTG_CTRL REGISTER */
+#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5)
+#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4)
+#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3)
+#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0)
+
+/* UTMI_OTG_STATUS REGISTER */
+#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31)
+#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9)
+#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
+#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4)
+#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3)
+#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2)
+#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
+
+struct dwc3_omap {
+ /* device lock */
+ spinlock_t lock;
+
+ struct platform_device *dwc3;
+ struct device *dev;
+
+ int irq;
+ void __iomem *base;
+
+ void *context;
+ u32 resource_size;
+
+ u32 dma_status:1;
+};
+
+static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
+{
+ struct dwc3_omap *omap = _omap;
+ u32 reg;
+
+ spin_lock(&omap->lock);
+
+ reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
+
+ if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
+ dev_dbg(omap->dev, "DMA Disable was Cleared\n");
+ omap->dma_status = false;
+ }
+
+ if (reg & USBOTGSS_IRQ1_OEVT)
+ dev_dbg(omap->dev, "OTG Event\n");
+
+ if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE)
+ dev_dbg(omap->dev, "DRVVBUS Rise\n");
+
+ if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE)
+ dev_dbg(omap->dev, "CHRGVBUS Rise\n");
+
+ if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE)
+ dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
+
+ if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE)
+ dev_dbg(omap->dev, "IDPULLUP Rise\n");
+
+ if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL)
+ dev_dbg(omap->dev, "DRVVBUS Fall\n");
+
+ if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL)
+ dev_dbg(omap->dev, "CHRGVBUS Fall\n");
+
+ if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL)
+ dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
+
+ if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
+ dev_dbg(omap->dev, "IDPULLUP Fall\n");
+
+ dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
+
+ reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0);
+ dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
+
+ spin_unlock(&omap->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit dwc3_omap_probe(struct platform_device *pdev)
+{
+ struct dwc3_omap_data *pdata = pdev->dev.platform_data;
+ struct platform_device *dwc3;
+ struct dwc3_omap *omap;
+ struct resource *res;
+
+ int ret = -ENOMEM;
+ int irq;
+
+ u32 reg;
+
+ void __iomem *base;
+ void *context;
+
+ omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+ if (!omap) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, omap);
+
+ irq = platform_get_irq(pdev, 1);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing IRQ resource\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory base resource\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ base = ioremap_nocache(res->start, resource_size(res));
+ if (!base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ goto err1;
+ }
+
+ dwc3 = platform_device_alloc("dwc3-omap", -1);
+ if (!dwc3) {
+ dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
+ goto err2;
+ }
+
+ context = kzalloc(resource_size(res), GFP_KERNEL);
+ if (!context) {
+ dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n");
+ goto err3;
+ }
+
+ spin_lock_init(&omap->lock);
+ dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
+
+ dwc3->dev.parent = &pdev->dev;
+ dwc3->dev.dma_mask = pdev->dev.dma_mask;
+ dwc3->dev.dma_parms = pdev->dev.dma_parms;
+ omap->resource_size = resource_size(res);
+ omap->context = context;
+ omap->dev = &pdev->dev;
+ omap->irq = irq;
+ omap->base = base;
+ omap->dwc3 = dwc3;
+
+ reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "missing platform data\n");
+ } else {
+ switch (pdata->utmi_mode) {
+ case DWC3_OMAP_UTMI_MODE_SW:
+ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ case DWC3_OMAP_UTMI_MODE_HW:
+ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ default:
+ dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n",
+ pdata->utmi_mode);
+ }
+ }
+
+ dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
+
+ /* check the DMA Status */
+ reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
+ omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
+
+ /* Set No-Idle and No-Standby */
+ reg &= ~(USBOTGSS_STANDBYMODE_MASK
+ | USBOTGSS_IDLEMODE_MASK);
+
+ reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
+ | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
+
+ dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
+
+ ret = request_irq(omap->irq, dwc3_omap_interrupt, 0,
+ "dwc3-omap", omap);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n",
+ omap->irq, ret);
+ goto err4;
+ }
+
+ /* enable all IRQs */
+ reg = USBOTGSS_IRQO_COREIRQ_ST;
+ dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
+
+ reg = (USBOTGSS_IRQ1_OEVT |
+ USBOTGSS_IRQ1_DRVVBUS_RISE |
+ USBOTGSS_IRQ1_CHRGVBUS_RISE |
+ USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQ1_IDPULLUP_RISE |
+ USBOTGSS_IRQ1_DRVVBUS_FALL |
+ USBOTGSS_IRQ1_CHRGVBUS_FALL |
+ USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQ1_IDPULLUP_FALL);
+
+ dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
+
+ ret = platform_device_add_resources(dwc3, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
+ goto err5;
+ }
+
+ ret = platform_device_add(dwc3);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dwc3 device\n");
+ goto err5;
+ }
+
+ return 0;
+
+err5:
+ free_irq(omap->irq, omap);
+
+err4:
+ kfree(omap->context);
+
+err3:
+ platform_device_put(dwc3);
+
+err2:
+ iounmap(base);
+
+err1:
+ kfree(omap);
+
+err0:
+ return ret;
+}
+
+static int __devexit dwc3_omap_remove(struct platform_device *pdev)
+{
+ struct dwc3_omap *omap = platform_get_drvdata(pdev);
+
+ platform_device_unregister(omap->dwc3);
+
+ free_irq(omap->irq, omap);
+ iounmap(omap->base);
+
+ kfree(omap->context);
+ kfree(omap);
+
+ return 0;
+}
+
+static const struct of_device_id of_dwc3_matach[] = {
+ {
+ "ti,dwc3",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_matach);
+
+static struct platform_driver dwc3_omap_driver = {
+ .probe = dwc3_omap_probe,
+ .remove = __devexit_p(dwc3_omap_remove),
+ .driver = {
+ .name = "omap-dwc3",
+ .of_match_table = of_dwc3_matach,
+ },
+};
+
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
+
+static int __devinit dwc3_omap_init(void)
+{
+ return platform_driver_register(&dwc3_omap_driver);
+}
+module_init(dwc3_omap_init);
+
+static void __exit dwc3_omap_exit(void)
+{
+ platform_driver_unregister(&dwc3_omap_driver);
+}
+module_exit(dwc3_omap_exit);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
new file mode 100644
index 000000000000..f77c00042685
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -0,0 +1,219 @@
+/**
+ * dwc3-pci.c - PCI Specific glue layer
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+/* FIXME define these in <linux/pci_ids.h> */
+#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
+#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
+
+#define DWC3_PCI_DEVS_POSSIBLE 32
+
+struct dwc3_pci {
+ struct device *dev;
+ struct platform_device *dwc3;
+};
+
+static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
+
+static int dwc3_pci_get_device_id(struct dwc3_pci *glue)
+{
+ int id;
+
+again:
+ id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
+ if (id < DWC3_PCI_DEVS_POSSIBLE) {
+ int old;
+
+ old = test_and_set_bit(id, dwc3_pci_devs);
+ if (old)
+ goto again;
+ } else {
+ dev_err(glue->dev, "no space for new device\n");
+ id = -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id)
+{
+ int ret;
+
+ if (id < 0)
+ return;
+
+ ret = test_bit(id, dwc3_pci_devs);
+ WARN(!ret, "Device: %s\nID %d not in use\n",
+ dev_driver_string(glue->dev), id);
+ clear_bit(id, dwc3_pci_devs);
+}
+
+static int __devinit dwc3_pci_probe(struct pci_dev *pci,
+ const struct pci_device_id *id)
+{
+ struct resource res[2];
+ struct platform_device *dwc3;
+ struct dwc3_pci *glue;
+ int ret = -ENOMEM;
+ int devid;
+
+ glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ if (!glue) {
+ dev_err(&pci->dev, "not enough memory\n");
+ goto err0;
+ }
+
+ glue->dev = &pci->dev;
+
+ ret = pci_enable_device(pci);
+ if (ret) {
+ dev_err(&pci->dev, "failed to enable pci device\n");
+ goto err1;
+ }
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_set_master(pci);
+
+ devid = dwc3_pci_get_device_id(glue);
+ if (devid < 0)
+ goto err2;
+
+ dwc3 = platform_device_alloc("dwc3-pci", devid);
+ if (!dwc3) {
+ dev_err(&pci->dev, "couldn't allocate dwc3 device\n");
+ goto err3;
+ }
+
+ memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
+
+ res[0].start = pci_resource_start(pci, 0);
+ res[0].end = pci_resource_end(pci, 0);
+ res[0].name = "dwc_usb3";
+ res[0].flags = IORESOURCE_MEM;
+
+ res[1].start = pci->irq;
+ res[1].name = "dwc_usb3";
+ res[1].flags = IORESOURCE_IRQ;
+
+ ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
+ if (ret) {
+ dev_err(&pci->dev, "couldn't add resources to dwc3 device\n");
+ goto err4;
+ }
+
+ pci_set_drvdata(pci, glue);
+
+ dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask);
+
+ dwc3->dev.dma_mask = pci->dev.dma_mask;
+ dwc3->dev.dma_parms = pci->dev.dma_parms;
+ dwc3->dev.parent = &pci->dev;
+ glue->dwc3 = dwc3;
+
+ ret = platform_device_add(dwc3);
+ if (ret) {
+ dev_err(&pci->dev, "failed to register dwc3 device\n");
+ goto err4;
+ }
+
+ return 0;
+
+err4:
+ pci_set_drvdata(pci, NULL);
+ platform_device_put(dwc3);
+
+err3:
+ dwc3_pci_put_device_id(glue, devid);
+
+err2:
+ pci_disable_device(pci);
+
+err1:
+ kfree(pci);
+
+err0:
+ return ret;
+}
+
+static void __devexit dwc3_pci_remove(struct pci_dev *pci)
+{
+ struct dwc3_pci *glue = pci_get_drvdata(pci);
+
+ dwc3_pci_put_device_id(glue, glue->dwc3->id);
+ platform_device_unregister(glue->dwc3);
+ pci_set_drvdata(pci, NULL);
+ pci_disable_device(pci);
+ kfree(glue);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
+ PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
+ },
+ { } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
+
+static struct pci_driver dwc3_pci_driver = {
+ .name = "pci-dwc3",
+ .id_table = dwc3_pci_id_table,
+ .probe = dwc3_pci_probe,
+ .remove = __devexit_p(dwc3_pci_remove),
+};
+
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
+
+static int __devinit dwc3_pci_init(void)
+{
+ return pci_register_driver(&dwc3_pci_driver);
+}
+module_init(dwc3_pci_init);
+
+static void __exit dwc3_pci_exit(void)
+{
+ pci_unregister_driver(&dwc3_pci_driver);
+}
+module_exit(dwc3_pci_exit);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
new file mode 100644
index 000000000000..69a4e43ddf59
--- /dev/null
+++ b/drivers/usb/dwc3/ep0.c
@@ -0,0 +1,804 @@
+/**
+ * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+
+static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event);
+
+static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+{
+ switch (state) {
+ case EP0_UNCONNECTED:
+ return "Unconnected";
+ case EP0_SETUP_PHASE:
+ return "Setup Phase";
+ case EP0_DATA_PHASE:
+ return "Data Phase";
+ case EP0_STATUS_PHASE:
+ return "Status Phase";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
+ u32 len, u32 type)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_trb_hw *trb_hw;
+ struct dwc3_trb trb;
+ struct dwc3_ep *dep;
+
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY) {
+ dev_vdbg(dwc->dev, "%s: still busy\n", dep->name);
+ return 0;
+ }
+
+ trb_hw = dwc->ep0_trb;
+ memset(&trb, 0, sizeof(trb));
+
+ trb.trbctl = type;
+ trb.bplh = buf_dma;
+ trb.length = len;
+
+ trb.hwo = 1;
+ trb.lst = 1;
+ trb.ioc = 1;
+ trb.isp_imi = 1;
+
+ dwc3_trb_to_hw(&trb, trb_hw);
+
+ memset(&params, 0, sizeof(params));
+ params.param0 = upper_32_bits(dwc->ep0_trb_addr);
+ params.param1 = lower_32_bits(dwc->ep0_trb_addr);
+
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
+ DWC3_DEPCMD_STARTTRANSFER, &params);
+ if (ret < 0) {
+ dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
+ return ret;
+ }
+
+ dep->flags |= DWC3_EP_BUSY;
+ dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->number);
+
+ dwc->ep0_next_event = DWC3_EP0_COMPLETE;
+
+ return 0;
+}
+
+static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
+ struct dwc3_request *req)
+{
+ int ret = 0;
+
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
+ req->epnum = dep->number;
+
+ list_add_tail(&req->list, &dep->request_list);
+
+ /*
+ * Gadget driver might not be quick enough to queue a request
+ * before we get a Transfer Not Ready event on this endpoint.
+ *
+ * In that case, we will set DWC3_EP_PENDING_REQUEST. When that
+ * flag is set, it's telling us that as soon as Gadget queues the
+ * required request, we should kick the transfer here because the
+ * IRQ we were waiting for is long gone.
+ */
+ if (dep->flags & DWC3_EP_PENDING_REQUEST) {
+ struct dwc3 *dwc = dep->dwc;
+ unsigned direction;
+ u32 type;
+
+ direction = !!(dep->flags & DWC3_EP0_DIR_IN);
+
+ if (dwc->ep0state == EP0_STATUS_PHASE) {
+ type = dwc->three_stage_setup
+ ? DWC3_TRBCTL_CONTROL_STATUS3
+ : DWC3_TRBCTL_CONTROL_STATUS2;
+ } else if (dwc->ep0state == EP0_DATA_PHASE) {
+ type = DWC3_TRBCTL_CONTROL_DATA;
+ } else {
+ /* should never happen */
+ WARN_ON(1);
+ return 0;
+ }
+
+ ret = dwc3_ep0_start_trans(dwc, direction,
+ req->request.dma, req->request.length, type);
+ dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
+ DWC3_EP0_DIR_IN);
+ }
+
+ return ret;
+}
+
+int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (!dep->desc) {
+ dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
+ request, dep->name);
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ /* we share one TRB for ep0/1 */
+ if (!list_empty(&dwc->eps[0]->request_list) ||
+ !list_empty(&dwc->eps[1]->request_list) ||
+ dwc->ep0_status_pending) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n",
+ request, dep->name, request->length,
+ dwc3_ep0_state_string(dwc->ep0state));
+
+ ret = __dwc3_gadget_ep0_queue(dep, req);
+
+out:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep = dwc->eps[0];
+
+ /* stall is always issued on EP0 */
+ __dwc3_gadget_ep_set_halt(dwc->eps[0], 1);
+ dwc->eps[0]->flags = DWC3_EP_ENABLED;
+
+ if (!list_empty(&dep->request_list)) {
+ struct dwc3_request *req;
+
+ req = next_request(&dep->request_list);
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ }
+
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
+void dwc3_ep0_out_start(struct dwc3 *dwc)
+{
+ int ret;
+
+ ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ DWC3_TRBCTL_CONTROL_SETUP);
+ WARN_ON(ret < 0);
+}
+
+static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
+{
+ struct dwc3_ep *dep;
+ u32 windex = le16_to_cpu(wIndex_le);
+ u32 epnum;
+
+ epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
+ if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ epnum |= 1;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_ENABLED)
+ return dep;
+
+ return NULL;
+}
+
+static void dwc3_ep0_send_status_response(struct dwc3 *dwc)
+{
+ dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr,
+ dwc->ep0_usb_req.length,
+ DWC3_TRBCTL_CONTROL_DATA);
+}
+
+/*
+ * ch 9.4.5
+ */
+static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ struct dwc3_ep *dep;
+ u32 recip;
+ u16 usb_status = 0;
+ __le16 *response_pkt;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ /*
+ * We are self-powered. U1/U2/LTM will be set later
+ * once we handle this states. RemoteWakeup is 0 on SS
+ */
+ usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+ break;
+
+ case USB_RECIP_INTERFACE:
+ /*
+ * Function Remote Wake Capable D0
+ * Function Remote Wakeup D1
+ */
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
+ return -EINVAL;
+
+ if (dep->flags & DWC3_EP_STALL)
+ usb_status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ response_pkt = (__le16 *) dwc->setup_buf;
+ *response_pkt = cpu_to_le16(usb_status);
+ dwc->ep0_usb_req.length = sizeof(*response_pkt);
+ dwc->ep0_status_pending = 1;
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ struct dwc3_ep *dep;
+ u32 recip;
+ u32 wValue;
+ u32 wIndex;
+ u32 reg;
+ int ret;
+ u32 mode;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+
+ /*
+ * 9.4.1 says only only for SS, in AddressState only for
+ * default control pipe
+ */
+ switch (wValue) {
+ case USB_DEVICE_U1_ENABLE:
+ case USB_DEVICE_U2_ENABLE:
+ case USB_DEVICE_LTM_ENABLE:
+ if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+ return -EINVAL;
+ if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+ return -EINVAL;
+ }
+
+ /* XXX add U[12] & LTM */
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ mode = wIndex >> 8;
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_TSTCTRL_MASK;
+
+ switch (mode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ reg |= mode << 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case USB_RECIP_INTERFACE:
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
+ if (wIndex & USB_INTRF_FUNC_SUSPEND_LP)
+ /* XXX enable Low power suspend */
+ ;
+ if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
+ /* XXX enable remote wakeup */
+ ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
+ return -EINVAL;
+ ret = __dwc3_gadget_ep_set_halt(dep, set);
+ if (ret)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ u32 addr;
+ u32 reg;
+
+ addr = le16_to_cpu(ctrl->wValue);
+ if (addr > 127)
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_DEVADDR_MASK);
+ reg |= DWC3_DCFG_DEVADDR(addr);
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ if (addr)
+ dwc->dev_state = DWC3_ADDRESS_STATE;
+ else
+ dwc->dev_state = DWC3_DEFAULT_STATE;
+
+ return 0;
+}
+
+static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ spin_unlock(&dwc->lock);
+ ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ spin_lock(&dwc->lock);
+ return ret;
+}
+
+static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ u32 cfg;
+ int ret;
+
+ dwc->start_config_issued = false;
+ cfg = le16_to_cpu(ctrl->wValue);
+
+ switch (dwc->dev_state) {
+ case DWC3_DEFAULT_STATE:
+ return -EINVAL;
+ break;
+
+ case DWC3_ADDRESS_STATE:
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ /* if the cfg matches and the cfg is non zero */
+ if (!ret && cfg)
+ dwc->dev_state = DWC3_CONFIGURED_STATE;
+ break;
+
+ case DWC3_CONFIGURED_STATE:
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ if (!cfg)
+ dwc->dev_state = DWC3_ADDRESS_STATE;
+ break;
+ }
+ return 0;
+}
+
+static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n");
+ ret = dwc3_ep0_handle_status(dwc, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
+ ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
+ ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
+ ret = dwc3_ep0_set_address(dwc, ctrl);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ ret = dwc3_ep0_set_config(dwc, ctrl);
+ break;
+ default:
+ dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ break;
+ };
+
+ return ret;
+}
+
+static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
+ int ret;
+ u32 len;
+
+ if (!dwc->gadget_driver)
+ goto err;
+
+ len = le16_to_cpu(ctrl->wLength);
+ if (!len) {
+ dwc->three_stage_setup = false;
+ dwc->ep0_expect_in = false;
+ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
+ } else {
+ dwc->three_stage_setup = true;
+ dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN);
+ dwc->ep0_next_event = DWC3_EP0_NRDY_DATA;
+ }
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ ret = dwc3_ep0_std_request(dwc, ctrl);
+ else
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+
+ if (ret >= 0)
+ return;
+
+err:
+ dwc3_ep0_stall_and_restart(dwc);
+}
+
+static void dwc3_ep0_complete_data(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_request *r = NULL;
+ struct usb_request *ur;
+ struct dwc3_trb trb;
+ struct dwc3_ep *dep;
+ u32 transferred;
+ u8 epnum;
+
+ epnum = event->endpoint_number;
+ dep = dwc->eps[epnum];
+
+ dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
+
+ if (!dwc->ep0_status_pending) {
+ r = next_request(&dwc->eps[0]->request_list);
+ ur = &r->request;
+ } else {
+ ur = &dwc->ep0_usb_req;
+ dwc->ep0_status_pending = 0;
+ }
+
+ dwc3_trb_to_nat(dwc->ep0_trb, &trb);
+
+ if (dwc->ep0_bounced) {
+ struct dwc3_ep *ep0 = dwc->eps[0];
+
+ transferred = min_t(u32, ur->length,
+ ep0->endpoint.maxpacket - trb.length);
+ memcpy(ur->buf, dwc->ep0_bounce, transferred);
+ dwc->ep0_bounced = false;
+ } else {
+ transferred = ur->length - trb.length;
+ ur->actual += transferred;
+ }
+
+ if ((epnum & 1) && ur->actual < ur->length) {
+ /* for some reason we did not get everything out */
+
+ dwc3_ep0_stall_and_restart(dwc);
+ } else {
+ /*
+ * handle the case where we have to send a zero packet. This
+ * seems to be case when req.length > maxpacket. Could it be?
+ */
+ if (r)
+ dwc3_gadget_giveback(dep, r, 0);
+ }
+}
+
+static void dwc3_ep0_complete_req(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_request *r;
+ struct dwc3_ep *dep;
+
+ dep = dwc->eps[0];
+
+ if (!list_empty(&dep->request_list)) {
+ r = next_request(&dep->request_list);
+
+ dwc3_gadget_giveback(dep, r, 0);
+ }
+
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
+static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+
+ dep->flags &= ~DWC3_EP_BUSY;
+
+ switch (dwc->ep0state) {
+ case EP0_SETUP_PHASE:
+ dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n");
+ dwc3_ep0_inspect_setup(dwc, event);
+ break;
+
+ case EP0_DATA_PHASE:
+ dev_vdbg(dwc->dev, "Data Phase\n");
+ dwc3_ep0_complete_data(dwc, event);
+ break;
+
+ case EP0_STATUS_PHASE:
+ dev_vdbg(dwc->dev, "Status Phase\n");
+ dwc3_ep0_complete_req(dwc, event);
+ break;
+ default:
+ WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
+ }
+}
+
+static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
+static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep;
+ struct dwc3_request *req;
+ int ret;
+
+ dep = dwc->eps[0];
+ dwc->ep0state = EP0_DATA_PHASE;
+
+ if (dwc->ep0_status_pending) {
+ dwc3_ep0_send_status_response(dwc);
+ return;
+ }
+
+ if (list_empty(&dep->request_list)) {
+ dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+
+ if (event->endpoint_number)
+ dep->flags |= DWC3_EP0_DIR_IN;
+ return;
+ }
+
+ req = next_request(&dep->request_list);
+ req->direction = !!event->endpoint_number;
+
+ dwc->ep0state = EP0_DATA_PHASE;
+ if (req->request.length == 0) {
+ 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)) {
+ dwc3_map_buffer_to_dma(req);
+
+ 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 {
+ dwc3_map_buffer_to_dma(req);
+
+ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+ req->request.dma, req->request.length,
+ DWC3_TRBCTL_CONTROL_DATA);
+ }
+
+ WARN_ON(ret < 0);
+}
+
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ u32 type;
+ int ret;
+
+ dwc->ep0state = EP0_STATUS_PHASE;
+
+ type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
+ : DWC3_TRBCTL_CONTROL_STATUS2;
+
+ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+ dwc->ctrl_req_addr, 0, type);
+
+ WARN_ON(ret < 0);
+}
+
+static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ switch (event->status) {
+ case DEPEVT_STATUS_CONTROL_SETUP:
+ dev_vdbg(dwc->dev, "Control Setup\n");
+ dwc3_ep0_do_control_setup(dwc, event);
+ break;
+
+ case DEPEVT_STATUS_CONTROL_DATA:
+ dev_vdbg(dwc->dev, "Control Data\n");
+
+ if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
+ dev_vdbg(dwc->dev, "Expected %d got %d\n",
+ dwc->ep0_next_event,
+ DWC3_EP0_NRDY_DATA);
+
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+
+ /*
+ * One of the possible error cases is when Host _does_
+ * request for Data Phase, but it does so on the wrong
+ * direction.
+ *
+ * Here, we already know ep0_next_event is DATA (see above),
+ * so we only need to check for direction.
+ */
+ if (dwc->ep0_expect_in != event->endpoint_number) {
+ dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+
+ dwc3_ep0_do_control_data(dwc, event);
+ break;
+
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ dev_vdbg(dwc->dev, "Control Status\n");
+
+ if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
+ dev_vdbg(dwc->dev, "Expected %d got %d\n",
+ dwc->ep0_next_event,
+ DWC3_EP0_NRDY_STATUS);
+
+ dwc3_ep0_stall_and_restart(dwc);
+ return;
+ }
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+}
+
+void dwc3_ep0_interrupt(struct dwc3 *dwc,
+ const const struct dwc3_event_depevt *event)
+{
+ u8 epnum = event->endpoint_number;
+
+ dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
+ dwc3_ep_event_string(event->endpoint_event),
+ epnum >> 1, (epnum & 1) ? "in" : "out",
+ dwc3_ep0_state_string(dwc->ep0state));
+
+ switch (event->endpoint_event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_ep0_xfer_complete(dwc, event);
+ break;
+
+ case DWC3_DEPEVT_XFERNOTREADY:
+ dwc3_ep0_xfernotready(dwc, event);
+ break;
+
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ case DWC3_DEPEVT_STREAMEVT:
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ break;
+ }
+}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
new file mode 100644
index 000000000000..fa824cfdd2eb
--- /dev/null
+++ b/drivers/usb/dwc3/gadget.c
@@ -0,0 +1,2104 @@
+/**
+ * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+void dwc3_map_buffer_to_dma(struct dwc3_request *req)
+{
+ struct dwc3 *dwc = req->dep->dwc;
+
+ if (req->request.length == 0) {
+ /* req->request.dma = dwc->setup_buf_addr; */
+ return;
+ }
+
+ if (req->request.dma == DMA_ADDR_INVALID) {
+ req->request.dma = dma_map_single(dwc->dev, req->request.buf,
+ req->request.length, req->direction
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = true;
+ }
+}
+
+void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
+{
+ struct dwc3 *dwc = req->dep->dwc;
+
+ if (req->request.length == 0) {
+ req->request.dma = DMA_ADDR_INVALID;
+ return;
+ }
+
+ if (req->mapped) {
+ dma_unmap_single(dwc->dev, req->request.dma,
+ req->request.length, req->direction
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ req->request.dma = DMA_ADDR_INVALID;
+ }
+}
+
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (req->queued) {
+ dep->busy_slot++;
+ /*
+ * Skip LINK TRB. We can't use req->trb and check for
+ * DWC3_TRBCTL_LINK_TRB because it points the TRB we just
+ * completed (not the LINK TRB).
+ */
+ if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->desc))
+ dep->busy_slot++;
+ }
+ list_del(&req->list);
+
+ if (req->request.status == -EINPROGRESS)
+ req->request.status = status;
+
+ dwc3_unmap_buffer_from_dma(req);
+
+ dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
+ req, dep->name, req->request.actual,
+ req->request.length, status);
+
+ spin_unlock(&dwc->lock);
+ req->request.complete(&req->dep->endpoint, &req->request);
+ spin_lock(&dwc->lock);
+}
+
+static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case DWC3_DEPCMD_DEPSTARTCFG:
+ return "Start New Configuration";
+ case DWC3_DEPCMD_ENDTRANSFER:
+ return "End Transfer";
+ case DWC3_DEPCMD_UPDATETRANSFER:
+ return "Update Transfer";
+ case DWC3_DEPCMD_STARTTRANSFER:
+ return "Start Transfer";
+ case DWC3_DEPCMD_CLEARSTALL:
+ return "Clear Stall";
+ case DWC3_DEPCMD_SETSTALL:
+ return "Set Stall";
+ case DWC3_DEPCMD_GETSEQNUMBER:
+ return "Get Data Sequence Number";
+ case DWC3_DEPCMD_SETTRANSFRESOURCE:
+ return "Set Endpoint Transfer Resource";
+ case DWC3_DEPCMD_SETEPCONFIG:
+ return "Set Endpoint Configuration";
+ default:
+ return "UNKNOWN command";
+ }
+}
+
+int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+{
+ struct dwc3_ep *dep = dwc->eps[ep];
+ u32 timeout = 500;
+ u32 reg;
+
+ dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n",
+ dep->name,
+ dwc3_gadget_ep_cmd_string(cmd), params->param0,
+ params->param1, params->param2);
+
+ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
+ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
+ dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
+
+ dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
+ if (!(reg & DWC3_DEPCMD_CMDACT)) {
+ dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ DWC3_DEPCMD_STATUS(reg));
+ return 0;
+ }
+
+ /*
+ * We can't sleep here, because it is also called from
+ * interrupt context.
+ */
+ timeout--;
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ udelay(1);
+ } while (1);
+}
+
+static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
+ struct dwc3_trb_hw *trb)
+{
+ u32 offset = (char *) trb - (char *) dep->trb_pool;
+
+ return dep->trb_pool_dma + offset;
+}
+
+static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (dep->trb_pool)
+ return 0;
+
+ if (dep->number == 0 || dep->number == 1)
+ return 0;
+
+ dep->trb_pool = dma_alloc_coherent(dwc->dev,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ &dep->trb_pool_dma, GFP_KERNEL);
+ if (!dep->trb_pool) {
+ dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
+ dep->name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void dwc3_free_trb_pool(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ dep->trb_pool, dep->trb_pool_dma);
+
+ dep->trb_pool = NULL;
+ dep->trb_pool_dma = 0;
+}
+
+static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+
+ memset(&params, 0x00, sizeof(params));
+
+ if (dep->number != 1) {
+ cmd = DWC3_DEPCMD_DEPSTARTCFG;
+ /* XferRscIdx == 0 for ep0 and 2 for the remaining */
+ if (dep->number > 1) {
+ if (dwc->start_config_issued)
+ return 0;
+ dwc->start_config_issued = true;
+ cmd |= DWC3_DEPCMD_PARAM(2);
+ }
+
+ return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, &params);
+ }
+
+ return 0;
+}
+
+static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+
+ memset(&params, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
+ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
+ | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
+
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
+ | DWC3_DEPCFG_XFER_NOT_READY_EN;
+
+ if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) {
+ params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+ | DWC3_DEPCFG_STREAM_EVENT_EN;
+ dep->stream_capable = true;
+ }
+
+ if (usb_endpoint_xfer_isoc(desc))
+ params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
+
+ /*
+ * We are doing 1:1 mapping for endpoints, meaning
+ * Physical Endpoints 2 maps to Logical Endpoint 2 and
+ * so on. We consider the direction bit as part of the physical
+ * endpoint number. So USB endpoint 0x81 is 0x03.
+ */
+ params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number);
+
+ /*
+ * We must use the lower 16 TX FIFOs even though
+ * HW might have more
+ */
+ if (dep->direction)
+ params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);
+
+ if (desc->bInterval) {
+ params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1);
+ dep->interval = 1 << (desc->bInterval - 1);
+ }
+
+ return dwc3_send_gadget_ep_cmd(dwc, dep->number,
+ DWC3_DEPCMD_SETEPCONFIG, &params);
+}
+
+static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+
+ memset(&params, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
+
+ return dwc3_send_gadget_ep_cmd(dwc, dep->number,
+ DWC3_DEPCMD_SETTRANSFRESOURCE, &params);
+}
+
+/**
+ * __dwc3_gadget_ep_enable - Initializes a HW endpoint
+ * @dep: endpoint to be initialized
+ * @desc: USB Endpoint Descriptor
+ *
+ * Caller should take care of locking
+ */
+static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+ int ret = -ENOMEM;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_start_config(dwc, dep);
+ if (ret)
+ return ret;
+ }
+
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc);
+ if (ret)
+ return ret;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ struct dwc3_trb_hw *trb_st_hw;
+ struct dwc3_trb_hw *trb_link_hw;
+ struct dwc3_trb trb_link;
+
+ ret = dwc3_gadget_set_xfer_resource(dwc, dep);
+ if (ret)
+ return ret;
+
+ dep->desc = desc;
+ dep->type = usb_endpoint_type(desc);
+ dep->flags |= DWC3_EP_ENABLED;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg |= DWC3_DALEPENA_EP(dep->number);
+ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+
+ if (!usb_endpoint_xfer_isoc(desc))
+ return 0;
+
+ memset(&trb_link, 0, sizeof(trb_link));
+
+ /* Link TRB for ISOC. The HWO but is never reset */
+ trb_st_hw = &dep->trb_pool[0];
+
+ trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw);
+ trb_link.trbctl = DWC3_TRBCTL_LINK_TRB;
+ trb_link.hwo = true;
+
+ trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1];
+ dwc3_trb_to_hw(&trb_link, trb_link_hw);
+ }
+
+ return 0;
+}
+
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
+static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+
+ if (!list_empty(&dep->req_queued))
+ dwc3_stop_active_transfer(dwc, dep->number);
+
+ while (!list_empty(&dep->request_list)) {
+ req = next_request(&dep->request_list);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+}
+
+/**
+ * __dwc3_gadget_ep_disable - Disables a HW endpoint
+ * @dep: the endpoint to disable
+ *
+ * This function also removes requests which are currently processed ny the
+ * hardware and those which are not yet scheduled.
+ * Caller should take care of locking.
+ */
+static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+
+ dwc3_remove_requests(dwc, dep);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg &= ~DWC3_DALEPENA_EP(dep->number);
+ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+
+ dep->stream_capable = false;
+ dep->desc = NULL;
+ dep->type = 0;
+ dep->flags = 0;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+static int dwc3_gadget_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("dwc3: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!desc->wMaxPacketSize) {
+ pr_debug("dwc3: missing wMaxPacketSize\n");
+ return -EINVAL;
+ }
+
+ dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
+
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ strncat(dep->name, "-control", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ strncat(dep->name, "-isoc", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ strncat(dep->name, "-bulk", sizeof(dep->name));
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ strncat(dep->name, "-int", sizeof(dep->name));
+ break;
+ default:
+ dev_err(dwc->dev, "invalid endpoint transfer type\n");
+ }
+
+ if (dep->flags & DWC3_EP_ENABLED) {
+ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
+ dep->name);
+ return 0;
+ }
+
+ dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_enable(dep, desc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct dwc3_ep *dep;
+ struct dwc3 *dwc;
+ unsigned long flags;
+ int ret;
+
+ if (!ep) {
+ pr_debug("dwc3: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ dep = to_dwc3_ep(ep);
+ dwc = dep->dwc;
+
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
+ dep->name);
+ return 0;
+ }
+
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+ dep->number >> 1,
+ (dep->number & 1) ? "in" : "out");
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_disable(dep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct dwc3_request *req;
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req) {
+ dev_err(dwc->dev, "not enough memory\n");
+ return NULL;
+ }
+
+ req->epnum = dep->number;
+ req->dep = dep;
+ req->request.dma = DMA_ADDR_INVALID;
+
+ return &req->request;
+}
+
+static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+
+ kfree(req);
+}
+
+/*
+ * dwc3_prepare_trbs - setup TRBs from requests
+ * @dep: endpoint for which requests are being prepared
+ * @starting: true if the endpoint is idle and no requests are queued.
+ *
+ * The functions goes through the requests list and setups TRBs for the
+ * transfers. The functions returns once there are not more TRBs available or
+ * it run out of requests.
+ */
+static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
+ bool starting)
+{
+ struct dwc3_request *req, *n, *ret = NULL;
+ struct dwc3_trb_hw *trb_hw;
+ struct dwc3_trb trb;
+ u32 trbs_left;
+
+ BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
+
+ /* the first request must not be queued */
+ trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
+ /*
+ * if busy & slot are equal than it is either full or empty. If we are
+ * starting to proceed requests then we are empty. Otherwise we ar
+ * full and don't do anything
+ */
+ if (!trbs_left) {
+ if (!starting)
+ return NULL;
+ trbs_left = DWC3_TRB_NUM;
+ /*
+ * In case we start from scratch, we queue the ISOC requests
+ * starting from slot 1. This is done because we use ring
+ * buffer and have no LST bit to stop us. Instead, we place
+ * IOC bit TRB_NUM/4. We try to avoid to having an interrupt
+ * after the first request so we start at slot 1 and have
+ * 7 requests proceed before we hit the first IOC.
+ * Other transfer types don't use the ring buffer and are
+ * processed from the first TRB until the last one. Since we
+ * don't wrap around we have to start at the beginning.
+ */
+ if (usb_endpoint_xfer_isoc(dep->desc)) {
+ dep->busy_slot = 1;
+ dep->free_slot = 1;
+ } else {
+ dep->busy_slot = 0;
+ dep->free_slot = 0;
+ }
+ }
+
+ /* The last TRB is a link TRB, not used for xfer */
+ if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
+ return NULL;
+
+ list_for_each_entry_safe(req, n, &dep->request_list, list) {
+ unsigned int last_one = 0;
+ unsigned int cur_slot;
+
+ trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+ cur_slot = dep->free_slot;
+ dep->free_slot++;
+
+ /* Skip the LINK-TRB on ISOC */
+ if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->desc))
+ continue;
+
+ dwc3_gadget_move_request_queued(req);
+ memset(&trb, 0, sizeof(trb));
+ trbs_left--;
+
+ /* Is our TRB pool empty? */
+ if (!trbs_left)
+ last_one = 1;
+ /* Is this the last request? */
+ if (list_empty(&dep->request_list))
+ last_one = 1;
+
+ /*
+ * FIXME we shouldn't need to set LST bit always but we are
+ * facing some weird problem with the Hardware where it doesn't
+ * complete even though it has been previously started.
+ *
+ * While we're debugging the problem, as a workaround to
+ * multiple TRBs handling, use only one TRB at a time.
+ */
+ last_one = 1;
+
+ req->trb = trb_hw;
+ if (!ret)
+ ret = req;
+
+ trb.bplh = req->request.dma;
+
+ if (usb_endpoint_xfer_isoc(dep->desc)) {
+ trb.isp_imi = true;
+ trb.csp = true;
+ } else {
+ trb.lst = last_one;
+ }
+
+ if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
+ trb.sid_sofn = req->request.stream_id;
+
+ switch (usb_endpoint_type(dep->desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
+
+ /* IOC every DWC3_TRB_NUM / 4 so we can refill */
+ if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+ trb.ioc = last_one;
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ trb.trbctl = DWC3_TRBCTL_NORMAL;
+ break;
+ default:
+ /*
+ * This is only possible with faulty memory because we
+ * checked it already :)
+ */
+ BUG();
+ }
+
+ trb.length = req->request.length;
+ trb.hwo = true;
+
+ dwc3_trb_to_hw(&trb, trb_hw);
+ req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);
+
+ if (last_one)
+ break;
+ }
+
+ return ret;
+}
+
+static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
+ int start_new)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_request *req;
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+ u32 cmd;
+
+ if (start_new && (dep->flags & DWC3_EP_BUSY)) {
+ dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name);
+ return -EBUSY;
+ }
+ dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+
+ /*
+ * If we are getting here after a short-out-packet we don't enqueue any
+ * new requests as we try to set the IOC bit only on the last request.
+ */
+ if (start_new) {
+ if (list_empty(&dep->req_queued))
+ dwc3_prepare_trbs(dep, start_new);
+
+ /* req points to the first request which will be sent */
+ req = next_request(&dep->req_queued);
+ } else {
+ /*
+ * req points to the first request where HWO changed
+ * from 0 to 1
+ */
+ req = dwc3_prepare_trbs(dep, start_new);
+ }
+ if (!req) {
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+ return 0;
+ }
+
+ memset(&params, 0, sizeof(params));
+ params.param0 = upper_32_bits(req->trb_dma);
+ params.param1 = lower_32_bits(req->trb_dma);
+
+ if (start_new)
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+ else
+ cmd = DWC3_DEPCMD_UPDATETRANSFER;
+
+ cmd |= DWC3_DEPCMD_PARAM(cmd_param);
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+ if (ret < 0) {
+ dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
+
+ /*
+ * FIXME we need to iterate over the list of requests
+ * here and stop, unmap, free and del each of the linked
+ * requests instead of we do now.
+ */
+ dwc3_unmap_buffer_from_dma(req);
+ list_del(&req->list);
+ return ret;
+ }
+
+ dep->flags |= DWC3_EP_BUSY;
+ dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->number);
+ if (!dep->res_trans_idx)
+ printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__);
+ return 0;
+}
+
+static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ req->request.actual = 0;
+ req->request.status = -EINPROGRESS;
+ req->direction = dep->direction;
+ req->epnum = dep->number;
+
+ /*
+ * We only add to our list of requests now and
+ * start consuming the list once we get XferNotReady
+ * IRQ.
+ *
+ * That way, we avoid doing anything that we don't need
+ * to do now and defer it until the point we receive a
+ * particular token from the Host side.
+ *
+ * This will also avoid Host cancelling URBs due to too
+ * many NACKs.
+ */
+ dwc3_map_buffer_to_dma(req);
+ list_add_tail(&req->list, &dep->request_list);
+
+ /*
+ * There is one special case: XferNotReady with
+ * empty list of requests. We need to kick the
+ * transfer here in that situation, otherwise
+ * we will be NAKing forever.
+ *
+ * If we get XferNotReady before gadget driver
+ * has a chance to queue a request, we will ACK
+ * the IRQ but won't be able to receive the data
+ * until the next request is queued. The following
+ * code is handling exactly that.
+ */
+ if (dep->flags & DWC3_EP_PENDING_REQUEST) {
+ int ret;
+ int start_trans;
+
+ start_trans = 1;
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ dep->flags & DWC3_EP_BUSY)
+ start_trans = 0;
+
+ ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
+ if (ret && ret != -EBUSY) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dep->name);
+ }
+ };
+
+ return 0;
+}
+
+static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+
+ int ret;
+
+ if (!dep->desc) {
+ dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
+ request, ep->name);
+ return -ESHUTDOWN;
+ }
+
+ dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
+ request, ep->name, request->length);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ ret = __dwc3_gadget_ep_queue(dep, req);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct dwc3_request *req = to_dwc3_request(request);
+ struct dwc3_request *r = NULL;
+
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ list_for_each_entry(r, &dep->request_list, list) {
+ if (r == req)
+ break;
+ }
+
+ if (r != req) {
+ list_for_each_entry(r, &dep->req_queued, list) {
+ if (r == req)
+ break;
+ }
+ if (r == req) {
+ /* wait until it is processed */
+ dwc3_stop_active_transfer(dwc, dep->number);
+ goto out0;
+ }
+ dev_err(dwc->dev, "request %p was not queued to %s\n",
+ request, ep->name);
+ ret = -EINVAL;
+ goto out0;
+ }
+
+ /* giveback the request */
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+
+out0:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
+ int ret;
+
+ memset(&params, 0x00, sizeof(params));
+
+ 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,
+ DWC3_DEPCMD_SETSTALL, &params);
+ if (ret)
+ dev_err(dwc->dev, "failed to %s STALL on %s\n",
+ value ? "set" : "clear",
+ dep->name);
+ else
+ dep->flags |= DWC3_EP_STALL;
+ } else {
+ if (dep->flags & DWC3_EP_WEDGE)
+ return 0;
+
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
+ DWC3_DEPCMD_CLEARSTALL, &params);
+ if (ret)
+ dev_err(dwc->dev, "failed to %s STALL on %s\n",
+ value ? "set" : "clear",
+ dep->name);
+ else
+ dep->flags &= ~DWC3_EP_STALL;
+ }
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ unsigned long flags;
+
+ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ if (usb_endpoint_xfer_isoc(dep->desc)) {
+ dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = __dwc3_gadget_ep_set_halt(dep, value);
+out:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+
+ dep->flags |= DWC3_EP_WEDGE;
+
+ return dwc3_gadget_ep_set_halt(ep, 1);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
+ .enable = dwc3_gadget_ep0_enable,
+ .disable = dwc3_gadget_ep0_disable,
+ .alloc_request = dwc3_gadget_ep_alloc_request,
+ .free_request = dwc3_gadget_ep_free_request,
+ .queue = dwc3_gadget_ep0_queue,
+ .dequeue = dwc3_gadget_ep_dequeue,
+ .set_halt = dwc3_gadget_ep_set_halt,
+ .set_wedge = dwc3_gadget_ep_set_wedge,
+};
+
+static const struct usb_ep_ops dwc3_gadget_ep_ops = {
+ .enable = dwc3_gadget_ep_enable,
+ .disable = dwc3_gadget_ep_disable,
+ .alloc_request = dwc3_gadget_ep_alloc_request,
+ .free_request = dwc3_gadget_ep_free_request,
+ .queue = dwc3_gadget_ep_queue,
+ .dequeue = dwc3_gadget_ep_dequeue,
+ .set_halt = dwc3_gadget_ep_set_halt,
+ .set_wedge = dwc3_gadget_ep_set_wedge,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_gadget_get_frame(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ return DWC3_DSTS_SOFFN(reg);
+}
+
+static int dwc3_gadget_wakeup(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ unsigned long timeout;
+ unsigned long flags;
+
+ u32 reg;
+
+ int ret = 0;
+
+ u8 link_state;
+ u8 speed;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * According to the Databook Remote wakeup request should
+ * be issued only when the device is in early suspend state.
+ *
+ * We can check that via USB Link State bits in DSTS register.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+ if (speed == DWC3_DSTS_SUPERSPEED) {
+ dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ link_state = DWC3_DSTS_USBLNKST(reg);
+
+ switch (link_state) {
+ case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
+ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
+ break;
+ default:
+ dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
+ link_state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+
+ /*
+ * Switch link state to Recovery. In HS/FS/LS this means
+ * RemoteWakeup Request
+ */
+ reg |= DWC3_DCTL_ULSTCHNG_RECOVERY;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ /* wait for at least 2000us */
+ usleep_range(2000, 2500);
+
+ /* write zeroes to Link Change Request */
+ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ /* pool until Link State change to ON */
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (!(time_after(jiffies, timeout))) {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+ /* in HS, means ON */
+ if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
+ break;
+ }
+
+ if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
+ dev_err(dwc->dev, "failed to send remote wakeup\n");
+ ret = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
+ int is_selfpowered)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+
+ dwc->is_selfpowered = !!is_selfpowered;
+
+ return 0;
+}
+
+static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+{
+ u32 reg;
+ u32 timeout = 500;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (is_on)
+ reg |= DWC3_DCTL_RUN_STOP;
+ else
+ reg &= ~DWC3_DCTL_RUN_STOP;
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (is_on) {
+ if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+ break;
+ } else {
+ if (reg & DWC3_DSTS_DEVCTRLHLT)
+ break;
+ }
+ timeout--;
+ if (!timeout)
+ break;
+ udelay(1);
+ } while (1);
+
+ dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
+ dwc->gadget_driver
+ ? dwc->gadget_driver->function : "no-function",
+ is_on ? "connect" : "disconnect");
+}
+
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ is_on = !!is_on;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_run_stop(dwc, is_on);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static int dwc3_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_ep *dep;
+ unsigned long flags;
+ int ret = 0;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ if (dwc->gadget_driver) {
+ dev_err(dwc->dev, "%s is already bound to %s\n",
+ dwc->gadget.name,
+ dwc->gadget_driver->driver.name);
+ ret = -EBUSY;
+ goto err0;
+ }
+
+ dwc->gadget_driver = driver;
+ dwc->gadget.dev.driver = &driver->driver;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+ reg &= ~DWC3_GCTL_SCALEDOWN(3);
+ reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG);
+ reg &= ~DWC3_GCTL_DISSCRAMBLE;
+ reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
+
+ switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) {
+ case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
+ reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+ break;
+ default:
+ dev_dbg(dwc->dev, "No power optimization available\n");
+ }
+
+ /*
+ * WORKAROUND: DWC3 revisions <1.90a have a bug
+ * when The device fails to connect at SuperSpeed
+ * and falls back to high-speed mode which causes
+ * the device to enter in a Connect/Disconnect loop
+ */
+ if (dwc->revision < DWC3_REVISION_190A)
+ reg |= DWC3_GCTL_U2RSTECN;
+
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_SPEED_MASK);
+ reg |= DWC3_DCFG_SUPERSPEED;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ dwc->start_config_issued = false;
+
+ /* Start with SuperSpeed Default */
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err0;
+ }
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ goto err1;
+ }
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+
+err1:
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+err0:
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return ret;
+}
+
+static int dwc3_gadget_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
+ dwc->gadget_driver = NULL;
+ dwc->gadget.dev.driver = NULL;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+static const struct usb_gadget_ops dwc3_gadget_ops = {
+ .get_frame = dwc3_gadget_get_frame,
+ .wakeup = dwc3_gadget_wakeup,
+ .set_selfpowered = dwc3_gadget_set_selfpowered,
+ .pullup = dwc3_gadget_pullup,
+ .udc_start = dwc3_gadget_start,
+ .udc_stop = dwc3_gadget_stop,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ u8 epnum;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
+
+ for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ if (!dep) {
+ dev_err(dwc->dev, "can't allocate endpoint %d\n",
+ epnum);
+ return -ENOMEM;
+ }
+
+ dep->dwc = dwc;
+ dep->number = epnum;
+ dwc->eps[epnum] = dep;
+
+ snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
+ (epnum & 1) ? "in" : "out");
+ dep->endpoint.name = dep->name;
+ dep->direction = (epnum & 1);
+
+ if (epnum == 0 || epnum == 1) {
+ dep->endpoint.maxpacket = 512;
+ dep->endpoint.ops = &dwc3_gadget_ep0_ops;
+ if (!epnum)
+ dwc->gadget.ep0 = &dep->endpoint;
+ } else {
+ int ret;
+
+ dep->endpoint.maxpacket = 1024;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ ret = dwc3_alloc_trb_pool(dep);
+ if (ret) {
+ dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name);
+ return ret;
+ }
+ }
+ INIT_LIST_HEAD(&dep->request_list);
+ INIT_LIST_HEAD(&dep->req_queued);
+ }
+
+ return 0;
+}
+
+static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ u8 epnum;
+
+ for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ dep = dwc->eps[epnum];
+ dwc3_free_trb_pool(dep);
+
+ if (epnum != 0 && epnum != 1)
+ list_del(&dep->endpoint.ep_list);
+
+ kfree(dep);
+ }
+}
+
+static void dwc3_gadget_release(struct device *dev)
+{
+ dev_dbg(dev, "%s\n", __func__);
+}
+
+/* -------------------------------------------------------------------------- */
+static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
+{
+ struct dwc3_request *req;
+ struct dwc3_trb trb;
+ unsigned int count;
+ unsigned int s_pkt = 0;
+
+ do {
+ req = next_request(&dep->req_queued);
+ if (!req)
+ break;
+
+ dwc3_trb_to_nat(req->trb, &trb);
+
+ if (trb.hwo && status != -ESHUTDOWN)
+ /*
+ * We continue despite the error. There is not much we
+ * can do. If we don't clean in up we loop for ever. If
+ * we skip the TRB than it gets overwritten reused after
+ * a while since we use them in a ring buffer. a BUG()
+ * would help. Lets hope that if this occures, someone
+ * fixes the root cause instead of looking away :)
+ */
+ dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
+ dep->name, req->trb);
+ count = trb.length;
+
+ if (dep->direction) {
+ if (count) {
+ dev_err(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ status = -ECONNRESET;
+ }
+ } else {
+ if (count && (event->status & DEPEVT_STATUS_SHORT))
+ s_pkt = 1;
+ }
+
+ /*
+ * We assume here we will always receive the entire data block
+ * which we should receive. Meaning, if we program RX to
+ * receive 4K but we receive only 2K, we assume that's all we
+ * should receive and we simply bounce the request back to the
+ * gadget driver for further processing.
+ */
+ req->request.actual += req->request.length - count;
+ dwc3_gadget_giveback(dep, req, status);
+ if (s_pkt)
+ break;
+ if ((event->status & DEPEVT_STATUS_LST) && trb.lst)
+ break;
+ if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc)
+ break;
+ } while (1);
+
+ if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc)
+ return 0;
+ return 1;
+}
+
+static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event,
+ int start_new)
+{
+ unsigned status = 0;
+ int clean_busy;
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
+ if (clean_busy) {
+ dep->flags &= ~DWC3_EP_BUSY;
+ dep->res_trans_idx = 0;
+ }
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+ u32 uf;
+
+ if (list_empty(&dep->request_list)) {
+ dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+ dep->name);
+ return;
+ }
+
+ if (event->parameters) {
+ u32 mask;
+
+ mask = ~(dep->interval - 1);
+ uf = event->parameters & mask;
+ /* 4 micro frames in the future */
+ uf += dep->interval * 4;
+ } else {
+ uf = 0;
+ }
+
+ __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 requests. It is possible that this
+ * request and a few other 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 requets 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,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep;
+ u8 epnum = event->endpoint_number;
+
+ dep = dwc->eps[epnum];
+
+ dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
+ dwc3_ep_event_string(event->endpoint_event));
+
+ if (epnum == 0 || epnum == 1) {
+ dwc3_ep0_interrupt(dwc, event);
+ return;
+ }
+
+ switch (event->endpoint_event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ if (usb_endpoint_xfer_isoc(dep->desc)) {
+ dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
+ dep->name);
+ return;
+ }
+
+ dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
+ break;
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ if (!usb_endpoint_xfer_isoc(dep->desc)) {
+ dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
+ dep->name);
+ return;
+ }
+
+ dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
+ break;
+ case DWC3_DEPEVT_XFERNOTREADY:
+ if (usb_endpoint_xfer_isoc(dep->desc)) {
+ dwc3_gadget_start_isoc(dwc, dep, event);
+ } else {
+ int ret;
+
+ dev_vdbg(dwc->dev, "%s: reason %s\n",
+ dep->name, event->status
+ ? "Transfer Active"
+ : "Transfer Not Active");
+
+ ret = __dwc3_gadget_kick_transfer(dep, 0, 1);
+ if (!ret || ret == -EBUSY)
+ return;
+
+ dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dep->name);
+ }
+
+ break;
+ case DWC3_DEPEVT_STREAMEVT:
+ if (!usb_endpoint_xfer_bulk(dep->desc)) {
+ dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
+ dep->name);
+ return;
+ }
+
+ switch (event->status) {
+ case DEPEVT_STREAMEVT_FOUND:
+ dev_vdbg(dwc->dev, "Stream %d found and started\n",
+ event->parameters);
+
+ break;
+ case DEPEVT_STREAMEVT_NOTFOUND:
+ /* FALLTHROUGH */
+ default:
+ dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
+ }
+ break;
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
+ break;
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ dwc3_ep_cmd_compl(dep, event);
+ break;
+ }
+}
+
+static void dwc3_disconnect_gadget(struct dwc3 *dwc)
+{
+ if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+ spin_unlock(&dwc->lock);
+ dwc->gadget_driver->disconnect(&dwc->gadget);
+ spin_lock(&dwc->lock);
+ }
+}
+
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
+{
+ struct dwc3_ep *dep;
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ dep = dwc->eps[epnum];
+
+ WARN_ON(!dep->res_trans_idx);
+ if (dep->res_trans_idx) {
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+ WARN_ON_ONCE(ret);
+ dep->res_trans_idx = 0;
+ }
+}
+
+static void dwc3_stop_active_transfers(struct dwc3 *dwc)
+{
+ u32 epnum;
+
+ for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ struct dwc3_ep *dep;
+
+ dep = dwc->eps[epnum];
+ if (!(dep->flags & DWC3_EP_ENABLED))
+ continue;
+
+ dwc3_remove_requests(dwc, dep);
+ }
+}
+
+static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
+{
+ u32 epnum;
+
+ for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ struct dwc3_ep *dep;
+ struct dwc3_gadget_ep_cmd_params params;
+ int ret;
+
+ dep = dwc->eps[epnum];
+
+ if (!(dep->flags & DWC3_EP_STALL))
+ continue;
+
+ dep->flags &= ~DWC3_EP_STALL;
+
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
+ DWC3_DEPCMD_CLEARSTALL, &params);
+ WARN_ON_ONCE(ret);
+ }
+}
+
+static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+{
+ 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_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+#endif
+
+ dwc3_stop_active_transfers(dwc);
+ dwc3_disconnect_gadget(dwc);
+ dwc->start_config_issued = false;
+
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+
+ if (on)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+}
+
+static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+
+ if (on)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+}
+
+static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ dev_vdbg(dwc->dev, "%s\n", __func__);
+
+ /* Enable PHYs */
+ dwc3_gadget_usb2_phy_power(dwc, true);
+ dwc3_gadget_usb3_phy_power(dwc, true);
+
+ if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
+ dwc3_disconnect_gadget(dwc);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_TSTCTRL_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc3_stop_active_transfers(dwc);
+ dwc3_clear_stall_all_ep(dwc);
+ dwc->start_config_issued = false;
+
+ /* Reset device address to zero */
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~(DWC3_DCFG_DEVADDR_MASK);
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
+{
+ u32 reg;
+ u32 usb30_clock = DWC3_GCTL_CLK_BUS;
+
+ /*
+ * We change the clock only at SS but I dunno why I would want to do
+ * this. Maybe it becomes part of the power saving plan.
+ */
+
+ if (speed != DWC3_DSTS_SUPERSPEED)
+ return;
+
+ /*
+ * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
+ * each time on Connect Done.
+ */
+ if (!usb30_clock)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+{
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ dwc3_gadget_usb2_phy_power(dwc, false);
+ break;
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ dwc3_gadget_usb3_phy_power(dwc, false);
+ break;
+ }
+}
+
+static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+ u32 reg;
+ u8 speed;
+
+ dev_vdbg(dwc->dev, "%s\n", __func__);
+
+ memset(&params, 0x00, sizeof(params));
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+ dwc->speed = speed;
+
+ dwc3_update_ram_clk_sel(dwc, speed);
+
+ switch (speed) {
+ case DWC3_DCFG_SUPERSPEED:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ dwc->gadget.ep0->maxpacket = 512;
+ dwc->gadget.speed = USB_SPEED_SUPER;
+ break;
+ case DWC3_DCFG_HIGHSPEED:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case DWC3_DCFG_FULLSPEED2:
+ case DWC3_DCFG_FULLSPEED1:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ dwc->gadget.ep0->maxpacket = 64;
+ dwc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case DWC3_DCFG_LOWSPEED:
+ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
+ dwc->gadget.ep0->maxpacket = 8;
+ dwc->gadget.speed = USB_SPEED_LOW;
+ break;
+ }
+
+ /* Disable unneded PHY */
+ dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
+
+ dep = dwc->eps[0];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ dep = dwc->eps[1];
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ /*
+ * Configure PHY via GUSB3PIPECTLn if required.
+ *
+ * Update GTXFIFOSIZn
+ *
+ * In both cases reset values should be sufficient.
+ */
+}
+
+static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
+{
+ dev_vdbg(dwc->dev, "%s\n", __func__);
+
+ /*
+ * TODO take core out of low power mode when that's
+ * implemented.
+ */
+
+ dwc->gadget_driver->resume(&dwc->gadget);
+}
+
+static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
+ unsigned int evtinfo)
+{
+ /* The fith bit says SuperSpeed yes or no. */
+ dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
+
+ dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
+}
+
+static void dwc3_gadget_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_devt *event)
+{
+ switch (event->type) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ dwc3_gadget_disconnect_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_RESET:
+ dwc3_gadget_reset_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ dwc3_gadget_conndone_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ dwc3_gadget_wakeup_interrupt(dwc);
+ break;
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
+ break;
+ case DWC3_DEVICE_EVENT_EOPF:
+ dev_vdbg(dwc->dev, "End of Periodic Frame\n");
+ break;
+ case DWC3_DEVICE_EVENT_SOF:
+ dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
+ break;
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ dev_vdbg(dwc->dev, "Erratic Error\n");
+ break;
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ dev_vdbg(dwc->dev, "Command Complete\n");
+ break;
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ dev_vdbg(dwc->dev, "Overflow\n");
+ break;
+ default:
+ dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ }
+}
+
+static void dwc3_process_event_entry(struct dwc3 *dwc,
+ const union dwc3_event *event)
+{
+ /* Endpoint IRQ, handle it and return early */
+ if (event->type.is_devspec == 0) {
+ /* depevt */
+ return dwc3_endpoint_interrupt(dwc, &event->depevt);
+ }
+
+ switch (event->type.type) {
+ case DWC3_EVENT_TYPE_DEV:
+ dwc3_gadget_interrupt(dwc, &event->devt);
+ break;
+ /* REVISIT what to do with Carkit and I2C events ? */
+ default:
+ dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw);
+ }
+}
+
+static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
+{
+ struct dwc3_event_buffer *evt;
+ int left;
+ u32 count;
+
+ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
+ count &= DWC3_GEVNTCOUNT_MASK;
+ if (!count)
+ return IRQ_NONE;
+
+ evt = dwc->ev_buffs[buf];
+ left = count;
+
+ while (left > 0) {
+ union dwc3_event event;
+
+ memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw));
+ dwc3_process_event_entry(dwc, &event);
+ /*
+ * XXX we wrap around correctly to the next entry as almost all
+ * entries are 4 bytes in size. There is one entry which has 12
+ * bytes which is a regular entry followed by 8 bytes data. ATM
+ * I don't know how things are organized if were get next to the
+ * a boundary so I worry about that once we try to handle that.
+ */
+ evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
+ left -= 4;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ int i;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&dwc->lock);
+
+ for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
+ irqreturn_t status;
+
+ status = dwc3_process_event_buf(dwc, i);
+ if (status == IRQ_HANDLED)
+ ret = status;
+ }
+
+ spin_unlock(&dwc->lock);
+
+ return ret;
+}
+
+/**
+ * dwc3_gadget_init - Initializes gadget related registers
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+int __devinit dwc3_gadget_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret;
+ int irq;
+
+ dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ &dwc->ctrl_req_addr, GFP_KERNEL);
+ if (!dwc->ctrl_req) {
+ dev_err(dwc->dev, "failed to allocate ctrl request\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ &dwc->ep0_trb_addr, GFP_KERNEL);
+ if (!dwc->ep0_trb) {
+ dev_err(dwc->dev, "failed to allocate ep0 trb\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dwc->setup_buf = dma_alloc_coherent(dwc->dev,
+ sizeof(*dwc->setup_buf) * 2,
+ &dwc->setup_buf_addr, GFP_KERNEL);
+ if (!dwc->setup_buf) {
+ dev_err(dwc->dev, "failed to allocate setup buffer\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+ 512, &dwc->ep0_bounce_addr, GFP_KERNEL);
+ if (!dwc->ep0_bounce) {
+ dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ dev_set_name(&dwc->gadget.dev, "gadget");
+
+ dwc->gadget.ops = &dwc3_gadget_ops;
+ dwc->gadget.is_dualspeed = true;
+ dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget.dev.parent = dwc->dev;
+
+ dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);
+
+ dwc->gadget.dev.dma_parms = dwc->dev->dma_parms;
+ dwc->gadget.dev.dma_mask = dwc->dev->dma_mask;
+ dwc->gadget.dev.release = dwc3_gadget_release;
+ dwc->gadget.name = "dwc3-gadget";
+
+ /*
+ * REVISIT: Here we should clear all pending IRQs to be
+ * sure we're starting from a well known location.
+ */
+
+ ret = dwc3_gadget_init_endpoints(dwc);
+ if (ret)
+ goto err4;
+
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+
+ ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED,
+ "dwc3", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ irq, ret);
+ goto err5;
+ }
+
+ /* Enable all but Start and End of Frame IRQs */
+ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
+ DWC3_DEVTEN_EVNTOVERFLOWEN |
+ DWC3_DEVTEN_CMDCMPLTEN |
+ DWC3_DEVTEN_ERRTICERREN |
+ DWC3_DEVTEN_WKUPEVTEN |
+ DWC3_DEVTEN_ULSTCNGEN |
+ DWC3_DEVTEN_CONNECTDONEEN |
+ DWC3_DEVTEN_USBRSTEN |
+ DWC3_DEVTEN_DISCONNEVTEN);
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+
+ ret = device_register(&dwc->gadget.dev);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register gadget device\n");
+ put_device(&dwc->gadget.dev);
+ goto err6;
+ }
+
+ ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register udc\n");
+ goto err7;
+ }
+
+ return 0;
+
+err7:
+ device_unregister(&dwc->gadget.dev);
+
+err6:
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+ free_irq(irq, dwc);
+
+err5:
+ dwc3_gadget_free_endpoints(dwc);
+
+err4:
+ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+ dwc->ep0_bounce_addr);
+
+err3:
+ dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
+ dwc->setup_buf, dwc->setup_buf_addr);
+
+err2:
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
+
+err1:
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+
+err0:
+ return ret;
+}
+
+void dwc3_gadget_exit(struct dwc3 *dwc)
+{
+ int irq;
+ int i;
+
+ usb_del_gadget_udc(&dwc->gadget);
+ irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+
+ dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+ free_irq(irq, dwc);
+
+ for (i = 0; i < ARRAY_SIZE(dwc->eps); i++)
+ __dwc3_gadget_ep_disable(dwc->eps[i]);
+
+ dwc3_gadget_free_endpoints(dwc);
+
+ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+ dwc->ep0_bounce_addr);
+
+ dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
+ dwc->setup_buf, dwc->setup_buf_addr);
+
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dwc->ep0_trb, dwc->ep0_trb_addr);
+
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req, dwc->ctrl_req_addr);
+
+ device_unregister(&dwc->gadget.dev);
+}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
new file mode 100644
index 000000000000..71145a449d99
--- /dev/null
+++ b/drivers/usb/dwc3/gadget.h
@@ -0,0 +1,211 @@
+/**
+ * gadget.h - DesignWare USB3 DRD Gadget Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DRIVERS_USB_DWC3_GADGET_H
+#define __DRIVERS_USB_DWC3_GADGET_H
+
+#include <linux/list.h>
+#include <linux/usb/gadget.h>
+#include "io.h"
+
+struct dwc3;
+#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
+#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+
+/* DEPCFG parameter 1 */
+#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
+#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
+#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
+#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
+#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
+#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_BULK_BASED (1 << 30)
+#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
+
+/* DEPCFG parameter 0 */
+#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
+#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+
+/* DEPXFERCFG parameter 0 */
+#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
+
+struct dwc3_gadget_ep_cmd_params {
+ u32 param2;
+ u32 param1;
+ u32 param0;
+};
+
+/* -------------------------------------------------------------------------- */
+
+struct dwc3_request {
+ struct usb_request request;
+ struct list_head list;
+ struct dwc3_ep *dep;
+
+ u8 epnum;
+ struct dwc3_trb_hw *trb;
+ dma_addr_t trb_dma;
+
+ unsigned direction:1;
+ unsigned mapped:1;
+ unsigned queued:1;
+};
+#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
+
+static inline struct dwc3_request *next_request(struct list_head *list)
+{
+ if (list_empty(list))
+ return NULL;
+
+ return list_first_entry(list, struct dwc3_request, list);
+}
+
+static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
+{
+ struct dwc3_ep *dep = req->dep;
+
+ req->queued = true;
+ list_move_tail(&req->list, &dep->req_queued);
+}
+
+#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE)
+int dwc3_gadget_init(struct dwc3 *dwc);
+void dwc3_gadget_exit(struct dwc3 *dwc);
+#else
+static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; }
+static inline void dwc3_gadget_exit(struct dwc3 *dwc) { }
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+{
+ return 0;
+}
+#endif
+
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status);
+
+void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event);
+void dwc3_ep0_out_start(struct dwc3 *dwc);
+int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags);
+int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
+int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+ unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+void dwc3_map_buffer_to_dma(struct dwc3_request *req);
+void dwc3_unmap_buffer_from_dma(struct dwc3_request *req);
+
+/**
+ * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
+ * @dwc: DesignWare USB3 Pointer
+ * @number: DWC endpoint number
+ *
+ * Caller should take care of locking
+ */
+static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
+{
+ u32 res_id;
+
+ res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
+
+ return DWC3_DEPCMD_GET_RSC_IDX(res_id);
+}
+
+/**
+ * dwc3_gadget_event_string - returns event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ return "Disconnect";
+ case DWC3_DEVICE_EVENT_RESET:
+ return "Reset";
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ return "Connection Done";
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ return "Link Status Change";
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ return "WakeUp";
+ case DWC3_DEVICE_EVENT_EOPF:
+ return "End-Of-Frame";
+ case DWC3_DEVICE_EVENT_SOF:
+ return "Start-Of-Frame";
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ return "Erratic Error";
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ return "Command Complete";
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ return "Overflow";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * dwc3_ep_event_string - returns event name
+ * @event: then event code
+ */
+static inline const char *dwc3_ep_event_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ return "Transfer Complete";
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ return "Transfer In-Progress";
+ case DWC3_DEPEVT_XFERNOTREADY:
+ return "Transfer Not Ready";
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ return "FIFO";
+ case DWC3_DEPEVT_STREAMEVT:
+ return "Stream";
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ return "Endpoint Command Complete";
+ }
+
+ return "UNKNOWN";
+}
+
+#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
new file mode 100644
index 000000000000..bc957db1ea4b
--- /dev/null
+++ b/drivers/usb/dwc3/io.h
@@ -0,0 +1,54 @@
+/**
+ * io.h - DesignWare USB3 DRD IO Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DRIVERS_USB_DWC3_IO_H
+#define __DRIVERS_USB_DWC3_IO_H
+
+#include <asm/io.h>
+
+static inline u32 dwc3_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel(value, base + offset);
+}
+
+#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 5a084b9cfa3c..b21cd376c11a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -96,6 +96,22 @@ config USB_GADGET_VBUS_DRAW
This value will be used except for system-specific gadget
drivers that have more specific information.
+config USB_GADGET_STORAGE_NUM_BUFFERS
+ int "Number of storage pipeline buffers"
+ range 2 4
+ default 2
+ help
+ Usually 2 buffers are enough to establish a good buffering
+ pipeline. The number may be increased in order to compensate
+ for a bursty VFS behaviour. For instance there may be CPU wake up
+ latencies that makes the VFS to appear bursty in a system with
+ an CPU on-demand governor. Especially if DMA is doing IO to
+ offload the CPU. In this case the CPU will go into power
+ save often and spin up occasionally to move data within VFS.
+ If selecting USB_GADGET_DEBUG_FILES this value may be set by
+ a module parameter as well.
+ If unsure, say 2.
+
#
# USB Peripheral Controller Support
#
@@ -255,12 +271,11 @@ config USB_S3C_HSOTG
integrated into the S3C64XX series SoC.
config USB_IMX
- tristate "Freescale IMX USB Peripheral Controller"
- depends on ARCH_MX1
+ tristate "Freescale i.MX1 USB Peripheral Controller"
+ depends on ARCH_MXC
help
- Freescale's IMX series include an integrated full speed
- USB 1.1 device controller. The controller in the IMX series
- is register-compatible.
+ Freescale's i.MX1 includes an integrated full speed
+ USB 1.1 device controller.
It has Six fixed-function endpoints, as well as endpoint
zero (for control transfers).
@@ -303,6 +318,18 @@ config USB_PXA_U2O
PXA9xx Processor series include a high speed USB2.0 device
controller, which support high speed and full speed USB peripheral.
+config USB_GADGET_DWC3
+ tristate "DesignWare USB3.0 (DRD) Controller"
+ depends on USB_DWC3
+ select USB_GADGET_DUALSPEED
+ select USB_GADGET_SUPERSPEED
+ help
+ DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
+ which can be configured for peripheral-only, host-only, hub-only
+ and Dual-Role operation. This Controller was first integrated into
+ the OMAP5 series of processors. More information about the OMAP5
+ version of this controller, refer to http://www.ti.com/omap5.
+
#
# Controllers available in both integrated and discrete versions
#
@@ -846,6 +873,16 @@ config USB_G_NOKIA
It's only really useful for N900 hardware. If you're building
a kernel for N900, say Y or M here. If unsure, say N.
+config USB_G_ACM_MS
+ tristate "CDC Composite Device (ACM and mass storage)"
+ depends on BLOCK
+ help
+ This driver provides two functions in one configuration:
+ a mass storage, and a CDC ACM (serial port) link.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_acm_ms".
+
config USB_G_MULTI
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
depends on BLOCK && NET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9ba725af4a08..b54ac6190890 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,7 +28,7 @@ obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
obj-$(CONFIG_USB_EG20T) += pch_udc.o
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
-mv_udc-y := mv_udc_core.o mv_udc_phy.o
+mv_udc-y := mv_udc_core.o
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
@@ -51,6 +51,7 @@ g_dbgp-y := dbgp.o
g_nokia-y := nokia.o
g_webcam-y := webcam.o
g_ncm-y := ncm.o
+g_acm_ms-y := acm_ms.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_AUDIO) += g_audio.o
@@ -69,3 +70,4 @@ obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
+obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c
new file mode 100644
index 000000000000..fdb7aec3bd0c
--- /dev/null
+++ b/drivers/usb/gadget/acm_ms.c
@@ -0,0 +1,256 @@
+/*
+ * acm_ms.c -- Composite driver, with ACM and mass storage support
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: David Brownell
+ * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de>
+ *
+ * Heavily based on multi.c and cdc2.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+
+#include "u_serial.h"
+
+#define DRIVER_DESC "Composite Gadget (ACM + MS)"
+#define DRIVER_VERSION "2011/10/10"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
+#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module. So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+#include "u_serial.c"
+#include "f_acm.c"
+#include "f_mass_storage.c"
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 1,
+
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM),
+ .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ /*.bNumConfigurations = DYNAMIC*/
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+
+static char manufacturer[50];
+
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer,
+ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+static struct fsg_common fsg_common;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We _always_ have both ACM and mass storage functions.
+ */
+static int __init acm_ms_do_config(struct usb_configuration *c)
+{
+ int status;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+
+ status = acm_bind_config(c, 0);
+ if (status < 0)
+ return status;
+
+ status = fsg_bind_config(c->cdev, c, &fsg_common);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static struct usb_configuration acm_ms_config_driver = {
+ .label = DRIVER_DESC,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init acm_ms_bind(struct usb_composite_dev *cdev)
+{
+ int gcnum;
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
+ void *retp;
+
+ /* set up serial link layer */
+ status = gserial_setup(cdev->gadget, 1);
+ if (status < 0)
+ return status;
+
+ /* set up mass storage function */
+ retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
+ if (IS_ERR(retp)) {
+ status = PTR_ERR(retp);
+ goto fail0;
+ }
+
+ /* set bcdDevice */
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0) {
+ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+ } else {
+ WARNING(cdev, "controller '%s' not recognized; trying %s\n",
+ gadget->name,
+ acm_ms_config_driver.label);
+ device_desc.bcdDevice =
+ cpu_to_le16(0x0300 | 0x0099);
+ }
+
+ /*
+ * Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ /* device descriptor strings: manufacturer, product */
+ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail1;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ device_desc.iManufacturer = status;
+
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail1;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
+ device_desc.iProduct = status;
+
+ /* register our configuration */
+ status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
+ if (status < 0)
+ goto fail1;
+
+ dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+ DRIVER_DESC);
+ fsg_common_put(&fsg_common);
+ return 0;
+
+ /* error recovery */
+fail1:
+ fsg_common_put(&fsg_common);
+fail0:
+ gserial_cleanup();
+ return status;
+}
+
+static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
+{
+ gserial_cleanup();
+
+ return 0;
+}
+
+static struct usb_composite_driver acm_ms_driver = {
+ .name = "g_acm_ms",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .unbind = __exit_p(acm_ms_unbind),
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
+MODULE_LICENSE("GPL v2");
+
+static int __init init(void)
+{
+ return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&acm_ms_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 70f2b376c86d..4730016d7cd4 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
@@ -354,7 +345,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
writel(tmp, &dev->ep[ep->num].regs->ctl);
/* set max packet size */
- maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ maxpacket = usb_endpoint_maxp(desc);
tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
ep->ep.maxpacket = maxpacket;
@@ -3014,13 +3005,8 @@ __acquires(dev->lock)
/* link up all endpoints */
udc_setup_endpoints(dev);
- if (dev->gadget.speed == USB_SPEED_HIGH) {
- dev_info(&dev->pdev->dev, "Connect: speed = %s\n",
- "high");
- } else if (dev->gadget.speed == USB_SPEED_FULL) {
- dev_info(&dev->pdev->dev, "Connect: speed = %s\n",
- "full");
- }
+ dev_info(&dev->pdev->dev, "Connect: %s\n",
+ usb_speed_string(dev->gadget.speed));
/* init ep 0 */
activate_control_endpoints(dev);
diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h
index 1d1c7543468e..f87e29c65325 100644
--- a/drivers/usb/gadget/amd5536udc.h
+++ b/drivers/usb/gadget/amd5536udc.h
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef AMD5536UDC_H
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index ddb118a76807..8efe0fa9228d 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -9,16 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
*/
#undef VERBOSE_DEBUG
@@ -460,7 +450,7 @@ static void nuke(struct at91_ep *ep, int status)
{
struct at91_request *req;
- // terminer chaque requete dans la queue
+ /* terminate any request in the queue */
ep->stopped = 1;
if (list_empty(&ep->queue))
return;
@@ -487,7 +477,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|| !desc || ep->desc
|| _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
- || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0
+ || (maxpacket = usb_endpoint_maxp(desc)) == 0
|| maxpacket > ep->maxpacket) {
DBG("bad ep or descriptor\n");
return -EINVAL;
@@ -788,7 +778,7 @@ static const struct usb_ep_ops at91_ep_ops = {
.queue = at91_ep_queue,
.dequeue = at91_ep_dequeue,
.set_halt = at91_ep_set_halt,
- // there's only imprecise fifo status reporting
+ /* there's only imprecise fifo status reporting */
};
/*-------------------------------------------------------------------------*/
@@ -846,7 +836,7 @@ static void udc_reinit(struct at91_udc *udc)
ep->fifo_bank = 0;
ep->ep.maxpacket = ep->maxpacket;
ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i);
- // initialiser une queue par endpoint
+ /* initialize one queue per endpoint */
INIT_LIST_HEAD(&ep->queue);
}
}
@@ -952,7 +942,7 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active)
struct at91_udc *udc = to_udc(gadget);
unsigned long flags;
- // VDBG("vbus %s\n", is_active ? "on" : "off");
+ /* VDBG("vbus %s\n", is_active ? "on" : "off"); */
spin_lock_irqsave(&udc->lock, flags);
udc->vbus = (is_active != 0);
if (udc->driver)
@@ -1003,7 +993,7 @@ static const struct usb_gadget_ops at91_udc_ops = {
* VBUS-powered devices may also also want to support bigger
* power budgets after an appropriate SET_CONFIGURATION.
*/
- // .vbus_power = at91_vbus_power,
+ /* .vbus_power = at91_vbus_power, */
};
/*-------------------------------------------------------------------------*/
@@ -1072,7 +1062,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
ep->is_in = 0;
}
} else {
- // REVISIT this happens sometimes under load; why??
+ /* REVISIT this happens sometimes under load; why?? */
ERR("SETUP len %d, csr %08x\n", rxcount, csr);
status = -EINVAL;
}
@@ -1451,7 +1441,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP);
at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP);
- // VDBG("bus suspend\n");
+ /* VDBG("bus suspend\n"); */
if (udc->suspended)
continue;
udc->suspended = 1;
@@ -1473,7 +1463,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP);
at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
- // VDBG("bus resume\n");
+ /* VDBG("bus resume\n"); */
if (!udc->suspended)
continue;
udc->suspended = 0;
@@ -1820,7 +1810,7 @@ static int __init at91udc_probe(struct platform_device *pdev)
/* request UDC and maybe VBUS irqs */
udc->udp_irq = platform_get_irq(pdev, 0);
retval = request_irq(udc->udp_irq, at91_udc_irq,
- IRQF_DISABLED, driver_name, udc);
+ 0, driver_name, udc);
if (retval < 0) {
DBG("request irq %d failed\n", udc->udp_irq);
goto fail1;
@@ -1848,7 +1838,7 @@ static int __init at91udc_probe(struct platform_device *pdev)
jiffies + VBUS_POLL_TIMEOUT);
} else {
if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
- IRQF_DISABLED, driver_name, udc)) {
+ 0, driver_name, udc)) {
DBG("request vbus irq %d failed\n",
udc->board.vbus_pin);
retval = -EBUSY;
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h
index 108ca54f9092..3c0315b86ace 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/at91_udc.h
@@ -7,16 +7,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef AT91_UDC_H
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 5b1665eb1bef..271a9d873608 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -527,7 +527,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
- maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
+ maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
|| ep->index == 0
@@ -571,7 +571,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
* Bits 11:12 specify number of _additional_
* transactions per microframe.
*/
- nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1;
+ nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1;
if (nr_trans > 3)
return -EINVAL;
@@ -1718,13 +1718,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
spin_lock(&udc->lock);
}
- if (status & USBA_HIGH_SPEED) {
- DBG(DBG_BUS, "High-speed bus reset detected\n");
+ if (status & USBA_HIGH_SPEED)
udc->gadget.speed = USB_SPEED_HIGH;
- } else {
- DBG(DBG_BUS, "Full-speed bus reset detected\n");
+ else
udc->gadget.speed = USB_SPEED_FULL;
- }
+ DBG(DBG_BUS, "%s bus reset detected\n",
+ usb_speed_string(udc->gadget.speed));
ep0 = &usba_ep[0];
ep0->desc = &usba_ep0_desc;
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index b1c1afbb8750..672674c2fb3d 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 470981ad6f77..4eedfe557154 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -3,17 +3,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
*/
#include <linux/module.h>
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 1265a8502ea0..83428f56253b 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -2101,7 +2101,7 @@ static int ep_enable(struct usb_ep *ep,
mEp->num = usb_endpoint_num(desc);
mEp->type = usb_endpoint_type(desc);
- mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
+ mEp->ep.maxpacket = usb_endpoint_maxp(desc);
dbg_event(_usb_addr(mEp), "ENABLE", 0);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index aef47414f5d5..8a5529d214fb 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -7,15 +7,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
@@ -164,7 +155,7 @@ int config_ep_by_speed(struct usb_gadget *g,
ep_found:
/* commit results */
- _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize);
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
@@ -551,9 +542,9 @@ static int bos_desc(struct usb_composite_dev *cdev)
if (cdev->gadget->ops->get_config_params)
cdev->gadget->ops->get_config_params(&dcd_config_params);
else {
- dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT);
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
}
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
@@ -626,25 +617,9 @@ static int set_config(struct usb_composite_dev *cdev,
result = 0;
}
- INFO(cdev, "%s speed config #%d: %s\n",
- ({ char *speed;
- switch (gadget->speed) {
- case USB_SPEED_LOW:
- speed = "low";
- break;
- case USB_SPEED_FULL:
- speed = "full";
- break;
- case USB_SPEED_HIGH:
- speed = "high";
- break;
- case USB_SPEED_SUPER:
- speed = "super";
- break;
- default:
- speed = "?";
- break;
- } ; speed; }), number, c ? c->label : "unconfigured");
+ INFO(cdev, "%s config #%d: %s\n",
+ usb_speed_string(gadget->speed),
+ number, c ? c->label : "unconfigured");
if (!c)
goto done;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index b2c001334876..7542a72ce51a 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -7,15 +7,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/errno.h>
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
index 8beefdd36787..f855ecf7a637 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/dbgp.c
@@ -4,7 +4,6 @@
* Copyright (C) 2010 Stephane Duverger
*
* Released under the GPLv2.
- *
*/
/* verbose messages */
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index e755a9d267fc..ab8f1b488d54 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
@@ -439,7 +430,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
* maximum packet size.
* For SS devices the wMaxPacketSize is limited by 1024.
*/
- max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
+ max = usb_endpoint_maxp(desc) & 0x7ff;
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
@@ -1277,7 +1268,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
int tmp;
/* high bandwidth mode */
- tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
+ tmp = usb_endpoint_maxp(ep->desc);
tmp = (tmp >> 11) & 0x03;
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 7a7e6b7e1fd6..596a0b464e61 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -7,16 +7,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/kernel.h>
@@ -158,7 +148,7 @@ ep_matches (
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
- max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
+ max = 0x7ff & usb_endpoint_maxp(desc);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high/super speed */
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index aafc84f33e26..0cd764d59351 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index a9a4eade7e80..ec7ffcd0d0cd 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -460,7 +460,7 @@ static int audio_set_endpoint_req(struct usb_function *f,
switch (ctrl->bRequest) {
case UAC_SET_CUR:
- value = 0;
+ value = len;
break;
case UAC_SET_MIN:
@@ -499,7 +499,7 @@ static int audio_get_endpoint_req(struct usb_function *f,
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
- value = 3;
+ value = len;
break;
case UAC_GET_MEM:
break;
@@ -681,17 +681,18 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
status = -ENOMEM;
- /* supcard all relevant hardware speeds... we expect that when
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(f_audio_desc);
+
+ /*
+ * support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
-
- /* copy descriptors, and track endpoint copies */
if (gadget_is_dualspeed(c->cdev->gadget)) {
c->highspeed = true;
f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
- } else
- f->descriptors = usb_copy_descriptors(f_audio_desc);
+ }
return 0;
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 3691a0cb9465..11c07cb7d337 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
index 046c6d0e6960..1a7b2dd7d408 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index c161a9aaeb7e..6b1c20b6c9b2 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -12,15 +12,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 83a266bdb40e..b2113420b806 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -7,15 +7,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index ca660d40b11a..6d87f288df4e 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 5b9339582007..52583a235330 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -112,8 +112,7 @@
* is not loaded (an empty string as "filename" in the fsg_config
* structure causes error). The CD-ROM emulation includes a single
* data track and no audio tracks; hence there need be only one
- * backing file per LUN. Note also that the CD-ROM block length is
- * set to 512 rather than the more common value 2048.
+ * backing file per LUN.
*
*
* MSF includes support for module parameters. If gadget using it
@@ -363,7 +362,7 @@ struct fsg_common {
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
- struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
+ struct fsg_buffhd *buffhds;
int cmnd_size;
u8 cmnd[MAX_COMMAND_SIZE];
@@ -745,7 +744,6 @@ static int do_read(struct fsg_common *common)
u32 amount_left;
loff_t file_offset, file_offset_tmp;
unsigned int amount;
- unsigned int partial_page;
ssize_t nread;
/*
@@ -771,7 +769,7 @@ static int do_read(struct fsg_common *common)
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
return -EINVAL;
}
- file_offset = ((loff_t) lba) << 9;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
/* Carry out the file reads */
amount_left = common->data_size_from_cmnd;
@@ -784,18 +782,10 @@ static int do_read(struct fsg_common *common)
* Try to read the remaining amount.
* But don't read more than the buffer size.
* And don't try to read past the end of the file.
- * Finally, if we're not at a page boundary, don't read past
- * the next page.
- * If this means reading 0 then we were asked to read past
- * the end of file.
*/
amount = min(amount_left, FSG_BUFLEN);
amount = min((loff_t)amount,
curlun->file_length - file_offset);
- partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
- if (partial_page > 0)
- amount = min(amount, (unsigned int)PAGE_CACHE_SIZE -
- partial_page);
/* Wait for the next buffer to become available */
bh = common->next_buffhd_to_fill;
@@ -812,7 +802,8 @@ static int do_read(struct fsg_common *common)
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
bh->inreq->length = 0;
bh->state = BUF_STATE_FULL;
@@ -835,18 +826,25 @@ static int do_read(struct fsg_common *common)
} else if (nread < amount) {
LDBG(curlun, "partial file read: %d/%u\n",
(int)nread, amount);
- nread -= (nread & 511); /* Round down to a block */
+ nread = round_down(nread, curlun->blksize);
}
file_offset += nread;
amount_left -= nread;
common->residue -= nread;
+
+ /*
+ * Except at the end of the transfer, nread will be
+ * equal to the buffer size, which is divisible by the
+ * bulk-in maxpacket size.
+ */
bh->inreq->length = nread;
bh->state = BUF_STATE_FULL;
/* If an error occurred, report it and its position */
if (nread < amount) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -877,7 +875,6 @@ static int do_write(struct fsg_common *common)
u32 amount_left_to_req, amount_left_to_write;
loff_t usb_offset, file_offset, file_offset_tmp;
unsigned int amount;
- unsigned int partial_page;
ssize_t nwritten;
int rc;
@@ -921,7 +918,7 @@ static int do_write(struct fsg_common *common)
/* Carry out the file writes */
get_some_more = 1;
- file_offset = usb_offset = ((loff_t) lba) << 9;
+ file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
amount_left_to_req = common->data_size_from_cmnd;
amount_left_to_write = common->data_size_from_cmnd;
@@ -933,41 +930,21 @@ static int do_write(struct fsg_common *common)
/*
* Figure out how much we want to get:
- * Try to get the remaining amount.
- * But don't get more than the buffer size.
- * And don't try to go past the end of the file.
- * If we're not at a page boundary,
- * don't go past the next page.
- * If this means getting 0, then we were asked
- * to write past the end of file.
- * Finally, round down to a block boundary.
+ * Try to get the remaining amount,
+ * but not more than the buffer size.
*/
amount = min(amount_left_to_req, FSG_BUFLEN);
- amount = min((loff_t)amount,
- curlun->file_length - usb_offset);
- partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
- if (partial_page > 0)
- amount = min(amount,
- (unsigned int)PAGE_CACHE_SIZE - partial_page);
-
- if (amount == 0) {
+
+ /* Beyond the end of the backing file? */
+ if (usb_offset >= curlun->file_length) {
get_some_more = 0;
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = usb_offset >> 9;
+ curlun->sense_data_info =
+ usb_offset >> curlun->blkbits;
curlun->info_valid = 1;
continue;
}
- amount -= amount & 511;
- if (amount == 0) {
-
- /*
- * Why were we were asked to transfer a
- * partial block?
- */
- get_some_more = 0;
- continue;
- }
/* Get the next buffer */
usb_offset += amount;
@@ -977,12 +954,11 @@ static int do_write(struct fsg_common *common)
get_some_more = 0;
/*
- * amount is always divisible by 512, hence by
- * the bulk-out maxpacket size
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
*/
- bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
- bh->outreq->short_not_ok = 1;
+ set_bulk_out_req_length(common, bh, amount);
if (!start_out_transfer(common, bh))
/* Dunno what to do if common->fsg is NULL */
return -EIO;
@@ -1002,7 +978,8 @@ static int do_write(struct fsg_common *common)
/* Did something go wrong with the transfer? */
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1016,6 +993,16 @@ static int do_write(struct fsg_common *common)
amount = curlun->file_length - file_offset;
}
+ /* Don't accept excess data. The spec doesn't say
+ * what to do in this case. We'll ignore the error.
+ */
+ amount = min(amount, bh->bulk_out_intended_length);
+
+ /* Don't write a partial block */
+ amount = round_down(amount, curlun->blksize);
+ if (amount == 0)
+ goto empty_write;
+
/* Perform the write */
file_offset_tmp = file_offset;
nwritten = vfs_write(curlun->filp,
@@ -1033,8 +1020,7 @@ static int do_write(struct fsg_common *common)
} else if (nwritten < amount) {
LDBG(curlun, "partial file write: %d/%u\n",
(int)nwritten, amount);
- nwritten -= (nwritten & 511);
- /* Round down to a block */
+ nwritten = round_down(nwritten, curlun->blksize);
}
file_offset += nwritten;
amount_left_to_write -= nwritten;
@@ -1043,13 +1029,15 @@ static int do_write(struct fsg_common *common)
/* If an error occurred, report it and its position */
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
+ empty_write:
/* Did the host decide to stop early? */
- if (bh->outreq->actual != bh->outreq->length) {
+ if (bh->outreq->actual < bh->bulk_out_intended_length) {
common->short_packet_received = 1;
break;
}
@@ -1129,8 +1117,8 @@ static int do_verify(struct fsg_common *common)
return -EIO; /* No default reply */
/* Prepare to carry out the file verify */
- amount_left = verification_length << 9;
- file_offset = ((loff_t) lba) << 9;
+ amount_left = verification_length << curlun->blkbits;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
/* Write out all the dirty buffers before invalidating them */
fsg_lun_fsync_sub(curlun);
@@ -1148,8 +1136,6 @@ static int do_verify(struct fsg_common *common)
* Try to read the remaining amount, but not more than
* the buffer size.
* And don't try to read past the end of the file.
- * If this means reading 0 then we were asked to read
- * past the end of file.
*/
amount = min(amount_left, FSG_BUFLEN);
amount = min((loff_t)amount,
@@ -1157,7 +1143,8 @@ static int do_verify(struct fsg_common *common)
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1179,11 +1166,12 @@ static int do_verify(struct fsg_common *common)
} else if (nread < amount) {
LDBG(curlun, "partial file verify: %d/%u\n",
(int)nread, amount);
- nread -= nread & 511; /* Round down to a sector */
+ nread = round_down(nread, curlun->blksize);
}
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1289,7 +1277,7 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
/* Max logical block */
- put_unaligned_be32(512, &buf[4]); /* Block length */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
return 8;
}
@@ -1527,7 +1515,7 @@ static int do_read_format_capacities(struct fsg_common *common,
put_unaligned_be32(curlun->num_sectors, &buf[0]);
/* Number of blocks */
- put_unaligned_be32(512, &buf[4]); /* Block length */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
buf[4] = 0x02; /* Current capacity */
return 12;
}
@@ -1607,7 +1595,7 @@ static int throw_away_data(struct fsg_common *common)
common->next_buffhd_to_drain = bh->next;
/* A short packet or an error ends everything */
- if (bh->outreq->actual != bh->outreq->length ||
+ if (bh->outreq->actual < bh->bulk_out_intended_length ||
bh->outreq->status != 0) {
raise_exception(common,
FSG_STATE_ABORT_BULK_OUT);
@@ -1623,12 +1611,11 @@ static int throw_away_data(struct fsg_common *common)
amount = min(common->usb_amount_left, FSG_BUFLEN);
/*
- * amount is always divisible by 512, hence by
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
* the bulk-out maxpacket size.
*/
- bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
- bh->outreq->short_not_ok = 1;
+ set_bulk_out_req_length(common, bh, amount);
if (!start_out_transfer(common, bh))
/* Dunno what to do if common->fsg is NULL */
return -EIO;
@@ -2022,7 +2009,8 @@ static int do_scsi_command(struct fsg_common *common)
case READ_6:
i = common->cmnd[4];
- common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
+ common->curlun->blkbits;
reply = check_command(common, 6, DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1,
"READ(6)");
@@ -2032,7 +2020,8 @@ static int do_scsi_command(struct fsg_common *common)
case READ_10:
common->data_size_from_cmnd =
- get_unaligned_be16(&common->cmnd[7]) << 9;
+ get_unaligned_be16(&common->cmnd[7]) <<
+ common->curlun->blkbits;
reply = check_command(common, 10, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)");
@@ -2042,7 +2031,8 @@ static int do_scsi_command(struct fsg_common *common)
case READ_12:
common->data_size_from_cmnd =
- get_unaligned_be32(&common->cmnd[6]) << 9;
+ get_unaligned_be32(&common->cmnd[6]) <<
+ common->curlun->blkbits;
reply = check_command(common, 12, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)");
@@ -2142,7 +2132,8 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_6:
i = common->cmnd[4];
- common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
+ common->curlun->blkbits;
reply = check_command(common, 6, DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1,
"WRITE(6)");
@@ -2152,7 +2143,8 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_10:
common->data_size_from_cmnd =
- get_unaligned_be16(&common->cmnd[7]) << 9;
+ get_unaligned_be16(&common->cmnd[7]) <<
+ common->curlun->blkbits;
reply = check_command(common, 10, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)");
@@ -2162,7 +2154,8 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_12:
common->data_size_from_cmnd =
- get_unaligned_be32(&common->cmnd[6]) << 9;
+ get_unaligned_be32(&common->cmnd[6]) <<
+ common->curlun->blkbits;
reply = check_command(common, 12, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)");
@@ -2297,7 +2290,6 @@ static int get_next_command(struct fsg_common *common)
/* Queue a request to read a Bulk-only CBW */
set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
if (!start_out_transfer(common, bh))
/* Don't know what to do if common->fsg is NULL */
return -EIO;
@@ -2348,7 +2340,7 @@ reset:
if (common->fsg) {
fsg = common->fsg;
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
if (bh->inreq) {
@@ -2401,12 +2393,11 @@ reset:
goto reset;
fsg->bulk_out->driver_data = common;
fsg->bulk_out_enabled = 1;
- common->bulk_out_maxpacket =
- le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize);
+ common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
@@ -2475,7 +2466,7 @@ static void handle_exception(struct fsg_common *common)
/* Cancel all the pending transfers */
if (likely(common->fsg)) {
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
if (bh->inreq_busy)
usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
@@ -2487,7 +2478,7 @@ static void handle_exception(struct fsg_common *common)
/* Wait until everything is idle */
for (;;) {
int num_active = 0;
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
num_active += bh->inreq_busy + bh->outreq_busy;
}
@@ -2510,7 +2501,7 @@ static void handle_exception(struct fsg_common *common)
*/
spin_lock_irq(&common->lock);
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
bh->state = BUF_STATE_EMPTY;
}
@@ -2719,6 +2710,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
int nluns, i, rc;
char *pathbuf;
+ rc = fsg_num_buffers_validate();
+ if (rc != 0)
+ return ERR_PTR(rc);
+
/* Find out how many LUNs there should be */
nluns = cfg->nluns;
if (nluns < 1 || nluns > FSG_MAX_LUNS) {
@@ -2737,6 +2732,14 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
common->free_storage_on_release = 0;
}
+ common->buffhds = kcalloc(fsg_num_buffers,
+ sizeof *(common->buffhds), GFP_KERNEL);
+ if (!common->buffhds) {
+ if (common->free_storage_on_release)
+ kfree(common);
+ return ERR_PTR(-ENOMEM);
+ }
+
common->ops = cfg->ops;
common->private_data = cfg->private_data;
@@ -2814,7 +2817,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
/* Data buffers cyclic list */
bh = common->buffhds;
- i = FSG_NUM_BUFFERS;
+ i = fsg_num_buffers;
goto buffhds_first_it;
do {
bh->next = bh + 1;
@@ -2940,12 +2943,13 @@ static void fsg_common_release(struct kref *ref)
{
struct fsg_buffhd *bh = common->buffhds;
- unsigned i = FSG_NUM_BUFFERS;
+ unsigned i = fsg_num_buffers;
do {
kfree(bh->buf);
} while (++bh, --i);
}
+ kfree(common->buffhds);
if (common->free_storage_on_release)
kfree(common);
}
@@ -3019,6 +3023,28 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
}
}
+ if (gadget_is_superspeed(gadget)) {
+ unsigned max_burst;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+ fsg_ss_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+ f->ss_descriptors = usb_copy_descriptors(fsg_ss_function);
+ if (unlikely(!f->ss_descriptors)) {
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+
return 0;
autoconf_fail:
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c
new file mode 100644
index 000000000000..67b222908cf9
--- /dev/null
+++ b/drivers/usb/gadget/f_midi.c
@@ -0,0 +1,998 @@
+/*
+ * f_midi.c -- USB MIDI class function driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * Rewritten for the composite framework
+ * Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
+ *
+ * Based on drivers/usb/gadget/f_audio.c,
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * and drivers/usb/gadget/midi.c,
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+static const char f_midi_shortname[] = "f_midi";
+static const char f_midi_longname[] = "MIDI Gadget";
+
+/*
+ * We can only handle 16 cables on one single endpoint, as cable numbers are
+ * stored in 4-bit fields. And as the interface currently only holds one
+ * single endpoint, this is the maximum number of ports we can allow.
+ */
+#define MAX_PORTS 16
+
+/*
+ * This is a gadget, and the IN/OUT naming is from the host's perspective.
+ * USB -> OUT endpoint -> rawmidi
+ * USB <- IN endpoint <- rawmidi
+ */
+struct gmidi_in_port {
+ struct f_midi *midi;
+ int active;
+ uint8_t cable;
+ uint8_t state;
+#define STATE_UNKNOWN 0
+#define STATE_1PARAM 1
+#define STATE_2PARAM_1 2
+#define STATE_2PARAM_2 3
+#define STATE_SYSEX_0 4
+#define STATE_SYSEX_1 5
+#define STATE_SYSEX_2 6
+ uint8_t data[2];
+};
+
+struct f_midi {
+ struct usb_function func;
+ struct usb_gadget *gadget;
+ struct usb_ep *in_ep, *out_ep;
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+
+ struct snd_rawmidi_substream *in_substream[MAX_PORTS];
+ struct snd_rawmidi_substream *out_substream[MAX_PORTS];
+ struct gmidi_in_port *in_port[MAX_PORTS];
+
+ unsigned long out_triggered;
+ struct tasklet_struct tasklet;
+ unsigned int in_ports;
+ unsigned int out_ports;
+ int index;
+ char *id;
+ unsigned int buflen, qlen;
+};
+
+static inline struct f_midi *func_to_midi(struct usb_function *f)
+{
+ return container_of(f, struct f_midi, func);
+}
+
+static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(16);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+
+/* B.3.1 Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc __initdata = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ /* .bNumEndpoints = DYNAMIC */
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ /* .iInterface = DYNAMIC */
+};
+
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
+ .bLength = UAC_DT_AC_HEADER_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdADC = cpu_to_le16(0x0100),
+ .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
+ .bInCollection = 1,
+ /* .baInterfaceNr = DYNAMIC */
+};
+
+/* B.4.1 Standard MS Interface Descriptor */
+static struct usb_interface_descriptor ms_interface_desc __initdata = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ /* .iInterface = DYNAMIC */
+};
+
+/* B.4.2 Class-Specific MS Interface Descriptor */
+static struct usb_ms_header_descriptor ms_header_desc __initdata = {
+ .bLength = USB_DT_MS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = cpu_to_le16(0x0100),
+ /* .wTotalLength = DYNAMIC */
+};
+
+/* B.4.3 Embedded MIDI IN Jack Descriptor */
+static struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
+ .bLength = USB_DT_MIDI_IN_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ /* .bJackID = DYNAMIC */
+};
+
+/* B.4.4 Embedded MIDI OUT Jack Descriptor */
+static struct usb_midi_out_jack_descriptor_16 jack_out_emb_desc = {
+ /* .bLength = DYNAMIC */
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ /* .bJackID = DYNAMIC */
+ /* .bNrInputPins = DYNAMIC */
+ /* .pins = DYNAMIC */
+};
+
+/* B.5.1 Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
+ /* .bLength = DYNAMIC */
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ /* .bNumEmbMIDIJack = DYNAMIC */
+ /* .baAssocJackID = DYNAMIC */
+};
+
+/* B.6.1 Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
+ /* .bLength = DYNAMIC */
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ /* .bNumEmbMIDIJack = DYNAMIC */
+ /* .baAssocJackID = DYNAMIC */
+};
+
+/* string IDs are assigned dynamically */
+
+#define STRING_FUNC_IDX 0
+
+static struct usb_string midi_string_defs[] = {
+ [STRING_FUNC_IDX].s = "MIDI function",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings midi_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = midi_string_defs,
+};
+
+static struct usb_gadget_strings *midi_strings[] = {
+ &midi_stringtab,
+ NULL,
+};
+
+static struct usb_request *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 free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static const uint8_t f_midi_cin_length[] = {
+ 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void f_midi_read_data(struct usb_ep *ep, int cable,
+ uint8_t *data, int length)
+{
+ struct f_midi *midi = ep->driver_data;
+ struct snd_rawmidi_substream *substream = midi->out_substream[cable];
+
+ if (!substream)
+ /* Nobody is listening - throw it on the floor. */
+ return;
+
+ if (!test_bit(cable, &midi->out_triggered))
+ return;
+
+ snd_rawmidi_receive(substream, data, length);
+}
+
+static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+ unsigned int i;
+ u8 *buf = req->buf;
+
+ for (i = 0; i + 3 < req->actual; i += 4)
+ if (buf[i] != 0) {
+ int cable = buf[i] >> 4;
+ int length = f_midi_cin_length[buf[i] & 0x0f];
+ f_midi_read_data(ep, cable, &buf[i + 1], length);
+ }
+}
+
+static void
+f_midi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_midi *midi = ep->driver_data;
+ struct usb_composite_dev *cdev = midi->func.config->cdev;
+ int status = req->status;
+
+ switch (status) {
+ case 0: /* normal completion */
+ if (ep == midi->out_ep) {
+ /* We received stuff. req is queued again, below */
+ f_midi_handle_out_data(ep, req);
+ } else if (ep == midi->in_ep) {
+ /* Our transmit completed. See if there's more to go.
+ * f_midi_transmit eats req, don't queue it again. */
+ f_midi_transmit(midi, req);
+ return;
+ }
+ break;
+
+ /* this endpoint is normally active while we're configured */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+ req->actual, req->length);
+ if (ep == midi->out_ep)
+ f_midi_handle_out_data(ep, req);
+
+ free_ep_req(ep, req);
+ return;
+
+ case -EOVERFLOW: /* buffer overrun on read means that
+ * we didn't provide a big enough buffer.
+ */
+ default:
+ DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+ break;
+ case -EREMOTEIO: /* short read */
+ break;
+ }
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n",
+ ep->name, req->length, status);
+ usb_ep_set_halt(ep);
+ /* FIXME recover later ... somehow */
+ }
+}
+
+static int f_midi_start_ep(struct f_midi *midi,
+ struct usb_function *f,
+ struct usb_ep *ep)
+{
+ int err;
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ if (ep->driver_data)
+ usb_ep_disable(ep);
+
+ err = config_ep_by_speed(midi->gadget, f, ep);
+ if (err) {
+ ERROR(cdev, "can't configure %s: %d\n", ep->name, err);
+ return err;
+ }
+
+ err = usb_ep_enable(ep);
+ if (err) {
+ ERROR(cdev, "can't start %s: %d\n", ep->name, err);
+ return err;
+ }
+
+ ep->driver_data = midi;
+
+ return 0;
+}
+
+static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_midi *midi = func_to_midi(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ unsigned i;
+ int err;
+
+ err = f_midi_start_ep(midi, f, midi->in_ep);
+ if (err)
+ return err;
+
+ err = f_midi_start_ep(midi, f, midi->out_ep);
+ if (err)
+ return err;
+
+ if (midi->out_ep->driver_data)
+ usb_ep_disable(midi->out_ep);
+
+ err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
+ if (err) {
+ ERROR(cdev, "can't configure %s: %d\n",
+ midi->out_ep->name, err);
+ return err;
+ }
+
+ err = usb_ep_enable(midi->out_ep);
+ if (err) {
+ ERROR(cdev, "can't start %s: %d\n",
+ midi->out_ep->name, err);
+ return err;
+ }
+
+ midi->out_ep->driver_data = midi;
+
+ /* allocate a bunch of read buffers and queue them all at once. */
+ for (i = 0; i < midi->qlen && err == 0; i++) {
+ struct usb_request *req =
+ alloc_ep_req(midi->out_ep, midi->buflen);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req->complete = f_midi_complete;
+ err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
+ if (err) {
+ ERROR(midi, "%s queue req: %d\n",
+ midi->out_ep->name, err);
+ }
+ }
+
+ return 0;
+}
+
+static void f_midi_disable(struct usb_function *f)
+{
+ struct f_midi *midi = func_to_midi(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "disable\n");
+
+ /*
+ * just disable endpoints, forcing completion of pending i/o.
+ * all our completion handlers free their requests in this case.
+ */
+ usb_ep_disable(midi->in_ep);
+ usb_ep_disable(midi->out_ep);
+}
+
+static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct f_midi *midi = func_to_midi(f);
+ struct snd_card *card;
+
+ DBG(cdev, "unbind\n");
+
+ /* just to be sure */
+ f_midi_disable(f);
+
+ card = midi->card;
+ midi->card = NULL;
+ if (card)
+ snd_card_free(card);
+
+ kfree(midi->id);
+ midi->id = NULL;
+
+ usb_free_descriptors(f->descriptors);
+ kfree(midi);
+}
+
+static int f_midi_snd_free(struct snd_device *device)
+{
+ return 0;
+}
+
+static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
+ uint8_t p1, uint8_t p2, uint8_t p3)
+{
+ unsigned length = req->length;
+ u8 *buf = (u8 *)req->buf + length;
+
+ buf[0] = p0;
+ buf[1] = p1;
+ buf[2] = p2;
+ buf[3] = p3;
+ req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void f_midi_transmit_byte(struct usb_request *req,
+ struct gmidi_in_port *port, uint8_t b)
+{
+ uint8_t p0 = port->cable << 4;
+
+ if (b >= 0xf8) {
+ f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+ } else if (b >= 0xf0) {
+ switch (b) {
+ case 0xf0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case 0xf1:
+ case 0xf3:
+ port->data[0] = b;
+ port->state = STATE_1PARAM;
+ break;
+ case 0xf2:
+ port->data[0] = b;
+ port->state = STATE_2PARAM_1;
+ break;
+ case 0xf4:
+ case 0xf5:
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf6:
+ f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf7:
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ f_midi_transmit_packet(req,
+ p0 | 0x05, 0xf7, 0, 0);
+ break;
+ case STATE_SYSEX_1:
+ f_midi_transmit_packet(req,
+ p0 | 0x06, port->data[0], 0xf7, 0);
+ break;
+ case STATE_SYSEX_2:
+ f_midi_transmit_packet(req,
+ p0 | 0x07, port->data[0],
+ port->data[1], 0xf7);
+ break;
+ }
+ port->state = STATE_UNKNOWN;
+ break;
+ }
+ } else if (b >= 0x80) {
+ port->data[0] = b;
+ if (b >= 0xc0 && b <= 0xdf)
+ port->state = STATE_1PARAM;
+ else
+ port->state = STATE_2PARAM_1;
+ } else { /* b < 0x80 */
+ switch (port->state) {
+ case STATE_1PARAM:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ } else {
+ p0 |= 0x02;
+ port->state = STATE_UNKNOWN;
+ }
+ f_midi_transmit_packet(req, p0, port->data[0], b, 0);
+ break;
+ case STATE_2PARAM_1:
+ port->data[1] = b;
+ port->state = STATE_2PARAM_2;
+ break;
+ case STATE_2PARAM_2:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ port->state = STATE_2PARAM_1;
+ } else {
+ p0 |= 0x03;
+ port->state = STATE_UNKNOWN;
+ }
+ f_midi_transmit_packet(req,
+ p0, port->data[0], port->data[1], b);
+ break;
+ case STATE_SYSEX_0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case STATE_SYSEX_1:
+ port->data[1] = b;
+ port->state = STATE_SYSEX_2;
+ break;
+ case STATE_SYSEX_2:
+ f_midi_transmit_packet(req,
+ p0 | 0x04, port->data[0], port->data[1], b);
+ port->state = STATE_SYSEX_0;
+ break;
+ }
+ }
+}
+
+static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
+{
+ struct usb_ep *ep = midi->in_ep;
+ int i;
+
+ if (!ep)
+ return;
+
+ if (!req)
+ req = alloc_ep_req(ep, midi->buflen);
+
+ if (!req) {
+ ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
+ return;
+ }
+ req->length = 0;
+ req->complete = f_midi_complete;
+
+ for (i = 0; i < MAX_PORTS; i++) {
+ struct gmidi_in_port *port = midi->in_port[i];
+ struct snd_rawmidi_substream *substream = midi->in_substream[i];
+
+ if (!port || !port->active || !substream)
+ continue;
+
+ while (req->length + 3 < midi->buflen) {
+ uint8_t b;
+ if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
+ port->active = 0;
+ break;
+ }
+ f_midi_transmit_byte(req, port, b);
+ }
+ }
+
+ if (req->length > 0)
+ usb_ep_queue(ep, req, GFP_ATOMIC);
+ else
+ free_ep_req(ep, req);
+}
+
+static void f_midi_in_tasklet(unsigned long data)
+{
+ struct f_midi *midi = (struct f_midi *) data;
+ f_midi_transmit(midi, NULL);
+}
+
+static int f_midi_in_open(struct snd_rawmidi_substream *substream)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ if (!midi->in_port[substream->number])
+ return -EINVAL;
+
+ VDBG(midi, "%s()\n", __func__);
+ midi->in_substream[substream->number] = substream;
+ midi->in_port[substream->number]->state = STATE_UNKNOWN;
+ return 0;
+}
+
+static int f_midi_in_close(struct snd_rawmidi_substream *substream)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ VDBG(midi, "%s()\n", __func__);
+ return 0;
+}
+
+static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ if (!midi->in_port[substream->number])
+ return;
+
+ VDBG(midi, "%s() %d\n", __func__, up);
+ midi->in_port[substream->number]->active = up;
+ if (up)
+ tasklet_hi_schedule(&midi->tasklet);
+}
+
+static int f_midi_out_open(struct snd_rawmidi_substream *substream)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ if (substream->number >= MAX_PORTS)
+ return -EINVAL;
+
+ VDBG(midi, "%s()\n", __func__);
+ midi->out_substream[substream->number] = substream;
+ return 0;
+}
+
+static int f_midi_out_close(struct snd_rawmidi_substream *substream)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ VDBG(midi, "%s()\n", __func__);
+ return 0;
+}
+
+static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct f_midi *midi = substream->rmidi->private_data;
+
+ VDBG(midi, "%s()\n", __func__);
+
+ if (up)
+ set_bit(substream->number, &midi->out_triggered);
+ else
+ clear_bit(substream->number, &midi->out_triggered);
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+ .open = f_midi_in_open,
+ .close = f_midi_in_close,
+ .trigger = f_midi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+ .open = f_midi_out_open,
+ .close = f_midi_out_close,
+ .trigger = f_midi_out_trigger
+};
+
+/* register as a sound "card" */
+static int f_midi_register_card(struct f_midi *midi)
+{
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = f_midi_snd_free,
+ };
+
+ err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card);
+ if (err < 0) {
+ ERROR(midi, "snd_card_create() failed\n");
+ goto fail;
+ }
+ midi->card = card;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops);
+ if (err < 0) {
+ ERROR(midi, "snd_device_new() failed: error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(card->driver, f_midi_longname);
+ strcpy(card->longname, f_midi_longname);
+ strcpy(card->shortname, f_midi_shortname);
+
+ /* Set up rawmidi */
+ snd_component_add(card, "MIDI");
+ err = snd_rawmidi_new(card, card->longname, 0,
+ midi->out_ports, midi->in_ports, &rmidi);
+ if (err < 0) {
+ ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err);
+ goto fail;
+ }
+ midi->rmidi = rmidi;
+ strcpy(rmidi->name, card->shortname);
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = midi;
+
+ /*
+ * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+ * It's an upside-down world being a gadget.
+ */
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+ snd_card_set_dev(card, &midi->gadget->dev);
+
+ /* register it - we're ready to go */
+ err = snd_card_register(card);
+ if (err < 0) {
+ ERROR(midi, "snd_card_register() failed\n");
+ goto fail;
+ }
+
+ VDBG(midi, "%s() finished ok\n", __func__);
+ return 0;
+
+fail:
+ if (midi->card) {
+ snd_card_free(midi->card);
+ midi->card = NULL;
+ }
+ return err;
+}
+
+/* MIDI function driver setup/binding */
+
+static int __init
+f_midi_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_descriptor_header *midi_function[(MAX_PORTS * 2) + 12];
+ struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
+ struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_midi *midi = func_to_midi(f);
+ int status, n, jack = 1, i = 0;
+
+ /* maybe allocate device-global string ID */
+ if (midi_string_defs[0].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ goto fail;
+ midi_string_defs[0].id = status;
+ }
+
+ /* We have two interfaces, AudioControl and MIDIStreaming */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ ac_interface_desc.bInterfaceNumber = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ ms_interface_desc.bInterfaceNumber = status;
+ ac_header_desc.baInterfaceNr[0] = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc);
+ if (!midi->in_ep)
+ goto fail;
+ midi->in_ep->driver_data = cdev; /* claim */
+
+ midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc);
+ if (!midi->out_ep)
+ goto fail;
+ midi->out_ep->driver_data = cdev; /* claim */
+
+ /*
+ * construct the function's descriptor set. As the number of
+ * input and output MIDI ports is configurable, we have to do
+ * it that way.
+ */
+
+ /* add the headers - these are always the same */
+ midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
+ midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
+ midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
+
+ /* calculate the header's wTotalLength */
+ n = USB_DT_MS_HEADER_SIZE
+ + (1 + midi->in_ports) * USB_DT_MIDI_IN_SIZE
+ + (1 + midi->out_ports) * USB_DT_MIDI_OUT_SIZE(1);
+ ms_header_desc.wTotalLength = cpu_to_le16(n);
+
+ midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
+
+ /* we have one embedded IN jack */
+ jack_in_emb_desc.bJackID = jack++;
+ midi_function[i++] = (struct usb_descriptor_header *) &jack_in_emb_desc;
+
+ /* and a dynamic amount of external IN jacks */
+ for (n = 0; n < midi->in_ports; n++) {
+ struct usb_midi_in_jack_descriptor *ext = &jack_in_ext_desc[n];
+
+ ext->bLength = USB_DT_MIDI_IN_SIZE;
+ ext->bDescriptorType = USB_DT_CS_INTERFACE;
+ ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
+ ext->bJackType = USB_MS_EXTERNAL;
+ ext->bJackID = jack++;
+ ext->iJack = 0;
+
+ midi_function[i++] = (struct usb_descriptor_header *) ext;
+ }
+
+ /* one embedded OUT jack ... */
+ jack_out_emb_desc.bLength = USB_DT_MIDI_OUT_SIZE(midi->in_ports);
+ jack_out_emb_desc.bJackID = jack++;
+ jack_out_emb_desc.bNrInputPins = midi->in_ports;
+ /* ... which referencess all external IN jacks */
+ for (n = 0; n < midi->in_ports; n++) {
+ jack_out_emb_desc.pins[n].baSourceID = jack_in_ext_desc[n].bJackID;
+ jack_out_emb_desc.pins[n].baSourcePin = 1;
+ }
+
+ midi_function[i++] = (struct usb_descriptor_header *) &jack_out_emb_desc;
+
+ /* and multiple external OUT jacks ... */
+ for (n = 0; n < midi->out_ports; n++) {
+ struct usb_midi_out_jack_descriptor_1 *ext = &jack_out_ext_desc[n];
+ int m;
+
+ ext->bLength = USB_DT_MIDI_OUT_SIZE(1);
+ ext->bDescriptorType = USB_DT_CS_INTERFACE;
+ ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
+ ext->bJackType = USB_MS_EXTERNAL;
+ ext->bJackID = jack++;
+ ext->bNrInputPins = 1;
+ ext->iJack = 0;
+ /* ... which all reference the same embedded IN jack */
+ for (m = 0; m < midi->out_ports; m++) {
+ ext->pins[m].baSourceID = jack_in_emb_desc.bJackID;
+ ext->pins[m].baSourcePin = 1;
+ }
+
+ midi_function[i++] = (struct usb_descriptor_header *) ext;
+ }
+
+ /* configure the endpoint descriptors ... */
+ ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
+ ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
+ for (n = 0; n < midi->in_ports; n++)
+ ms_out_desc.baAssocJackID[n] = jack_in_emb_desc.bJackID;
+
+ ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
+ ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
+ for (n = 0; n < midi->out_ports; n++)
+ ms_in_desc.baAssocJackID[n] = jack_out_emb_desc.bJackID;
+
+ /* ... and add them to the list */
+ midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
+ midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc;
+ midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc;
+ midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
+ midi_function[i++] = NULL;
+
+ /*
+ * support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ /* copy descriptors, and track endpoint copies */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ c->highspeed = true;
+ bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
+ bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
+ f->hs_descriptors = usb_copy_descriptors(midi_function);
+ } else {
+ f->descriptors = usb_copy_descriptors(midi_function);
+ }
+
+ return 0;
+
+fail:
+ /* we might as well release our claims on endpoints */
+ if (midi->out_ep)
+ midi->out_ep->driver_data = NULL;
+ if (midi->in_ep)
+ midi->in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+/**
+ * f_midi_bind_config - add USB MIDI function to a configuration
+ * @c: the configuration to supcard the USB audio function
+ * @index: the soundcard index to use for the ALSA device creation
+ * @id: the soundcard id to use for the ALSA device creation
+ * @buflen: the buffer length to use
+ * @qlen the number of read requests to pre-allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ */
+int __init f_midi_bind_config(struct usb_configuration *c,
+ int index, char *id,
+ unsigned int in_ports,
+ unsigned int out_ports,
+ unsigned int buflen,
+ unsigned int qlen)
+{
+ struct f_midi *midi;
+ int status, i;
+
+ /* sanity check */
+ if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
+ return -EINVAL;
+
+ /* allocate and initialize one new instance */
+ midi = kzalloc(sizeof *midi, GFP_KERNEL);
+ if (!midi) {
+ status = -ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < in_ports; i++) {
+ struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ status = -ENOMEM;
+ goto setup_fail;
+ }
+
+ port->midi = midi;
+ port->active = 0;
+ port->cable = i;
+ midi->in_port[i] = port;
+ }
+
+ midi->gadget = c->cdev->gadget;
+ tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+
+ /* set up ALSA midi devices */
+ midi->in_ports = in_ports;
+ midi->out_ports = out_ports;
+ status = f_midi_register_card(midi);
+ if (status < 0)
+ goto setup_fail;
+
+ midi->func.name = "gmidi function";
+ midi->func.strings = midi_strings;
+ midi->func.bind = f_midi_bind;
+ midi->func.unbind = f_midi_unbind;
+ midi->func.set_alt = f_midi_set_alt;
+ midi->func.disable = f_midi_disable;
+
+ midi->id = kstrdup(id, GFP_KERNEL);
+ midi->index = index;
+ midi->buflen = buflen;
+ midi->qlen = qlen;
+
+ status = usb_add_function(c, &midi->func);
+ if (status)
+ goto setup_fail;
+
+ return 0;
+
+setup_fail:
+ for (--i; i >= 0; i--)
+ kfree(midi->in_port[i]);
+ kfree(midi);
+fail:
+ return status;
+}
+
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index ae69ed7e6b99..aab8eded045b 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -13,15 +13,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
index 394502abeb96..e3f74bf5da2d 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/f_obex.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index 8f3eab1af885..349077033338 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -8,16 +8,6 @@
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/mm.h>
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 3ea4666be3d0..704d1d94f72a 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -11,15 +11,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index e18b4f520951..168906d2b5d4 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
index 3dc53754ab60..c1540648125a 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/slab.h>
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index 7a8b9aa4aea5..2022fe492148 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -8,7 +8,6 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h
index e18a6636c283..abf832935134 100644
--- a/drivers/usb/gadget/f_uvc.h
+++ b/drivers/usb/gadget/f_uvc.h
@@ -8,7 +8,6 @@
* 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.
- *
*/
#ifndef _F_UVC_H_
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 639e14a2fd15..3ac4f51cd0bb 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -69,8 +69,7 @@
* each LUN would be settable independently as a disk drive or a CD-ROM
* drive, but currently all LUNs have to be the same type. The CD-ROM
* emulation includes a single data track and no audio tracks; hence there
- * need be only one backing file per LUN. Note also that the CD-ROM block
- * length is set to 512 rather than the more common value 2048.
+ * need be only one backing file per LUN.
*
* Requirements are modest; only a bulk-in and a bulk-out endpoint are
* needed (an interrupt-out endpoint is also needed for CBI). The memory
@@ -461,7 +460,6 @@ struct fsg_dev {
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
- struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
int thread_wakeup_needed;
struct completion thread_notifier;
@@ -488,6 +486,8 @@ struct fsg_dev {
unsigned int nluns;
struct fsg_lun *luns;
struct fsg_lun *curlun;
+ /* Must be the last entry */
+ struct fsg_buffhd buffhds[];
};
typedef void (*fsg_routine_t)(struct fsg_dev *);
@@ -586,7 +586,19 @@ dev_qualifier = {
.bNumConfigurations = 1,
};
+static int populate_bos(struct fsg_dev *fsg, u8 *buf)
+{
+ memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE);
+ buf += USB_DT_BOS_SIZE;
+
+ memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE);
+ buf += USB_DT_USB_EXT_CAP_SIZE;
+ memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE);
+
+ return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE
+ + USB_DT_USB_EXT_CAP_SIZE;
+}
/*
* Config descriptors must agree with the code that sets configurations
@@ -935,7 +947,8 @@ static int standard_setup_req(struct fsg_dev *fsg,
break;
case USB_DT_DEVICE_QUALIFIER:
VDBG(fsg, "get device qualifier\n");
- if (!gadget_is_dualspeed(fsg->gadget))
+ if (!gadget_is_dualspeed(fsg->gadget) ||
+ fsg->gadget->speed == USB_SPEED_SUPER)
break;
/*
* Assume ep0 uses the same maxpacket value for both
@@ -948,7 +961,8 @@ static int standard_setup_req(struct fsg_dev *fsg,
case USB_DT_OTHER_SPEED_CONFIG:
VDBG(fsg, "get other-speed config descriptor\n");
- if (!gadget_is_dualspeed(fsg->gadget))
+ if (!gadget_is_dualspeed(fsg->gadget) ||
+ fsg->gadget->speed == USB_SPEED_SUPER)
break;
goto get_config;
case USB_DT_CONFIG:
@@ -967,7 +981,15 @@ get_config:
value = usb_gadget_get_string(&fsg_stringtab,
w_value & 0xff, req->buf);
break;
+
+ case USB_DT_BOS:
+ VDBG(fsg, "get bos descriptor\n");
+
+ if (gadget_is_superspeed(fsg->gadget))
+ value = populate_bos(fsg, req->buf);
+ break;
}
+
break;
/* One config, two speeds */
@@ -1136,7 +1158,6 @@ static int do_read(struct fsg_dev *fsg)
u32 amount_left;
loff_t file_offset, file_offset_tmp;
unsigned int amount;
- unsigned int partial_page;
ssize_t nread;
/* Get the starting Logical Block Address and check that it's
@@ -1158,7 +1179,7 @@ static int do_read(struct fsg_dev *fsg)
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
return -EINVAL;
}
- file_offset = ((loff_t) lba) << 9;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
/* Carry out the file reads */
amount_left = fsg->data_size_from_cmnd;
@@ -1171,17 +1192,10 @@ static int do_read(struct fsg_dev *fsg)
* Try to read the remaining amount.
* But don't read more than the buffer size.
* And don't try to read past the end of the file.
- * Finally, if we're not at a page boundary, don't read past
- * the next page.
- * If this means reading 0 then we were asked to read past
- * the end of file. */
+ */
amount = min((unsigned int) amount_left, mod_data.buflen);
amount = min((loff_t) amount,
curlun->file_length - file_offset);
- partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
- if (partial_page > 0)
- amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
- partial_page);
/* Wait for the next buffer to become available */
bh = fsg->next_buffhd_to_fill;
@@ -1196,7 +1210,7 @@ static int do_read(struct fsg_dev *fsg)
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
bh->inreq->length = 0;
bh->state = BUF_STATE_FULL;
@@ -1221,18 +1235,23 @@ static int do_read(struct fsg_dev *fsg)
} else if (nread < amount) {
LDBG(curlun, "partial file read: %d/%u\n",
(int) nread, amount);
- nread -= (nread & 511); // Round down to a block
+ nread = round_down(nread, curlun->blksize);
}
file_offset += nread;
amount_left -= nread;
fsg->residue -= nread;
+
+ /* Except at the end of the transfer, nread will be
+ * equal to the buffer size, which is divisible by the
+ * bulk-in maxpacket size.
+ */
bh->inreq->length = nread;
bh->state = BUF_STATE_FULL;
/* If an error occurred, report it and its position */
if (nread < amount) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1262,7 +1281,6 @@ static int do_write(struct fsg_dev *fsg)
u32 amount_left_to_req, amount_left_to_write;
loff_t usb_offset, file_offset, file_offset_tmp;
unsigned int amount;
- unsigned int partial_page;
ssize_t nwritten;
int rc;
@@ -1303,7 +1321,7 @@ static int do_write(struct fsg_dev *fsg)
/* Carry out the file writes */
get_some_more = 1;
- file_offset = usb_offset = ((loff_t) lba) << 9;
+ file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;
while (amount_left_to_write > 0) {
@@ -1313,38 +1331,20 @@ static int do_write(struct fsg_dev *fsg)
if (bh->state == BUF_STATE_EMPTY && get_some_more) {
/* Figure out how much we want to get:
- * Try to get the remaining amount.
- * But don't get more than the buffer size.
- * And don't try to go past the end of the file.
- * If we're not at a page boundary,
- * don't go past the next page.
- * If this means getting 0, then we were asked
- * to write past the end of file.
- * Finally, round down to a block boundary. */
+ * Try to get the remaining amount,
+ * but not more than the buffer size.
+ */
amount = min(amount_left_to_req, mod_data.buflen);
- amount = min((loff_t) amount, curlun->file_length -
- usb_offset);
- partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
- if (partial_page > 0)
- amount = min(amount,
- (unsigned int) PAGE_CACHE_SIZE - partial_page);
-
- if (amount == 0) {
+
+ /* Beyond the end of the backing file? */
+ if (usb_offset >= curlun->file_length) {
get_some_more = 0;
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = usb_offset >> 9;
+ curlun->sense_data_info = usb_offset >> curlun->blkbits;
curlun->info_valid = 1;
continue;
}
- amount -= (amount & 511);
- if (amount == 0) {
-
- /* Why were we were asked to transfer a
- * partial block? */
- get_some_more = 0;
- continue;
- }
/* Get the next buffer */
usb_offset += amount;
@@ -1353,11 +1353,11 @@ static int do_write(struct fsg_dev *fsg)
if (amount_left_to_req == 0)
get_some_more = 0;
- /* amount is always divisible by 512, hence by
- * the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
- bh->outreq->short_not_ok = 1;
+ /* Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(fsg, bh, amount);
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
fsg->next_buffhd_to_fill = bh->next;
@@ -1376,7 +1376,7 @@ static int do_write(struct fsg_dev *fsg)
/* Did something go wrong with the transfer? */
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1390,6 +1390,16 @@ static int do_write(struct fsg_dev *fsg)
amount = curlun->file_length - file_offset;
}
+ /* Don't accept excess data. The spec doesn't say
+ * what to do in this case. We'll ignore the error.
+ */
+ amount = min(amount, bh->bulk_out_intended_length);
+
+ /* Don't write a partial block */
+ amount = round_down(amount, curlun->blksize);
+ if (amount == 0)
+ goto empty_write;
+
/* Perform the write */
file_offset_tmp = file_offset;
nwritten = vfs_write(curlun->filp,
@@ -1408,8 +1418,7 @@ static int do_write(struct fsg_dev *fsg)
} else if (nwritten < amount) {
LDBG(curlun, "partial file write: %d/%u\n",
(int) nwritten, amount);
- nwritten -= (nwritten & 511);
- // Round down to a block
+ nwritten = round_down(nwritten, curlun->blksize);
}
file_offset += nwritten;
amount_left_to_write -= nwritten;
@@ -1418,13 +1427,14 @@ static int do_write(struct fsg_dev *fsg)
/* If an error occurred, report it and its position */
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
+ empty_write:
/* Did the host decide to stop early? */
- if (bh->outreq->actual != bh->outreq->length) {
+ if (bh->outreq->actual < bh->bulk_out_intended_length) {
fsg->short_packet_received = 1;
break;
}
@@ -1500,8 +1510,8 @@ static int do_verify(struct fsg_dev *fsg)
return -EIO; // No default reply
/* Prepare to carry out the file verify */
- amount_left = verification_length << 9;
- file_offset = ((loff_t) lba) << 9;
+ amount_left = verification_length << curlun->blkbits;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
/* Write out all the dirty buffers before invalidating them */
fsg_lun_fsync_sub(curlun);
@@ -1519,15 +1529,14 @@ static int do_verify(struct fsg_dev *fsg)
* Try to read the remaining amount, but not more than
* the buffer size.
* And don't try to read past the end of the file.
- * If this means reading 0 then we were asked to read
- * past the end of file. */
+ */
amount = min((unsigned int) amount_left, mod_data.buflen);
amount = min((loff_t) amount,
curlun->file_length - file_offset);
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1550,11 +1559,11 @@ static int do_verify(struct fsg_dev *fsg)
} else if (nread < amount) {
LDBG(curlun, "partial file verify: %d/%u\n",
(int) nread, amount);
- nread -= (nread & 511); // Round down to a sector
+ nread = round_down(nread, curlun->blksize);
}
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- curlun->sense_data_info = file_offset >> 9;
+ curlun->sense_data_info = file_offset >> curlun->blkbits;
curlun->info_valid = 1;
break;
}
@@ -1668,7 +1677,7 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
/* Max logical block */
- put_unaligned_be32(512, &buf[4]); /* Block length */
+ put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */
return 8;
}
@@ -1890,7 +1899,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg,
put_unaligned_be32(curlun->num_sectors, &buf[0]);
/* Number of blocks */
- put_unaligned_be32(512, &buf[4]); /* Block length */
+ put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */
buf[4] = 0x02; /* Current capacity */
return 12;
}
@@ -1969,7 +1978,7 @@ static int throw_away_data(struct fsg_dev *fsg)
fsg->next_buffhd_to_drain = bh->next;
/* A short packet or an error ends everything */
- if (bh->outreq->actual != bh->outreq->length ||
+ if (bh->outreq->actual < bh->bulk_out_intended_length ||
bh->outreq->status != 0) {
raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
return -EINTR;
@@ -1983,11 +1992,11 @@ static int throw_away_data(struct fsg_dev *fsg)
amount = min(fsg->usb_amount_left,
(u32) mod_data.buflen);
- /* amount is always divisible by 512, hence by
- * the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
- bh->outreq->short_not_ok = 1;
+ /* Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(fsg, bh, amount);
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
fsg->next_buffhd_to_fill = bh->next;
@@ -2415,7 +2424,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case READ_6:
i = fsg->cmnd[4];
- fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1,
"READ(6)")) == 0)
@@ -2424,7 +2433,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case READ_10:
fsg->data_size_from_cmnd =
- get_unaligned_be16(&fsg->cmnd[7]) << 9;
+ get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)")) == 0)
@@ -2433,7 +2442,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case READ_12:
fsg->data_size_from_cmnd =
- get_unaligned_be32(&fsg->cmnd[6]) << 9;
+ get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)")) == 0)
@@ -2519,7 +2528,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case WRITE_6:
i = fsg->cmnd[4];
- fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1,
"WRITE(6)")) == 0)
@@ -2528,7 +2537,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case WRITE_10:
fsg->data_size_from_cmnd =
- get_unaligned_be16(&fsg->cmnd[7]) << 9;
+ get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)")) == 0)
@@ -2537,7 +2546,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
case WRITE_12:
fsg->data_size_from_cmnd =
- get_unaligned_be32(&fsg->cmnd[6]) << 9;
+ get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)")) == 0)
@@ -2666,7 +2675,6 @@ static int get_next_command(struct fsg_dev *fsg)
/* Queue a request to read a Bulk-only CBW */
set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
@@ -2752,7 +2760,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting)
reset:
/* Deallocate the requests */
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
if (bh->inreq) {
@@ -2791,29 +2799,32 @@ reset:
/* Enable the endpoints */
d = fsg_ep_desc(fsg->gadget,
- &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+ &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc,
+ &fsg_ss_bulk_in_desc);
if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
goto reset;
fsg->bulk_in_enabled = 1;
d = fsg_ep_desc(fsg->gadget,
- &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+ &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc,
+ &fsg_ss_bulk_out_desc);
if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
goto reset;
fsg->bulk_out_enabled = 1;
- fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
+ fsg->bulk_out_maxpacket = usb_endpoint_maxp(d);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
if (transport_is_cbi()) {
d = fsg_ep_desc(fsg->gadget,
- &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc);
+ &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc,
+ &fsg_ss_intr_in_desc);
if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0)
goto reset;
fsg->intr_in_enabled = 1;
}
/* Allocate the requests */
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
@@ -2862,17 +2873,10 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config)
fsg->config = new_config;
if ((rc = do_set_interface(fsg, 0)) != 0)
fsg->config = 0; // Reset on errors
- else {
- char *speed;
-
- switch (fsg->gadget->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- default: speed = "?"; break;
- }
- INFO(fsg, "%s speed config #%d\n", speed, fsg->config);
- }
+ else
+ INFO(fsg, "%s config #%d\n",
+ usb_speed_string(fsg->gadget->speed),
+ fsg->config);
}
return rc;
}
@@ -2909,7 +2913,7 @@ static void handle_exception(struct fsg_dev *fsg)
/* Cancel all the pending transfers */
if (fsg->intreq_busy)
usb_ep_dequeue(fsg->intr_in, fsg->intreq);
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &fsg->buffhds[i];
if (bh->inreq_busy)
usb_ep_dequeue(fsg->bulk_in, bh->inreq);
@@ -2920,7 +2924,7 @@ static void handle_exception(struct fsg_dev *fsg)
/* Wait until everything is idle */
for (;;) {
num_active = fsg->intreq_busy;
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &fsg->buffhds[i];
num_active += bh->inreq_busy + bh->outreq_busy;
}
@@ -2942,7 +2946,7 @@ static void handle_exception(struct fsg_dev *fsg)
* state, and the exception. Then invoke the handler. */
spin_lock_irq(&fsg->lock);
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
bh = &fsg->buffhds[i];
bh->state = BUF_STATE_EMPTY;
}
@@ -3149,6 +3153,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
DBG(fsg, "unbind\n");
clear_bit(REGISTERED, &fsg->atomic_bitflags);
+ /* If the thread isn't already dead, tell it to exit now */
+ if (fsg->state != FSG_STATE_TERMINATED) {
+ raise_exception(fsg, FSG_STATE_EXIT);
+ wait_for_completion(&fsg->thread_notifier);
+
+ /* The cleanup routine waits for this completion also */
+ complete(&fsg->thread_notifier);
+ }
+
/* Unregister the sysfs attribute files and the LUNs */
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
@@ -3162,17 +3175,8 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
}
}
- /* If the thread isn't already dead, tell it to exit now */
- if (fsg->state != FSG_STATE_TERMINATED) {
- raise_exception(fsg, FSG_STATE_EXIT);
- wait_for_completion(&fsg->thread_notifier);
-
- /* The cleanup routine waits for this completion also */
- complete(&fsg->thread_notifier);
- }
-
/* Free the data buffers */
- for (i = 0; i < FSG_NUM_BUFFERS; ++i)
+ for (i = 0; i < fsg_num_buffers; ++i)
kfree(fsg->buffhds[i].buf);
/* Free the request and buffer for endpoint 0 */
@@ -3445,6 +3449,24 @@ static int __init fsg_bind(struct usb_gadget *gadget)
fsg_fs_intr_in_desc.bEndpointAddress;
}
+ if (gadget_is_superspeed(gadget)) {
+ unsigned max_burst;
+
+ fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, mod_data.buflen / 1024, 15);
+
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_ss_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+ }
+
if (gadget_is_otg(gadget))
fsg_otg_desc.bmAttributes |= USB_OTG_HNP;
@@ -3460,7 +3482,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
req->complete = ep0_complete;
/* Allocate the data buffers */
- for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
/* Allocate for the bulk-in endpoint. We assume that
@@ -3471,7 +3493,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
goto out;
bh->next = bh + 1;
}
- fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0];
+ fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0];
/* This should reflect the actual gadget power source */
usb_gadget_set_selfpowered(gadget);
@@ -3561,11 +3583,7 @@ static void fsg_resume(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver fsg_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .speed = USB_SPEED_HIGH,
-#else
- .speed = USB_SPEED_FULL,
-#endif
+ .speed = USB_SPEED_SUPER,
.function = (char *) fsg_string_product,
.unbind = fsg_unbind,
.disconnect = fsg_disconnect,
@@ -3587,7 +3605,9 @@ static int __init fsg_alloc(void)
{
struct fsg_dev *fsg;
- fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+ fsg = kzalloc(sizeof *fsg +
+ fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL);
+
if (!fsg)
return -ENOMEM;
spin_lock_init(&fsg->lock);
@@ -3605,6 +3625,10 @@ static int __init fsg_init(void)
int rc;
struct fsg_dev *fsg;
+ rc = fsg_num_buffers_validate();
+ if (rc != 0)
+ return rc;
+
if ((rc = fsg_alloc()) != 0)
return rc;
fsg = the_fsg;
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 3bf872e1ad39..2a03e4de11c1 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -540,7 +540,7 @@ static int qe_ep_init(struct qe_udc *udc,
int reval = 0;
u16 max = 0;
- max = le16_to_cpu(desc->wMaxPacketSize);
+ max = usb_endpoint_maxp(desc);
/* check the max package size validate for this endpoint */
/* Refer to USB2.0 spec table 9-13,
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index de24a4233c25..b2c44e1d5813 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -559,7 +559,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
- max = le16_to_cpu(desc->wMaxPacketSize);
+ max = usb_endpoint_maxp(desc);
/* Disable automatic zlp generation. Driver is responsible to indicate
* explicitly through req->req.zero. This is needed to enable multi-td
@@ -1715,34 +1715,31 @@ static void dtd_complete_irq(struct fsl_udc *udc)
}
}
+static inline enum usb_device_speed portscx_device_speed(u32 reg)
+{
+ switch (speed & PORTSCX_PORT_SPEED_MASK) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ return USB_SPEED_HIGH;
+ case PORTSCX_PORT_SPEED_FULL:
+ return USB_SPEED_FULL;
+ case PORTSCX_PORT_SPEED_LOW:
+ return USB_SPEED_LOW;
+ default:
+ return USB_SPEED_UNKNOWN;
+ }
+}
+
/* Process a port change interrupt */
static void port_change_irq(struct fsl_udc *udc)
{
- u32 speed;
-
if (udc->bus_reset)
udc->bus_reset = 0;
/* Bus resetting is finished */
- if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
+ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET))
/* Get the speed */
- speed = (fsl_readl(&dr_regs->portsc1)
- & PORTSCX_PORT_SPEED_MASK);
- switch (speed) {
- case PORTSCX_PORT_SPEED_HIGH:
- udc->gadget.speed = USB_SPEED_HIGH;
- break;
- case PORTSCX_PORT_SPEED_FULL:
- udc->gadget.speed = USB_SPEED_FULL;
- break;
- case PORTSCX_PORT_SPEED_LOW:
- udc->gadget.speed = USB_SPEED_LOW;
- break;
- default:
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- break;
- }
- }
+ udc->gadget.speed =
+ portscx_device_speed(fsl_readl(&dr_regs->portsc1));
/* Update USB state */
if (!udc->resume_state)
@@ -2167,20 +2164,8 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
default:
s = "None"; break;
}
- s;} ), ( {
- char *s;
- switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
- case PORTSCX_PORT_SPEED_FULL:
- s = "Full Speed"; break;
- case PORTSCX_PORT_SPEED_LOW:
- s = "Low Speed"; break;
- case PORTSCX_PORT_SPEED_HIGH:
- s = "High Speed"; break;
- default:
- s = "Undefined"; break;
- }
- s;
- } ),
+ s;} ),
+ usb_speed_string(portscx_device_speed(tmp_reg)),
(tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
"Normal PHY mode" : "Low power mode",
(tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index 4ec888f90002..e593f2849fa9 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -220,7 +210,7 @@ static int config_ep(struct fusb300_ep *ep,
info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
- info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ info.maxpacket = usb_endpoint_maxp(desc);
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if ((info.type == USB_ENDPOINT_XFER_INT) ||
@@ -1479,7 +1469,7 @@ static int __init fusb300_probe(struct platform_device *pdev)
fusb300->gadget.name = udc_name;
fusb300->reg = reg;
- ret = request_irq(ires->start, fusb300_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED,
udc_name, fusb300);
if (ret < 0) {
pr_err("request_irq error (%d)\n", ret);
@@ -1487,7 +1477,7 @@ static int __init fusb300_probe(struct platform_device *pdev)
}
ret = request_irq(ires1->start, fusb300_irq,
- IRQF_DISABLED | IRQF_SHARED, udc_name, fusb300);
+ IRQF_SHARED, udc_name, fusb300);
if (ret < 0) {
pr_err("request_irq1 error (%d)\n", ret);
goto clean_up;
diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h
index f51aa2ef1f90..92745bd03064 100644
--- a/drivers/usb/gadget/fusb300_udc.h
+++ b/drivers/usb/gadget/fusb300_udc.h
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 704c2800ac00..0519d77915ec 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define pr_fmt(fmt) "g_ffs: " fmt
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index f3a83cd0ef50..a8855d0b7f3b 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -31,6 +31,7 @@
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
+#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
@@ -115,6 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x30;
else if (gadget_is_net2272(gadget))
return 0x31;
+ else if (gadget_is_dwc3(gadget))
+ return 0x32;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 8b9220e128a7..8fcde37aa6d4 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -36,134 +36,43 @@
#include "gadget_chips.h"
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
+#include "composite.c"
#include "usbstring.c"
#include "config.c"
#include "epautoconf.c"
+#include "f_midi.c"
/*-------------------------------------------------------------------------*/
-
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
-#define DRIVER_VERSION "25 Jul 2006"
-
static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
static int index = SNDRV_DEFAULT_IDX1;
-static char *id = SNDRV_DEFAULT_STR1;
-
-module_param(index, int, 0444);
+module_param(index, int, S_IRUGO);
MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
-module_param(id, charp, 0444);
-MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
-
-/* Some systems will want different product identifiers published in the
- * device descriptor, either numbers or strings or both. These string
- * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
- */
-
-static ushort idVendor;
-module_param(idVendor, ushort, S_IRUGO);
-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-
-static ushort idProduct;
-module_param(idProduct, ushort, S_IRUGO);
-MODULE_PARM_DESC(idProduct, "USB Product ID");
-
-static ushort bcdDevice;
-module_param(bcdDevice, ushort, S_IRUGO);
-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-
-static char *iManufacturer;
-module_param(iManufacturer, charp, S_IRUGO);
-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-
-static char *iProduct;
-module_param(iProduct, charp, S_IRUGO);
-MODULE_PARM_DESC(iProduct, "USB Product string");
-
-static char *iSerialNumber;
-module_param(iSerialNumber, charp, S_IRUGO);
-MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
-
-/*
- * this version autoconfigures as much as possible,
- * which is reasonable for most "bulk-only" drivers.
- */
-static const char *EP_IN_NAME;
-static const char *EP_OUT_NAME;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(id, charp, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ 256
-
-
-/* This is a gadget, and the IN/OUT naming is from the host's perspective.
- USB -> OUT endpoint -> rawmidi
- USB <- IN endpoint <- rawmidi */
-struct gmidi_in_port {
- struct gmidi_device* dev;
- int active;
- uint8_t cable; /* cable number << 4 */
- uint8_t state;
-#define STATE_UNKNOWN 0
-#define STATE_1PARAM 1
-#define STATE_2PARAM_1 2
-#define STATE_2PARAM_2 3
-#define STATE_SYSEX_0 4
-#define STATE_SYSEX_1 5
-#define STATE_SYSEX_2 6
- uint8_t data[2];
-};
-
-struct gmidi_device {
- spinlock_t lock;
- struct usb_gadget *gadget;
- struct usb_request *req; /* for control responses */
- u8 config;
- struct usb_ep *in_ep, *out_ep;
- struct snd_card *card;
- struct snd_rawmidi *rmidi;
- struct snd_rawmidi_substream *in_substream;
- struct snd_rawmidi_substream *out_substream;
-
- /* For the moment we only support one port in
- each direction, but in_port is kept as a
- separate struct so we can have more later. */
- struct gmidi_in_port in_port;
- unsigned long out_triggered;
- struct tasklet_struct tasklet;
-};
-
-static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
-
-
-#define DBG(d, fmt, args...) \
- dev_dbg(&(d)->gadget->dev , fmt , ## args)
-#define VDBG(d, fmt, args...) \
- dev_vdbg(&(d)->gadget->dev , fmt , ## args)
-#define ERROR(d, fmt, args...) \
- dev_err(&(d)->gadget->dev , fmt , ## args)
-#define INFO(d, fmt, args...) \
- dev_info(&(d)->gadget->dev , fmt , ## args)
-
-
-static unsigned buflen = 256;
-static unsigned qlen = 32;
-
+static unsigned int buflen = 256;
module_param(buflen, uint, S_IRUGO);
+MODULE_PARM_DESC(buflen, "MIDI buffer length");
+
+static unsigned int qlen = 32;
module_param(qlen, uint, S_IRUGO);
+MODULE_PARM_DESC(qlen, "USB read request queue length");
+static unsigned int in_ports = 1;
+module_param(in_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(in_ports, "Number of MIDI input ports");
+
+static unsigned int out_ports = 1;
+module_param(out_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
/* Thanks to Grey Innovation for donating this product ID.
*
@@ -173,1149 +82,124 @@ module_param(qlen, uint, S_IRUGO);
#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */
#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */
+/* string IDs are assigned dynamically */
-/*
- * DESCRIPTORS ... most are static, but strings and (full)
- * configuration descriptors are built on demand.
- */
-
-#define STRING_MANUFACTURER 25
-#define STRING_PRODUCT 42
-#define STRING_SERIAL 101
-#define STRING_MIDI_GADGET 250
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_DESCRIPTION_IDX 2
-/* We only have the one configuration, it's number 1. */
-#define GMIDI_CONFIG 1
-
-/* We have two interfaces- AudioControl and MIDIStreaming */
-#define GMIDI_AC_INTERFACE 0
-#define GMIDI_MS_INTERFACE 1
-#define GMIDI_NUM_INTERFACES 2
-
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
-DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
-DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
-
-/* B.1 Device Descriptor */
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
- .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
- .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
- .iManufacturer = STRING_MANUFACTURER,
- .iProduct = STRING_PRODUCT,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
};
-/* B.2 Configuration Descriptor */
-static struct usb_config_descriptor config_desc = {
- .bLength = USB_DT_CONFIG_SIZE,
- .bDescriptorType = USB_DT_CONFIG,
- /* compute wTotalLength on the fly */
- .bNumInterfaces = GMIDI_NUM_INTERFACES,
- .bConfigurationValue = GMIDI_CONFIG,
- .iConfiguration = STRING_MIDI_GADGET,
- /*
- * FIXME: When embedding this driver in a device,
- * these need to be set to reflect the actual
- * power properties of the device. Is it selfpowered?
- */
- .bmAttributes = USB_CONFIG_ATT_ONE,
- .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
-};
-
-/* B.3.1 Standard AC Interface Descriptor */
-static const struct usb_interface_descriptor ac_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = GMIDI_AC_INTERFACE,
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
- .iInterface = STRING_MIDI_GADGET,
-};
-
-/* B.3.2 Class-Specific AC Interface Descriptor */
-static const struct uac1_ac_header_descriptor_1 ac_header_desc = {
- .bLength = UAC_DT_AC_HEADER_SIZE(1),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_HEADER,
- .bcdADC = cpu_to_le16(0x0100),
- .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
- .bInCollection = 1,
- .baInterfaceNr = {
- [0] = GMIDI_MS_INTERFACE,
- }
-};
-
-/* B.4.1 Standard MS Interface Descriptor */
-static const struct usb_interface_descriptor ms_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = GMIDI_MS_INTERFACE,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
- .iInterface = STRING_MIDI_GADGET,
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = "Grey Innovation",
+ [STRING_PRODUCT_IDX].s = "MIDI Gadget",
+ [STRING_DESCRIPTION_IDX].s = "MIDI",
+ { } /* end of list */
};
-/* B.4.2 Class-Specific MS Interface Descriptor */
-static const struct usb_ms_header_descriptor ms_header_desc = {
- .bLength = USB_DT_MS_HEADER_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_HEADER,
- .bcdMSC = cpu_to_le16(0x0100),
- .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE
- + 2*USB_DT_MIDI_IN_SIZE
- + 2*USB_DT_MIDI_OUT_SIZE(1)),
-};
-
-#define JACK_IN_EMB 1
-#define JACK_IN_EXT 2
-#define JACK_OUT_EMB 3
-#define JACK_OUT_EXT 4
-
-/* B.4.3 MIDI IN Jack Descriptors */
-static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
- .bLength = USB_DT_MIDI_IN_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
- .bJackType = USB_MS_EMBEDDED,
- .bJackID = JACK_IN_EMB,
-};
-
-static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = {
- .bLength = USB_DT_MIDI_IN_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
- .bJackType = USB_MS_EXTERNAL,
- .bJackID = JACK_IN_EXT,
-};
-
-/* B.4.4 MIDI OUT Jack Descriptors */
-static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = {
- .bLength = USB_DT_MIDI_OUT_SIZE(1),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
- .bJackType = USB_MS_EMBEDDED,
- .bJackID = JACK_OUT_EMB,
- .bNrInputPins = 1,
- .pins = {
- [0] = {
- .baSourceID = JACK_IN_EXT,
- .baSourcePin = 1,
- }
- }
-};
-
-static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = {
- .bLength = USB_DT_MIDI_OUT_SIZE(1),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
- .bJackType = USB_MS_EXTERNAL,
- .bJackID = JACK_OUT_EXT,
- .bNrInputPins = 1,
- .pins = {
- [0] = {
- .baSourceID = JACK_IN_EMB,
- .baSourcePin = 1,
- }
- }
-};
-
-/* B.5.1 Standard Bulk OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor bulk_out_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */
-static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = {
- .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
- .bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = USB_MS_GENERAL,
- .bNumEmbMIDIJack = 1,
- .baAssocJackID = {
- [0] = JACK_IN_EMB,
- }
-};
-
-/* B.6.1 Standard Bulk IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor bulk_in_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
-static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = {
- .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
- .bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = USB_MS_GENERAL,
- .bNumEmbMIDIJack = 1,
- .baAssocJackID = {
- [0] = JACK_OUT_EMB,
- }
-};
-
-static const struct usb_descriptor_header *gmidi_function [] = {
- (struct usb_descriptor_header *)&ac_interface_desc,
- (struct usb_descriptor_header *)&ac_header_desc,
- (struct usb_descriptor_header *)&ms_interface_desc,
-
- (struct usb_descriptor_header *)&ms_header_desc,
- (struct usb_descriptor_header *)&jack_in_emb_desc,
- (struct usb_descriptor_header *)&jack_in_ext_desc,
- (struct usb_descriptor_header *)&jack_out_emb_desc,
- (struct usb_descriptor_header *)&jack_out_ext_desc,
- /* If you add more jacks, update ms_header_desc.wTotalLength */
-
- (struct usb_descriptor_header *)&bulk_out_desc,
- (struct usb_descriptor_header *)&ms_out_desc,
- (struct usb_descriptor_header *)&bulk_in_desc,
- (struct usb_descriptor_header *)&ms_in_desc,
- NULL,
-};
-
-static char manufacturer[50];
-static char product_desc[40] = "MIDI Gadget";
-static char serial_number[20];
-
-/* static strings, in UTF-8 */
-static struct usb_string strings [] = {
- { STRING_MANUFACTURER, manufacturer, },
- { STRING_PRODUCT, product_desc, },
- { STRING_SERIAL, serial_number, },
- { STRING_MIDI_GADGET, longname, },
- { } /* end of list */
-};
-
-static struct usb_gadget_strings stringtab = {
+static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
- .strings = strings,
+ .strings = strings_dev,
};
-static int config_buf(struct usb_gadget *gadget,
- u8 *buf, u8 type, unsigned index)
-{
- int len;
-
- /* only one configuration */
- if (index != 0) {
- return -EINVAL;
- }
- len = usb_gadget_config_buf(&config_desc,
- buf, USB_BUFSIZ, gmidi_function);
- if (len < 0) {
- return len;
- }
- ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
- return len;
-}
-
-static struct usb_request *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 free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
- kfree(req->buf);
- usb_ep_free_request(ep, req);
-}
-
-static const uint8_t gmidi_cin_length[] = {
- 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
};
-/*
- * Receives a chunk of MIDI data.
- */
-static void gmidi_read_data(struct usb_ep *ep, int cable,
- uint8_t *data, int length)
-{
- struct gmidi_device *dev = ep->driver_data;
- /* cable is ignored, because for now we only have one. */
-
- if (!dev->out_substream) {
- /* Nobody is listening - throw it on the floor. */
- return;
- }
- if (!test_bit(dev->out_substream->number, &dev->out_triggered)) {
- return;
- }
- snd_rawmidi_receive(dev->out_substream, data, length);
-}
-
-static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
-{
- unsigned i;
- u8 *buf = req->buf;
-
- for (i = 0; i + 3 < req->actual; i += 4) {
- if (buf[i] != 0) {
- int cable = buf[i] >> 4;
- int length = gmidi_cin_length[buf[i] & 0x0f];
- gmidi_read_data(ep, cable, &buf[i + 1], length);
- }
- }
-}
-
-static void gmidi_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct gmidi_device *dev = ep->driver_data;
- int status = req->status;
-
- switch (status) {
- case 0: /* normal completion */
- if (ep == dev->out_ep) {
- /* we received stuff.
- req is queued again, below */
- gmidi_handle_out_data(ep, req);
- } else if (ep == dev->in_ep) {
- /* our transmit completed.
- see if there's more to go.
- gmidi_transmit eats req, don't queue it again. */
- gmidi_transmit(dev, req);
- return;
- }
- break;
-
- /* this endpoint is normally active while we're configured */
- case -ECONNABORTED: /* hardware forced ep reset */
- case -ECONNRESET: /* request dequeued */
- case -ESHUTDOWN: /* disconnect from host */
- VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
- req->actual, req->length);
- if (ep == dev->out_ep) {
- gmidi_handle_out_data(ep, req);
- }
- free_ep_req(ep, req);
- return;
-
- case -EOVERFLOW: /* buffer overrun on read means that
- * we didn't provide a big enough
- * buffer.
- */
- default:
- DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
- status, req->actual, req->length);
- break;
- case -EREMOTEIO: /* short read */
- break;
- }
-
- status = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (status) {
- ERROR(dev, "kill %s: resubmit %d bytes --> %d\n",
- ep->name, req->length, status);
- usb_ep_set_halt(ep);
- /* FIXME recover later ... somehow */
- }
-}
-
-static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
-{
- int err = 0;
- struct usb_request *req;
- struct usb_ep *ep;
- unsigned i;
-
- dev->in_ep->desc = &bulk_in_desc;
- err = usb_ep_enable(dev->in_ep);
- if (err) {
- ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
- goto fail;
- }
- dev->in_ep->driver_data = dev;
-
- dev->out_ep->desc = &bulk_out_desc;
- err = usb_ep_enable(dev->out_ep);
- if (err) {
- ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
- goto fail;
- }
- dev->out_ep->driver_data = dev;
-
- /* allocate a bunch of read buffers and queue them all at once. */
- ep = dev->out_ep;
- for (i = 0; i < qlen && err == 0; i++) {
- req = alloc_ep_req(ep, buflen);
- if (req) {
- req->complete = gmidi_complete;
- err = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (err) {
- DBG(dev, "%s queue req: %d\n", ep->name, err);
- }
- } else {
- err = -ENOMEM;
- }
- }
-fail:
- /* caller is responsible for cleanup on error */
- return err;
-}
-
-
-static void gmidi_reset_config(struct gmidi_device *dev)
-{
- if (dev->config == 0) {
- return;
- }
-
- DBG(dev, "reset config\n");
-
- /* just disable endpoints, forcing completion of pending i/o.
- * all our completion handlers free their requests in this case.
- */
- usb_ep_disable(dev->in_ep);
- usb_ep_disable(dev->out_ep);
- dev->config = 0;
-}
-
-/* change our operational config. this code must agree with the code
- * that returns config descriptors, and altsetting code.
- *
- * it's also responsible for power management interactions. some
- * configurations might not work with our current power sources.
- *
- * note that some device controller hardware will constrain what this
- * code can do, perhaps by disallowing more than one configuration or
- * by limiting configuration choices (like the pxa2xx).
- */
-static int
-gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
-{
- int result = 0;
- struct usb_gadget *gadget = dev->gadget;
-
-#if 0
- /* FIXME */
- /* Hacking this bit out fixes a bug where on receipt of two
- USB_REQ_SET_CONFIGURATION messages, we end up with no
- buffered OUT requests waiting for data. This is clearly
- hiding a bug elsewhere, because if the config didn't
- change then we really shouldn't do anything. */
- /* Having said that, when we do "change" from config 1
- to config 1, we at least gmidi_reset_config() which
- clears out any requests on endpoints, so it's not like
- we leak or anything. */
- if (number == dev->config) {
- return 0;
- }
-#endif
-
- gmidi_reset_config(dev);
-
- switch (number) {
- case GMIDI_CONFIG:
- result = set_gmidi_config(dev, gfp_flags);
- break;
- default:
- result = -EINVAL;
- /* FALL THROUGH */
- case 0:
- return result;
- }
-
- if (!result && (!dev->in_ep || !dev->out_ep)) {
- result = -ENODEV;
- }
- if (result) {
- gmidi_reset_config(dev);
- } else {
- char *speed;
-
- switch (gadget->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- default: speed = "?"; break;
- }
-
- dev->config = number;
- INFO(dev, "%s speed\n", speed);
- }
- return result;
-}
-
-
-static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req)
-{
- if (req->status || req->actual != req->length) {
- DBG((struct gmidi_device *) ep->driver_data,
- "setup complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
- }
-}
-
-/*
- * The setup() callback implements all the ep0 functionality that's
- * not handled lower down, in hardware or the hardware driver (like
- * device and endpoint feature flags, and their status). It's all
- * housekeeping for the gadget function we're implementing. Most of
- * the work is in config-specific setup.
- */
-static int gmidi_setup(struct usb_gadget *gadget,
- const struct usb_ctrlrequest *ctrl)
-{
- struct gmidi_device *dev = get_gadget_data(gadget);
- struct usb_request *req = dev->req;
- int value = -EOPNOTSUPP;
- u16 w_index = le16_to_cpu(ctrl->wIndex);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u16 w_length = le16_to_cpu(ctrl->wLength);
-
- /* usually this stores reply data in the pre-allocated ep0 buffer,
- * but config change events will reconfigure hardware.
- */
- req->zero = 0;
- switch (ctrl->bRequest) {
-
- case USB_REQ_GET_DESCRIPTOR:
- if (ctrl->bRequestType != USB_DIR_IN) {
- goto unknown;
- }
- switch (w_value >> 8) {
-
- case USB_DT_DEVICE:
- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
- value = min(w_length, (u16) sizeof(device_desc));
- memcpy(req->buf, &device_desc, value);
- break;
- case USB_DT_CONFIG:
- value = config_buf(gadget, req->buf,
- w_value >> 8,
- w_value & 0xff);
- if (value >= 0) {
- value = min(w_length, (u16)value);
- }
- break;
-
- case USB_DT_STRING:
- /* wIndex == language code.
- * this driver only handles one language, you can
- * add string tables for other languages, using
- * any UTF-8 characters
- */
- value = usb_gadget_get_string(&stringtab,
- w_value & 0xff, req->buf);
- if (value >= 0) {
- value = min(w_length, (u16)value);
- }
- break;
- }
- break;
-
- /* currently two configs, two speeds */
- case USB_REQ_SET_CONFIGURATION:
- if (ctrl->bRequestType != 0) {
- goto unknown;
- }
- if (gadget->a_hnp_support) {
- DBG(dev, "HNP available\n");
- } else if (gadget->a_alt_hnp_support) {
- DBG(dev, "HNP needs a different root port\n");
- } else {
- VDBG(dev, "HNP inactive\n");
- }
- spin_lock(&dev->lock);
- value = gmidi_set_config(dev, w_value, GFP_ATOMIC);
- spin_unlock(&dev->lock);
- break;
- case USB_REQ_GET_CONFIGURATION:
- if (ctrl->bRequestType != USB_DIR_IN) {
- goto unknown;
- }
- *(u8 *)req->buf = dev->config;
- value = min(w_length, (u16)1);
- break;
-
- /* until we add altsetting support, or other interfaces,
- * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
- * and already killed pending endpoint I/O.
- */
- case USB_REQ_SET_INTERFACE:
- if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
- goto unknown;
- }
- spin_lock(&dev->lock);
- if (dev->config && w_index < GMIDI_NUM_INTERFACES
- && w_value == 0)
- {
- u8 config = dev->config;
-
- /* resets interface configuration, forgets about
- * previous transaction state (queued bufs, etc)
- * and re-inits endpoint state (toggle etc)
- * no response queued, just zero status == success.
- * if we had more than one interface we couldn't
- * use this "reset the config" shortcut.
- */
- gmidi_reset_config(dev);
- gmidi_set_config(dev, config, GFP_ATOMIC);
- value = 0;
- }
- spin_unlock(&dev->lock);
- break;
- case USB_REQ_GET_INTERFACE:
- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
- goto unknown;
- }
- if (!dev->config) {
- break;
- }
- if (w_index >= GMIDI_NUM_INTERFACES) {
- value = -EDOM;
- break;
- }
- *(u8 *)req->buf = 0;
- value = min(w_length, (u16)1);
- break;
-
- default:
-unknown:
- VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
- }
-
- /* respond with data transfer before status phase? */
- if (value >= 0) {
- req->length = value;
- req->zero = value < w_length;
- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
- if (value < 0) {
- DBG(dev, "ep_queue --> %d\n", value);
- req->status = 0;
- gmidi_setup_complete(gadget->ep0, req);
- }
- }
-
- /* device either stalls (value < 0) or reports success */
- return value;
-}
-
-static void gmidi_disconnect(struct usb_gadget *gadget)
-{
- struct gmidi_device *dev = get_gadget_data(gadget);
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- gmidi_reset_config(dev);
-
- /* a more significant application might have some non-usb
- * activities to quiesce here, saving resources like power
- * or pushing the notification up a network stack.
- */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- /* next we may get setup() calls to enumerate new connections;
- * or an unbind() during shutdown (including removing module).
- */
-}
-
-static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
-{
- struct gmidi_device *dev = get_gadget_data(gadget);
- struct snd_card *card;
-
- DBG(dev, "unbind\n");
-
- card = dev->card;
- dev->card = NULL;
- if (card) {
- snd_card_free(card);
- }
-
- /* we've already been disconnected ... no i/o is active */
- if (dev->req) {
- dev->req->length = USB_BUFSIZ;
- free_ep_req(gadget->ep0, dev->req);
- }
- kfree(dev);
- set_gadget_data(gadget, NULL);
-}
-
-static int gmidi_snd_free(struct snd_device *device)
+static int __exit midi_unbind(struct usb_composite_dev *dev)
{
return 0;
}
-static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0,
- uint8_t p1, uint8_t p2, uint8_t p3)
-{
- unsigned length = req->length;
- u8 *buf = (u8 *)req->buf + length;
-
- buf[0] = p0;
- buf[1] = p1;
- buf[2] = p2;
- buf[3] = p3;
- req->length = length + 4;
-}
-
-/*
- * Converts MIDI commands to USB MIDI packets.
- */
-static void gmidi_transmit_byte(struct usb_request *req,
- struct gmidi_in_port *port, uint8_t b)
-{
- uint8_t p0 = port->cable;
-
- if (b >= 0xf8) {
- gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
- } else if (b >= 0xf0) {
- switch (b) {
- case 0xf0:
- port->data[0] = b;
- port->state = STATE_SYSEX_1;
- break;
- case 0xf1:
- case 0xf3:
- port->data[0] = b;
- port->state = STATE_1PARAM;
- break;
- case 0xf2:
- port->data[0] = b;
- port->state = STATE_2PARAM_1;
- break;
- case 0xf4:
- case 0xf5:
- port->state = STATE_UNKNOWN;
- break;
- case 0xf6:
- gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
- port->state = STATE_UNKNOWN;
- break;
- case 0xf7:
- switch (port->state) {
- case STATE_SYSEX_0:
- gmidi_transmit_packet(req,
- p0 | 0x05, 0xf7, 0, 0);
- break;
- case STATE_SYSEX_1:
- gmidi_transmit_packet(req,
- p0 | 0x06, port->data[0], 0xf7, 0);
- break;
- case STATE_SYSEX_2:
- gmidi_transmit_packet(req,
- p0 | 0x07, port->data[0],
- port->data[1], 0xf7);
- break;
- }
- port->state = STATE_UNKNOWN;
- break;
- }
- } else if (b >= 0x80) {
- port->data[0] = b;
- if (b >= 0xc0 && b <= 0xdf)
- port->state = STATE_1PARAM;
- else
- port->state = STATE_2PARAM_1;
- } else { /* b < 0x80 */
- switch (port->state) {
- case STATE_1PARAM:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- } else {
- p0 |= 0x02;
- port->state = STATE_UNKNOWN;
- }
- gmidi_transmit_packet(req, p0, port->data[0], b, 0);
- break;
- case STATE_2PARAM_1:
- port->data[1] = b;
- port->state = STATE_2PARAM_2;
- break;
- case STATE_2PARAM_2:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- port->state = STATE_2PARAM_1;
- } else {
- p0 |= 0x03;
- port->state = STATE_UNKNOWN;
- }
- gmidi_transmit_packet(req,
- p0, port->data[0], port->data[1], b);
- break;
- case STATE_SYSEX_0:
- port->data[0] = b;
- port->state = STATE_SYSEX_1;
- break;
- case STATE_SYSEX_1:
- port->data[1] = b;
- port->state = STATE_SYSEX_2;
- break;
- case STATE_SYSEX_2:
- gmidi_transmit_packet(req,
- p0 | 0x04, port->data[0], port->data[1], b);
- port->state = STATE_SYSEX_0;
- break;
- }
- }
-}
-
-static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req)
-{
- struct usb_ep *ep = dev->in_ep;
- struct gmidi_in_port *port = &dev->in_port;
-
- if (!ep) {
- return;
- }
- if (!req) {
- req = alloc_ep_req(ep, buflen);
- }
- if (!req) {
- ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n");
- return;
- }
- req->length = 0;
- req->complete = gmidi_complete;
-
- if (port->active) {
- while (req->length + 3 < buflen) {
- uint8_t b;
- if (snd_rawmidi_transmit(dev->in_substream, &b, 1)
- != 1)
- {
- port->active = 0;
- break;
- }
- gmidi_transmit_byte(req, port, b);
- }
- }
- if (req->length > 0) {
- usb_ep_queue(ep, req, GFP_ATOMIC);
- } else {
- free_ep_req(ep, req);
- }
-}
-
-static void gmidi_in_tasklet(unsigned long data)
-{
- struct gmidi_device *dev = (struct gmidi_device *)data;
-
- gmidi_transmit(dev, NULL);
-}
-
-static int gmidi_in_open(struct snd_rawmidi_substream *substream)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_in_open\n");
- dev->in_substream = substream;
- dev->in_port.state = STATE_UNKNOWN;
- return 0;
-}
-
-static int gmidi_in_close(struct snd_rawmidi_substream *substream)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_in_close\n");
- return 0;
-}
-
-static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_in_trigger %d\n", up);
- dev->in_port.active = up;
- if (up) {
- tasklet_hi_schedule(&dev->tasklet);
- }
-}
-
-static int gmidi_out_open(struct snd_rawmidi_substream *substream)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_out_open\n");
- dev->out_substream = substream;
- return 0;
-}
-
-static int gmidi_out_close(struct snd_rawmidi_substream *substream)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_out_close\n");
- return 0;
-}
-
-static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
-{
- struct gmidi_device *dev = substream->rmidi->private_data;
-
- VDBG(dev, "gmidi_out_trigger %d\n", up);
- if (up) {
- set_bit(substream->number, &dev->out_triggered);
- } else {
- clear_bit(substream->number, &dev->out_triggered);
- }
-}
-
-static struct snd_rawmidi_ops gmidi_in_ops = {
- .open = gmidi_in_open,
- .close = gmidi_in_close,
- .trigger = gmidi_in_trigger,
+static struct usb_configuration midi_config = {
+ .label = "MIDI Gadget",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
};
-static struct snd_rawmidi_ops gmidi_out_ops = {
- .open = gmidi_out_open,
- .close = gmidi_out_close,
- .trigger = gmidi_out_trigger
-};
-
-/* register as a sound "card" */
-static int gmidi_register_card(struct gmidi_device *dev)
+static int __init midi_bind_config(struct usb_configuration *c)
{
- struct snd_card *card;
- struct snd_rawmidi *rmidi;
- int err;
- int out_ports = 1;
- int in_ports = 1;
- static struct snd_device_ops ops = {
- .dev_free = gmidi_snd_free,
- };
-
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
- if (err < 0) {
- ERROR(dev, "snd_card_create failed\n");
- goto fail;
- }
- dev->card = card;
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops);
- if (err < 0) {
- ERROR(dev, "snd_device_new failed: error %d\n", err);
- goto fail;
- }
-
- strcpy(card->driver, longname);
- strcpy(card->longname, longname);
- strcpy(card->shortname, shortname);
-
- /* Set up rawmidi */
- dev->in_port.dev = dev;
- dev->in_port.active = 0;
- snd_component_add(card, "MIDI");
- err = snd_rawmidi_new(card, "USB MIDI Gadget", 0,
- out_ports, in_ports, &rmidi);
- if (err < 0) {
- ERROR(dev, "snd_rawmidi_new failed: error %d\n", err);
- goto fail;
- }
- dev->rmidi = rmidi;
- strcpy(rmidi->name, card->shortname);
- rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
- rmidi->private_data = dev;
-
- /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
- It's an upside-down world being a gadget. */
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
-
- snd_card_set_dev(card, &dev->gadget->dev);
-
- /* register it - we're ready to go */
- err = snd_card_register(card);
- if (err < 0) {
- ERROR(dev, "snd_card_register failed\n");
- goto fail;
- }
-
- VDBG(dev, "gmidi_register_card finished ok\n");
- return 0;
-
-fail:
- if (dev->card) {
- snd_card_free(dev->card);
- dev->card = NULL;
- }
- return err;
+ return f_midi_bind_config(c, index, id,
+ in_ports, out_ports,
+ buflen, qlen);
}
-/*
- * Creates an output endpoint, and initializes output ports.
- */
-static int __init gmidi_bind(struct usb_gadget *gadget)
+static int __init midi_bind(struct usb_composite_dev *cdev)
{
- struct gmidi_device *dev;
- struct usb_ep *in_ep, *out_ep;
- int gcnum, err = 0;
+ struct usb_gadget *gadget = cdev->gadget;
+ int gcnum, status;
- /* support optional vendor/distro customization */
- if (idVendor) {
- if (!idProduct) {
- pr_err("idVendor needs idProduct!\n");
- return -ENODEV;
- }
- device_desc.idVendor = cpu_to_le16(idVendor);
- device_desc.idProduct = cpu_to_le16(idProduct);
- if (bcdDevice) {
- device_desc.bcdDevice = cpu_to_le16(bcdDevice);
- }
- }
- if (iManufacturer) {
- strlcpy(manufacturer, iManufacturer, sizeof(manufacturer));
- } else {
- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- }
- if (iProduct) {
- strlcpy(product_desc, iProduct, sizeof(product_desc));
- }
- if (iSerialNumber) {
- device_desc.iSerialNumber = STRING_SERIAL,
- strlcpy(serial_number, iSerialNumber, sizeof(serial_number));
- }
+ status = usb_string_id(cdev);
+ if (status < 0)
+ return status;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ device_desc.iManufacturer = status;
- /* Bulk-only drivers like this one SHOULD be able to
- * autoconfigure on any sane usb controller driver,
- * but there may also be important quirks to address.
- */
- usb_ep_autoconfig_reset(gadget);
- in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
- if (!in_ep) {
-autoconf_fail:
- pr_err("%s: can't autoconfigure on %s\n",
- shortname, gadget->name);
- return -ENODEV;
- }
- EP_IN_NAME = in_ep->name;
- in_ep->driver_data = in_ep; /* claim */
+ status = usb_string_id(cdev);
+ if (status < 0)
+ return status;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
+ device_desc.iProduct = status;
- out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
- if (!out_ep) {
- goto autoconf_fail;
- }
- EP_OUT_NAME = out_ep->name;
- out_ep->driver_data = out_ep; /* claim */
+ /* config description */
+ status = usb_string_id(cdev);
+ if (status < 0)
+ return status;
+ strings_dev[STRING_DESCRIPTION_IDX].id = status;
+
+ midi_config.iConfiguration = status;
gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- } else {
+ if (gcnum < 0) {
/* gmidi is so simple (no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
* so warn about unrecognized controllers, don't panic.
*/
pr_warning("%s: controller '%s' not recognized\n",
- shortname, gadget->name);
+ __func__, gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x9999);
+ } else {
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
}
+ status = usb_add_config(cdev, &midi_config, midi_bind_config);
+ if (status < 0)
+ return status;
- /* ok, we made sense of the hardware ... */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- return -ENOMEM;
- }
- spin_lock_init(&dev->lock);
- dev->gadget = gadget;
- dev->in_ep = in_ep;
- dev->out_ep = out_ep;
- set_gadget_data(gadget, dev);
- tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
-
- /* preallocate control response and buffer */
- dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ);
- if (!dev->req) {
- err = -ENOMEM;
- goto fail;
- }
-
- dev->req->complete = gmidi_setup_complete;
-
- gadget->ep0->driver_data = dev;
-
- INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
- EP_OUT_NAME, EP_IN_NAME);
-
- /* register as an ALSA sound card */
- err = gmidi_register_card(dev);
- if (err < 0) {
- goto fail;
- }
-
- VDBG(dev, "gmidi_bind finished ok\n");
+ pr_info("%s\n", longname);
return 0;
-
-fail:
- gmidi_unbind(gadget);
- return err;
-}
-
-
-static void gmidi_suspend(struct usb_gadget *gadget)
-{
- struct gmidi_device *dev = get_gadget_data(gadget);
-
- if (gadget->speed == USB_SPEED_UNKNOWN) {
- return;
- }
-
- DBG(dev, "suspend\n");
-}
-
-static void gmidi_resume(struct usb_gadget *gadget)
-{
- struct gmidi_device *dev = get_gadget_data(gadget);
-
- DBG(dev, "resume\n");
}
-
-static struct usb_gadget_driver gmidi_driver = {
- .speed = USB_SPEED_FULL,
- .function = (char *)longname,
- .unbind = gmidi_unbind,
-
- .setup = gmidi_setup,
- .disconnect = gmidi_disconnect,
-
- .suspend = gmidi_suspend,
- .resume = gmidi_resume,
-
- .driver = {
- .name = (char *)shortname,
- .owner = THIS_MODULE,
- },
+static struct usb_composite_driver midi_driver = {
+ .name = (char *) longname,
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
+ .unbind = __exit_p(midi_unbind),
};
-static int __init gmidi_init(void)
+static int __init midi_init(void)
{
- return usb_gadget_probe_driver(&gmidi_driver, gmidi_bind);
+ return usb_composite_probe(&midi_driver, midi_bind);
}
-module_init(gmidi_init);
+module_init(midi_init);
-static void __exit gmidi_cleanup(void)
+static void __exit midi_cleanup(void)
{
- usb_gadget_unregister_driver(&gmidi_driver);
+ usb_composite_unregister(&midi_driver);
}
-module_exit(gmidi_cleanup);
+module_exit(midi_cleanup);
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index 9fb575034a0e..f888c3ede860 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index 692fd9b2248b..2d978c0e7ced 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -689,7 +689,7 @@ static int imx_ep_enable(struct usb_ep *usb_ep,
return -EINVAL;
}
- if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) {
+ if (imx_ep->fifosize < usb_endpoint_maxp(desc)) {
D_ERR(imx_usb->dev,
"<%s> bad %s maxpacket\n", __func__, usb_ep->name);
return -ERANGE;
@@ -1478,7 +1478,7 @@ static int __init imx_udc_probe(struct platform_device *pdev)
for (i = 0; i < IMX_USB_NB_EP + 1; i++) {
ret = request_irq(imx_usb->usbd_int[i], intr_handler(i),
- IRQF_DISABLED, driver_name, imx_usb);
+ 0, driver_name, imx_usb);
if (ret) {
dev_err(&pdev->dev, "can't get irq %i, err %d\n",
imx_usb->usbd_int[i], ret);
diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h
index 7136c242b4ec..d118fb777840 100644
--- a/drivers/usb/gadget/imx_udc.h
+++ b/drivers/usb/gadget/imx_udc.h
@@ -8,11 +8,6 @@
* 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.
*/
#ifndef __LINUX_USB_GADGET_IMX_H
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 1b240990448f..a392ec0d2d51 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index a06e2c27b435..c9fa3bf5b377 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -5,16 +5,6 @@
* 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.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -60,9 +50,6 @@ static const char driver_name[] = "langwell_udc";
static const char driver_desc[] = DRIVER_DESC;
-/* controller device global variable */
-static struct langwell_udc *the_controller;
-
/* for endpoint 0 operations */
static const struct usb_endpoint_descriptor
langwell_ep0_desc = {
@@ -283,7 +270,7 @@ static int langwell_ep_enable(struct usb_ep *_ep,
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
- max = le16_to_cpu(desc->wMaxPacketSize);
+ max = usb_endpoint_maxp(desc);
/*
* disable HW zero length termination select
@@ -1321,9 +1308,12 @@ static int langwell_pullup(struct usb_gadget *_gadget, int is_on)
return 0;
}
-static int langwell_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int langwell_stop(struct usb_gadget_driver *driver);
+static int langwell_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+
+static int langwell_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops langwell_ops = {
@@ -1345,8 +1335,8 @@ static const struct usb_gadget_ops langwell_ops = {
/* D+ pullup, software-controlled connect/disconnect to USB host */
.pullup = langwell_pullup,
- .start = langwell_start,
- .stop = langwell_stop,
+ .udc_start = langwell_start,
+ .udc_stop = langwell_stop,
};
@@ -1561,7 +1551,7 @@ static void stop_activity(struct langwell_udc *dev,
static ssize_t show_function(struct device *_dev,
struct device_attribute *attr, char *buf)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = dev_get_drvdata(_dev);
if (!dev->driver || !dev->driver->function
|| strlen(dev->driver->function) > PAGE_SIZE)
@@ -1572,11 +1562,25 @@ static ssize_t show_function(struct device *_dev,
static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+static inline enum usb_device_speed lpm_device_speed(u32 reg)
+{
+ switch (LPM_PSPD(reg)) {
+ case LPM_SPEED_HIGH:
+ return USB_SPEED_HIGH;
+ case LPM_SPEED_FULL:
+ return USB_SPEED_FULL;
+ case LPM_SPEED_LOW:
+ return USB_SPEED_LOW;
+ default:
+ return USB_SPEED_UNKNOWN;
+ }
+}
+
/* device "langwell_udc" sysfs attribute file */
static ssize_t show_langwell_udc(struct device *_dev,
struct device_attribute *attr, char *buf)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = dev_get_drvdata(_dev);
struct langwell_request *req;
struct langwell_ep *ep = NULL;
char *next;
@@ -1700,20 +1704,7 @@ static ssize_t show_langwell_udc(struct device *_dev,
"BmAttributes: %d\n\n",
LPM_PTS(tmp_reg),
(tmp_reg & LPM_STS) ? 1 : 0,
- ({
- char *s;
- switch (LPM_PSPD(tmp_reg)) {
- case LPM_SPEED_FULL:
- s = "Full Speed"; break;
- case LPM_SPEED_LOW:
- s = "Low Speed"; break;
- case LPM_SPEED_HIGH:
- s = "High Speed"; break;
- default:
- s = "Unknown Speed"; break;
- }
- s;
- }),
+ usb_speed_string(lpm_device_speed(tmp_reg)),
(tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force",
(tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled",
LPM_BA(tmp_reg));
@@ -1821,7 +1812,7 @@ static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL);
static ssize_t store_remote_wakeup(struct device *_dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = dev_get_drvdata(_dev);
unsigned long flags;
ssize_t rc = count;
@@ -1857,21 +1848,15 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup);
* the driver might get unbound.
*/
-static int langwell_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int langwell_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = gadget_to_langwell(g);
unsigned long flags;
int retval;
- if (!dev)
- return -ENODEV;
-
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
- if (dev->driver)
- return -EBUSY;
-
spin_lock_irqsave(&dev->lock, flags);
/* hook up the driver ... */
@@ -1881,18 +1866,9 @@ static int langwell_start(struct usb_gadget_driver *driver,
spin_unlock_irqrestore(&dev->lock, flags);
- retval = bind(&dev->gadget);
- if (retval) {
- dev_dbg(&dev->pdev->dev, "bind to driver %s --> %d\n",
- driver->driver.name, retval);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
-
retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
if (retval)
- goto err_unbind;
+ goto err;
dev->usb_state = USB_STATE_ATTACHED;
dev->ep0_state = WAIT_FOR_SETUP;
@@ -1909,31 +1885,27 @@ static int langwell_start(struct usb_gadget_driver *driver,
dev_info(&dev->pdev->dev, "register driver: %s\n",
driver->driver.name);
dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
+
return 0;
-err_unbind:
- driver->unbind(&dev->gadget);
+err:
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
+
return retval;
}
/* unregister gadget driver */
-static int langwell_stop(struct usb_gadget_driver *driver)
+static int langwell_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = gadget_to_langwell(g);
unsigned long flags;
- if (!dev)
- return -ENODEV;
-
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
- if (unlikely(!driver || !driver->unbind))
- return -EINVAL;
-
/* exit PHY low power suspend */
if (dev->pdev->device != 0x0829)
langwell_phy_low_power(dev, 0);
@@ -1956,8 +1928,6 @@ static int langwell_stop(struct usb_gadget_driver *driver)
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
- /* unbind gadget driver */
- driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
@@ -1966,6 +1936,7 @@ static int langwell_stop(struct usb_gadget_driver *driver)
dev_info(&dev->pdev->dev, "unregistered driver '%s'\n",
driver->driver.name);
dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
+
return 0;
}
@@ -2657,12 +2628,10 @@ done:
dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
}
-
/* port change detect interrupt handler */
static void handle_port_change(struct langwell_udc *dev)
{
u32 portsc1, devlc;
- u32 speed;
dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
@@ -2677,24 +2646,9 @@ static void handle_port_change(struct langwell_udc *dev)
/* bus reset is finished */
if (!(portsc1 & PORTS_PR)) {
/* get the speed */
- speed = LPM_PSPD(devlc);
- switch (speed) {
- case LPM_SPEED_HIGH:
- dev->gadget.speed = USB_SPEED_HIGH;
- break;
- case LPM_SPEED_FULL:
- dev->gadget.speed = USB_SPEED_FULL;
- break;
- case LPM_SPEED_LOW:
- dev->gadget.speed = USB_SPEED_LOW;
- break;
- default:
- dev->gadget.speed = USB_SPEED_UNKNOWN;
- break;
- }
- dev_vdbg(&dev->pdev->dev,
- "speed = %d, dev->gadget.speed = %d\n",
- speed, dev->gadget.speed);
+ dev->gadget.speed = lpm_device_speed(devlc);
+ dev_vdbg(&dev->pdev->dev, "dev->gadget.speed = %d\n",
+ dev->gadget.speed);
}
/* LPM L0 to L1 */
@@ -2969,7 +2923,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev)
handle_port_change(dev);
}
- /* suspend interrrupt */
+ /* suspend interrupt */
if (irq_sts & STS_SLI) {
dev_vdbg(&dev->pdev->dev, "suspend interrupt\n");
handle_bus_suspend(dev);
@@ -2999,7 +2953,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev)
/* release device structure */
static void gadget_release(struct device *_dev)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = dev_get_drvdata(_dev);
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
@@ -3057,7 +3011,7 @@ static void sram_deinit(struct langwell_udc *dev)
/* tear down the binding between this driver and the pci device */
static void langwell_udc_remove(struct pci_dev *pdev)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = pci_get_drvdata(pdev);
DECLARE_COMPLETION(done);
@@ -3124,8 +3078,6 @@ static void langwell_udc_remove(struct pci_dev *pdev)
/* free dev, wait for the release() finished */
wait_for_completion(&done);
-
- the_controller = NULL;
}
@@ -3144,11 +3096,6 @@ static int langwell_udc_probe(struct pci_dev *pdev,
size_t size;
int retval;
- if (the_controller) {
- dev_warn(&pdev->dev, "ignoring\n");
- return -EBUSY;
- }
-
/* alloc, and start init */
dev = kzalloc(sizeof *dev, GFP_KERNEL);
if (dev == NULL) {
@@ -3368,8 +3315,6 @@ static int langwell_udc_probe(struct pci_dev *pdev,
"After langwell_udc_probe(), print all registers:\n");
print_all_registers(dev);
- the_controller = dev;
-
retval = device_register(&dev->gadget.dev);
if (retval)
goto error;
@@ -3404,7 +3349,7 @@ error:
/* device controller suspend */
static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = pci_get_drvdata(pdev);
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
@@ -3452,7 +3397,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
/* device controller resume */
static int langwell_udc_resume(struct pci_dev *pdev)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = pci_get_drvdata(pdev);
size_t size;
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
@@ -3534,7 +3479,7 @@ static int langwell_udc_resume(struct pci_dev *pdev)
/* pci driver shutdown */
static void langwell_udc_shutdown(struct pci_dev *pdev)
{
- struct langwell_udc *dev = the_controller;
+ struct langwell_udc *dev = pci_get_drvdata(pdev);
u32 usbmode;
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h
index f1d9c1bb04f3..ef79e242b7b0 100644
--- a/drivers/usb/gadget/langwell_udc.h
+++ b/drivers/usb/gadget/langwell_udc.h
@@ -5,16 +5,6 @@
* 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.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/usb/langwell_udc.h>
@@ -231,3 +221,5 @@ struct langwell_udc {
u16 dev_status;
};
+#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget)
+
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 491f825ed5c9..91d0af2a24a8 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/module.h>
@@ -370,7 +360,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
ep->pipectr = get_pipectr_addr(pipenum);
ep->pipenum = pipenum;
- ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ ep->ep.maxpacket = usb_endpoint_maxp(desc);
m66592->pipenum2ep[pipenum] = ep;
m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
INIT_LIST_HEAD(&ep->queue);
@@ -447,7 +437,7 @@ static int alloc_pipe_config(struct m66592_ep *ep,
ep->type = info.type;
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
info.dir_in = 1;
@@ -1674,7 +1664,7 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->timer.data = (unsigned long)m66592;
m66592->reg = reg;
- ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(ires->start, m66592_irq, IRQF_SHARED,
udc_name, m66592);
if (ret < 0) {
pr_err("request_irq error (%d)\n", ret);
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
index 7b93d579af37..9d9f7e39f037 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef __M66592_UDC_H__
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index d3eb27427c58..e24f72f82a47 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
@@ -169,7 +160,7 @@ static struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
.iProduct = DRIVER_DESC,
- .max_speed = USB_SPEED_HIGH,
+ .max_speed = USB_SPEED_SUPER,
.needs_serial = 1,
};
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 8c7b74717d85..7e7f515b8b19 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h
index 65f1f7c3bd4e..daa75c12f336 100644
--- a/drivers/usb/gadget/mv_udc.h
+++ b/drivers/usb/gadget/mv_udc.h
@@ -1,3 +1,11 @@
+/*
+ * 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 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.
+ */
#ifndef __MV_UDC_H
#define __MV_UDC_H
@@ -194,14 +202,25 @@ struct mv_udc {
unsigned int ep0_dir;
unsigned int dev_addr;
+ unsigned int test_mode;
int errors;
unsigned softconnect:1,
vbus_active:1,
remote_wakeup:1,
softconnected:1,
- force_fs:1;
- struct clk *clk;
+ force_fs:1,
+ clock_gating:1,
+ active:1;
+
+ struct work_struct vbus_work;
+ struct workqueue_struct *qwork;
+
+ struct mv_usb_platform_data *pdata;
+
+ /* some SOC has mutiple clock sources for USB*/
+ unsigned int clknum;
+ struct clk *clk[0];
};
/* endpoint data structure */
@@ -225,6 +244,7 @@ struct mv_req {
struct mv_dtd *dtd, *head, *tail;
struct mv_ep *ep;
struct list_head queue;
+ unsigned int test_mode;
unsigned dtd_count;
unsigned mapped:1;
};
@@ -289,6 +309,4 @@ struct mv_dtd {
struct mv_dtd *next_dtd_virt;
};
-extern int mv_udc_phy_init(unsigned int base);
-
#endif
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index ce1ac2bcb314..892412103dd8 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -1,3 +1,14 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ * Neil Zhang <zhangwm@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
@@ -22,6 +33,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/platform_data/mv_usb.h>
#include <asm/system.h>
#include <asm/unaligned.h>
@@ -45,6 +57,8 @@
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
+static DECLARE_COMPLETION(release_done);
+
static const char driver_name[] = "mv_udc";
static const char driver_desc[] = DRIVER_DESC;
@@ -53,6 +67,7 @@ static struct mv_udc *the_controller;
int mv_usb_otgsc;
static void nuke(struct mv_ep *ep, int status);
+static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
/* for endpoint 0 operations */
static const struct usb_endpoint_descriptor mv_ep0_desc = {
@@ -82,14 +97,16 @@ static void ep0_reset(struct mv_udc *udc)
(EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
| EP_QUEUE_HEAD_IOS;
+ ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE;
+
epctrlx = readl(&udc->op_regs->epctrlx[0]);
if (i) { /* TX */
- epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST
+ epctrlx |= EPCTRL_TX_ENABLE
| (USB_ENDPOINT_XFER_CONTROL
<< EPCTRL_TX_EP_TYPE_SHIFT);
} else { /* RX */
- epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST
+ epctrlx |= EPCTRL_RX_ENABLE
| (USB_ENDPOINT_XFER_CONTROL
<< EPCTRL_RX_EP_TYPE_SHIFT);
}
@@ -122,6 +139,7 @@ static int process_ep_req(struct mv_udc *udc, int index,
int i, direction;
int retval = 0;
u32 errors;
+ u32 bit_pos;
curr_dqh = &udc->ep_dqh[index];
direction = index % 2;
@@ -139,10 +157,20 @@ static int process_ep_req(struct mv_udc *udc, int index,
errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK;
if (!errors) {
- remaining_length +=
+ remaining_length =
(curr_dtd->size_ioc_sts & DTD_PACKET_SIZE)
>> DTD_LENGTH_BIT_POS;
actual -= remaining_length;
+
+ if (remaining_length) {
+ if (direction) {
+ dev_dbg(&udc->dev->dev,
+ "TX dTD remains data\n");
+ retval = -EPROTO;
+ break;
+ } else
+ break;
+ }
} else {
dev_info(&udc->dev->dev,
"complete_tr error: ep=%d %s: error = 0x%x\n",
@@ -164,6 +192,20 @@ static int process_ep_req(struct mv_udc *udc, int index,
if (retval)
return retval;
+ if (direction == EP_DIR_OUT)
+ bit_pos = 1 << curr_req->ep->ep_num;
+ else
+ bit_pos = 1 << (16 + curr_req->ep->ep_num);
+
+ while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) {
+ if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) {
+ while (readl(&udc->op_regs->epstatus) & bit_pos)
+ udelay(1);
+ break;
+ }
+ udelay(1);
+ }
+
curr_req->req.actual = actual;
return 0;
@@ -335,7 +377,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
} else {
/* Write dQH next pointer and terminate bit to 0 */
dqh->next_dtd_ptr = req->head->td_dma
- & EP_QUEUE_HEAD_NEXT_POINTER_MASK;;
+ & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
dqh->size_ioc_int_sts = 0;
/* Ensure that updates to the QH will occur before priming. */
@@ -376,7 +418,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
}
}
done:
- return retval;;
+ return retval;
}
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
@@ -481,6 +523,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
u16 max = 0;
u32 bit_pos, epctrlx, direction;
unsigned char zlt = 0, ios = 0, mult = 0;
+ unsigned long flags;
ep = container_of(_ep, struct mv_ep, ep);
udc = ep->udc;
@@ -493,7 +536,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
return -ESHUTDOWN;
direction = ep_dir(ep);
- max = le16_to_cpu(desc->wMaxPacketSize);
+ max = usb_endpoint_maxp(desc);
/*
* disable HW zero length termination select
@@ -501,9 +544,6 @@ static int mv_ep_enable(struct usb_ep *_ep,
*/
zlt = 1;
- /* Get the endpoint queue head address */
- dqh = (struct mv_dqh *)ep->dqh;
-
bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
/* Check if the Endpoint is Primed */
@@ -532,7 +572,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
case USB_ENDPOINT_XFER_ISOC:
/* Calculate transactions needed for high bandwidth iso */
mult = (unsigned char)(1 + ((max >> 11) & 0x03));
- max = max & 0x8ff; /* bit 0~10 */
+ max = max & 0x7ff; /* bit 0~10 */
/* 3 transactions at most */
if (mult > 3)
goto en_done;
@@ -540,6 +580,10 @@ static int mv_ep_enable(struct usb_ep *_ep,
default:
goto en_done;
}
+
+ spin_lock_irqsave(&udc->lock, flags);
+ /* Get the endpoint queue head address */
+ dqh = ep->dqh;
dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
| (mult << EP_QUEUE_HEAD_MULT_POS)
| (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0)
@@ -572,18 +616,20 @@ static int mv_ep_enable(struct usb_ep *_ep,
*/
epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
if ((epctrlx & EPCTRL_RX_ENABLE) == 0) {
- epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ epctrlx |= (USB_ENDPOINT_XFER_BULK
<< EPCTRL_RX_EP_TYPE_SHIFT);
writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
}
epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
if ((epctrlx & EPCTRL_TX_ENABLE) == 0) {
- epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ epctrlx |= (USB_ENDPOINT_XFER_BULK
<< EPCTRL_TX_EP_TYPE_SHIFT);
writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
}
+ spin_unlock_irqrestore(&udc->lock, flags);
+
return 0;
en_done:
return -EINVAL;
@@ -595,6 +641,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
struct mv_ep *ep;
struct mv_dqh *dqh;
u32 bit_pos, epctrlx, direction;
+ unsigned long flags;
ep = container_of(_ep, struct mv_ep, ep);
if ((_ep == NULL) || !ep->desc)
@@ -605,6 +652,8 @@ static int mv_ep_disable(struct usb_ep *_ep)
/* Get the endpoint queue head address */
dqh = ep->dqh;
+ spin_lock_irqsave(&udc->lock, flags);
+
direction = ep_dir(ep);
bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
@@ -623,6 +672,9 @@ static int mv_ep_disable(struct usb_ep *_ep)
ep->desc = NULL;
ep->stopped = 1;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
return 0;
}
@@ -655,37 +707,28 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep)
{
struct mv_udc *udc;
u32 bit_pos, direction;
- struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+ struct mv_ep *ep;
unsigned int loops;
+ if (!_ep)
+ return;
+
+ ep = container_of(_ep, struct mv_ep, ep);
+ if (!ep->desc)
+ return;
+
udc = ep->udc;
direction = ep_dir(ep);
- bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
- /*
- * Flushing will halt the pipe
- * Write 1 to the Flush register
- */
- writel(bit_pos, &udc->op_regs->epflush);
- /* Wait until flushing completed */
- loops = LOOPS(FLUSH_TIMEOUT);
- while (readl(&udc->op_regs->epflush) & bit_pos) {
- /*
- * ENDPTFLUSH bit should be cleared to indicate this
- * operation is complete
- */
- if (loops == 0) {
- dev_err(&udc->dev->dev,
- "TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x\n",
- (unsigned)readl(&udc->op_regs->epflush),
- (unsigned)bit_pos);
- return;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
+ if (ep->ep_num == 0)
+ bit_pos = (1 << 16) | 1;
+ else if (direction == EP_DIR_OUT)
+ bit_pos = 1 << ep->ep_num;
+ else
+ bit_pos = 1 << (16 + ep->ep_num);
+
loops = LOOPS(EPSTATUS_TIMEOUT);
- while (readl(&udc->op_regs->epstatus) & bit_pos) {
+ do {
unsigned int inter_loops;
if (loops == 0) {
@@ -700,7 +743,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep)
/* Wait until flushing completed */
inter_loops = LOOPS(FLUSH_TIMEOUT);
- while (readl(&udc->op_regs->epflush) & bit_pos) {
+ while (readl(&udc->op_regs->epflush)) {
/*
* ENDPTFLUSH bit should be cleared to indicate this
* operation is complete
@@ -717,7 +760,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep)
udelay(LOOPS_USEC);
}
loops--;
- }
+ } while (readl(&udc->op_regs->epstatus) & bit_pos);
}
/* queues (submits) an I/O request to an endpoint */
@@ -987,6 +1030,22 @@ static struct usb_ep_ops mv_ep_ops = {
.fifo_flush = mv_ep_fifo_flush, /* flush fifo */
};
+static void udc_clock_enable(struct mv_udc *udc)
+{
+ unsigned int i;
+
+ for (i = 0; i < udc->clknum; i++)
+ clk_enable(udc->clk[i]);
+}
+
+static void udc_clock_disable(struct mv_udc *udc)
+{
+ unsigned int i;
+
+ for (i = 0; i < udc->clknum; i++)
+ clk_disable(udc->clk[i]);
+}
+
static void udc_stop(struct mv_udc *udc)
{
u32 tmp;
@@ -1075,6 +1134,40 @@ static int udc_reset(struct mv_udc *udc)
return 0;
}
+static int mv_udc_enable(struct mv_udc *udc)
+{
+ int retval;
+
+ if (udc->clock_gating == 0 || udc->active)
+ return 0;
+
+ dev_dbg(&udc->dev->dev, "enable udc\n");
+ udc_clock_enable(udc);
+ if (udc->pdata->phy_init) {
+ retval = udc->pdata->phy_init(udc->phy_regs);
+ if (retval) {
+ dev_err(&udc->dev->dev,
+ "init phy error %d\n", retval);
+ udc_clock_disable(udc);
+ return retval;
+ }
+ }
+ udc->active = 1;
+
+ return 0;
+}
+
+static void mv_udc_disable(struct mv_udc *udc)
+{
+ if (udc->clock_gating && udc->active) {
+ dev_dbg(&udc->dev->dev, "disable udc\n");
+ if (udc->pdata->phy_deinit)
+ udc->pdata->phy_deinit(udc->phy_regs);
+ udc_clock_disable(udc);
+ udc->active = 0;
+ }
+}
+
static int mv_udc_get_frame(struct usb_gadget *gadget)
{
struct mv_udc *udc;
@@ -1110,22 +1203,68 @@ static int mv_udc_wakeup(struct usb_gadget *gadget)
return 0;
}
+static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct mv_udc *udc;
+ unsigned long flags;
+ int retval = 0;
+
+ udc = container_of(gadget, struct mv_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+
+ dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
+ __func__, udc->softconnect, udc->vbus_active);
+
+ udc->vbus_active = (is_active != 0);
+ if (udc->driver && udc->softconnect && udc->vbus_active) {
+ retval = mv_udc_enable(udc);
+ if (retval == 0) {
+ /* Clock is disabled, need re-init registers */
+ udc_reset(udc);
+ ep0_reset(udc);
+ udc_start(udc);
+ }
+ } else if (udc->driver && udc->softconnect) {
+ /* stop all the transfer in queue*/
+ stop_activity(udc, udc->driver);
+ udc_stop(udc);
+ mv_udc_disable(udc);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return retval;
+}
+
static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
{
struct mv_udc *udc;
unsigned long flags;
+ int retval = 0;
udc = container_of(gadget, struct mv_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
+ dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
+ __func__, udc->softconnect, udc->vbus_active);
+
udc->softconnect = (is_on != 0);
- if (udc->driver && udc->softconnect)
- udc_start(udc);
- else
+ if (udc->driver && udc->softconnect && udc->vbus_active) {
+ retval = mv_udc_enable(udc);
+ if (retval == 0) {
+ /* Clock is disabled, need re-init registers */
+ udc_reset(udc);
+ ep0_reset(udc);
+ udc_start(udc);
+ }
+ } else if (udc->driver && udc->vbus_active) {
+ /* stop all the transfer in queue*/
+ stop_activity(udc, udc->driver);
udc_stop(udc);
+ mv_udc_disable(udc);
+ }
spin_unlock_irqrestore(&udc->lock, flags);
- return 0;
+ return retval;
}
static int mv_udc_start(struct usb_gadget_driver *driver,
@@ -1140,17 +1279,15 @@ static const struct usb_gadget_ops mv_ops = {
/* tries to wake up the host connected to this gadget */
.wakeup = mv_udc_wakeup,
+ /* notify controller that VBUS is powered or not */
+ .vbus_session = mv_udc_vbus_session,
+
/* D+ pullup, software-controlled connect/disconnect to USB host */
.pullup = mv_udc_pullup,
.start = mv_udc_start,
.stop = mv_udc_stop,
};
-static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter)
-{
- dev_info(&udc->dev->dev, "Test Mode is not support yet\n");
-}
-
static int eps_init(struct mv_udc *udc)
{
struct mv_ep *ep;
@@ -1257,7 +1394,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
udc->usb_state = USB_STATE_ATTACHED;
udc->ep0_state = WAIT_FOR_SETUP;
- udc->ep0_dir = USB_DIR_OUT;
+ udc->ep0_dir = EP_DIR_OUT;
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1269,9 +1406,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
udc->gadget.dev.driver = NULL;
return retval;
}
- udc_reset(udc);
- ep0_reset(udc);
- udc_start(udc);
+
+ /* pullup is always on */
+ mv_udc_pullup(&udc->gadget, 1);
+
+ /* When boot with cable attached, there will be no vbus irq occurred */
+ if (udc->qwork)
+ queue_work(udc->qwork, &udc->vbus_work);
return 0;
}
@@ -1284,13 +1425,16 @@ static int mv_udc_stop(struct usb_gadget_driver *driver)
if (!udc)
return -ENODEV;
- udc_stop(udc);
-
spin_lock_irqsave(&udc->lock, flags);
+ mv_udc_enable(udc);
+ udc_stop(udc);
+
/* stop all usb activities */
udc->gadget.speed = USB_SPEED_UNKNOWN;
stop_activity(udc, driver);
+ mv_udc_disable(udc);
+
spin_unlock_irqrestore(&udc->lock, flags);
/* unbind gadget driver */
@@ -1301,6 +1445,31 @@ static int mv_udc_stop(struct usb_gadget_driver *driver)
return 0;
}
+static void mv_set_ptc(struct mv_udc *udc, u32 mode)
+{
+ u32 portsc;
+
+ portsc = readl(&udc->op_regs->portsc[0]);
+ portsc |= mode << 16;
+ writel(portsc, &udc->op_regs->portsc[0]);
+}
+
+static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct mv_udc *udc = the_controller;
+ struct mv_req *req = container_of(_req, struct mv_req, req);
+ unsigned long flags;
+
+ dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode);
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (req->test_mode) {
+ mv_set_ptc(udc, req->test_mode);
+ req->test_mode = 0;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
static int
udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
{
@@ -1310,6 +1479,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
ep = &udc->eps[0];
udc->ep0_dir = direction;
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
req = udc->status_req;
@@ -1323,9 +1493,21 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
req->ep = ep;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ if (udc->test_mode) {
+ req->req.complete = prime_status_complete;
+ req->test_mode = udc->test_mode;
+ udc->test_mode = 0;
+ } else
+ req->req.complete = NULL;
req->dtd_count = 0;
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf, req->req.length,
+ ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ }
+
/* prime the data phase */
if (!req_to_dtd(req))
retval = queue_dtd(ep, req);
@@ -1346,6 +1528,17 @@ out:
return retval;
}
+static void mv_udc_testmode(struct mv_udc *udc, u16 index)
+{
+ if (index <= TEST_FORCE_EN) {
+ udc->test_mode = index;
+ if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+ ep0_stall(udc);
+ } else
+ dev_err(&udc->dev->dev,
+ "This test mode(%d) is not supported\n", index);
+}
+
static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup)
{
udc->dev_addr = (u8)setup->wValue;
@@ -1360,7 +1553,7 @@ static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup)
static void ch9getstatus(struct mv_udc *udc, u8 ep_num,
struct usb_ctrlrequest *setup)
{
- u16 status;
+ u16 status = 0;
int retval;
if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
@@ -1388,6 +1581,8 @@ static void ch9getstatus(struct mv_udc *udc, u8 ep_num,
retval = udc_prime_status(udc, EP_DIR_IN, status, false);
if (retval)
ep0_stall(udc);
+ else
+ udc->ep0_state = DATA_STATE_XMIT;
}
static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
@@ -1402,9 +1597,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
case USB_DEVICE_REMOTE_WAKEUP:
udc->remote_wakeup = 0;
break;
- case USB_DEVICE_TEST_MODE:
- mv_udc_testmode(udc, 0, false);
- break;
default:
goto out;
}
@@ -1433,8 +1625,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
if (udc_prime_status(udc, EP_DIR_IN, 0, true))
ep0_stall(udc);
- else
- udc->ep0_state = DATA_STATE_XMIT;
out:
return;
}
@@ -1452,16 +1642,16 @@ static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
break;
case USB_DEVICE_TEST_MODE:
if (setup->wIndex & 0xFF
- && udc->gadget.speed != USB_SPEED_HIGH)
- goto out;
- if (udc->usb_state == USB_STATE_CONFIGURED
- || udc->usb_state == USB_STATE_ADDRESS
- || udc->usb_state == USB_STATE_DEFAULT)
- mv_udc_testmode(udc,
- setup->wIndex & 0xFF00, true);
- else
- goto out;
- break;
+ || udc->gadget.speed != USB_SPEED_HIGH)
+ ep0_stall(udc);
+
+ if (udc->usb_state != USB_STATE_CONFIGURED
+ && udc->usb_state != USB_STATE_ADDRESS
+ && udc->usb_state != USB_STATE_DEFAULT)
+ ep0_stall(udc);
+
+ mv_udc_testmode(udc, (setup->wIndex >> 8));
+ goto out;
default:
goto out;
}
@@ -1599,8 +1789,7 @@ static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr)
dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT];
/* Clear bit in ENDPTSETUPSTAT */
- temp = readl(&udc->op_regs->epsetupstat);
- writel(temp | (1 << ep_num), &udc->op_regs->epsetupstat);
+ writel((1 << ep_num), &udc->op_regs->epsetupstat);
/* while a hazard exists when setup package arrives */
do {
@@ -1871,23 +2060,57 @@ static irqreturn_t mv_udc_irq(int irq, void *dev)
return IRQ_HANDLED;
}
+static irqreturn_t mv_udc_vbus_irq(int irq, void *dev)
+{
+ struct mv_udc *udc = (struct mv_udc *)dev;
+
+ /* polling VBUS and init phy may cause too much time*/
+ if (udc->qwork)
+ queue_work(udc->qwork, &udc->vbus_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mv_udc_vbus_work(struct work_struct *work)
+{
+ struct mv_udc *udc;
+ unsigned int vbus;
+
+ udc = container_of(work, struct mv_udc, vbus_work);
+ if (!udc->pdata->vbus)
+ return;
+
+ vbus = udc->pdata->vbus->poll();
+ dev_info(&udc->dev->dev, "vbus is %d\n", vbus);
+
+ if (vbus == VBUS_HIGH)
+ mv_udc_vbus_session(&udc->gadget, 1);
+ else if (vbus == VBUS_LOW)
+ mv_udc_vbus_session(&udc->gadget, 0);
+}
+
/* release device structure */
static void gadget_release(struct device *_dev)
{
struct mv_udc *udc = the_controller;
complete(udc->done);
- kfree(udc);
}
-static int mv_udc_remove(struct platform_device *dev)
+static int __devexit mv_udc_remove(struct platform_device *dev)
{
struct mv_udc *udc = the_controller;
- DECLARE_COMPLETION(done);
+ int clk_i;
usb_del_gadget_udc(&udc->gadget);
- udc->done = &done;
+ if (udc->qwork) {
+ flush_workqueue(udc->qwork);
+ destroy_workqueue(udc->qwork);
+ }
+
+ if (udc->pdata && udc->pdata->vbus && udc->clock_gating)
+ free_irq(udc->pdata->vbus->irq, &dev->dev);
/* free memory allocated in probe */
if (udc->dtd_pool)
@@ -1902,6 +2125,8 @@ static int mv_udc_remove(struct platform_device *dev)
if (udc->irq)
free_irq(udc->irq, &dev->dev);
+ mv_udc_disable(udc);
+
if (udc->cap_regs)
iounmap(udc->cap_regs);
udc->cap_regs = NULL;
@@ -1915,45 +2140,62 @@ static int mv_udc_remove(struct platform_device *dev)
kfree(udc->status_req);
}
+ for (clk_i = 0; clk_i <= udc->clknum; clk_i++)
+ clk_put(udc->clk[clk_i]);
+
device_unregister(&udc->gadget.dev);
/* free dev, wait for the release() finished */
- wait_for_completion(&done);
+ wait_for_completion(udc->done);
+ kfree(udc);
the_controller = NULL;
return 0;
}
-int mv_udc_probe(struct platform_device *dev)
+static int __devinit mv_udc_probe(struct platform_device *dev)
{
+ struct mv_usb_platform_data *pdata = dev->dev.platform_data;
struct mv_udc *udc;
int retval = 0;
+ int clk_i = 0;
struct resource *r;
size_t size;
- udc = kzalloc(sizeof *udc, GFP_KERNEL);
+ if (pdata == NULL) {
+ dev_err(&dev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+
+ size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
+ udc = kzalloc(size, GFP_KERNEL);
if (udc == NULL) {
dev_err(&dev->dev, "failed to allocate memory for udc\n");
- retval = -ENOMEM;
- goto error;
+ return -ENOMEM;
}
+ the_controller = udc;
+ udc->done = &release_done;
+ udc->pdata = dev->dev.platform_data;
spin_lock_init(&udc->lock);
udc->dev = dev;
- udc->clk = clk_get(&dev->dev, "U2OCLK");
- if (IS_ERR(udc->clk)) {
- retval = PTR_ERR(udc->clk);
- goto error;
+ udc->clknum = pdata->clknum;
+ for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
+ udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]);
+ if (IS_ERR(udc->clk[clk_i])) {
+ retval = PTR_ERR(udc->clk[clk_i]);
+ goto err_put_clk;
+ }
}
- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o");
+ r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs");
if (r == NULL) {
dev_err(&dev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
- goto error;
+ goto err_put_clk;
}
udc->cap_regs = (struct mv_cap_regs __iomem *)
@@ -1961,29 +2203,31 @@ int mv_udc_probe(struct platform_device *dev)
if (udc->cap_regs == NULL) {
dev_err(&dev->dev, "failed to map I/O memory\n");
retval = -EBUSY;
- goto error;
+ goto err_put_clk;
}
- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy");
+ r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
if (r == NULL) {
dev_err(&dev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
- goto error;
+ goto err_iounmap_capreg;
}
udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r));
if (udc->phy_regs == 0) {
dev_err(&dev->dev, "failed to map phy I/O memory\n");
retval = -EBUSY;
- goto error;
+ goto err_iounmap_capreg;
}
/* we will acces controller register, so enable the clk */
- clk_enable(udc->clk);
- retval = mv_udc_phy_init(udc->phy_regs);
- if (retval) {
- dev_err(&dev->dev, "phy initialization error %d\n", retval);
- goto error;
+ udc_clock_enable(udc);
+ if (pdata->phy_init) {
+ retval = pdata->phy_init(udc->phy_regs);
+ if (retval) {
+ dev_err(&dev->dev, "phy init error %d\n", retval);
+ goto err_iounmap_phyreg;
+ }
}
udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs
@@ -1991,6 +2235,13 @@ int mv_udc_probe(struct platform_device *dev)
& CAPLENGTH_MASK));
udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
+ /*
+ * some platform will use usb to download image, it may not disconnect
+ * usb gadget before loading kernel. So first stop udc here.
+ */
+ udc_stop(udc);
+ writel(0xFFFFFFFF, &udc->op_regs->usbsts);
+
size = udc->max_eps * sizeof(struct mv_dqh) *2;
size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
udc->ep_dqh = dma_alloc_coherent(&dev->dev, size,
@@ -1999,7 +2250,7 @@ int mv_udc_probe(struct platform_device *dev)
if (udc->ep_dqh == NULL) {
dev_err(&dev->dev, "allocate dQH memory failed\n");
retval = -ENOMEM;
- goto error;
+ goto err_disable_clock;
}
udc->ep_dqh_size = size;
@@ -2012,7 +2263,7 @@ int mv_udc_probe(struct platform_device *dev)
if (!udc->dtd_pool) {
retval = -ENOMEM;
- goto error;
+ goto err_free_dma;
}
size = udc->max_eps * sizeof(struct mv_ep) *2;
@@ -2020,7 +2271,7 @@ int mv_udc_probe(struct platform_device *dev)
if (udc->eps == NULL) {
dev_err(&dev->dev, "allocate ep memory failed\n");
retval = -ENOMEM;
- goto error;
+ goto err_destroy_dma;
}
/* initialize ep0 status request structure */
@@ -2028,13 +2279,13 @@ int mv_udc_probe(struct platform_device *dev)
if (!udc->status_req) {
dev_err(&dev->dev, "allocate status_req memory failed\n");
retval = -ENOMEM;
- goto error;
+ goto err_free_eps;
}
INIT_LIST_HEAD(&udc->status_req->queue);
/* allocate a small amount of memory to get valid address */
udc->status_req->req.buf = kzalloc(8, GFP_KERNEL);
- udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+ udc->status_req->req.dma = DMA_ADDR_INVALID;
udc->resume_state = USB_STATE_NOTATTACHED;
udc->usb_state = USB_STATE_POWERED;
@@ -2045,15 +2296,15 @@ int mv_udc_probe(struct platform_device *dev)
if (r == NULL) {
dev_err(&dev->dev, "no IRQ resource defined\n");
retval = -ENODEV;
- goto error;
+ goto err_free_status_req;
}
udc->irq = r->start;
if (request_irq(udc->irq, mv_udc_irq,
- IRQF_DISABLED | IRQF_SHARED, driver_name, udc)) {
+ IRQF_SHARED, driver_name, udc)) {
dev_err(&dev->dev, "Request irq %d for UDC failed\n",
udc->irq);
retval = -ENODEV;
- goto error;
+ goto err_free_status_req;
}
/* initialize gadget structure */
@@ -2072,18 +2323,82 @@ int mv_udc_probe(struct platform_device *dev)
retval = device_register(&udc->gadget.dev);
if (retval)
- goto error;
+ goto err_free_irq;
eps_init(udc);
- the_controller = udc;
+ /* VBUS detect: we can disable/enable clock on demand.*/
+ if (pdata->vbus) {
+ udc->clock_gating = 1;
+ retval = request_threaded_irq(pdata->vbus->irq, NULL,
+ mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
+ if (retval) {
+ dev_info(&dev->dev,
+ "Can not request irq for VBUS, "
+ "disable clock gating\n");
+ udc->clock_gating = 0;
+ }
+
+ udc->qwork = create_singlethread_workqueue("mv_udc_queue");
+ if (!udc->qwork) {
+ dev_err(&dev->dev, "cannot create workqueue\n");
+ retval = -ENOMEM;
+ goto err_unregister;
+ }
+
+ INIT_WORK(&udc->vbus_work, mv_udc_vbus_work);
+ }
+
+ /*
+ * When clock gating is supported, we can disable clk and phy.
+ * If not, it means that VBUS detection is not supported, we
+ * have to enable vbus active all the time to let controller work.
+ */
+ if (udc->clock_gating) {
+ if (udc->pdata->phy_deinit)
+ udc->pdata->phy_deinit(udc->phy_regs);
+ udc_clock_disable(udc);
+ } else
+ udc->vbus_active = 1;
retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
- if (!retval)
- return retval;
-error:
- if (udc)
- mv_udc_remove(udc->dev);
+ if (retval)
+ goto err_unregister;
+
+ dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n",
+ udc->clock_gating ? "with" : "without");
+
+ return 0;
+
+err_unregister:
+ if (udc->pdata && udc->pdata->vbus && udc->clock_gating)
+ free_irq(pdata->vbus->irq, &dev->dev);
+ device_unregister(&udc->gadget.dev);
+err_free_irq:
+ free_irq(udc->irq, &dev->dev);
+err_free_status_req:
+ kfree(udc->status_req->req.buf);
+ kfree(udc->status_req);
+err_free_eps:
+ kfree(udc->eps);
+err_destroy_dma:
+ dma_pool_destroy(udc->dtd_pool);
+err_free_dma:
+ dma_free_coherent(&dev->dev, udc->ep_dqh_size,
+ udc->ep_dqh, udc->ep_dqh_dma);
+err_disable_clock:
+ if (udc->pdata->phy_deinit)
+ udc->pdata->phy_deinit(udc->phy_regs);
+ udc_clock_disable(udc);
+err_iounmap_phyreg:
+ iounmap((void *)udc->phy_regs);
+err_iounmap_capreg:
+ iounmap(udc->cap_regs);
+err_put_clk:
+ for (clk_i--; clk_i >= 0; clk_i--)
+ clk_put(udc->clk[clk_i]);
+ the_controller = NULL;
+ kfree(udc);
return retval;
}
@@ -2102,11 +2417,16 @@ static int mv_udc_resume(struct device *_dev)
struct mv_udc *udc = the_controller;
int retval;
- retval = mv_udc_phy_init(udc->phy_regs);
- if (retval) {
- dev_err(_dev, "phy initialization error %d\n", retval);
- return retval;
+ if (udc->pdata->phy_init) {
+ retval = udc->pdata->phy_init(udc->phy_regs);
+ if (retval) {
+ dev_err(&udc->dev->dev,
+ "init phy error %d when resume back\n",
+ retval);
+ return retval;
+ }
}
+
udc_reset(udc);
ep0_reset(udc);
udc_start(udc);
@@ -2120,9 +2440,21 @@ static const struct dev_pm_ops mv_udc_pm_ops = {
};
#endif
+static void mv_udc_shutdown(struct platform_device *dev)
+{
+ struct mv_udc *udc = the_controller;
+ u32 mode;
+
+ /* reset controller mode to IDLE */
+ mode = readl(&udc->op_regs->usbmode);
+ mode &= ~3;
+ writel(mode, &udc->op_regs->usbmode);
+}
+
static struct platform_driver udc_driver = {
.probe = mv_udc_probe,
.remove = __exit_p(mv_udc_remove),
+ .shutdown = mv_udc_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "pxa-u2o",
diff --git a/drivers/usb/gadget/mv_udc_phy.c b/drivers/usb/gadget/mv_udc_phy.c
deleted file mode 100644
index d4dea97e38a5..000000000000
--- a/drivers/usb/gadget/mv_udc_phy.c
+++ /dev/null
@@ -1,214 +0,0 @@
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/io.h>
-#include <linux/errno.h>
-
-#include <mach/cputype.h>
-
-#ifdef CONFIG_ARCH_MMP
-
-#define UTMI_REVISION 0x0
-#define UTMI_CTRL 0x4
-#define UTMI_PLL 0x8
-#define UTMI_TX 0xc
-#define UTMI_RX 0x10
-#define UTMI_IVREF 0x14
-#define UTMI_T0 0x18
-#define UTMI_T1 0x1c
-#define UTMI_T2 0x20
-#define UTMI_T3 0x24
-#define UTMI_T4 0x28
-#define UTMI_T5 0x2c
-#define UTMI_RESERVE 0x30
-#define UTMI_USB_INT 0x34
-#define UTMI_DBG_CTL 0x38
-#define UTMI_OTG_ADDON 0x3c
-
-/* For UTMICTRL Register */
-#define UTMI_CTRL_USB_CLK_EN (1 << 31)
-/* pxa168 */
-#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
-#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
-#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
-#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
-
-#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
-#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
-#define UTMI_CTRL_PU_REF_SHIFT 20
-#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
-#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
-#define UTMI_CTRL_PWR_UP_SHIFT 0
-/* For UTMI_PLL Register */
-#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
-#define UTMI_PLL_FBDIV_SHIFT 4
-#define UTMI_PLL_REFDIV_SHIFT 0
-#define UTMI_PLL_FBDIV_MASK 0x00000FF0
-#define UTMI_PLL_REFDIV_MASK 0x0000000F
-#define UTMI_PLL_ICP_MASK 0x00007000
-#define UTMI_PLL_KVCO_MASK 0x00031000
-#define UTMI_PLL_PLLCALI12_SHIFT 29
-#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
-#define UTMI_PLL_PLLVDD18_SHIFT 27
-#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
-#define UTMI_PLL_PLLVDD12_SHIFT 25
-#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
-#define UTMI_PLL_KVCO_SHIFT 15
-#define UTMI_PLL_ICP_SHIFT 12
-/* For UTMI_TX Register */
-#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
-#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
-#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26
-#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26)
-#define UTMI_TX_LOW_VDD_EN_SHIFT 11
-#define UTMI_TX_IMPCAL_VTH_SHIFT 14
-#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
-#define UTMI_TX_CK60_PHSEL_SHIFT 17
-#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
-#define UTMI_TX_TXVDD12_SHIFT 22
-#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
-#define UTMI_TX_AMP_SHIFT 0
-#define UTMI_TX_AMP_MASK (0x7 << 0)
-/* For UTMI_RX Register */
-#define UTMI_RX_SQ_THRESH_SHIFT 4
-#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
-#define UTMI_REG_SQ_LENGTH_SHIFT 15
-#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
-
-#define REG_RCAL_START 0x00001000
-#define VCOCAL_START 0x00200000
-#define KVCO_EXT 0x00400000
-#define PLL_READY 0x00800000
-#define CLK_BLK_EN 0x01000000
-#endif
-
-static unsigned int u2o_read(unsigned int base, unsigned int offset)
-{
- return readl(base + offset);
-}
-
-static void u2o_set(unsigned int base, unsigned int offset, unsigned int value)
-{
- unsigned int reg;
-
- reg = readl(base + offset);
- reg |= value;
- writel(reg, base + offset);
- readl(base + offset);
-}
-
-static void u2o_clear(unsigned int base, unsigned int offset,
- unsigned int value)
-{
- unsigned int reg;
-
- reg = readl(base + offset);
- reg &= ~value;
- writel(reg, base + offset);
- readl(base + offset);
-}
-
-static void u2o_write(unsigned int base, unsigned int offset,
- unsigned int value)
-{
- writel(value, base + offset);
- readl(base + offset);
-}
-
-#ifdef CONFIG_ARCH_MMP
-int mv_udc_phy_init(unsigned int base)
-{
- unsigned long timeout;
-
- /* Initialize the USB PHY power */
- if (cpu_is_pxa910()) {
- u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
- | (1 << UTMI_CTRL_PU_REF_SHIFT));
- }
-
- u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT);
- u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT);
-
- /* UTMI_PLL settings */
- u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
- | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
- | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
- | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
-
- u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT)
- | (0xb << UTMI_PLL_REFDIV_SHIFT)
- | (3 << UTMI_PLL_PLLVDD18_SHIFT)
- | (3 << UTMI_PLL_PLLVDD12_SHIFT)
- | (3 << UTMI_PLL_PLLCALI12_SHIFT)
- | (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT));
-
- /* UTMI_TX */
- u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
- | UTMI_TX_TXVDD12_MASK
- | UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK
- | UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK);
- u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT)
- | (4 << UTMI_TX_CK60_PHSEL_SHIFT)
- | (4 << UTMI_TX_IMPCAL_VTH_SHIFT)
- | (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT)
- | (3 << UTMI_TX_AMP_SHIFT));
-
- /* UTMI_RX */
- u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
- | UTMI_REG_SQ_LENGTH_MASK);
- if (cpu_is_pxa168())
- u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT)
- | (2 << UTMI_REG_SQ_LENGTH_SHIFT));
- else
- u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT)
- | (2 << UTMI_REG_SQ_LENGTH_SHIFT));
-
- /* UTMI_IVREF */
- if (cpu_is_pxa168())
- /*
- * fixing Microsoft Altair board interface with NEC hub issue -
- * Set UTMI_IVREF from 0x4a3 to 0x4bf
- */
- u2o_write(base, UTMI_IVREF, 0x4bf);
-
- /* calibrate */
- timeout = jiffies + 100;
- while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
- if (time_after(jiffies, timeout))
- return -ETIME;
- cpu_relax();
- }
-
- /* toggle VCOCAL_START bit of UTMI_PLL */
- udelay(200);
- u2o_set(base, UTMI_PLL, VCOCAL_START);
- udelay(40);
- u2o_clear(base, UTMI_PLL, VCOCAL_START);
-
- /* toggle REG_RCAL_START bit of UTMI_TX */
- udelay(200);
- u2o_set(base, UTMI_TX, REG_RCAL_START);
- udelay(40);
- u2o_clear(base, UTMI_TX, REG_RCAL_START);
- udelay(200);
-
- /* make sure phy is ready */
- timeout = jiffies + 100;
- while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
- if (time_after(jiffies, timeout))
- return -ETIME;
- cpu_relax();
- }
-
- if (cpu_is_pxa168()) {
- u2o_set(base, UTMI_RESERVE, 1 << 5);
- /* Turn on UTMI PHY OTG extension */
- u2o_write(base, UTMI_OTG_ADDON, 1);
- }
- return 0;
-}
-#else
-int mv_udc_phy_init(unsigned int base)
-{
- return 0;
-}
-#endif
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
index 62ee5087dcac..89530034dff1 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/ncm.c
@@ -14,15 +14,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define DEBUG */
diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h
index df886cec5ef4..b0e52fc277b4 100644
--- a/drivers/usb/gadget/ndis.h
+++ b/drivers/usb/gadget/ndis.h
@@ -10,12 +10,6 @@
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
- *
- * This code is distributed in the hope that it will be useful but
- * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- * DISCLAIMED. This includes but is not limited to warranties of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
*/
#ifndef _LINUX_NDIS_H
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
index ab98ea926a11..d1b76368472f 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/net2272.c
@@ -204,7 +204,7 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
- max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
+ max = usb_endpoint_maxp(desc) & 0x1fff;
spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7fff;
@@ -1172,17 +1172,18 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on)
return 0;
}
-static int net2272_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int net2272_stop(struct usb_gadget_driver *driver);
+static int net2272_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+static int net2272_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops net2272_ops = {
- .get_frame = net2272_get_frame,
- .wakeup = net2272_wakeup,
+ .get_frame = net2272_get_frame,
+ .wakeup = net2272_wakeup,
.set_selfpowered = net2272_set_selfpowered,
- .pullup = net2272_pullup,
- .start = net2272_start,
- .stop = net2272_stop,
+ .pullup = net2272_pullup,
+ .udc_start = net2272_start,
+ .udc_stop = net2272_stop,
};
/*---------------------------------------------------------------------------*/
@@ -1356,8 +1357,6 @@ net2272_set_fifo_mode(struct net2272 *dev, int mode)
/*---------------------------------------------------------------------------*/
-static struct net2272 *the_controller;
-
static void
net2272_usb_reset(struct net2272 *dev)
{
@@ -1453,20 +1452,17 @@ net2272_ep0_start(struct net2272 *dev)
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-static int net2272_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int net2272_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
{
- struct net2272 *dev = the_controller;
- int ret;
+ struct net2272 *dev;
unsigned i;
- if (!driver || !bind || !driver->unbind || !driver->setup ||
+ if (!driver || !driver->unbind || !driver->setup ||
driver->speed != USB_SPEED_HIGH)
return -EINVAL;
- if (!dev)
- return -ENODEV;
- if (dev->driver)
- return -EBUSY;
+
+ dev = container_of(_gadget, struct net2272, gadget);
for (i = 0; i < 4; ++i)
dev->ep[i].irqs = 0;
@@ -1475,14 +1471,6 @@ static int net2272_start(struct usb_gadget_driver *driver,
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- ret = bind(&dev->gadget);
- if (ret) {
- dev_dbg(dev->dev, "bind to driver %s --> %d\n",
- driver->driver.name, ret);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return ret;
- }
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
@@ -1510,33 +1498,21 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
for (i = 0; i < 4; ++i)
net2272_dequeue_all(&dev->ep[i]);
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
-
- }
net2272_usb_reinit(dev);
}
-static int net2272_stop(struct usb_gadget_driver *driver)
+static int net2272_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
{
- struct net2272 *dev = the_controller;
+ struct net2272 *dev;
unsigned long flags;
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver)
- return -EINVAL;
+ dev = container_of(_gadget, struct net2272, gadget);
spin_lock_irqsave(&dev->lock, flags);
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
- net2272_pullup(&dev->gadget, 0);
-
- driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
@@ -1764,8 +1740,8 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
dev->gadget.speed = USB_SPEED_HIGH;
else
dev->gadget.speed = USB_SPEED_FULL;
- dev_dbg(dev->dev, "%s speed\n",
- (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full");
+ dev_dbg(dev->dev, "%s\n",
+ usb_speed_string(dev->gadget.speed));
}
ep = &dev->ep[0];
@@ -2238,7 +2214,6 @@ net2272_remove(struct net2272 *dev)
device_remove_file(dev->dev, &dev_attr_registers);
dev_info(dev->dev, "unbind\n");
- the_controller = NULL;
}
static struct net2272 * __devinit
@@ -2246,11 +2221,6 @@ net2272_probe_init(struct device *dev, unsigned int irq)
{
struct net2272 *ret;
- if (the_controller) {
- dev_warn(dev, "ignoring\n");
- return ERR_PTR(-EBUSY);
- }
-
if (!irq) {
dev_dbg(dev, "No IRQ!\n");
return ERR_PTR(-ENODEV);
@@ -2307,8 +2277,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
dma_mode_string());
dev_info(dev->dev, "version: %s\n", driver_vers);
- the_controller = dev;
-
ret = device_register(&dev->gadget.dev);
if (ret)
goto err_irq;
@@ -2684,8 +2652,6 @@ net2272_plat_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n",
(net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no ");
- the_controller = dev;
-
return 0;
err_io:
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 3dd40b4e675c..7f1bc9a73cda 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -33,15 +33,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#undef DEBUG /* messages on error and most fault paths */
@@ -169,7 +160,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
return -EDOM;
/* sanity check ep-e/ep-f since their fifos are small */
- max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+ max = usb_endpoint_maxp (desc) & 0x1fff;
if (ep->num > 4 && max > 64)
return -ERANGE;
@@ -1410,17 +1401,18 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
return 0;
}
-static int net2280_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int net2280_stop(struct usb_gadget_driver *driver);
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops net2280_ops = {
.get_frame = net2280_get_frame,
.wakeup = net2280_wakeup,
.set_selfpowered = net2280_set_selfpowered,
.pullup = net2280_pullup,
- .start = net2280_start,
- .stop = net2280_stop,
+ .udc_start = net2280_start,
+ .udc_stop = net2280_stop,
};
/*-------------------------------------------------------------------------*/
@@ -1640,7 +1632,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
default:
val = "iso"; break;
}; val; }),
- le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
+ usb_endpoint_maxp (d) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
);
} else /* ep0 should only have one transfer queued */
@@ -1753,8 +1745,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode)
* perhaps to bind specific drivers to specific devices.
*/
-static struct net2280 *the_controller;
-
static void usb_reset (struct net2280 *dev)
{
u32 tmp;
@@ -1880,10 +1870,10 @@ static void ep0_start (struct net2280 *dev)
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-static int net2280_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
{
- struct net2280 *dev = the_controller;
+ struct net2280 *dev;
int retval;
unsigned i;
@@ -1891,14 +1881,11 @@ static int net2280_start(struct usb_gadget_driver *driver,
* (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
* "must not be used in normal operation"
*/
- if (!driver
- || driver->speed != USB_SPEED_HIGH
- || !bind || !driver->setup)
+ if (!driver || driver->speed != USB_SPEED_HIGH
+ || !driver->setup)
return -EINVAL;
- if (!dev)
- return -ENODEV;
- if (dev->driver)
- return -EBUSY;
+
+ dev = container_of (_gadget, struct net2280, gadget);
for (i = 0; i < 7; i++)
dev->ep [i].irqs = 0;
@@ -1908,14 +1895,6 @@ static int net2280_start(struct usb_gadget_driver *driver,
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
- if (retval) {
- DEBUG (dev, "bind to driver %s --> %d\n",
- driver->driver.name, retval);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
if (retval) goto err_unbind;
@@ -1961,33 +1940,21 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
for (i = 0; i < 7; i++)
nuke (&dev->ep [i]);
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock (&dev->lock);
- driver->disconnect (&dev->gadget);
- spin_lock (&dev->lock);
- }
-
usb_reinit (dev);
}
-static int net2280_stop(struct usb_gadget_driver *driver)
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
{
- struct net2280 *dev = the_controller;
+ struct net2280 *dev;
unsigned long flags;
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver || !driver->unbind)
- return -EINVAL;
+ dev = container_of (_gadget, struct net2280, gadget);
spin_lock_irqsave (&dev->lock, flags);
stop_activity (dev, driver);
spin_unlock_irqrestore (&dev->lock, flags);
- net2280_pullup (&dev->gadget, 0);
-
- driver->unbind (&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
@@ -2266,9 +2233,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
else
dev->gadget.speed = USB_SPEED_FULL;
net2280_led_speed (dev, dev->gadget.speed);
- DEBUG (dev, "%s speed\n",
- (dev->gadget.speed == USB_SPEED_HIGH)
- ? "high" : "full");
+ DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed));
}
ep = &dev->ep [0];
@@ -2481,7 +2446,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED);
/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
- * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
+ * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
* both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
* only indicates a change in the reset state).
*/
@@ -2709,8 +2674,6 @@ static void net2280_remove (struct pci_dev *pdev)
pci_set_drvdata (pdev, NULL);
INFO (dev, "unbind\n");
-
- the_controller = NULL;
}
/* wrap this driver around the specified device, but
@@ -2724,14 +2687,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
void __iomem *base = NULL;
int retval, i;
- /* if you want to support more than one controller in a system,
- * usb_gadget_driver_{register,unregister}() must change.
- */
- if (the_controller) {
- dev_warn (&pdev->dev, "ignoring\n");
- return -EBUSY;
- }
-
/* alloc, and start init */
dev = kzalloc (sizeof *dev, GFP_KERNEL);
if (dev == NULL){
@@ -2858,8 +2813,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
use_dma
? (use_dma_chaining ? "chaining" : "enabled")
: "disabled");
- the_controller = dev;
-
retval = device_register (&dev->gadget.dev);
if (retval) goto done;
retval = device_create_file (&pdev->dev, &dev_attr_registers);
diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h
index c36852263d93..a844be0d683a 100644
--- a/drivers/usb/gadget/net2280.h
+++ b/drivers/usb/gadget/net2280.h
@@ -11,15 +11,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/usb/net2280.h>
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 740c7daed279..788989a10223 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#undef DEBUG
@@ -166,15 +157,14 @@ static int omap_ep_enable(struct usb_ep *_ep,
if (!_ep || !desc || ep->desc
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
- || ep->maxpacket < le16_to_cpu
- (desc->wMaxPacketSize)) {
+ || ep->maxpacket < usb_endpoint_maxp(desc)) {
DBG("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
- maxp = le16_to_cpu (desc->wMaxPacketSize);
+ maxp = usb_endpoint_maxp(desc);
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& maxp != ep->maxpacket)
- || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket
+ || usb_endpoint_maxp(desc) > ep->maxpacket
|| !desc->wMaxPacketSize) {
DBG("%s, bad %s maxpacket\n", __func__, _ep->name);
return -ERANGE;
@@ -2968,7 +2958,7 @@ known:
}
#ifdef USE_ISO
status = request_irq(pdev->resource[3].start, omap_udc_iso_irq,
- IRQF_DISABLED, "omap_udc iso", udc);
+ 0, "omap_udc iso", udc);
if (status != 0) {
ERR("can't get irq %d, err %d\n",
(int) pdev->resource[3].start, status);
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index f96615ab6b77..550d6dcdf104 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -4,15 +4,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
@@ -947,7 +938,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep,
else
buff_size = UDC_EPOUT_BUFF_SIZE;
pch_udc_ep_set_bufsz(ep, buff_size, ep->in);
- pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize));
+ pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc));
pch_udc_ep_set_nak(ep);
pch_udc_ep_fifo_flush(ep, ep->in);
/* Configure the endpoint */
@@ -957,7 +948,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep,
(cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) |
(cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) |
(cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) |
- le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT;
+ usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT;
if (ep->in)
pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num));
@@ -1466,7 +1457,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
ep->desc = desc;
ep->halted = 0;
pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
- ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ ep->ep.maxpacket = usb_endpoint_maxp(desc);
pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
spin_unlock_irqrestore(&dev->lock, iflags);
return 0;
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index a341dde6f9c3..65a8834f274b 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
@@ -971,23 +962,15 @@ printer_set_config(struct printer_dev *dev, unsigned number)
usb_gadget_vbus_draw(dev->gadget,
dev->gadget->is_otg ? 8 : 100);
} else {
- char *speed;
unsigned power;
power = 2 * config_desc.bMaxPower;
usb_gadget_vbus_draw(dev->gadget, power);
- switch (gadget->speed) {
- case USB_SPEED_FULL: speed = "full"; break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- case USB_SPEED_HIGH: speed = "high"; break;
-#endif
- default: speed = "?"; break;
- }
-
dev->config = number;
- INFO(dev, "%s speed config #%d: %d mA, %s\n",
- speed, number, power, driver_desc);
+ INFO(dev, "%s config #%d: %d mA, %s\n",
+ usb_speed_string(gadget->speed),
+ number, power, driver_desc);
}
return result;
}
@@ -1611,7 +1594,7 @@ cleanup(void)
if (status)
ERROR(dev, "usb_gadget_unregister_driver %x\n", status);
- unregister_chrdev_region(g_printer_devno, 2);
+ unregister_chrdev_region(g_printer_devno, 1);
class_destroy(usb_gadget_class);
mutex_unlock(&usb_printer_gadget.lock_printer_io);
}
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index e4e59b4de25d..c090a7e3ecf8 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -11,16 +11,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* #define VERBOSE_DEBUG */
@@ -232,8 +222,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
- || ep->fifo_size < le16_to_cpu
- (desc->wMaxPacketSize)) {
+ || ep->fifo_size < usb_endpoint_maxp (desc)) {
DMSG("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
@@ -248,7 +237,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
/* hardware _could_ do smaller, but driver doesn't */
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
- && le16_to_cpu (desc->wMaxPacketSize)
+ && usb_endpoint_maxp (desc)
!= BULK_FIFO_SIZE)
|| !desc->wMaxPacketSize) {
DMSG("%s, bad %s maxpacket\n", __func__, _ep->name);
@@ -264,7 +253,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
ep->desc = desc;
ep->stopped = 0;
ep->pio_irqs = 0;
- ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
+ ep->ep.maxpacket = usb_endpoint_maxp (desc);
/* flush fifo (mostly for OUT buffers) */
pxa25x_ep_fifo_flush (_ep);
@@ -401,7 +390,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
{
unsigned max;
- max = le16_to_cpu(ep->desc->wMaxPacketSize);
+ max = usb_endpoint_maxp(ep->desc);
do {
unsigned count;
int is_last, is_short;
@@ -671,8 +660,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
* we can report per-packet status. that also helps with dma.
*/
if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
- && req->req.length > le16_to_cpu
- (ep->desc->wMaxPacketSize)))
+ && req->req.length > usb_endpoint_maxp (ep->desc)))
return -EMSGSIZE;
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
@@ -1105,7 +1093,7 @@ udc_seq_show(struct seq_file *m, void *_d)
tmp = *dev->ep [i].reg_udccs;
seq_printf(m,
"%s max %d %s udccs %02x irqs %lu\n",
- ep->ep.name, le16_to_cpu(desc->wMaxPacketSize),
+ ep->ep.name, usb_endpoint_maxp(desc),
"pio", tmp, ep->pio_irqs);
/* TODO translate all five groups of udccs bits! */
@@ -2202,7 +2190,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
/* irq setup after old hardware state is cleaned up */
retval = request_irq(irq, pxa25x_udc_irq,
- IRQF_DISABLED, driver_name, dev);
+ 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %d, err %d\n",
driver_name, irq, retval);
@@ -2214,7 +2202,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
if (machine_is_lubbock()) {
retval = request_irq(LUBBOCK_USB_DISC_IRQ,
lubbock_vbus_irq,
- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+ IRQF_SAMPLE_RANDOM,
driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
@@ -2223,7 +2211,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
}
retval = request_irq(LUBBOCK_USB_IRQ,
lubbock_vbus_irq,
- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+ IRQF_SAMPLE_RANDOM,
driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
index f572c5617462..8eaf4e43726b 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __LINUX_USB_GADGET_PXA25X_H
@@ -161,8 +152,6 @@ static struct pxa25x_udc *the_controller;
#ifdef DEBUG
-static int is_vbus_present(void);
-
static const char *state_name[] = {
"EP0_IDLE",
"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
@@ -214,8 +203,7 @@ dump_state(struct pxa25x_udc *dev)
u32 tmp;
unsigned i;
- DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
- is_vbus_present() ? "host " : "disconnected",
+ DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
state_name[dev->ep0state],
UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
dump_udccr("udccr");
@@ -232,9 +220,6 @@ dump_state(struct pxa25x_udc *dev)
} else
DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
- if (!is_vbus_present())
- return;
-
dump_udccs0 ("udccs0");
DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
dev->stats.write.bytes, dev->stats.write.ops,
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 85b68c75dc9d..18b6b091f2a6 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -8,16 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/module.h>
#include <linux/kernel.h>
@@ -1439,7 +1429,7 @@ static int pxa_ep_enable(struct usb_ep *_ep,
return -EINVAL;
}
- if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+ if (ep->fifo_size < usb_endpoint_maxp(desc)) {
ep_err(ep, "bad maxpacket\n");
return -ERANGE;
}
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
index b01696eab068..7f4e8f424e80 100644
--- a/drivers/usb/gadget/pxa27x_udc.h
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __LINUX_USB_GADGET_PXA27X_H
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index 50991e5bd5e8..68a826a1b866 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/module.h>
@@ -28,13 +18,14 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "r8a66597-udc.h"
-#define DRIVER_VERSION "2009-08-18"
+#define DRIVER_VERSION "2011-09-26"
static const char udc_name[] = "r8a66597_udc";
static const char *r8a66597_ep_name[] = {
@@ -115,13 +106,15 @@ static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum)
u16 pid = 0;
unsigned long offset;
- if (pipenum == 0)
+ if (pipenum == 0) {
pid = r8a66597_read(r8a66597, DCPCTR) & PID;
- else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
offset = get_pipectr_addr(pipenum);
pid = r8a66597_read(r8a66597, offset) & PID;
- } else
- printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+ pipenum);
+ }
return pid;
}
@@ -131,13 +124,15 @@ static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum,
{
unsigned long offset;
- if (pipenum == 0)
+ if (pipenum == 0) {
r8a66597_mdfy(r8a66597, pid, PID, DCPCTR);
- else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
offset = get_pipectr_addr(pipenum);
r8a66597_mdfy(r8a66597, pid, PID, offset);
- } else
- printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+ pipenum);
+ }
}
static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum)
@@ -160,13 +155,15 @@ static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum)
u16 ret = 0;
unsigned long offset;
- if (pipenum == 0)
+ if (pipenum == 0) {
ret = r8a66597_read(r8a66597, DCPCTR);
- else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
offset = get_pipectr_addr(pipenum);
ret = r8a66597_read(r8a66597, offset);
- } else
- printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+ pipenum);
+ }
return ret;
}
@@ -177,13 +174,63 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum)
pipe_stop(r8a66597, pipenum);
- if (pipenum == 0)
+ if (pipenum == 0) {
r8a66597_bset(r8a66597, SQCLR, DCPCTR);
- else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
offset = get_pipectr_addr(pipenum);
r8a66597_bset(r8a66597, SQCLR, offset);
- } else
- printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+ pipenum);
+ }
+}
+
+static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ unsigned long offset;
+
+ pipe_stop(r8a66597, pipenum);
+
+ if (pipenum == 0) {
+ r8a66597_bset(r8a66597, SQSET, DCPCTR);
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ r8a66597_bset(r8a66597, SQSET, offset);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597),
+ "unexpect pipe num(%d)\n", pipenum);
+ }
+}
+
+static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ unsigned long offset;
+
+ if (pipenum == 0) {
+ return r8a66597_read(r8a66597, DCPCTR) & SQMON;
+ } else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+ offset = get_pipectr_addr(pipenum);
+ return r8a66597_read(r8a66597, offset) & SQMON;
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597),
+ "unexpect pipe num(%d)\n", pipenum);
+ }
+
+ return 0;
+}
+
+static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ return control_reg_sqmon(r8a66597, pipenum);
+}
+
+static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum,
+ u16 toggle)
+{
+ if (toggle)
+ control_reg_sqset(r8a66597, pipenum);
+ else
+ control_reg_sqclr(r8a66597, pipenum);
}
static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum)
@@ -222,18 +269,51 @@ static inline unsigned short mbw_value(struct r8a66597 *r8a66597)
return MBW_16;
}
+static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum,
+ u16 isel, u16 fifosel)
+{
+ u16 tmp, mask, loop;
+ int i = 0;
+
+ if (!pipenum) {
+ mask = ISEL | CURPIPE;
+ loop = isel;
+ } else {
+ mask = CURPIPE;
+ loop = pipenum;
+ }
+ r8a66597_mdfy(r8a66597, loop, mask, fifosel);
+
+ do {
+ tmp = r8a66597_read(r8a66597, fifosel);
+ if (i++ > 1000000) {
+ dev_err(r8a66597_to_dev(r8a66597),
+ "r8a66597: register%x, loop %x "
+ "is timeout\n", fifosel, loop);
+ break;
+ }
+ ndelay(1);
+ } while ((tmp & mask) != loop);
+}
+
static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum)
{
struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
if (ep->use_dma)
- return;
+ r8a66597_bclr(r8a66597, DREQE, ep->fifosel);
r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel);
ndelay(450);
- r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+ if (r8a66597_is_sudmac(r8a66597) && ep->use_dma)
+ r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel);
+ else
+ r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+
+ if (ep->use_dma)
+ r8a66597_bset(r8a66597, DREQE, ep->fifosel);
}
static int pipe_buffer_setting(struct r8a66597 *r8a66597,
@@ -297,17 +377,18 @@ static void pipe_buffer_release(struct r8a66597 *r8a66597,
if (info->pipe == 0)
return;
- if (is_bulk_pipe(info->pipe))
+ if (is_bulk_pipe(info->pipe)) {
r8a66597->bulk--;
- else if (is_interrupt_pipe(info->pipe))
+ } else if (is_interrupt_pipe(info->pipe)) {
r8a66597->interrupt--;
- else if (is_isoc_pipe(info->pipe)) {
+ } else if (is_isoc_pipe(info->pipe)) {
r8a66597->isochronous--;
if (info->type == R8A66597_BULK)
r8a66597->bulk--;
- } else
- printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n",
- info->pipe);
+ } else {
+ dev_err(r8a66597_to_dev(r8a66597),
+ "ep_release: unexpect pipenum (%d)\n", info->pipe);
+ }
}
static void pipe_initialize(struct r8a66597_ep *ep)
@@ -337,11 +418,17 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
ep->fifoaddr = CFIFO;
ep->fifosel = CFIFOSEL;
ep->fifoctr = CFIFOCTR;
- ep->fifotrn = 0;
ep->pipectr = get_pipectr_addr(pipenum);
+ if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) {
+ ep->pipetre = get_pipetre_addr(pipenum);
+ ep->pipetrn = get_pipetrn_addr(pipenum);
+ } else {
+ ep->pipetre = 0;
+ ep->pipetrn = 0;
+ }
ep->pipenum = pipenum;
- ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ ep->ep.maxpacket = usb_endpoint_maxp(desc);
r8a66597->pipenum2ep[pipenum] = ep;
r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK]
= ep;
@@ -381,7 +468,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
case USB_ENDPOINT_XFER_BULK:
if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) {
if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
- printk(KERN_ERR "bulk pipe is insufficient\n");
+ dev_err(r8a66597_to_dev(r8a66597),
+ "bulk pipe is insufficient\n");
return -ENODEV;
} else {
info.pipe = R8A66597_BASE_PIPENUM_ISOC
@@ -397,7 +485,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
break;
case USB_ENDPOINT_XFER_INT:
if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) {
- printk(KERN_ERR "interrupt pipe is insufficient\n");
+ dev_err(r8a66597_to_dev(r8a66597),
+ "interrupt pipe is insufficient\n");
return -ENODEV;
}
info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt;
@@ -406,7 +495,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
break;
case USB_ENDPOINT_XFER_ISOC:
if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
- printk(KERN_ERR "isochronous pipe is insufficient\n");
+ dev_err(r8a66597_to_dev(r8a66597),
+ "isochronous pipe is insufficient\n");
return -ENODEV;
}
info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous;
@@ -414,13 +504,13 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
counter = &r8a66597->isochronous;
break;
default:
- printk(KERN_ERR "unexpect xfer type\n");
+ dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n");
return -EINVAL;
}
ep->type = info.type;
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
info.dir_in = 1;
@@ -429,7 +519,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
ret = pipe_buffer_setting(r8a66597, &info);
if (ret < 0) {
- printk(KERN_ERR "pipe_buffer_setting fail\n");
+ dev_err(r8a66597_to_dev(r8a66597),
+ "pipe_buffer_setting fail\n");
return ret;
}
@@ -495,6 +586,124 @@ static void start_ep0_write(struct r8a66597_ep *ep,
}
}
+static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum,
+ u16 fifosel)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE;
+ if (tmp == pipenum)
+ r8a66597_change_curpipe(r8a66597, 0, 0, fifosel);
+}
+
+static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum,
+ int enable)
+{
+ struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
+ u16 tmp, toggle;
+
+ /* check current BFRE bit */
+ r8a66597_write(r8a66597, pipenum, PIPESEL);
+ tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE;
+ if ((enable && tmp) || (!enable && !tmp))
+ return;
+
+ /* change BFRE bit */
+ pipe_stop(r8a66597, pipenum);
+ disable_fifosel(r8a66597, pipenum, CFIFOSEL);
+ disable_fifosel(r8a66597, pipenum, D0FIFOSEL);
+ disable_fifosel(r8a66597, pipenum, D1FIFOSEL);
+
+ toggle = save_usb_toggle(r8a66597, pipenum);
+
+ r8a66597_write(r8a66597, pipenum, PIPESEL);
+ if (enable)
+ r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG);
+ else
+ r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG);
+
+ /* initialize for internal BFRE flag */
+ r8a66597_bset(r8a66597, ACLRM, ep->pipectr);
+ r8a66597_bclr(r8a66597, ACLRM, ep->pipectr);
+
+ restore_usb_toggle(r8a66597, pipenum, toggle);
+}
+
+static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
+ struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ struct r8a66597_dma *dma;
+
+ if (!r8a66597_is_sudmac(r8a66597))
+ return -ENODEV;
+
+ /* Check transfer type */
+ if (!is_bulk_pipe(ep->pipenum))
+ return -EIO;
+
+ if (r8a66597->dma.used)
+ return -EBUSY;
+
+ /* set SUDMAC parameters */
+ dma = &r8a66597->dma;
+ dma->used = 1;
+ if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ dma->dir = 1;
+ } else {
+ dma->dir = 0;
+ change_bfre_mode(r8a66597, ep->pipenum, 1);
+ }
+
+ /* set r8a66597_ep paramters */
+ ep->use_dma = 1;
+ ep->dma = dma;
+ ep->fifoaddr = D0FIFO;
+ ep->fifosel = D0FIFOSEL;
+ ep->fifoctr = D0FIFOCTR;
+
+ /* dma mapping */
+ req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597),
+ req->req.buf, req->req.length,
+ dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static void sudmac_free_channel(struct r8a66597 *r8a66597,
+ struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ if (!r8a66597_is_sudmac(r8a66597))
+ return;
+
+ dma_unmap_single(r8a66597_to_dev(ep->r8a66597),
+ req->req.dma, req->req.length,
+ ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ r8a66597_bclr(r8a66597, DREQE, ep->fifosel);
+ r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel);
+
+ ep->dma->used = 0;
+ ep->use_dma = 0;
+ ep->fifoaddr = CFIFO;
+ ep->fifosel = CFIFOSEL;
+ ep->fifoctr = CFIFOCTR;
+}
+
+static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep,
+ struct r8a66597_request *req)
+{
+ BUG_ON(req->req.length == 0);
+
+ r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG);
+ r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA);
+ r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC);
+ r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL);
+
+ r8a66597_sudmac_write(r8a66597, DEN, CH0DEN);
+}
+
static void start_packet_write(struct r8a66597_ep *ep,
struct r8a66597_request *req)
{
@@ -505,11 +714,29 @@ static void start_packet_write(struct r8a66597_ep *ep,
disable_irq_empty(r8a66597, ep->pipenum);
pipe_start(r8a66597, ep->pipenum);
- tmp = r8a66597_read(r8a66597, ep->fifoctr);
- if (unlikely((tmp & FRDY) == 0))
- pipe_irq_enable(r8a66597, ep->pipenum);
- else
- irq_packet_write(ep, req);
+ if (req->req.length == 0) {
+ transfer_complete(ep, req, 0);
+ } else {
+ r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS);
+ if (sudmac_alloc_channel(r8a66597, ep, req) < 0) {
+ /* PIO mode */
+ pipe_change(r8a66597, ep->pipenum);
+ disable_irq_empty(r8a66597, ep->pipenum);
+ pipe_start(r8a66597, ep->pipenum);
+ tmp = r8a66597_read(r8a66597, ep->fifoctr);
+ if (unlikely((tmp & FRDY) == 0))
+ pipe_irq_enable(r8a66597, ep->pipenum);
+ else
+ irq_packet_write(ep, req);
+ } else {
+ /* DMA mode */
+ pipe_change(r8a66597, ep->pipenum);
+ disable_irq_nrdy(r8a66597, ep->pipenum);
+ pipe_start(r8a66597, ep->pipenum);
+ enable_irq_nrdy(r8a66597, ep->pipenum);
+ sudmac_start(r8a66597, ep, req);
+ }
+ }
}
static void start_packet_read(struct r8a66597_ep *ep,
@@ -524,17 +751,26 @@ static void start_packet_read(struct r8a66597_ep *ep,
pipe_start(r8a66597, pipenum);
pipe_irq_enable(r8a66597, pipenum);
} else {
- if (ep->use_dma) {
- r8a66597_bset(r8a66597, TRCLR, ep->fifosel);
- pipe_change(r8a66597, pipenum);
- r8a66597_bset(r8a66597, TRENB, ep->fifosel);
+ pipe_stop(r8a66597, pipenum);
+ if (ep->pipetre) {
+ enable_irq_nrdy(r8a66597, pipenum);
+ r8a66597_write(r8a66597, TRCLR, ep->pipetre);
r8a66597_write(r8a66597,
- (req->req.length + ep->ep.maxpacket - 1)
- / ep->ep.maxpacket,
- ep->fifotrn);
+ DIV_ROUND_UP(req->req.length, ep->ep.maxpacket),
+ ep->pipetrn);
+ r8a66597_bset(r8a66597, TRENB, ep->pipetre);
+ }
+
+ if (sudmac_alloc_channel(r8a66597, ep, req) < 0) {
+ /* PIO mode */
+ change_bfre_mode(r8a66597, ep->pipenum, 0);
+ pipe_start(r8a66597, pipenum); /* trigger once */
+ pipe_irq_enable(r8a66597, pipenum);
+ } else {
+ pipe_change(r8a66597, pipenum);
+ sudmac_start(r8a66597, ep, req);
+ pipe_start(r8a66597, pipenum); /* trigger once */
}
- pipe_start(r8a66597, pipenum); /* trigger once */
- pipe_irq_enable(r8a66597, pipenum);
}
}
@@ -564,7 +800,8 @@ static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req)
control_end(ep->r8a66597, 0);
break;
default:
- printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq);
+ dev_err(r8a66597_to_dev(ep->r8a66597),
+ "start_ep0: unexpect ctsq(%x)\n", ctsq);
break;
}
}
@@ -690,6 +927,9 @@ __acquires(r8a66597->lock)
if (!list_empty(&ep->queue))
restart = 1;
+ if (ep->use_dma)
+ sudmac_free_channel(ep->r8a66597, ep, req);
+
spin_unlock(&ep->r8a66597->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&ep->r8a66597->lock);
@@ -718,7 +958,8 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req)
do {
tmp = r8a66597_read(r8a66597, ep->fifoctr);
if (i++ > 100000) {
- printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus"
+ dev_err(r8a66597_to_dev(r8a66597),
+ "pipe0 is busy. maybe cpu i/o bus "
"conflict. please power off this controller.");
return;
}
@@ -733,7 +974,7 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req)
/* write fifo */
if (req->req.buf) {
if (size > 0)
- r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size);
+ r8a66597_write_fifo(r8a66597, ep, buf, size);
if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
}
@@ -769,7 +1010,8 @@ static void irq_packet_write(struct r8a66597_ep *ep,
if (unlikely((tmp & FRDY) == 0)) {
pipe_stop(r8a66597, pipenum);
pipe_irq_disable(r8a66597, pipenum);
- printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum);
+ dev_err(r8a66597_to_dev(r8a66597),
+ "write fifo not ready. pipnum=%d\n", pipenum);
return;
}
@@ -780,7 +1022,7 @@ static void irq_packet_write(struct r8a66597_ep *ep,
/* write fifo */
if (req->req.buf) {
- r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size);
+ r8a66597_write_fifo(r8a66597, ep, buf, size);
if ((size == 0)
|| ((size % ep->ep.maxpacket) != 0)
|| ((bufsize != ep->ep.maxpacket)
@@ -819,7 +1061,7 @@ static void irq_packet_read(struct r8a66597_ep *ep,
req->req.status = -EPIPE;
pipe_stop(r8a66597, pipenum);
pipe_irq_disable(r8a66597, pipenum);
- printk(KERN_ERR "read fifo not ready");
+ dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready");
return;
}
@@ -1095,7 +1337,7 @@ static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597)
break;
default:
r8a66597->gadget.speed = USB_SPEED_UNKNOWN;
- printk(KERN_ERR "USB speed unknown\n");
+ dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n");
}
}
@@ -1158,11 +1400,71 @@ __acquires(r8a66597->lock)
control_end(r8a66597, 0);
break;
default:
- printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+ dev_err(r8a66597_to_dev(r8a66597),
+ "ctrl_stage: unexpect ctsq(%x)\n", ctsq);
break;
}
}
+static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep)
+{
+ u16 pipenum;
+ struct r8a66597_request *req;
+ u32 len;
+ int i = 0;
+
+ pipenum = ep->pipenum;
+ pipe_change(r8a66597, pipenum);
+
+ while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) {
+ udelay(1);
+ if (unlikely(i++ >= 10000)) { /* timeout = 10 msec */
+ dev_err(r8a66597_to_dev(r8a66597),
+ "%s: FRDY was not set (%d)\n",
+ __func__, pipenum);
+ return;
+ }
+ }
+
+ r8a66597_bset(r8a66597, BCLR, ep->fifoctr);
+ req = get_request_from_ep(ep);
+
+ /* prepare parameters */
+ len = r8a66597_sudmac_read(r8a66597, CH0CBC);
+ req->req.actual += len;
+
+ /* clear */
+ r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR);
+
+ /* check transfer finish */
+ if ((!req->req.zero && (req->req.actual == req->req.length))
+ || (len % ep->ep.maxpacket)) {
+ if (ep->dma->dir) {
+ disable_irq_ready(r8a66597, pipenum);
+ enable_irq_empty(r8a66597, pipenum);
+ } else {
+ /* Clear the interrupt flag for next transfer */
+ r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
+ transfer_complete(ep, req, 0);
+ }
+ }
+}
+
+static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597)
+{
+ u32 irqsts;
+ struct r8a66597_ep *ep;
+ u16 pipenum;
+
+ irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS);
+ if (irqsts & CH0ENDS) {
+ r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR);
+ pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE);
+ ep = r8a66597->pipenum2ep[pipenum];
+ sudmac_finish(r8a66597, ep);
+ }
+}
+
static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
{
struct r8a66597 *r8a66597 = _r8a66597;
@@ -1173,6 +1475,9 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
u16 savepipe;
u16 mask0;
+ if (r8a66597_is_sudmac(r8a66597))
+ r8a66597_sudmac_irq(r8a66597);
+
spin_lock(&r8a66597->lock);
intsts0 = r8a66597_read(r8a66597, INTSTS0);
@@ -1433,23 +1738,18 @@ static struct usb_ep_ops r8a66597_ep_ops = {
};
/*-------------------------------------------------------------------------*/
-static struct r8a66597 *the_controller;
-
-static int r8a66597_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int r8a66597_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct r8a66597 *r8a66597 = the_controller;
+ struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
int retval;
if (!driver
|| driver->speed != USB_SPEED_HIGH
- || !bind
|| !driver->setup)
return -EINVAL;
if (!r8a66597)
return -ENODEV;
- if (r8a66597->driver)
- return -EBUSY;
/* hook up the driver */
driver->driver.bus = NULL;
@@ -1458,14 +1758,8 @@ static int r8a66597_start(struct usb_gadget_driver *driver,
retval = device_add(&r8a66597->gadget.dev);
if (retval) {
- printk(KERN_ERR "device_add error (%d)\n", retval);
- goto error;
- }
-
- retval = bind(&r8a66597->gadget);
- if (retval) {
- printk(KERN_ERR "bind to driver error (%d)\n", retval);
- device_del(&r8a66597->gadget.dev);
+ dev_err(r8a66597_to_dev(r8a66597), "device_add error (%d)\n",
+ retval);
goto error;
}
@@ -1489,23 +1783,17 @@ error:
return retval;
}
-static int r8a66597_stop(struct usb_gadget_driver *driver)
+static int r8a66597_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct r8a66597 *r8a66597 = the_controller;
+ struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
unsigned long flags;
- if (driver != r8a66597->driver || !driver->unbind)
- return -EINVAL;
-
spin_lock_irqsave(&r8a66597->lock, flags);
- if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN)
- r8a66597_usb_disconnect(r8a66597);
r8a66597_bclr(r8a66597, VBSE, INTENB0);
disable_controller(r8a66597);
spin_unlock_irqrestore(&r8a66597->lock, flags);
- driver->unbind(&r8a66597->gadget);
-
device_del(&r8a66597->gadget.dev);
r8a66597->driver = NULL;
return 0;
@@ -1535,8 +1823,8 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on)
static struct usb_gadget_ops r8a66597_gadget_ops = {
.get_frame = r8a66597_get_frame,
- .start = r8a66597_start,
- .stop = r8a66597_stop,
+ .udc_start = r8a66597_start,
+ .udc_stop = r8a66597_stop,
.pullup = r8a66597_pullup,
};
@@ -1547,6 +1835,8 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
usb_del_gadget_udc(&r8a66597->gadget);
del_timer_sync(&r8a66597->timer);
iounmap(r8a66597->reg);
+ if (r8a66597->pdata->sudmac)
+ iounmap(r8a66597->sudmac_reg);
free_irq(platform_get_irq(pdev, 0), r8a66597);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
#ifdef CONFIG_HAVE_CLK
@@ -1563,6 +1853,26 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
+static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
+ if (!res) {
+ dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n");
+ return -ENODEV;
+ }
+
+ r8a66597->sudmac_reg = ioremap(res->start, resource_size(res));
+ if (r8a66597->sudmac_reg == NULL) {
+ dev_err(&pdev->dev, "ioremap error(sudmac).\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int __init r8a66597_probe(struct platform_device *pdev)
{
#ifdef CONFIG_HAVE_CLK
@@ -1579,7 +1889,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
- printk(KERN_ERR "platform_get_resource error.\n");
+ dev_err(&pdev->dev, "platform_get_resource error.\n");
goto clean_up;
}
@@ -1589,14 +1899,14 @@ static int __init r8a66597_probe(struct platform_device *pdev)
if (irq < 0) {
ret = -ENODEV;
- printk(KERN_ERR "platform_get_irq error.\n");
+ dev_err(&pdev->dev, "platform_get_irq error.\n");
goto clean_up;
}
reg = ioremap(res->start, resource_size(res));
if (reg == NULL) {
ret = -ENOMEM;
- printk(KERN_ERR "ioremap error.\n");
+ dev_err(&pdev->dev, "ioremap error.\n");
goto clean_up;
}
@@ -1604,7 +1914,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
if (r8a66597 == NULL) {
ret = -ENOMEM;
- printk(KERN_ERR "kzalloc error\n");
+ dev_err(&pdev->dev, "kzalloc error\n");
goto clean_up;
}
@@ -1640,13 +1950,18 @@ static int __init r8a66597_probe(struct platform_device *pdev)
clk_enable(r8a66597->clk);
}
#endif
+ if (r8a66597->pdata->sudmac) {
+ ret = r8a66597_sudmac_ioremap(r8a66597, pdev);
+ if (ret < 0)
+ goto clean_up2;
+ }
disable_controller(r8a66597); /* make sure controller is disabled */
- ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED,
+ ret = request_irq(irq, r8a66597_irq, IRQF_SHARED,
udc_name, r8a66597);
if (ret < 0) {
- printk(KERN_ERR "request_irq error (%d)\n", ret);
+ dev_err(&pdev->dev, "request_irq error (%d)\n", ret);
goto clean_up2;
}
@@ -1672,13 +1987,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
r8a66597->ep[0].fifoaddr = CFIFO;
r8a66597->ep[0].fifosel = CFIFOSEL;
r8a66597->ep[0].fifoctr = CFIFOCTR;
- r8a66597->ep[0].fifotrn = 0;
r8a66597->ep[0].pipectr = get_pipectr_addr(0);
r8a66597->pipenum2ep[0] = &r8a66597->ep[0];
r8a66597->epaddr2ep[0] = &r8a66597->ep[0];
- the_controller = r8a66597;
-
r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep,
GFP_KERNEL);
if (r8a66597->ep0_req == NULL)
@@ -1705,6 +2017,8 @@ clean_up2:
#endif
clean_up:
if (r8a66597) {
+ if (r8a66597->sudmac_reg)
+ iounmap(r8a66597->sudmac_reg);
if (r8a66597->ep0_req)
r8a66597_free_request(&r8a66597->ep[0].ep,
r8a66597->ep0_req);
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h
index 503f766c23a7..8e3de61cd4b8 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/r8a66597-udc.h
@@ -8,16 +8,6 @@
* 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#ifndef __R8A66597_H__
@@ -53,6 +43,7 @@
((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \
(pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC)))
+#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac)
struct r8a66597_pipe_info {
u16 pipe;
u16 epnum;
@@ -70,6 +61,7 @@ struct r8a66597_request {
struct r8a66597_ep {
struct usb_ep ep;
struct r8a66597 *r8a66597;
+ struct r8a66597_dma *dma;
struct list_head queue;
unsigned busy:1;
@@ -85,13 +77,20 @@ struct r8a66597_ep {
unsigned char fifoaddr;
unsigned char fifosel;
unsigned char fifoctr;
- unsigned char fifotrn;
unsigned char pipectr;
+ unsigned char pipetre;
+ unsigned char pipetrn;
+};
+
+struct r8a66597_dma {
+ unsigned used:1;
+ unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */
};
struct r8a66597 {
spinlock_t lock;
void __iomem *reg;
+ void __iomem *sudmac_reg;
#ifdef CONFIG_HAVE_CLK
struct clk *clk;
@@ -104,6 +103,7 @@ struct r8a66597 {
struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *epaddr2ep[16];
+ struct r8a66597_dma dma;
struct timer_list timer;
struct usb_request *ep0_req; /* for internal request */
@@ -124,6 +124,7 @@ struct r8a66597 {
#define gadget_to_r8a66597(_gadget) \
container_of(_gadget, struct r8a66597, gadget)
#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget)
+#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent)
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
{
@@ -182,12 +183,27 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
iowrite16(val, r8a66597->reg + offset);
}
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+ u16 val, u16 pat, unsigned long offset)
+{
+ u16 tmp;
+ tmp = r8a66597_read(r8a66597, offset);
+ tmp = tmp & (~pat);
+ tmp = tmp | val;
+ r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, val, 0, offset)
+
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
- unsigned long offset,
+ struct r8a66597_ep *ep,
unsigned char *buf,
int len)
{
- void __iomem *fifoaddr = r8a66597->reg + offset;
+ void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr;
int adj = 0;
int i;
@@ -215,18 +231,12 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
adj = 0x01; /* 16-bit wide */
}
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
for (i = 0; i < len; i++)
iowrite8(buf[i], fifoaddr + adj - (i & adj));
-}
-
-static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
- u16 val, u16 pat, unsigned long offset)
-{
- u16 tmp;
- tmp = r8a66597_read(r8a66597, offset);
- tmp = tmp & (~pat);
- tmp = tmp | val;
- r8a66597_write(r8a66597, tmp, offset);
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
}
static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
@@ -251,12 +261,21 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
return clock;
}
-#define r8a66597_bclr(r8a66597, val, offset) \
- r8a66597_mdfy(r8a66597, 0, val, offset)
-#define r8a66597_bset(r8a66597, val, offset) \
- r8a66597_mdfy(r8a66597, val, 0, offset)
+static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597,
+ unsigned long offset)
+{
+ return ioread32(r8a66597->sudmac_reg + offset);
+}
+
+static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val,
+ unsigned long offset)
+{
+ iowrite32(val, r8a66597->sudmac_reg + offset);
+}
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
+#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
#define enable_irq_ready(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, BRDYENB)
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 8bdee67ce09a..a552453dc946 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -1951,30 +1951,26 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
case S3C_DSTS_EnumSpd_FS:
case S3C_DSTS_EnumSpd_FS48:
hsotg->gadget.speed = USB_SPEED_FULL;
- dev_info(hsotg->dev, "new device is full-speed\n");
-
ep0_mps = EP0_MPS_LIMIT;
ep_mps = 64;
break;
case S3C_DSTS_EnumSpd_HS:
- dev_info(hsotg->dev, "new device is high-speed\n");
hsotg->gadget.speed = USB_SPEED_HIGH;
-
ep0_mps = EP0_MPS_LIMIT;
ep_mps = 512;
break;
case S3C_DSTS_EnumSpd_LS:
hsotg->gadget.speed = USB_SPEED_LOW;
- dev_info(hsotg->dev, "new device is low-speed\n");
-
/* note, we don't actually support LS in this driver at the
* moment, and the documentation seems to imply that it isn't
* supported by the PHYs on some of the devices.
*/
break;
}
+ dev_info(hsotg->dev, "new device is %s\n",
+ usb_speed_string(hsotg->gadget.speed));
/* we should now know the maximum packet size for an
* endpoint, so set the endpoints to a default value. */
@@ -2297,7 +2293,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
return -EINVAL;
}
- mps = le16_to_cpu(desc->wMaxPacketSize);
+ mps = usb_endpoint_maxp(desc);
/* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index 3fa717c5f4bc..8d54f893cefe 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -26,6 +26,7 @@
#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include <linux/prefetch.h>
#include <mach/regs-s3c2443-clock.h>
@@ -137,6 +138,7 @@ struct s3c_hsudc {
struct usb_gadget_driver *driver;
struct device *dev;
struct s3c24xx_hsudc_platdata *pd;
+ struct otg_transceiver *transceiver;
spinlock_t lock;
void __iomem *regs;
struct resource *mem_rsrc;
@@ -759,11 +761,11 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
if (!_ep || !desc || hsep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| hsep->bEndpointAddress != desc->bEndpointAddress
- || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+ || ep_maxpacket(hsep) < usb_endpoint_maxp(desc))
return -EINVAL;
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
- && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+ && usb_endpoint_maxp(desc) != ep_maxpacket(hsep))
|| !desc->wMaxPacketSize)
return -ERANGE;
@@ -779,7 +781,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
hsep->stopped = hsep->wedge = 0;
hsep->desc = desc;
- hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ hsep->ep.maxpacket = usb_endpoint_maxp(desc);
s3c_hsudc_set_halt(_ep, 0);
__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
@@ -1171,6 +1173,22 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
return ret;
}
+ /* connect to bus through transceiver */
+ if (hsudc->transceiver) {
+ ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget);
+ if (ret) {
+ dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
+ hsudc->gadget.name);
+ driver->unbind(&hsudc->gadget);
+
+ device_del(&hsudc->gadget.dev);
+
+ hsudc->driver = NULL;
+ hsudc->gadget.dev.driver = NULL;
+ return ret;
+ }
+ }
+
enable_irq(hsudc->irq);
dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
@@ -1201,6 +1219,9 @@ static int s3c_hsudc_stop(struct usb_gadget_driver *driver)
s3c_hsudc_stop_activity(hsudc, driver);
spin_unlock_irqrestore(&hsudc->lock, flags);
+ if (hsudc->transceiver)
+ (void) otg_set_peripheral(hsudc->transceiver, NULL);
+
driver->unbind(&hsudc->gadget);
device_del(&hsudc->gadget.dev);
disable_irq(hsudc->irq);
@@ -1220,10 +1241,24 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
return s3c_hsudc_read_frameno(to_hsudc(gadget));
}
+static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct s3c_hsudc *hsudc = the_controller;
+
+ if (!hsudc)
+ return -ENODEV;
+
+ if (hsudc->transceiver)
+ return otg_set_power(hsudc->transceiver, mA);
+
+ return -EOPNOTSUPP;
+}
+
static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
.get_frame = s3c_hsudc_gadget_getframe,
.start = s3c_hsudc_start,
.stop = s3c_hsudc_stop,
+ .vbus_draw = s3c_hsudc_vbus_draw,
};
static int s3c_hsudc_probe(struct platform_device *pdev)
@@ -1247,6 +1282,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
hsudc->dev = dev;
hsudc->pd = pdev->dev.platform_data;
+ hsudc->transceiver = otg_get_transceiver();
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "unable to obtain driver resource data\n");
@@ -1269,19 +1306,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
goto err_remap;
}
- ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "unable to obtain IRQ number\n");
- goto err_irq;
- }
- hsudc->irq = ret;
-
- ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
- if (ret < 0) {
- dev_err(dev, "irq request failed\n");
- goto err_irq;
- }
-
spin_lock_init(&hsudc->lock);
device_initialize(&hsudc->gadget.dev);
@@ -1299,6 +1323,19 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
s3c_hsudc_setup_ep(hsudc);
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "unable to obtain IRQ number\n");
+ goto err_irq;
+ }
+ hsudc->irq = ret;
+
+ ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+ if (ret < 0) {
+ dev_err(dev, "irq request failed\n");
+ goto err_irq;
+ }
+
hsudc->uclk = clk_get(&pdev->dev, "usb-device");
if (IS_ERR(hsudc->uclk)) {
dev_err(dev, "failed to find usb-device clock source\n");
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 8d31848aab09..b8643771fa80 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -10,16 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/module.h>
@@ -1082,7 +1072,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
- max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
+ max = usb_endpoint_maxp(desc) & 0x1fff;
local_irq_save (flags);
_ep->maxpacket = max & 0x7ff;
@@ -1903,7 +1893,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
/* irq setup after old hardware state is cleaned up */
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
- IRQF_DISABLED, gadget_name, udc);
+ 0, gadget_name, udc);
if (retval != 0) {
dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
@@ -1927,7 +1917,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
}
retval = request_irq(irq, s3c2410_udc_vbus_irq,
- IRQF_DISABLED | IRQF_TRIGGER_RISING
+ IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
gadget_name, udc);
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h
index 9e0bece4f241..a48f619cb1cc 100644
--- a/drivers/usb/gadget/s3c2410_udc.h
+++ b/drivers/usb/gadget/s3c2410_udc.h
@@ -9,16 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef _S3C2410_UDC_H
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index d3dd227a2bfc..c7f291a331df 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
@@ -52,6 +43,12 @@
* characters rather then a pointer to void.
*/
+/*
+ * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
+ * sets the number of pipeline buffers (length of the fsg_buffhd array).
+ * The valid range of num_buffers is: num >= 2 && num <= 4.
+ */
+
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
@@ -247,6 +244,8 @@ struct fsg_lun {
u32 sense_data_info;
u32 unit_attention_data;
+ unsigned int blkbits; /* Bits of logical block size of bound block device */
+ unsigned int blksize; /* logical block size of bound block device */
struct device dev;
};
@@ -262,8 +261,31 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
#define EP0_BUFSIZE 256
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
-/* Number of buffers we will use. 2 is enough for double-buffering */
-#define FSG_NUM_BUFFERS 2
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
+MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_DEBUG */
+
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(void)
+{
+ if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ return 0;
+ pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+ fsg_num_buffers, 2 ,4);
+ return -EINVAL;
+}
/* Default size of buffer length. */
#define FSG_BUFLEN ((u32)16384)
@@ -493,12 +515,128 @@ static struct usb_descriptor_header *fsg_hs_function[] = {
NULL,
};
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_ss_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ .wBytesPerInterval = cpu_to_le16(2),
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = {
+ .bLength = USB_DT_USB_EXT_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_CAP_TYPE_EXT,
+
+ .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT),
+};
+
+static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = {
+ .bLength = USB_DT_USB_SS_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_SS_CAP_TYPE,
+
+ /* .bmAttributes = LTM is not supported yet */
+
+ .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION
+ | USB_FULL_SPEED_OPERATION
+ | USB_HIGH_SPEED_OPERATION
+ | USB_5GBPS_OPERATION),
+ .bFunctionalitySupport = USB_LOW_SPEED_OPERATION,
+ .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT,
+ .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT,
+};
+
+static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = {
+ .bLength = USB_DT_BOS_SIZE,
+ .bDescriptorType = USB_DT_BOS,
+
+ .wTotalLength = USB_DT_BOS_SIZE
+ + USB_DT_USB_EXT_CAP_SIZE
+ + USB_DT_USB_SS_CAP_SIZE,
+
+ .bNumDeviceCaps = 2,
+};
+
+static struct usb_descriptor_header *fsg_ss_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
+#endif
+ NULL,
+};
+
/* Maxpacket and other transfer characteristics vary by speed. */
static __maybe_unused struct usb_endpoint_descriptor *
fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs)
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return ss;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return hs;
return fs;
}
@@ -580,13 +718,24 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
rc = (int) size;
goto out;
}
- num_sectors = size >> 9; /* File size in 512-byte blocks */
+
+ if (curlun->cdrom) {
+ curlun->blksize = 2048;
+ curlun->blkbits = 11;
+ } else if (inode->i_bdev) {
+ curlun->blksize = bdev_logical_block_size(inode->i_bdev);
+ curlun->blkbits = blksize_bits(curlun->blksize);
+ } else {
+ curlun->blksize = 512;
+ curlun->blkbits = 9;
+ }
+
+ num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */
min_sectors = 1;
if (curlun->cdrom) {
- num_sectors &= ~3; /* Reduce to a multiple of 2048 */
- min_sectors = 300*4; /* Smallest track is 300 frames */
- if (num_sectors >= 256*60*75*4) {
- num_sectors = (256*60*75 - 1) * 4;
+ min_sectors = 300; /* Smallest track is 300 frames */
+ if (num_sectors >= 256*60*75) {
+ num_sectors = 256*60*75 - 1;
LINFO(curlun, "file too big: %s\n", filename);
LINFO(curlun, "using only first %d blocks\n",
(int) num_sectors);
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index dfed4c1d96c0..29c854bbca44 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index c966440ddd70..8012357e98aa 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -9,15 +9,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __U_ETHER_H
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index a8aa46962d81..3a4a664bab44 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -552,9 +552,8 @@ recycle:
/* Push from tty to ldisc; without low_latency set this is handled by
* a workqueue, so we won't get callbacks and can hold port_lock
*/
- if (tty && do_push) {
+ if (tty && do_push)
tty_flip_buffer_push(tty);
- }
/* We want our data queue to become empty ASAP, keeping data
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 05ba47214361..022baeca7c94 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -356,7 +356,7 @@ static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store);
static ssize_t usb_udc_softconn_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
- struct usb_udc *udc = dev_get_drvdata(dev);
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
if (sysfs_streq(buf, "connect")) {
usb_gadget_connect(udc->gadget);
@@ -375,23 +375,8 @@ static ssize_t usb_udc_speed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
- struct usb_gadget *gadget = udc->gadget;
-
- switch (gadget->speed) {
- case USB_SPEED_LOW:
- return snprintf(buf, PAGE_SIZE, "low-speed\n");
- case USB_SPEED_FULL:
- return snprintf(buf, PAGE_SIZE, "full-speed\n");
- case USB_SPEED_HIGH:
- return snprintf(buf, PAGE_SIZE, "high-speed\n");
- case USB_SPEED_WIRELESS:
- return snprintf(buf, PAGE_SIZE, "wireless\n");
- case USB_SPEED_SUPER:
- return snprintf(buf, PAGE_SIZE, "super-speed\n");
- case USB_SPEED_UNKNOWN: /* FALLTHROUGH */
- default:
- return snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
- }
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ usb_speed_string(udc->gadget->speed));
}
static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL);
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h
index 5b7919460fd2..bc78c606c12b 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/uvc.h
@@ -8,7 +8,6 @@
* 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.
- *
*/
#ifndef _UVC_GADGET_H_
@@ -56,6 +55,7 @@ struct uvc_event
#include <linux/usb.h> /* For usb_endpoint_* */
#include <linux/usb/gadget.h>
#include <linux/videodev2.h>
+#include <linux/version.h>
#include <media/v4l2-fh.h>
#include "uvc_queue.h"
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
index aa0ad34e0f1f..d776adb2da67 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/uvc_queue.c
@@ -8,7 +8,6 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
index 52f8f9e513af..f6e083b50191 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/uvc_v4l2.c
@@ -8,7 +8,6 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
*/
#include <linux/kernel.h>
@@ -16,7 +15,6 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
index b08f35438d70..b0e53a8ea4f7 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/uvc_video.c
@@ -8,7 +8,6 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index df6882de50bf..668fe128f2ef 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -8,8 +8,8 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
*/
+
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/usb/video.h>
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 00e2fd2d4791..20697cc132d1 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -8,15 +8,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ab085f12d570..060e0e2b1ae6 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -19,7 +19,7 @@ config USB_C67X00_HCD
config USB_XHCI_HCD
tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)"
- depends on USB && PCI && EXPERIMENTAL
+ depends on USB && USB_ARCH_HAS_XHCI && EXPERIMENTAL
---help---
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
"SuperSpeed" host controller hardware.
@@ -515,6 +515,19 @@ config USB_R8A66597_HCD
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
+config USB_RENESAS_USBHS_HCD
+ tristate "Renesas USBHS HCD support"
+ depends on USB
+ depends on USB_RENESAS_USBHS
+ help
+ The Renesas USBHS is a USB 2.0 host and peripheral controller.
+
+ Enable this option if your board has this chip, and you want
+ to use it as a host controller. If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called renesas-usbhs.
+
config USB_WHCI_HCD
tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)"
depends on EXPERIMENTAL
@@ -544,11 +557,11 @@ config USB_HWA_HCD
will be called "hwa-hc".
config USB_IMX21_HCD
- tristate "iMX21 HCD support"
- depends on USB && ARM && MACH_MX21
+ tristate "i.MX21 HCD support"
+ depends on USB && ARM && ARCH_MXC
help
This driver enables support for the on-chip USB host in the
- iMX21 processor.
+ i.MX21 processor.
To compile this driver as a module, choose M here: the
module will be called "imx21-hcd".
@@ -578,3 +591,10 @@ config USB_OCTEON_OHCI
config USB_OCTEON2_COMMON
bool
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
+
+config USB_PXA168_EHCI
+ bool "Marvell PXA168 on-chip EHCI HCD support"
+ depends on USB_EHCI_HCD && ARCH_MMP
+ help
+ Enable support for Marvell PXA168 SoC's on-chip EHCI
+ host controller
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 436cd71c6bec..7ca290fcb070 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -11,8 +11,9 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
-xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o
+xhci-hcd-y := xhci.o xhci-mem.o
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
+xhci-hcd-$(CONFIG_PCI) += xhci-pci.o
obj-$(CONFIG_USB_WHCI_HCD) += whci/
diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c
index 4d2e88d04dab..afb6743cf094 100644
--- a/drivers/usb/host/ehci-ath79.c
+++ b/drivers/usb/host/ehci-ath79.c
@@ -163,7 +163,7 @@ static int ehci_ath79_probe(struct platform_device *pdev)
goto err_release_region;
}
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto err_iounmap;
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index e480dc17394d..18bafa99fe57 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -123,7 +123,7 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
ehci->hcs_params = readl(&ehci->caps->hcs_params);
ret = usb_add_hcd(hcd, pdev->resource[1].start,
- IRQF_DISABLED | IRQF_SHARED);
+ IRQF_SHARED);
if (ret == 0) {
platform_set_drvdata(pdev, hcd);
return ret;
@@ -236,7 +236,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 40a844c1dbb4..d6d74d2e09f4 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -697,6 +697,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
}
#undef DBG_SCHED_LIMIT
+static const char *rh_state_string(struct ehci_hcd *ehci)
+{
+ switch (ehci->rh_state) {
+ case EHCI_RH_HALTED:
+ return "halted";
+ case EHCI_RH_SUSPENDED:
+ return "suspended";
+ case EHCI_RH_RUNNING:
+ return "running";
+ }
+ return "?";
+}
+
static ssize_t fill_registers_buffer(struct debug_buffer *buf)
{
struct usb_hcd *hcd;
@@ -730,11 +743,11 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
temp = scnprintf (next, size,
"bus %s, device %s\n"
"%s\n"
- "EHCI %x.%02x, hcd state %d\n",
+ "EHCI %x.%02x, rh state %s\n",
hcd->self.controller->bus->name,
dev_name(hcd->self.controller),
hcd->product_desc,
- i >> 8, i & 0x0ff, hcd->state);
+ i >> 8, i & 0x0ff, rh_state_string(ehci));
size -= temp;
next += temp;
@@ -808,7 +821,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
next += temp;
temp = scnprintf (next, size, "uframe %04x\n",
- ehci_readl(ehci, &ehci->regs->frame_index));
+ ehci_read_frame_index(ehci));
size -= temp;
next += temp;
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 34a3140d1e5f..e90344a17631 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -134,7 +134,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
/* Don't need to set host mode here. It will be done by tdi_reset() */
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval != 0)
goto err4;
@@ -392,7 +392,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
dev_dbg(dev, "suspending...\n");
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
dev->power.power_state = PMSG_SUSPEND;
/* ignore non-host interrupts */
@@ -481,7 +481,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- hcd->state = HC_STATE_RUNNING;
+ ehci->rh_state = EHCI_RH_RUNNING;
dev->power.power_state = PMSG_ON;
tmp = ehci_readl(ehci, &ehci->regs->command);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 47aa22d43d63..3ff9f82f7263 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -95,7 +95,7 @@ static const char hcd_name [] = "ehci_hcd";
#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)
- /* 200-ms async qh unlink delay */
+ /* 5-ms async qh unlink delay */
/* Initial IRQ latency: faster than hw default */
static int log2_irq_thresh = 0; // 0 to 6
@@ -238,7 +238,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
error = handshake(ehci, ptr, mask, done, usec);
if (error) {
ehci_halt(ehci);
- ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ ehci->rh_state = EHCI_RH_HALTED;
ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
ptr, mask, done, error);
}
@@ -278,7 +278,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
command |= CMD_RESET;
dbg_cmd (ehci, "reset", command);
ehci_writel(ehci, command, &ehci->regs->command);
- ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+ ehci->rh_state = EHCI_RH_HALTED;
ehci->next_statechange = jiffies;
retval = handshake (ehci, &ehci->regs->command,
CMD_RESET, 0, 250 * 1000);
@@ -307,7 +307,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
u32 temp;
#ifdef DEBUG
- if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ if (ehci->rh_state != EHCI_RH_RUNNING)
BUG ();
#endif
@@ -356,7 +356,7 @@ static void ehci_iaa_watchdog(unsigned long param)
*/
if (ehci->reclaim
&& !timer_pending(&ehci->iaa_watchdog)
- && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
+ && ehci->rh_state == EHCI_RH_RUNNING) {
u32 cmd, status;
/* If we get here, IAA is *REALLY* late. It's barely
@@ -496,7 +496,7 @@ static void ehci_work (struct ehci_hcd *ehci)
* misplace IRQs, and should let us run completely without IRQs.
* such lossage has been observed on both VT6202 and VT8235.
*/
- if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
+ if (ehci->rh_state == EHCI_RH_RUNNING &&
(ehci->async->qh_next.ptr != NULL ||
ehci->periodic_sched != 0))
timer_action (ehci, TIMER_IO_WATCHDOG);
@@ -516,7 +516,7 @@ static void ehci_stop (struct usb_hcd *hcd)
del_timer_sync(&ehci->iaa_watchdog);
spin_lock_irq(&ehci->lock);
- if (HC_IS_RUNNING (hcd->state))
+ if (ehci->rh_state == EHCI_RH_RUNNING)
ehci_quiesce (ehci);
ehci_silence_controller(ehci);
@@ -741,7 +741,7 @@ static int ehci_run (struct usb_hcd *hcd)
* be started before the port switching actions could complete.
*/
down_write(&ehci_cf_port_reset_rwsem);
- hcd->state = HC_STATE_RUNNING;
+ ehci->rh_state = EHCI_RH_RUNNING;
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
msleep(5);
@@ -768,6 +768,35 @@ static int ehci_run (struct usb_hcd *hcd)
return 0;
}
+static int __maybe_unused ehci_setup (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci->regs = (void __iomem *)ehci->caps +
+ 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);
+
+ ehci->sbrn = HCD_USB2;
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci_reset(ehci);
+
+ return 0;
+}
+
/*-------------------------------------------------------------------------*/
static irqreturn_t ehci_irq (struct usb_hcd *hcd)
@@ -788,7 +817,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* Shared IRQ? */
masked_status = status & INTR_MASK;
- if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) {
+ if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) {
spin_unlock(&ehci->lock);
return IRQ_NONE;
}
@@ -952,7 +981,7 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
/* failfast */
- if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim)
+ 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
@@ -1079,7 +1108,7 @@ rescan:
goto idle_timeout;
}
- if (!HC_IS_RUNNING (hcd->state))
+ if (ehci->rh_state != EHCI_RH_RUNNING)
qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) {
case QH_STATE_LINKED:
@@ -1166,8 +1195,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
static int ehci_get_frame (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) %
- ehci->periodic_size;
+ return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size;
}
/*-------------------------------------------------------------------------*/
@@ -1291,6 +1319,16 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_grlib_driver
#endif
+#ifdef CONFIG_USB_PXA168_EHCI
+#include "ehci-pxa168.c"
+#define PLATFORM_DRIVER ehci_pxa168_driver
+#endif
+
+#ifdef CONFIG_NLM_XLR
+#include "ehci-xls.c"
+#define PLATFORM_DRIVER ehci_xls_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
!defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 4c32cb19b405..77bbb2357e47 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -236,10 +236,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
/* stop schedules, clean any completed work */
- if (HC_IS_RUNNING(hcd->state)) {
+ if (ehci->rh_state == EHCI_RH_RUNNING)
ehci_quiesce (ehci);
- hcd->state = HC_STATE_QUIESCING;
- }
ehci->command = ehci_readl(ehci, &ehci->regs->command);
ehci_work(ehci);
@@ -313,7 +311,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
/* turn off now-idle HC */
ehci_halt (ehci);
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
if (ehci->reclaim)
end_unlink_async(ehci);
@@ -382,6 +380,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* restore CMD_RUN, framelist size, and irq threshold */
ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ ehci->rh_state = EHCI_RH_RUNNING;
/* Some controller/firmware combinations need a delay during which
* they set up the port statuses. See Bugzilla #8190. */
@@ -451,7 +450,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
}
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
- hcd->state = HC_STATE_RUNNING;
/* Now we can safely re-enable irqs */
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
@@ -563,7 +561,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
- if (!HC_IS_RUNNING(hcd->state))
+ if (ehci->rh_state != EHCI_RH_RUNNING)
return 0;
/* init status to no-changes */
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index 555a73c864b5..55978fcfa4b2 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -236,7 +236,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
priv->hcd = hcd;
platform_set_drvdata(pdev, priv);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto err_add;
diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c
index c3ba3ed5f3a6..ba1f51361134 100644
--- a/drivers/usb/host/ehci-octeon.c
+++ b/drivers/usb/host/ehci-octeon.c
@@ -155,7 +155,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev)
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret) {
dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
goto err3;
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 45240321ca09..e39b0297bad1 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -228,7 +228,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
/* cache this readonly data; minimize chip reads */
omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret) {
dev_err(dev, "failed to add hcd with err %d\n", ret);
goto err_add_hcd;
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 395bdb0248d5..a68a2a5c4b83 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -277,7 +277,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)
printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n");
}
- err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err4;
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 1102ce65a3a9..f4b627d343ac 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
pci_dev_put(p_smbus);
}
break;
+ case PCI_VENDOR_ID_NETMOS:
+ /* MosChip frame-index-register bug */
+ ehci_info(ehci, "applying MosChip frame-index workaround\n");
+ ehci->frame_index_bug = 1;
+ break;
}
/* optional debug port, normally in the first BAR */
@@ -439,7 +444,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
#endif
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 64626a777d61..2dc32da75cfc 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -167,7 +167,7 @@ static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev)
ps3_system_bus_set_drvdata(dev, hcd);
- result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
+ result = usb_add_hcd(hcd, virq, 0);
if (result) {
dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
diff --git a/drivers/usb/host/ehci-pxa168.c b/drivers/usb/host/ehci-pxa168.c
new file mode 100644
index 000000000000..ac0c16e8f539
--- /dev/null
+++ b/drivers/usb/host/ehci-pxa168.c
@@ -0,0 +1,363 @@
+/*
+ * drivers/usb/host/ehci-pxa168.c
+ *
+ * Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
+ *
+ * Based on drivers/usb/host/ehci-orion.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/pxa168.h>
+
+#define USB_PHY_CTRL_REG 0x4
+#define USB_PHY_PLL_REG 0x8
+#define USB_PHY_TX_REG 0xc
+
+#define FBDIV_SHIFT 4
+
+#define ICP_SHIFT 12
+#define ICP_15 2
+#define ICP_20 3
+#define ICP_25 4
+
+#define KVCO_SHIFT 15
+
+#define PLLCALI12_SHIFT 25
+#define CALI12_VDD 0
+#define CALI12_09 1
+#define CALI12_10 2
+#define CALI12_11 3
+
+#define PLLVDD12_SHIFT 27
+#define VDD12_VDD 0
+#define VDD12_10 1
+#define VDD12_11 2
+#define VDD12_12 3
+
+#define PLLVDD18_SHIFT 29
+#define VDD18_19 0
+#define VDD18_20 1
+#define VDD18_21 2
+#define VDD18_22 3
+
+
+#define PLL_READY (1 << 23)
+#define VCOCAL_START (1 << 21)
+#define REG_RCAL_START (1 << 12)
+
+struct pxa168_usb_drv_data {
+ struct ehci_hcd ehci;
+ struct clk *pxa168_usb_clk;
+ struct resource *usb_phy_res;
+ void __iomem *usb_phy_reg_base;
+};
+
+static int ehci_pxa168_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ ehci_reset(ehci);
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /*
+ * data structure init
+ */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ hcd->has_tt = 1;
+
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_pxa168_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Marvell PXA168 EHCI",
+ .hcd_priv_size = sizeof(struct pxa168_usb_drv_data),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_pxa168_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int pxa168_usb_phy_init(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *usb_phy_reg_base;
+ struct pxa168_usb_pdata *pdata;
+ struct pxa168_usb_drv_data *drv_data;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ unsigned long reg_val;
+ int pll_retry_cont = 10000, err = 0;
+
+ drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv;
+ pdata = (struct pxa168_usb_pdata *)pdev->dev.platform_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no PHY register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ ehci_pxa168_hc_driver.description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ return -EBUSY;
+ }
+
+ usb_phy_reg_base = ioremap(res->start, resource_size(res));
+ if (usb_phy_reg_base == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ err = -EFAULT;
+ goto err1;
+ }
+ drv_data->usb_phy_reg_base = usb_phy_reg_base;
+ drv_data->usb_phy_res = res;
+
+ /* If someone wants to init USB phy in board specific way */
+ if (pdata && pdata->phy_init)
+ return pdata->phy_init(usb_phy_reg_base);
+
+ /* Power up the PHY and PLL */
+ writel(readl(usb_phy_reg_base + USB_PHY_CTRL_REG) | 0x3,
+ usb_phy_reg_base + USB_PHY_CTRL_REG);
+
+ /* Configure PHY PLL */
+ reg_val = readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~(0x7e03ffff);
+ reg_val |= (VDD18_22 << PLLVDD18_SHIFT | VDD12_12 << PLLVDD12_SHIFT |
+ CALI12_11 << PLLCALI12_SHIFT | 3 << KVCO_SHIFT |
+ ICP_15 << ICP_SHIFT | 0xee << FBDIV_SHIFT | 0xb);
+ writel(reg_val, usb_phy_reg_base + USB_PHY_PLL_REG);
+
+ /* Make sure PHY PLL is ready */
+ while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) {
+ if (!(pll_retry_cont--)) {
+ dev_dbg(&pdev->dev, "USB PHY PLL not ready\n");
+ err = -EIO;
+ goto err2;
+ }
+ }
+
+ /* Toggle VCOCAL_START bit of U2PLL for PLL calibration */
+ udelay(200);
+ writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) | VCOCAL_START,
+ usb_phy_reg_base + USB_PHY_PLL_REG);
+ udelay(40);
+ writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~VCOCAL_START,
+ usb_phy_reg_base + USB_PHY_PLL_REG);
+
+ /* Toggle REG_RCAL_START bit of U2PTX for impedance calibration */
+ udelay(400);
+ writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) | REG_RCAL_START,
+ usb_phy_reg_base + USB_PHY_TX_REG);
+ udelay(40);
+ writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) & ~REG_RCAL_START,
+ usb_phy_reg_base + USB_PHY_TX_REG);
+
+ /* Make sure PHY PLL is ready again */
+ pll_retry_cont = 0;
+ while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) {
+ if (!(pll_retry_cont--)) {
+ dev_dbg(&pdev->dev, "USB PHY PLL not ready\n");
+ err = -EIO;
+ goto err2;
+ }
+ }
+
+ return 0;
+err2:
+ iounmap(usb_phy_reg_base);
+err1:
+ release_mem_region(res->start, resource_size(res));
+ return err;
+}
+
+static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct pxa168_usb_drv_data *drv_data;
+ void __iomem *regs;
+ int irq, err = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing pxa168-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ err = -ENODEV;
+ goto err1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ err = -ENODEV;
+ goto err1;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ ehci_pxa168_hc_driver.description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ err = -EBUSY;
+ goto err1;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ err = -EFAULT;
+ goto err2;
+ }
+
+ hcd = usb_create_hcd(&ehci_pxa168_hc_driver,
+ &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err3;
+ }
+
+ drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv;
+
+ /* Enable USB clock */
+ drv_data->pxa168_usb_clk = clk_get(&pdev->dev, "PXA168-USBCLK");
+ if (IS_ERR(drv_data->pxa168_usb_clk)) {
+ dev_err(&pdev->dev, "Couldn't get USB clock\n");
+ err = PTR_ERR(drv_data->pxa168_usb_clk);
+ goto err4;
+ }
+ clk_enable(drv_data->pxa168_usb_clk);
+
+ err = pxa168_usb_phy_init(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "USB PHY initialization failed\n");
+ goto err5;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ hcd->has_tt = 1;
+ ehci->sbrn = 0x20;
+
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
+ if (err)
+ goto err5;
+
+ return 0;
+
+err5:
+ clk_disable(drv_data->pxa168_usb_clk);
+ clk_put(drv_data->pxa168_usb_clk);
+err4:
+ usb_put_hcd(hcd);
+err3:
+ iounmap(regs);
+err2:
+ release_mem_region(res->start, resource_size(res));
+err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), err);
+
+ return err;
+}
+
+static int __exit ehci_pxa168_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct pxa168_usb_drv_data *drv_data =
+ (struct pxa168_usb_drv_data *)hcd->hcd_priv;
+
+ usb_remove_hcd(hcd);
+
+ /* Power down PHY & PLL */
+ writel(readl(drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG) & (~0x3),
+ drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG);
+
+ clk_disable(drv_data->pxa168_usb_clk);
+ clk_put(drv_data->pxa168_usb_clk);
+
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ iounmap(drv_data->usb_phy_reg_base);
+ release_mem_region(drv_data->usb_phy_res->start,
+ resource_size(drv_data->usb_phy_res));
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:pxa168-ehci");
+
+static struct platform_driver ehci_pxa168_driver = {
+ .probe = ehci_pxa168_drv_probe,
+ .remove = __exit_p(ehci_pxa168_drv_remove),
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver.name = "pxa168-ehci",
+};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 0917e3a32465..4e4066c35a09 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -111,8 +111,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
}
}
- /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
- wmb ();
hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
}
@@ -153,7 +151,7 @@ static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd,
spin_lock_irqsave(&ehci->lock, flags);
qh->clearing_tt = 0;
if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
- && HC_IS_RUNNING(hcd->state))
+ && ehci->rh_state == EHCI_RH_RUNNING)
qh_link_async(ehci, qh);
spin_unlock_irqrestore(&ehci->lock, flags);
}
@@ -425,7 +423,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* stop scanning when we reach qtds the hc is using */
} else if (likely (!stopped
- && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) {
+ && ehci->rh_state == EHCI_RH_RUNNING)) {
break;
/* scan the whole queue for unlinks whenever it stops */
@@ -433,7 +431,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
stopped = 1;
/* cancel everything if we halt, suspend, etc */
- if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
+ if (ehci->rh_state != EHCI_RH_RUNNING)
last_status = -ESHUTDOWN;
/* this qtd is active; skip it unless a previous qtd
@@ -724,7 +722,8 @@ qh_urb_transaction (
/*
* control requests may need a terminating data "status" ack;
- * bulk ones may need a terminating short packet (zero length).
+ * other OUT ones may need a terminating short packet
+ * (zero length).
*/
if (likely (urb->transfer_buffer_length != 0)) {
int one_more = 0;
@@ -733,7 +732,7 @@ qh_urb_transaction (
one_more = 1;
token ^= 0x0100; /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
- } else if (usb_pipebulk (urb->pipe)
+ } else if (usb_pipeout(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
one_more = 1;
@@ -977,9 +976,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* in case a clear of CMD_ASE didn't take yet */
(void)handshake(ehci, &ehci->regs->status,
STS_ASS, 0, 150);
- cmd |= CMD_ASE | CMD_RUN;
+ cmd |= CMD_ASE;
ehci_writel(ehci, cmd, &ehci->regs->command);
- ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
/* posted write need not be known to HC yet ... */
}
}
@@ -1058,7 +1056,7 @@ static struct ehci_qh *qh_append_tds (
*/
token = qtd->hw_token;
qtd->hw_token = HALT_BIT(ehci);
- wmb ();
+
dummy = qh->dummy;
dma = dummy->qtd_dma;
@@ -1168,14 +1166,13 @@ static void end_unlink_async (struct ehci_hcd *ehci)
qh_completions (ehci, qh);
- if (!list_empty (&qh->qtd_list)
- && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) {
qh_link_async (ehci, qh);
- else {
+ } else {
/* it's not free to turn the async schedule on/off; leave it
* active but idle for a while once it empties.
*/
- if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
+ if (ehci->rh_state == EHCI_RH_RUNNING
&& ehci->async->qh_next.qh == NULL)
timer_action (ehci, TIMER_ASYNC_OFF);
}
@@ -1211,7 +1208,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* stop async schedule right now? */
if (unlikely (qh == ehci->async)) {
/* can't get here without STS_ASS set */
- if (ehci_to_hcd(ehci)->state != HC_STATE_HALT
+ if (ehci->rh_state != EHCI_RH_HALTED
&& !ehci->reclaim) {
/* ... and CMD_IAAD clear */
ehci_writel(ehci, cmd & ~CMD_ASE,
@@ -1237,7 +1234,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
wmb ();
/* If the controller isn't running, we don't have to wait for it */
- if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) {
+ if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) {
/* if (unlikely (qh->reclaim != 0))
* this will recurse, probably not much
*/
@@ -1260,7 +1257,7 @@ static void scan_async (struct ehci_hcd *ehci)
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
- stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
+ stopped = (ehci->rh_state != EHCI_RH_RUNNING);
ehci->qh_scan_next = ehci->async->qh_next.qh;
while (ehci->qh_scan_next) {
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 9e77f1c8bdbd..024b65c4990d 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -136,7 +136,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
- err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
goto fail;
@@ -270,7 +270,7 @@ static int s5p_ehci_resume(struct device *dev)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 2abf8543f083..2e829fae6482 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -36,6 +36,27 @@
static int ehci_get_frame (struct usb_hcd *hcd);
+#ifdef CONFIG_PCI
+
+static unsigned ehci_read_frame_index(struct ehci_hcd *ehci)
+{
+ unsigned uf;
+
+ /*
+ * The MosChip MCS9990 controller updates its microframe counter
+ * a little before the frame counter, and occasionally we will read
+ * the invalid intermediate value. Avoid problems by checking the
+ * microframe number (the low-order 3 bits); if they are 0 then
+ * re-read the register to get the correct value.
+ */
+ uf = ehci_readl(ehci, &ehci->regs->frame_index);
+ if (unlikely(ehci->frame_index_bug && ((uf & 7) == 0)))
+ uf = ehci_readl(ehci, &ehci->regs->frame_index);
+ return uf;
+}
+
+#endif
+
/*-------------------------------------------------------------------------*/
/*
@@ -479,10 +500,9 @@ static int enable_periodic (struct ehci_hcd *ehci)
cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE;
ehci_writel(ehci, cmd, &ehci->regs->command);
/* posted write ... PSS happens later */
- ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
/* make sure ehci_work scans these */
- ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index)
+ ehci->next_uframe = ehci_read_frame_index(ehci)
% (ehci->periodic_size << 3);
if (unlikely(ehci->broken_periodic))
ehci->last_periodic_enable = ktime_get_real();
@@ -677,7 +697,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* reschedule QH iff another request is queued */
if (!list_empty(&qh->qtd_list) &&
- HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
+ ehci->rh_state == EHCI_RH_RUNNING) {
rc = qh_schedule(ehci, qh);
/* An error here likely indicates handshake failure
@@ -1409,7 +1429,7 @@ iso_stream_schedule (
goto fail;
}
- now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
+ now = ehci_read_frame_index(ehci) & (mod - 1);
/* Typical case: reuse current schedule, stream is still active.
* Hopefully there are no gaps from the host falling behind
@@ -2275,8 +2295,8 @@ scan_periodic (struct ehci_hcd *ehci)
* Touches as few pages as possible: cache-friendly.
*/
now_uframe = ehci->next_uframe;
- if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
- clock = ehci_readl(ehci, &ehci->regs->frame_index);
+ if (ehci->rh_state == EHCI_RH_RUNNING) {
+ clock = ehci_read_frame_index(ehci);
clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
} else {
clock = now_uframe + mod - 1;
@@ -2310,7 +2330,7 @@ restart:
union ehci_shadow temp;
int live;
- live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
+ live = (ehci->rh_state == EHCI_RH_RUNNING);
switch (hc32_to_cpu(ehci, type)) {
case Q_TYPE_QH:
/* handle any completions */
@@ -2435,7 +2455,7 @@ restart:
* We can't advance our scan without collecting the ISO
* transfers that are still pending in this frame.
*/
- if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
+ if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
ehci->next_uframe = now_uframe;
break;
}
@@ -2451,12 +2471,11 @@ restart:
if (now_uframe == clock) {
unsigned now;
- if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
+ if (ehci->rh_state != EHCI_RH_RUNNING
|| ehci->periodic_sched == 0)
break;
ehci->next_uframe = now_uframe;
- now = ehci_readl(ehci, &ehci->regs->frame_index) &
- (mod - 1);
+ now = ehci_read_frame_index(ehci) & (mod - 1);
if (now_uframe == now)
break;
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index 86a95bb80a61..9d9cf47d80da 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -168,7 +168,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
clk_enable(priv->fclk);
clk_enable(priv->iclk);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to add hcd");
goto fail_add_hcd;
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index dbf1e4ef3c17..b115b0b76e33 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -154,7 +154,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
ehci->clk = usbh_clk;
spear_start_ehci(ehci);
- retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval)
goto fail_add_hcd;
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 02b2bfd49a10..db9d1b4bfbdc 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -674,7 +674,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
}
#endif
- err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
goto fail;
diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c
index 47d749631bc7..54d1ab8aec49 100644
--- a/drivers/usb/host/ehci-vt8500.c
+++ b/drivers/usb/host/ehci-vt8500.c
@@ -133,7 +133,7 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
ehci_port_power(ehci, 1);
ret = usb_add_hcd(hcd, pdev->resource[1].start,
- IRQF_DISABLED | IRQF_SHARED);
+ IRQF_SHARED);
if (ret == 0) {
platform_set_drvdata(pdev, hcd);
return ret;
diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c
new file mode 100644
index 000000000000..fe74bd676018
--- /dev/null
+++ b/drivers/usb/host/ehci-xls.c
@@ -0,0 +1,161 @@
+/*
+ * EHCI HCD for Netlogic XLS processors.
+ *
+ * (C) Copyright 2011 Netlogic Microsystems Inc.
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/platform_device.h>
+
+static int ehci_xls_setup(struct usb_hcd *hcd)
+{
+ int retval;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(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);
+
+ 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,
+ struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int retval, irq;
+
+ /* Get our IRQ from an earlier registered Platform Resource */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+
+ /* Get our Memory Handle */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Error: MMIO Handle %s setup!\n",
+ dev_name(&pdev->dev));
+ return -ENODEV;
+ }
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto err2;
+ }
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err3;
+ }
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval != 0)
+ goto err4;
+ return retval;
+
+err4:
+ iounmap(hcd->regs);
+err3:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err2:
+ usb_put_hcd(hcd);
+err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev),
+ retval);
+ return retval;
+}
+
+static struct hc_driver ehci_xls_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "XLS EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+ .irq = ehci_irq,
+ .flags = HCD_USB2 | HCD_MEMORY,
+ .reset = ehci_xls_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ .get_frame_number = ehci_get_frame,
+
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_xls_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return ehci_xls_probe_internal(&ehci_xls_hc_driver, pdev);
+}
+
+static int ehci_xls_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+MODULE_ALIAS("ehci-xls");
+
+static struct platform_driver ehci_xls_driver = {
+ .probe = ehci_xls_probe,
+ .remove = ehci_xls_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ehci-xls",
+ },
+};
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index cc7d337ec355..0a5fda73b3f2 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -62,6 +62,12 @@ struct ehci_stats {
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+enum ehci_rh_state {
+ EHCI_RH_HALTED,
+ EHCI_RH_SUSPENDED,
+ EHCI_RH_RUNNING
+};
+
struct ehci_hcd { /* one per controller */
/* glue to PCI and HCD framework */
struct ehci_caps __iomem *caps;
@@ -70,6 +76,7 @@ struct ehci_hcd { /* one per controller */
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
+ enum ehci_rh_state rh_state;
/* async schedule support */
struct ehci_qh *async;
@@ -139,6 +146,7 @@ struct ehci_hcd { /* one per controller */
unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
+ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -740,6 +748,22 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_PCI
+
+/* For working around the MosChip frame-index-register bug */
+static unsigned ehci_read_frame_index(struct ehci_hcd *ehci);
+
+#else
+
+static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci)
+{
+ return ehci_readl(ehci, &ehci->regs->frame_index);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 572ea53b0226..4ed6d19f2a54 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -621,12 +621,15 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev)
goto err_pram;
}
- pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE);
+ pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64);
if (IS_ERR_VALUE(pram_addr)) {
dev_err(dev, "failed to allocate usb pram\n");
ret = -ENOMEM;
goto err_pram;
}
+
+ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB,
+ QE_CR_PROTOCOL_UNSPECIFIED, pram_addr);
fhci->pram = cpm_muram_addr(pram_addr);
/* GPIOs and pins */
@@ -686,7 +689,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev)
}
ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq,
- IRQF_DISABLED, "qe timer (usb)", hcd);
+ 0, "qe timer (usb)", hcd);
if (ret) {
dev_err(dev, "failed to request timer irq");
goto err_timer_irq;
@@ -745,7 +748,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev)
out_be16(&fhci->regs->usb_event, 0xffff);
out_be16(&fhci->regs->usb_mask, 0);
- ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, usb_irq, 0);
if (ret < 0)
goto err_add_hcd;
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
index a42ef380e917..2df851b4bc7c 100644
--- a/drivers/usb/host/fhci-sched.c
+++ b/drivers/usb/host/fhci-sched.c
@@ -1,7 +1,7 @@
/*
* Freescale QUICC Engine USB Host Controller Driver
*
- * Copyright (c) Freescale Semicondutor, Inc. 2006.
+ * Copyright (c) Freescale Semicondutor, Inc. 2006, 2011.
* Shlomi Gridish <gridish@freescale.com>
* Jerry Huang <Chang-Ming.Huang@freescale.com>
* Copyright (c) Logic Product Development, Inc. 2007
@@ -810,9 +810,11 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb)
ed->dev_addr = usb_pipedevice(urb->pipe);
ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe,
usb_pipeout(urb->pipe));
+ /* setup stage */
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true);
+ /* data stage */
if (data_len > 0) {
td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
@@ -820,9 +822,18 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb)
USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
true);
}
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
- usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT,
- USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+
+ /* status stage */
+ if (data_len > 0)
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ (usb_pipeout(urb->pipe) ? FHCI_TA_IN :
+ FHCI_TA_OUT),
+ USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+ else
+ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ FHCI_TA_IN,
+ USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
+
urb_state = US_CTRL_SETUP;
break;
case FHCI_TF_ISO:
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index af05718bdc73..2ee18cfa1efe 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1891,7 +1891,7 @@ static int imx21_probe(struct platform_device *pdev)
dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n",
(readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret != 0) {
dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret);
goto failed_add_hcd;
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index baae4ccd16ac..d91e5f211a76 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1639,7 +1639,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev)
goto err6;
}
- ret = usb_add_hcd(hcd, irq, irqflags | IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, irqflags);
if (ret)
goto err6;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 9c37dad3e816..e5fd8aa57af1 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -2358,7 +2358,7 @@ static int isp1362_hc_reset(struct usb_hcd *hcd)
unsigned long flags;
int clkrdy = 0;
- pr_info("%s:\n", __func__);
+ pr_debug("%s:\n", __func__);
if (isp1362_hcd->board && isp1362_hcd->board->reset) {
isp1362_hcd->board->reset(hcd->self.controller, 1);
@@ -2395,7 +2395,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd)
unsigned long flags;
u32 tmp;
- pr_info("%s:\n", __func__);
+ pr_debug("%s:\n", __func__);
del_timer_sync(&hcd->rh_timer);
@@ -2523,7 +2523,7 @@ static int isp1362_hc_start(struct usb_hcd *hcd)
u16 chipid;
unsigned long flags;
- pr_info("%s:\n", __func__);
+ pr_debug("%s:\n", __func__);
spin_lock_irqsave(&isp1362_hcd->lock, flags);
chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID);
@@ -2773,7 +2773,7 @@ static int __devinit isp1362_probe(struct platform_device *pdev)
if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
irq_flags |= IRQF_TRIGGER_LOW;
- retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED);
if (retval != 0)
goto err6;
pr_info("%s, irq %d\n", hcd->product_desc, irq);
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 840beda66dd9..27dfab80ed8f 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -21,8 +21,10 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/mm.h>
+#include <linux/timer.h>
#include <asm/unaligned.h>
#include <asm/cacheflush.h>
+#include <linux/gpio.h>
#include "isp1760-hcd.h"
@@ -39,7 +41,6 @@ struct isp1760_hcd {
int int_done_map;
struct memory_chunk memory_pool[BLOCKS];
struct list_head controlqhs, bulkqhs, interruptqhs;
- int active_ptds;
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024
@@ -48,6 +49,8 @@ struct isp1760_hcd {
unsigned long reset_done;
unsigned long next_statechange;
unsigned int devflags;
+
+ int rst_gpio;
};
static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
@@ -114,6 +117,7 @@ struct isp1760_qh {
u32 toggle;
u32 ping;
int slot;
+ int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */
};
struct urb_listitem {
@@ -432,6 +436,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
int result;
u32 scratch, hwmode;
+ /* low-level chip reset */
+ if (gpio_is_valid(priv->rst_gpio)) {
+ unsigned int rst_lvl;
+
+ rst_lvl = (priv->devflags &
+ ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0;
+
+ gpio_set_value(priv->rst_gpio, rst_lvl);
+ mdelay(50);
+ gpio_set_value(priv->rst_gpio, !rst_lvl);
+ }
+
/* Setup HW Mode Control: This assumes a level active-low interrupt */
hwmode = HW_DATA_BUS_32BIT;
@@ -489,10 +505,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
"analog" : "digital");
- /* This is weird: at the first plug-in of a device there seems to be
- one packet queued that never gets returned? */
- priv->active_ptds = -1;
-
/* ATL reset */
reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
mdelay(10);
@@ -514,83 +526,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
return priv_init(hcd);
}
-static void isp1760_init_maps(struct usb_hcd *hcd)
-{
- /*set last maps, for iso its only 1, else 32 tds bitmap*/
- reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000);
- reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
- reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
-
- reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff);
-
- reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
- ATL_BUF_FILL | INT_BUF_FILL);
-}
-
-static void isp1760_enable_interrupts(struct usb_hcd *hcd)
-{
- reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff);
- reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0);
- reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff);
- /* step 23 passed */
-}
-
-static int isp1760_run(struct usb_hcd *hcd)
-{
- int retval;
- u32 temp;
- u32 command;
- u32 chipid;
-
- hcd->uses_new_polling = 1;
-
- hcd->state = HC_STATE_RUNNING;
- isp1760_enable_interrupts(hcd);
- temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
-
- command = reg_read32(hcd->regs, HC_USBCMD);
- command &= ~(CMD_LRESET|CMD_RESET);
- command |= CMD_RUN;
- reg_write32(hcd->regs, HC_USBCMD, command);
-
- retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000);
- if (retval)
- return retval;
-
- /*
- * XXX
- * Spec says to write FLAG_CF as last config action, priv code grabs
- * the semaphore while doing so.
- */
- down_write(&ehci_cf_port_reset_rwsem);
- reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
-
- retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
- up_write(&ehci_cf_port_reset_rwsem);
- if (retval)
- return retval;
-
- chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
- dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
- chipid & 0xffff, chipid >> 16);
-
- /* PTD Register Init Part 2, Step 28 */
- /* enable INTs */
- isp1760_init_maps(hcd);
-
- /* GRR this is run-once init(), being done every time the HC starts.
- * So long as they're part of class devices, we can't do it init()
- * since the class device isn't created that early.
- */
- return 0;
-}
-
static u32 base_to_chip(u32 base)
{
return ((base - 0x400) >> 3);
@@ -813,28 +748,29 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
WARN_ON(slots[slot].qh);
WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC);
- slots[slot].qtd = qtd;
- slots[slot].qh = qh;
- qh->slot = slot;
- qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since
- interrupt routine may preempt and expects this value. */
- ptd_write(hcd->regs, ptd_offset, slot, ptd);
- priv->active_ptds++;
-
/* Make sure done map has not triggered from some unlinked transfer */
if (ptd_offset == ATL_PTD_OFFSET) {
priv->atl_done_map |= reg_read32(hcd->regs,
HC_ATL_PTD_DONEMAP_REG);
- priv->atl_done_map &= ~(1 << qh->slot);
+ priv->atl_done_map &= ~(1 << slot);
+ } else {
+ priv->int_done_map |= reg_read32(hcd->regs,
+ HC_INT_PTD_DONEMAP_REG);
+ priv->int_done_map &= ~(1 << slot);
+ }
+
+ qh->slot = slot;
+ qtd->status = QTD_XFER_STARTED;
+ slots[slot].timestamp = jiffies;
+ slots[slot].qtd = qtd;
+ slots[slot].qh = qh;
+ ptd_write(hcd->regs, ptd_offset, slot, ptd);
+ if (ptd_offset == ATL_PTD_OFFSET) {
skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
skip_map &= ~(1 << qh->slot);
reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
} else {
- priv->int_done_map |= reg_read32(hcd->regs,
- HC_INT_PTD_DONEMAP_REG);
- priv->int_done_map &= ~(1 << qh->slot);
-
skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
skip_map &= ~(1 << qh->slot);
reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
@@ -858,10 +794,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
if (qtd->status < QTD_XFER_COMPLETE)
break;
- if (list_is_last(&qtd->qtd_list, &qh->qtd_list))
- last_qtd = 1;
- else
- last_qtd = qtd->urb != qtd_next->urb;
+ last_qtd = last_qtd_of_urb(qtd, qh);
if ((!last_qtd) && (qtd->status == QTD_RETIRE))
qtd_next->status = QTD_RETIRE;
@@ -902,7 +835,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
urb_listitem = kmem_cache_zalloc(urb_listitem_cachep,
GFP_ATOMIC);
if (unlikely(!urb_listitem))
- break;
+ break; /* Try again on next call */
urb_listitem->urb = qtd->urb;
list_add_tail(&urb_listitem->urb_list, urb_list);
}
@@ -928,6 +861,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
return;
}
+ /* Make sure this endpoint's TT buffer is clean before queueing ptds */
+ if (qh->tt_buffer_dirty)
+ return;
+
if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd,
qtd_list)->urb->pipe)) {
ptd_offset = INT_PTD_OFFSET;
@@ -1168,11 +1105,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd,
return PTD_STATE_QTD_DONE;
}
-static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
+static void handle_done_ptds(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- u32 imask;
- irqreturn_t irqret = IRQ_NONE;
struct ptd ptd;
struct isp1760_qh *qh;
int slot;
@@ -1181,27 +1116,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
u32 ptd_offset;
struct isp1760_qtd *qtd;
int modified;
- static int last_active_ptds;
- int int_skip_map, atl_skip_map;
-
- spin_lock(&priv->lock);
-
- if (!(hcd->state & HC_STATE_RUNNING))
- goto leave;
-
- imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
- if (unlikely(!imask))
- goto leave;
- reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
+ int skip_map;
- int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
- atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
- priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
- priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
- priv->int_done_map &= ~int_skip_map;
- priv->atl_done_map &= ~atl_skip_map;
+ skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
+ priv->int_done_map &= ~skip_map;
+ skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
+ priv->atl_done_map &= ~skip_map;
- modified = priv->int_done_map | priv->atl_done_map;
+ modified = priv->int_done_map || priv->atl_done_map;
while (priv->int_done_map || priv->atl_done_map) {
if (priv->int_done_map) {
@@ -1240,7 +1162,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
slots[slot].qtd = NULL;
qh = slots[slot].qh;
slots[slot].qh = NULL;
- priv->active_ptds--;
qh->slot = -1;
WARN_ON(qtd->status != QTD_XFER_STARTED);
@@ -1281,6 +1202,15 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
case PTD_STATE_URB_RETIRE:
qtd->status = QTD_RETIRE;
+ if ((qtd->urb->dev->speed != USB_SPEED_HIGH) &&
+ (qtd->urb->status != -EPIPE) &&
+ (qtd->urb->status != -EREMOTEIO)) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(qtd->urb))
+ /* Clear failed; let's hope things work
+ anyway */
+ qh->tt_buffer_dirty = 0;
+ }
qtd = NULL;
qh->toggle = 0;
qh->ping = 0;
@@ -1311,22 +1241,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
if (modified)
schedule_ptds(hcd);
+}
- /* ISP1760 Errata 2 explains that interrupts may be missed (or not
- happen?) if two USB devices are running simultaneously. Perhaps
- this happens when a PTD is finished during interrupt handling;
- enable SOF interrupts if PTDs are still scheduled when exiting this
- interrupt handler, just to be safe. */
+static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ u32 imask;
+ irqreturn_t irqret = IRQ_NONE;
- if (priv->active_ptds != last_active_ptds) {
- if (priv->active_ptds > 0)
- reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
- INTERRUPT_ENABLE_SOT_MASK);
- else
- reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
- INTERRUPT_ENABLE_MASK);
- last_active_ptds = priv->active_ptds;
- }
+ spin_lock(&priv->lock);
+
+ if (!(hcd->state & HC_STATE_RUNNING))
+ goto leave;
+
+ imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
+ if (unlikely(!imask))
+ goto leave;
+ reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
+
+ priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
+ priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
+
+ handle_done_ptds(hcd);
irqret = IRQ_HANDLED;
leave:
@@ -1335,6 +1271,138 @@ leave:
return irqret;
}
+/*
+ * Workaround for problem described in chip errata 2:
+ *
+ * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
+ * One solution suggested in the errata is to use SOF interrupts _instead_of_
+ * ATL done interrupts (the "instead of" might be important since it seems
+ * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
+ * to set the PTD's done bit in addition to not generating an interrupt!).
+ *
+ * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
+ * done bit is not being set. This is bad - it blocks the endpoint until reboot.
+ *
+ * If we use SOF interrupts only, we get latency between ptd completion and the
+ * actual handling. This is very noticeable in testusb runs which takes several
+ * minutes longer without ATL interrupts.
+ *
+ * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
+ * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
+ * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
+ * completed and its done map bit is set.
+ *
+ * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
+ * not to cause too much lag when this HW bug occurs, while still hopefully
+ * ensuring that the check does not falsely trigger.
+ */
+#define SLOT_TIMEOUT 300
+#define SLOT_CHECK_PERIOD 200
+static struct timer_list errata2_timer;
+
+void errata2_function(unsigned long data)
+{
+ struct usb_hcd *hcd = (struct usb_hcd *) data;
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ int slot;
+ struct ptd ptd;
+ unsigned long spinflags;
+
+ spin_lock_irqsave(&priv->lock, spinflags);
+
+ for (slot = 0; slot < 32; slot++)
+ if (priv->atl_slots[slot].qh && time_after(jiffies,
+ priv->atl_slots[slot].timestamp +
+ SLOT_TIMEOUT * HZ / 1000)) {
+ ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ if (!FROM_DW0_VALID(ptd.dw0) &&
+ !FROM_DW3_ACTIVE(ptd.dw3))
+ priv->atl_done_map |= 1 << slot;
+ }
+
+ if (priv->atl_done_map)
+ handle_done_ptds(hcd);
+
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+
+ errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ add_timer(&errata2_timer);
+}
+
+static int isp1760_run(struct usb_hcd *hcd)
+{
+ int retval;
+ u32 temp;
+ u32 command;
+ u32 chipid;
+
+ hcd->uses_new_polling = 1;
+
+ hcd->state = HC_STATE_RUNNING;
+
+ /* Set PTD interrupt AND & OR maps */
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff);
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff);
+ reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff);
+ /* step 23 passed */
+
+ temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
+
+ command = reg_read32(hcd->regs, HC_USBCMD);
+ command &= ~(CMD_LRESET|CMD_RESET);
+ command |= CMD_RUN;
+ reg_write32(hcd->regs, HC_USBCMD, command);
+
+ retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000);
+ if (retval)
+ return retval;
+
+ /*
+ * XXX
+ * Spec says to write FLAG_CF as last config action, priv code grabs
+ * the semaphore while doing so.
+ */
+ down_write(&ehci_cf_port_reset_rwsem);
+ reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
+
+ retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
+ up_write(&ehci_cf_port_reset_rwsem);
+ if (retval)
+ return retval;
+
+ init_timer(&errata2_timer);
+ errata2_timer.function = errata2_function;
+ errata2_timer.data = (unsigned long) hcd;
+ errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ add_timer(&errata2_timer);
+
+ chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
+ dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
+ chipid & 0xffff, chipid >> 16);
+
+ /* PTD Register Init Part 2, Step 28 */
+
+ /* Setup registers controlling PTD checking */
+ reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000);
+ reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
+ reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
+ reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff);
+ reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff);
+ reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff);
+ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
+ ATL_BUF_FILL | INT_BUF_FILL);
+
+ /* GRR this is run-once init(), being done every time the HC starts.
+ * So long as they're part of class devices, we can't do it init()
+ * since the class device isn't created that early.
+ */
+ return 0;
+}
+
static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
{
qtd->data_buffer = databuffer;
@@ -1503,7 +1571,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
packetize_urb(hcd, urb, &new_qtds, mem_flags);
if (list_empty(&new_qtds))
return -ENOMEM;
- urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */
retval = 0;
spin_lock_irqsave(&priv->lock, spinflags);
@@ -1531,6 +1598,7 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
qh = qh_alloc(GFP_ATOMIC);
if (!qh) {
retval = -ENOMEM;
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
goto out;
}
list_add_tail(&qh->qh_list, ep_queue);
@@ -1570,7 +1638,41 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
}
qh->slot = -1;
- priv->active_ptds--;
+}
+
+/*
+ * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing
+ * any active transfer belonging to the urb in the process.
+ */
+static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd)
+{
+ struct urb *urb;
+ int urb_was_running;
+
+ urb = qtd->urb;
+ urb_was_running = 0;
+ list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) {
+ if (qtd->urb != urb)
+ break;
+
+ if (qtd->status >= QTD_XFER_STARTED)
+ urb_was_running = 1;
+ if (last_qtd_of_urb(qtd, qh) &&
+ (qtd->status >= QTD_XFER_COMPLETE))
+ urb_was_running = 0;
+
+ if (qtd->status == QTD_XFER_STARTED)
+ kill_transfer(hcd, urb, qh);
+ qtd->status = QTD_RETIRE;
+ }
+
+ if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(urb))
+ /* Clear failed; let's hope things work anyway */
+ qh->tt_buffer_dirty = 0;
+ }
}
static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
@@ -1595,9 +1697,8 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
if (qtd->urb == urb) {
- if (qtd->status == QTD_XFER_STARTED)
- kill_transfer(hcd, urb, qh);
- qtd->status = QTD_RETIRE;
+ dequeue_urb_from_qtd(hcd, qh, qtd);
+ break;
}
urb->status = status;
@@ -1622,12 +1723,11 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
if (!qh)
goto out;
- list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
- if (qtd->status == QTD_XFER_STARTED)
- kill_transfer(hcd, qtd->urb, qh);
- qtd->status = QTD_RETIRE;
- qtd->urb->status = -ECONNRESET;
- }
+ list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
+ if (qtd->status != QTD_RETIRE) {
+ dequeue_urb_from_qtd(hcd, qh, qtd);
+ qtd->urb->status = -ECONNRESET;
+ }
ep->hcpriv = NULL;
/* Cannot free qh here since it will be parsed by schedule_ptds() */
@@ -2021,6 +2121,8 @@ static void isp1760_stop(struct usb_hcd *hcd)
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 temp;
+ del_timer(&errata2_timer);
+
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
mdelay(20);
@@ -2048,6 +2150,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd)
reg_write32(hcd->regs, HC_USBCMD, command);
}
+static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ struct isp1760_qh *qh = ep->hcpriv;
+ unsigned long spinflags;
+
+ if (!qh)
+ return;
+
+ spin_lock_irqsave(&priv->lock, spinflags);
+ qh->tt_buffer_dirty = 0;
+ schedule_ptds(hcd);
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+}
+
+
static const struct hc_driver isp1760_hc_driver = {
.description = "isp1760-hcd",
.product_desc = "NXP ISP1760 USB Host Controller",
@@ -2064,6 +2183,7 @@ static const struct hc_driver isp1760_hc_driver = {
.get_frame_number = isp1760_get_frame,
.hub_status_data = isp1760_hub_status_data,
.hub_control = isp1760_hub_control,
+ .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete,
};
int __init init_kmem_once(void)
@@ -2102,6 +2222,7 @@ void deinit_kmem_cache(void)
struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
int irq, unsigned long irqflags,
+ int rst_gpio,
struct device *dev, const char *busname,
unsigned int devflags)
{
@@ -2121,6 +2242,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
priv = hcd_to_priv(hcd);
priv->devflags = devflags;
+ priv->rst_gpio = rst_gpio;
init_memory(priv);
hcd->regs = ioremap(res_start, res_len);
if (!hcd->regs) {
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
index 014a7dfadf91..33dc79ccaa6b 100644
--- a/drivers/usb/host/isp1760-hcd.h
+++ b/drivers/usb/host/isp1760-hcd.h
@@ -4,6 +4,7 @@
/* exports for if */
struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
int irq, unsigned long irqflags,
+ int rst_gpio,
struct device *dev, const char *busname,
unsigned int devflags);
int init_kmem_once(void);
@@ -73,7 +74,6 @@ void deinit_kmem_cache(void);
#define HC_EOT_INT (1 << 3)
#define HC_SOT_INT (1 << 1)
#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT)
-#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT)
#define HC_ISO_IRQ_MASK_OR_REG 0x318
#define HC_INT_IRQ_MASK_OR_REG 0x31C
@@ -107,6 +107,7 @@ struct ptd {
struct slotinfo {
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
+ unsigned long timestamp;
};
@@ -126,6 +127,7 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
+#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */
/* chip memory management */
struct memory_chunk {
@@ -188,6 +190,7 @@ struct memory_chunk {
#define DW3_BABBLE_BIT (1 << 29)
#define DW3_HALT_BIT (1 << 30)
#define DW3_ACTIVE_BIT (1 << 31)
+#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01)
#define INT_UNDERRUN (1 << 2)
#define INT_BABBLE (1 << 1)
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
index 7ee30056f373..2c7fc830c9e4 100644
--- a/drivers/usb/host/isp1760-if.c
+++ b/drivers/usb/host/isp1760-if.c
@@ -17,19 +17,28 @@
#include "isp1760-hcd.h"
-#ifdef CONFIG_PPC_OF
+#ifdef CONFIG_OF
+#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
#endif
#ifdef CONFIG_PCI
#include <linux/pci.h>
#endif
-#ifdef CONFIG_PPC_OF
+#ifdef CONFIG_OF
+struct isp1760 {
+ struct usb_hcd *hcd;
+ int rst_gpio;
+};
+
static int of_isp1760_probe(struct platform_device *dev)
{
- struct usb_hcd *hcd;
+ struct isp1760 *drvdata;
struct device_node *dp = dev->dev.of_node;
struct resource *res;
struct resource memory;
@@ -39,6 +48,11 @@ static int of_isp1760_probe(struct platform_device *dev)
int ret;
const unsigned int *prop;
unsigned int devflags = 0;
+ enum of_gpio_flags gpio_flags;
+
+ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
ret = of_address_to_resource(dp, 0, &memory);
if (ret)
@@ -78,32 +92,57 @@ static int of_isp1760_probe(struct platform_device *dev)
if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
- hcd = isp1760_register(memory.start, res_len, virq,
- IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
- devflags);
- if (IS_ERR(hcd)) {
- ret = PTR_ERR(hcd);
- goto release_reg;
+ drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
+ if (gpio_is_valid(drvdata->rst_gpio)) {
+ ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
+ if (!ret) {
+ if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
+ devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
+ gpio_direction_output(drvdata->rst_gpio, 0);
+ } else {
+ gpio_direction_output(drvdata->rst_gpio, 1);
+ }
+ } else {
+ drvdata->rst_gpio = ret;
+ }
}
- dev_set_drvdata(&dev->dev, hcd);
+ drvdata->hcd = isp1760_register(memory.start, res_len, virq,
+ IRQF_SHARED, drvdata->rst_gpio,
+ &dev->dev, dev_name(&dev->dev),
+ devflags);
+ if (IS_ERR(drvdata->hcd)) {
+ ret = PTR_ERR(drvdata->hcd);
+ goto free_gpio;
+ }
+
+ dev_set_drvdata(&dev->dev, drvdata);
return ret;
+free_gpio:
+ if (gpio_is_valid(drvdata->rst_gpio))
+ gpio_free(drvdata->rst_gpio);
release_reg:
release_mem_region(memory.start, res_len);
+ kfree(drvdata);
return ret;
}
static int of_isp1760_remove(struct platform_device *dev)
{
- struct usb_hcd *hcd = dev_get_drvdata(&dev->dev);
+ struct isp1760 *drvdata = dev_get_drvdata(&dev->dev);
dev_set_drvdata(&dev->dev, NULL);
- usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- usb_put_hcd(hcd);
+ usb_remove_hcd(drvdata->hcd);
+ iounmap(drvdata->hcd->regs);
+ release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
+ usb_put_hcd(drvdata->hcd);
+
+ if (gpio_is_valid(drvdata->rst_gpio))
+ gpio_free(drvdata->rst_gpio);
+
+ kfree(drvdata);
return 0;
}
@@ -240,7 +279,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
dev->dev.dma_mask = NULL;
hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
- IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
+ IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
devflags);
if (IS_ERR(hcd)) {
ret_status = -ENODEV;
@@ -313,7 +352,7 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev)
resource_size_t mem_size;
struct isp1760_platform_data *priv = pdev->dev.platform_data;
unsigned int devflags = 0;
- unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED;
+ unsigned long irqflags = IRQF_SHARED;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
@@ -351,7 +390,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev)
}
hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
- irqflags, &pdev->dev, dev_name(&pdev->dev), devflags);
+ irqflags, -ENOENT,
+ &pdev->dev, dev_name(&pdev->dev), devflags);
if (IS_ERR(hcd)) {
pr_warning("isp1760: Failed to register the HCD device\n");
ret = -ENODEV;
@@ -396,7 +436,7 @@ static int __init isp1760_init(void)
ret = platform_driver_register(&isp1760_plat_driver);
if (!ret)
any_ret = 0;
-#ifdef CONFIG_PPC_OF
+#ifdef CONFIG_OF
ret = platform_driver_register(&isp1760_of_driver);
if (!ret)
any_ret = 0;
@@ -416,7 +456,7 @@ module_init(isp1760_init);
static void __exit isp1760_exit(void)
{
platform_driver_unregister(&isp1760_plat_driver);
-#ifdef CONFIG_PPC_OF
+#ifdef CONFIG_OF
platform_driver_unregister(&isp1760_of_driver);
#endif
#ifdef CONFIG_PCI
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 944291e10f97..ba3a46b78b75 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -35,8 +35,7 @@ extern int usb_disabled(void);
static void at91_start_clock(void)
{
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_enable(hclk);
+ clk_enable(hclk);
clk_enable(iclk);
clk_enable(fclk);
clocked = 1;
@@ -46,8 +45,7 @@ static void at91_stop_clock(void)
{
clk_disable(fclk);
clk_disable(iclk);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_disable(hclk);
+ clk_disable(hclk);
clocked = 0;
}
@@ -142,8 +140,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
iclk = clk_get(&pdev->dev, "ohci_clk");
fclk = clk_get(&pdev->dev, "uhpck");
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- hclk = clk_get(&pdev->dev, "hck0");
+ hclk = clk_get(&pdev->dev, "hclk");
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
@@ -155,8 +152,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
/* Error handling */
at91_stop_hc(pdev);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_put(hclk);
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
@@ -192,8 +188,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
- clk_put(hclk);
+ clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
fclk = iclk = hclk = NULL;
@@ -223,6 +218,156 @@ ohci_at91_start (struct usb_hcd *hcd)
return 0;
}
+static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
+{
+ if (port < 0 || port >= 2)
+ return;
+
+ gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable);
+}
+
+static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
+{
+ if (port < 0 || port >= 2)
+ return -EINVAL;
+
+ return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted;
+}
+
+/*
+ * Update the status data from the hub with the over-current indicator change.
+ */
+static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+ int length = ohci_hub_status_data(hcd, buf);
+ int port;
+
+ for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) {
+ if (pdata->overcurrent_changed[port]) {
+ if (! length)
+ length = 1;
+ buf[0] |= 1 << (port + 1);
+ }
+ }
+
+ return length;
+}
+
+/*
+ * Look at the control requests to the root hub and see if we need to override.
+ */
+static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+ struct usb_hub_descriptor *desc;
+ int ret = -EINVAL;
+ u32 *data = (u32 *)buf;
+
+ dev_dbg(hcd->self.controller,
+ "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n",
+ hcd, typeReq, wValue, wIndex, buf, wLength);
+
+ switch (typeReq) {
+ case SetPortFeature:
+ if (wValue == USB_PORT_FEAT_POWER) {
+ dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
+ ohci_at91_usb_set_power(pdata, wIndex - 1, 1);
+ goto out;
+ }
+ break;
+
+ case ClearPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: C_OVER_CURRENT\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ pdata->overcurrent_changed[wIndex-1] = 0;
+ pdata->overcurrent_status[wIndex-1] = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_OVER_CURRENT:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: OVER_CURRENT\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ pdata->overcurrent_status[wIndex-1] = 0;
+ }
+
+ goto out;
+
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(hcd->self.controller,
+ "ClearPortFeature: POWER\n");
+
+ if (wIndex == 1 || wIndex == 2) {
+ ohci_at91_usb_set_power(pdata, wIndex - 1, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ if (ret)
+ goto out;
+
+ switch (typeReq) {
+ case GetHubDescriptor:
+
+ /* update the hub's descriptor */
+
+ desc = (struct usb_hub_descriptor *)buf;
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ /* remove the old configurations for power-switching, and
+ * over-current protection, and insert our new configuration
+ */
+
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
+ desc->wHubCharacteristics |= cpu_to_le16(0x0001);
+
+ if (pdata->overcurrent_supported) {
+ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
+ desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001);
+ }
+
+ dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
+ desc->wHubCharacteristics);
+
+ return ret;
+
+ case GetPortStatus:
+ /* check port status */
+
+ dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex);
+
+ if (wIndex == 1 || wIndex == 2) {
+ if (! ohci_at91_usb_get_power(pdata, wIndex-1)) {
+ *data &= ~cpu_to_le32(RH_PS_PPS);
+ }
+
+ if (pdata->overcurrent_changed[wIndex-1]) {
+ *data |= cpu_to_le32(RH_PS_OCIC);
+ }
+
+ if (pdata->overcurrent_status[wIndex-1]) {
+ *data |= cpu_to_le32(RH_PS_POCI);
+ }
+ }
+ }
+
+ out:
+ return ret;
+}
+
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_at91_hc_driver = {
@@ -258,8 +403,8 @@ static const struct hc_driver ohci_at91_hc_driver = {
/*
* root hub support
*/
- .hub_status_data = ohci_hub_status_data,
- .hub_control = ohci_hub_control,
+ .hub_status_data = ohci_at91_hub_status_data,
+ .hub_control = ohci_at91_hub_control,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -269,22 +414,71 @@ static const struct hc_driver ohci_at91_hc_driver = {
/*-------------------------------------------------------------------------*/
+static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct at91_usbh_data *pdata = pdev->dev.platform_data;
+ int val, gpio, port;
+
+ /* From the GPIO notifying the over-current situation, find
+ * out the corresponding port */
+ gpio = irq_to_gpio(irq);
+ for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) {
+ if (pdata->overcurrent_pin[port] == gpio)
+ break;
+ }
+
+ if (port == ARRAY_SIZE(pdata->overcurrent_pin)) {
+ dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n");
+ return IRQ_HANDLED;
+ }
+
+ val = gpio_get_value(gpio);
+
+ /* When notified of an over-current situation, disable power
+ on the corresponding port, and mark this port in
+ over-current. */
+ if (! val) {
+ ohci_at91_usb_set_power(pdata, port, 0);
+ pdata->overcurrent_status[port] = 1;
+ pdata->overcurrent_changed[port] = 1;
+ }
+
+ dev_dbg(& pdev->dev, "overcurrent situation %s\n",
+ val ? "exited" : "notified");
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
{
struct at91_usbh_data *pdata = pdev->dev.platform_data;
int i;
if (pdata) {
- /* REVISIT make the driver support per-port power switching,
- * and also overcurrent detection. Here we assume the ports
- * are always powered while this driver is active, and use
- * active-low power switches.
- */
for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) {
if (pdata->vbus_pin[i] <= 0)
continue;
gpio_request(pdata->vbus_pin[i], "ohci_vbus");
- gpio_direction_output(pdata->vbus_pin[i], 0);
+ ohci_at91_usb_set_power(pdata, i, 1);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) {
+ int ret;
+
+ if (pdata->overcurrent_pin[i] <= 0)
+ continue;
+ gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent");
+
+ ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]),
+ ohci_hcd_at91_overcurrent_irq,
+ IRQF_SHARED, "ohci_overcurrent", pdev);
+ if (ret) {
+ gpio_free(pdata->overcurrent_pin[i]);
+ dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n");
+ }
}
}
@@ -301,9 +495,16 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) {
if (pdata->vbus_pin[i] <= 0)
continue;
- gpio_direction_output(pdata->vbus_pin[i], 1);
+ ohci_at91_usb_set_power(pdata, i, 0);
gpio_free(pdata->vbus_pin[i]);
}
+
+ for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) {
+ if (pdata->overcurrent_pin[i] <= 0)
+ continue;
+ free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev);
+ gpio_free(pdata->overcurrent_pin[i]);
+ }
}
device_init_wakeup(&pdev->dev, 0);
diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c
index c620c50f6770..18d574d6958b 100644
--- a/drivers/usb/host/ohci-ath79.c
+++ b/drivers/usb/host/ohci-ath79.c
@@ -111,7 +111,7 @@ static int ohci_ath79_probe(struct platform_device *pdev)
ohci_hcd_init(hcd_to_ohci(hcd));
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret)
goto err_stop_hcd;
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 299d719495f1..9b66df8278f3 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -129,7 +129,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
ohci_hcd_init(hcd_to_ohci(hcd));
ret = usb_add_hcd(hcd, pdev->resource[1].start,
- IRQF_DISABLED | IRQF_SHARED);
+ IRQF_SHARED);
if (ret == 0) {
platform_set_drvdata(pdev, hcd);
return ret;
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index 6aca2c4453f7..843509778a33 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -344,7 +344,7 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
error = -ENODEV;
goto err4;
}
- error = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ error = usb_add_hcd(hcd, irq, 0);
if (error)
goto err4;
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 4e681613e7ae..dc45d489d00e 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -81,7 +81,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver,
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, pdev->resource[1].start, 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index f9cf3f04b742..34efd479e068 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_hcd_ath79_driver
#endif
+#ifdef CONFIG_NLM_XLR
+#include "ohci-xls.c"
+#define PLATFORM_DRIVER ohci_xls_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OMAP1_PLATFORM_DRIVER) && \
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 9154615292db..2f00040fc408 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -356,10 +356,7 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd)
msleep(20);
}
- /* Does the root hub have a port wakeup pending? */
- if (ohci_readl(ohci, &ohci->regs->intrstatus) &
- (OHCI_INTR_RD | OHCI_INTR_RHSC))
- usb_hcd_resume_root_hub(hcd);
+ usb_hcd_resume_root_hub(hcd);
}
/* Carry out polling-, autostop-, and autoresume-related state changes */
diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c
index d8b45647d1dc..d469bf9b9e54 100644
--- a/drivers/usb/host/ohci-octeon.c
+++ b/drivers/usb/host/ohci-octeon.c
@@ -164,7 +164,7 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev)
ohci_hcd_init(ohci);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret) {
dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
goto err3;
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 5645f70b9214..e4b8782cc6e2 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -14,7 +14,7 @@
* This file is licenced under the GPL.
*/
-#include <linux/signal.h> /* IRQF_DISABLED */
+#include <linux/signal.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -363,7 +363,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
retval = -ENXIO;
goto err3;
}
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, irq, 0);
if (retval)
goto err3;
diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c
index 6048f2f64f73..516ebc4d6cc2 100644
--- a/drivers/usb/host/ohci-omap3.c
+++ b/drivers/usb/host/ohci-omap3.c
@@ -149,7 +149,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "ohci");
- if (!ret) {
+ if (!res) {
dev_err(dev, "UHH OHCI get resource failed\n");
return -ENOMEM;
}
@@ -180,7 +180,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev)
ohci_hcd_init(hcd_to_ohci(hcd));
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret) {
dev_dbg(dev, "failed to add hcd with err %d\n", ret);
goto err_add_hcd;
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index 653d6a60edb5..0013db7bdf92 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -26,7 +26,7 @@
#include <mach/platform.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#define USB_CTRL IO_ADDRESS(PNX4008_PWRMAN_BASE + 0x64)
@@ -398,7 +398,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
ohci_hcd_init(ohci);
dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret == 0)
return ret;
diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c
index 28467e288a93..f13d08f94d6b 100644
--- a/drivers/usb/host/ohci-pnx8550.c
+++ b/drivers/usb/host/ohci-pnx8550.c
@@ -107,7 +107,7 @@ int usb_hcd_pnx8550_probe (const struct hc_driver *driver,
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, dev->resource[1].start, 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 0c12f4e14dcd..d24cc89de16f 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -143,7 +143,7 @@ static int __devinit ohci_hcd_ppc_of_probe(struct platform_device *op)
ohci_hcd_init(ohci);
- rv = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ rv = usb_add_hcd(hcd, irq, 0);
if (rv == 0)
return 0;
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
index c0f595c44487..1514b7067470 100644
--- a/drivers/usb/host/ohci-ppc-soc.c
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -80,7 +80,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
#endif
ohci_hcd_init(ohci);
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, irq, 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index 700950455f4d..6fd4fa1f19bb 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -164,7 +164,7 @@ static int __devinit ps3_ohci_probe(struct ps3_system_bus_device *dev)
ps3_system_bus_set_drvdata(dev, hcd);
- result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
+ result = usb_add_hcd(hcd, virq, 0);
if (result) {
dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 80be5472783a..29dfefe1c726 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -359,7 +359,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, irq, 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index dd24fc115e48..15dc51ded61a 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -428,7 +428,7 @@ static struct ed *ed_get (
ed->type = usb_pipetype(pipe);
info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7;
- info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16;
+ info |= usb_endpoint_maxp(&ep->desc) << 16;
if (udev->speed == USB_SPEED_LOW)
info |= ED_LOWSPEED;
/* only control transfers store pids in tds */
@@ -444,7 +444,7 @@ static struct ed *ed_get (
ed->load = usb_calc_bus_time (
udev->speed, !is_out,
ed->type == PIPE_ISOCHRONOUS,
- le16_to_cpu(ep->desc.wMaxPacketSize))
+ usb_endpoint_maxp(&ep->desc))
/ 1000;
}
}
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index 7c9a4d55526b..a1877c47601e 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -384,7 +384,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, dev->resource[1].start, 0);
if (retval != 0)
goto err_ioremap;
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 4204d9720d23..4bde4f9821ba 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -143,7 +143,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver,
sa1111_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, dev->irq[1], IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, dev->irq[1], 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c
index 14cecb52a9fe..afc4eb6bb9d0 100644
--- a/drivers/usb/host/ohci-sh.c
+++ b/drivers/usb/host/ohci-sh.c
@@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev)
hcd->regs = (void __iomem *)res->start;
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret != 0) {
err("Failed to add hcd");
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index 78918ca0da23..968cea2b6d4e 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -165,7 +165,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval)
goto err5;
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index 4fd4bea9ac7a..69874654f3b5 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -152,7 +152,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
spear_start_ohci(ohci_p);
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
index c4aea3b8315e..5ba18595d6f7 100644
--- a/drivers/usb/host/ohci-ssb.c
+++ b/drivers/usb/host/ohci-ssb.c
@@ -169,7 +169,7 @@ static int ssb_ohci_attach(struct ssb_device *dev)
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs)
goto err_put_hcd;
- err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
+ err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
if (err)
goto err_iounmap;
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
index 57ad1271fc9b..06331d931171 100644
--- a/drivers/usb/host/ohci-tmio.c
+++ b/drivers/usb/host/ohci-tmio.c
@@ -244,7 +244,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev)
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret)
goto err_add_hcd;
diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c
new file mode 100644
index 000000000000..a3a9c6f45b91
--- /dev/null
+++ b/drivers/usb/host/ohci-xls.c
@@ -0,0 +1,151 @@
+/*
+ * OHCI HCD for Netlogic XLS processors.
+ *
+ * (C) Copyright 2011 Netlogic Microsystems Inc.
+ *
+ * Based on ohci-au1xxx.c, and other Linux OHCI drivers.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+
+static int ohci_xls_probe_internal(const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ struct resource *res;
+ struct usb_hcd *hcd;
+ int retval, irq;
+
+ /* Get our IRQ from an earlier registered Platform Resource */
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ dev_err(&dev->dev, "Found HC with no IRQ\n");
+ return -ENODEV;
+ }
+
+ /* Get our Memory Handle */
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&dev->dev, "MMIO Handle incorrect!\n");
+ return -ENODEV;
+ }
+
+ hcd = usb_create_hcd(driver, &dev->dev, "XLS");
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&dev->dev, "Controller already in use\n");
+ retval = -EBUSY;
+ goto err2;
+ }
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_dbg(&dev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err3;
+ }
+
+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (retval != 0)
+ goto err4;
+ return retval;
+
+err4:
+ iounmap(hcd->regs);
+err3:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err2:
+ usb_put_hcd(hcd);
+err1:
+ dev_err(&dev->dev, "init fail, %d\n", retval);
+ return retval;
+}
+
+static int ohci_xls_reset(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci_hcd_init(ohci);
+ return ohci_init(ohci);
+}
+
+static int __devinit ohci_xls_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci;
+ int ret;
+
+ ohci = hcd_to_ohci(hcd);
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ err("can't start %s", hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+ return 0;
+}
+
+static struct hc_driver ohci_xls_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "XLS OHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+ .reset = ohci_xls_reset,
+ .start = ohci_xls_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+ .get_frame_number = ohci_get_frame,
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_xls_probe(struct platform_device *dev)
+{
+ int ret;
+
+ pr_debug("In ohci_xls_probe");
+ if (usb_disabled())
+ return -ENODEV;
+ ret = ohci_xls_probe_internal(&ohci_xls_hc_driver, dev);
+ return ret;
+}
+
+static int ohci_xls_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static struct platform_driver ohci_xls_driver = {
+ .probe = ohci_xls_probe,
+ .remove = ohci_xls_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ohci-xls-0",
+ .owner = THIS_MODULE,
+ },
+};
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 40a0d8b03ad7..e84ca1928dbf 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -959,7 +959,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
info.pipenum = get_empty_pipenum(r8a66597, ep);
info.address = get_urb_to_r8a66597_addr(r8a66597, urb);
info.epnum = usb_endpoint_num(ep);
- info.maxpacket = le16_to_cpu(ep->wMaxPacketSize);
+ info.maxpacket = usb_endpoint_maxp(ep);
info.type = get_r8a66597_type(usb_endpoint_type(ep));
info.bufnum = get_bufnum(info.pipenum);
info.buf_bsize = get_buf_bsize(info.pipenum);
@@ -2519,7 +2519,7 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->has_tt = 1;
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
+ ret = usb_add_hcd(hcd, irq, irq_trigger);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to add hcd\n");
goto clean_up3;
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 1a996245ab98..961d6638d8f9 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1729,7 +1729,7 @@ sl811h_probe(struct platform_device *dev)
* Use resource IRQ flags if set by platform device setup.
*/
irqflags |= IRQF_SHARED;
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags);
+ retval = usb_add_hcd(hcd, irq, irqflags);
if (retval != 0)
goto err6;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index fba99b120588..c8ae199cfbb8 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -294,50 +294,50 @@ __acquires(uhci->lock)
* and that remote wakeups should be enabled.
*/
egsm_enable = USBCMD_EGSM;
- uhci->RD_enable = 1;
int_enable = USBINTR_RESUME;
wakeup_enable = 1;
- /* In auto-stop mode wakeups must always be detected, but
- * Resume-Detect interrupts may be prohibited. (In the absence
- * of CONFIG_PM, they are always disallowed.)
+ /*
+ * In auto-stop mode, we must be able to detect new connections.
+ * The user can force us to poll by disabling remote wakeup;
+ * otherwise we will use the EGSM/RD mechanism.
*/
if (auto_stop) {
if (!device_may_wakeup(&rhdev->dev))
- int_enable = 0;
+ egsm_enable = int_enable = 0;
+ }
- /* In bus-suspend mode wakeups may be disabled, but if they are
- * allowed then so are Resume-Detect interrupts.
- */
- } else {
#ifdef CONFIG_PM
+ /*
+ * In bus-suspend mode, we use the wakeup setting specified
+ * for the root hub.
+ */
+ else {
if (!rhdev->do_remote_wakeup)
wakeup_enable = 0;
-#endif
}
+#endif
- /* EGSM causes the root hub to echo a 'K' signal (resume) out any
- * port which requests a remote wakeup. According to the USB spec,
- * every hub is supposed to do this. But if we are ignoring
- * remote-wakeup requests anyway then there's no point to it.
- * We also shouldn't enable EGSM if it's broken.
- */
- if (!wakeup_enable || global_suspend_mode_is_broken(uhci))
- egsm_enable = 0;
-
- /* If we're ignoring wakeup events then there's no reason to
- * enable Resume-Detect interrupts. We also shouldn't enable
- * them if they are broken or disallowed.
+ /*
+ * UHCI doesn't distinguish between wakeup requests from downstream
+ * devices and local connect/disconnect events. There's no way to
+ * enable one without the other; both are controlled by EGSM. Thus
+ * if wakeups are disallowed then EGSM must be turned off -- in which
+ * case remote wakeup requests from downstream during system sleep
+ * will be lost.
+ *
+ * In addition, if EGSM is broken then we can't use it. Likewise,
+ * if Resume-Detect interrupts are broken then we can't use them.
*
- * This logic may lead us to enabling RD but not EGSM. The UHCI
- * spec foolishly says that RD works only when EGSM is on, but
- * there's no harm in enabling it anyway -- perhaps some chips
- * will implement it!
+ * Finally, neither EGSM nor RD is useful by itself. Without EGSM,
+ * the RD status bit will never get set. Without RD, the controller
+ * won't generate interrupts to tell the system about wakeup events.
*/
- if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) ||
- !int_enable)
- uhci->RD_enable = int_enable = 0;
+ if (!wakeup_enable || global_suspend_mode_is_broken(uhci) ||
+ resume_detect_interrupts_are_broken(uhci))
+ egsm_enable = int_enable = 0;
+ uhci->RD_enable = !!int_enable;
uhci_writew(uhci, int_enable, USBINTR);
uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD);
mb();
@@ -364,10 +364,12 @@ __acquires(uhci->lock)
uhci->rh_state = new_state;
uhci->is_stopped = UHCI_IS_STOPPED;
- /* If interrupts don't work and remote wakeup is enabled then
- * the suspended root hub needs to be polled.
+ /*
+ * If remote wakeup is enabled but either EGSM or RD interrupts
+ * doesn't work, then we won't get an interrupt when a wakeup event
+ * occurs. Thus the suspended root hub needs to be polled.
*/
- if (!int_enable && wakeup_enable)
+ if (wakeup_enable && (!int_enable || !egsm_enable))
set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
else
clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 84ed28b34f93..f6ca80ee4cec 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -280,7 +280,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
qh->load = usb_calc_bus_time(udev->speed,
usb_endpoint_dir_in(&hep->desc),
qh->type == USB_ENDPOINT_XFER_ISOC,
- le16_to_cpu(hep->desc.wMaxPacketSize))
+ usb_endpoint_maxp(&hep->desc))
/ 1000 + 1;
} else { /* Skeleton QH */
@@ -792,7 +792,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
{
struct uhci_td *td;
unsigned long destination, status;
- int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
+ int maxsze = usb_endpoint_maxp(&qh->hep->desc);
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
__hc32 *plink;
@@ -918,7 +918,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
{
struct uhci_td *td;
unsigned long destination, status;
- int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
+ int maxsze = usb_endpoint_maxp(&qh->hep->desc);
int len = urb->transfer_buffer_length;
int this_sg_len;
dma_addr_t data;
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index ce5c9e51748e..c7f33123d4c0 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -65,6 +65,12 @@
/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17))
+/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */
+#define XHCI_L1C (1 << 16)
+
+/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
+#define XHCI_HLC (1 << 19)
+
/* command register values to disable interrupts and halt the HC */
/* start/stop HC execution - do not write unless HC is halted*/
#define XHCI_CMD_RUN (1 << 0)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 723f8231193d..431efe72b1f7 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -28,6 +28,25 @@
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
PORT_RC | PORT_PLC | PORT_PE)
+/* usb 1.1 root hub device descriptor */
+static u8 usb_bos_descriptor [] = {
+ USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */
+ USB_DT_BOS, /* __u8 bDescriptorType */
+ 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */
+ 0x1, /* __u8 bNumDeviceCaps */
+ /* First device capability */
+ USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */
+ USB_DT_DEVICE_CAPABILITY, /* Device Capability */
+ USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */
+ 0x00, /* bmAttributes, LTM off by default */
+ USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */
+ 0x03, /* bFunctionalitySupport,
+ USB 3.0 speed only */
+ 0x00, /* bU1DevExitLat, set later. */
+ 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */
+};
+
+
static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
struct usb_hub_descriptor *desc, int ports)
{
@@ -232,7 +251,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
continue;
speed = xhci->devs[i]->udev->speed;
if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3))
- && xhci->devs[i]->port == port) {
+ && xhci->devs[i]->fake_port == port) {
slot_id = i;
break;
}
@@ -392,13 +411,39 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array)
return max_ports;
}
+void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 link_state)
+{
+ u32 temp;
+
+ temp = xhci_readl(xhci, port_array[port_id]);
+ temp = xhci_port_state_to_neutral(temp);
+ temp &= ~PORT_PLS_MASK;
+ temp |= PORT_LINK_STROBE | link_state;
+ xhci_writel(xhci, temp, port_array[port_id]);
+}
+
+/* Test and clear port RWC bit */
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 port_bit)
+{
+ u32 temp;
+
+ temp = xhci_readl(xhci, port_array[port_id]);
+ if (temp & port_bit) {
+ temp = xhci_port_state_to_neutral(temp);
+ temp |= port_bit;
+ xhci_writel(xhci, temp, port_array[port_id]);
+ }
+}
+
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports;
unsigned long flags;
- u32 temp, temp1, status;
+ u32 temp, status;
int retval = 0;
__le32 __iomem **port_array;
int slot_id;
@@ -429,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_hub_descriptor(hcd, xhci,
(struct usb_hub_descriptor *) buf);
break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ if ((wValue & 0xff00) != (USB_DT_BOS << 8))
+ goto error;
+
+ if (hcd->speed != HCD_USB3)
+ goto error;
+
+ memcpy(buf, &usb_bos_descriptor,
+ USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+ buf[12] = HCS_U1_LATENCY(temp);
+ put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
case GetPortStatus:
if (!wIndex || wIndex > max_ports)
goto error;
@@ -472,11 +532,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "Resume USB2 port %d\n",
wIndex + 1);
bus_state->resume_done[wIndex] = 0;
- temp1 = xhci_port_state_to_neutral(temp);
- temp1 &= ~PORT_PLS_MASK;
- temp1 |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp1, port_array[wIndex]);
-
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
xhci_dbg(xhci, "set port %d resume\n",
wIndex + 1);
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
@@ -551,10 +608,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
temp = xhci_readl(xhci, port_array[wIndex]);
+ if ((temp & PORT_PLS_MASK) != XDEV_U0) {
+ /* Resume the port to U0 first */
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&xhci->lock, flags);
+ }
/* In spec software should not attempt to suspend
* a port unless the port reports that it is in the
* enabled (PED = ‘1’,PLS < ‘3’) state.
*/
+ temp = xhci_readl(xhci, port_array[wIndex]);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending device "
@@ -573,10 +639,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_stop_device(xhci, slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_U3;
- xhci_writel(xhci, temp, port_array[wIndex]);
+ xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(10); /* wait device to enter */
@@ -610,10 +673,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
}
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | link_state;
- xhci_writel(xhci, temp, port_array[wIndex]);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ link_state);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(20); /* wait device to enter */
@@ -677,24 +738,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if ((temp & PORT_PE) == 0)
goto error;
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_RESUME;
- xhci_writel(xhci, temp,
- port_array[wIndex]);
-
- spin_unlock_irqrestore(&xhci->lock,
- flags);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_RESUME);
+ spin_unlock_irqrestore(&xhci->lock, flags);
msleep(20);
spin_lock_irqsave(&xhci->lock, flags);
-
- temp = xhci_readl(xhci,
- port_array[wIndex]);
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp,
- port_array[wIndex]);
+ xhci_set_link_state(xhci, port_array, wIndex,
+ XDEV_U0);
}
bus_state->port_c_suspend |= 1 << wIndex;
@@ -910,25 +960,18 @@ int xhci_bus_resume(struct usb_hcd *hcd)
if (test_bit(port_index, &bus_state->bus_suspended) &&
(temp & PORT_PLS_MASK)) {
if (DEV_SUPERSPEED(temp)) {
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, port_array[port_index]);
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_U0);
} else {
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_RESUME;
- xhci_writel(xhci, temp, port_array[port_index]);
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(20);
spin_lock_irqsave(&xhci->lock, flags);
- temp = xhci_readl(xhci, port_array[port_index]);
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, port_array[port_index]);
+ xhci_set_link_state(xhci, port_array,
+ port_index, XDEV_U0);
}
/* wait for the port to enter U0 and report port link
* state change.
@@ -938,12 +981,8 @@ int xhci_bus_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&xhci->lock, flags);
/* Clear PLC */
- temp = xhci_readl(xhci, port_array[port_index]);
- if (temp & PORT_PLC) {
- temp = xhci_port_state_to_neutral(temp);
- temp |= PORT_PLC;
- xhci_writel(xhci, temp, port_array[port_index]);
- }
+ xhci_test_and_clear_bit(xhci, port_array, port_index,
+ PORT_PLC);
slot_id = xhci_find_slot_id_by_port(hcd,
xhci, port_index + 1);
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index d446886b22b0..42a22b8e6922 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -61,8 +61,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag
static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
{
- if (!seg)
- return;
if (seg->trbs) {
xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n",
seg->trbs, (unsigned long long)seg->dma);
@@ -81,7 +79,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
* related flags, such as End TRB, Toggle Cycle, and no snoop.
*/
static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
- struct xhci_segment *next, bool link_trbs)
+ struct xhci_segment *next, bool link_trbs, bool isoc)
{
u32 val;
@@ -97,7 +95,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
val &= ~TRB_TYPE_BITMASK;
val |= TRB_TYPE(TRB_LINK);
/* Always set the chain bit with 0.95 hardware */
- if (xhci_link_trb_quirk(xhci))
+ /* Set chain bit for isoc rings on AMD 0.96 host */
+ if (xhci_link_trb_quirk(xhci) ||
+ (isoc && (xhci->quirks & XHCI_AMD_0x96_HOST)))
val |= TRB_CHAIN;
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
@@ -112,18 +112,20 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
struct xhci_segment *seg;
struct xhci_segment *first_seg;
- if (!ring || !ring->first_seg)
+ if (!ring)
return;
- first_seg = ring->first_seg;
- seg = first_seg->next;
- xhci_dbg(xhci, "Freeing ring at %p\n", ring);
- while (seg != first_seg) {
- struct xhci_segment *next = seg->next;
- xhci_segment_free(xhci, seg);
- seg = next;
+ if (ring->first_seg) {
+ first_seg = ring->first_seg;
+ seg = first_seg->next;
+ xhci_dbg(xhci, "Freeing ring at %p\n", ring);
+ while (seg != first_seg) {
+ struct xhci_segment *next = seg->next;
+ xhci_segment_free(xhci, seg);
+ seg = next;
+ }
+ xhci_segment_free(xhci, first_seg);
+ ring->first_seg = NULL;
}
- xhci_segment_free(xhci, first_seg);
- ring->first_seg = NULL;
kfree(ring);
}
@@ -152,7 +154,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring)
* See section 4.9.1 and figures 15 and 16.
*/
static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
- unsigned int num_segs, bool link_trbs, gfp_t flags)
+ unsigned int num_segs, bool link_trbs, bool isoc, gfp_t flags)
{
struct xhci_ring *ring;
struct xhci_segment *prev;
@@ -178,12 +180,12 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
next = xhci_segment_alloc(xhci, flags);
if (!next)
goto fail;
- xhci_link_segments(xhci, prev, next, link_trbs);
+ xhci_link_segments(xhci, prev, next, link_trbs, isoc);
prev = next;
num_segs--;
}
- xhci_link_segments(xhci, prev, ring->first_seg, link_trbs);
+ xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc);
if (link_trbs) {
/* See section 4.9.2.1 and 6.4.4.1 */
@@ -229,14 +231,14 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
* pointers to the beginning of the ring.
*/
static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
- struct xhci_ring *ring)
+ struct xhci_ring *ring, bool isoc)
{
struct xhci_segment *seg = ring->first_seg;
do {
memset(seg->trbs, 0,
sizeof(union xhci_trb)*TRBS_PER_SEGMENT);
/* All endpoint rings have link TRBs */
- xhci_link_segments(xhci, seg, seg->next, 1);
+ xhci_link_segments(xhci, seg, seg->next, 1, isoc);
seg = seg->next;
} while (seg != ring->first_seg);
xhci_initialize_ring_info(ring);
@@ -315,7 +317,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
- pci_free_consistent(pdev,
+ dma_free_coherent(&pdev->dev,
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
stream_ctx, dma);
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
@@ -343,9 +345,9 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
- return pci_alloc_consistent(pdev,
+ return dma_alloc_coherent(&pdev->dev,
sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
- dma);
+ dma, mem_flags);
else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
return dma_pool_alloc(xhci->small_streams_pool,
mem_flags, dma);
@@ -540,7 +542,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
*/
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
stream_info->stream_rings[cur_stream] =
- xhci_ring_alloc(xhci, 1, true, mem_flags);
+ xhci_ring_alloc(xhci, 1, true, false, mem_flags);
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
@@ -687,11 +689,103 @@ static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
ep->xhci = xhci;
}
-/* All the xhci_tds in the ring's TD list should be freed at this point */
+static void xhci_free_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int slot_id)
+{
+ struct list_head *tt;
+ struct list_head *tt_list_head;
+ struct list_head *tt_next;
+ struct xhci_tt_bw_info *tt_info;
+
+ /* If the device never made it past the Set Address stage,
+ * it may not have the real_port set correctly.
+ */
+ if (virt_dev->real_port == 0 ||
+ virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) {
+ xhci_dbg(xhci, "Bad real port.\n");
+ return;
+ }
+
+ tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts);
+ if (list_empty(tt_list_head))
+ return;
+
+ list_for_each(tt, tt_list_head) {
+ tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list);
+ if (tt_info->slot_id == slot_id)
+ break;
+ }
+ /* Cautionary measure in case the hub was disconnected before we
+ * stored the TT information.
+ */
+ if (tt_info->slot_id != slot_id)
+ return;
+
+ tt_next = tt->next;
+ tt_info = list_entry(tt, struct xhci_tt_bw_info,
+ tt_list);
+ /* Multi-TT hubs will have more than one entry */
+ do {
+ list_del(tt);
+ kfree(tt_info);
+ tt = tt_next;
+ if (list_empty(tt_list_head))
+ break;
+ tt_next = tt->next;
+ tt_info = list_entry(tt, struct xhci_tt_bw_info,
+ tt_list);
+ } while (tt_info->slot_id == slot_id);
+}
+
+int xhci_alloc_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags)
+{
+ struct xhci_tt_bw_info *tt_info;
+ unsigned int num_ports;
+ int i, j;
+
+ if (!tt->multi)
+ num_ports = 1;
+ else
+ num_ports = hdev->maxchild;
+
+ for (i = 0; i < num_ports; i++, tt_info++) {
+ struct xhci_interval_bw_table *bw_table;
+
+ tt_info = kzalloc(sizeof(*tt_info), mem_flags);
+ if (!tt_info)
+ goto free_tts;
+ INIT_LIST_HEAD(&tt_info->tt_list);
+ list_add(&tt_info->tt_list,
+ &xhci->rh_bw[virt_dev->real_port - 1].tts);
+ tt_info->slot_id = virt_dev->udev->slot_id;
+ if (tt->multi)
+ tt_info->ttport = i+1;
+ bw_table = &tt_info->bw_table;
+ for (j = 0; j < XHCI_MAX_INTERVAL; j++)
+ INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
+ }
+ return 0;
+
+free_tts:
+ xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id);
+ return -ENOMEM;
+}
+
+
+/* All the xhci_tds in the ring's TD list should be freed at this point.
+ * Should be called with xhci->lock held if there is any chance the TT lists
+ * will be manipulated by the configure endpoint, allocate device, or update
+ * hub functions while this function is removing the TT entries from the list.
+ */
void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *dev;
int i;
+ int old_active_eps = 0;
/* Slot ID 0 is reserved */
if (slot_id == 0 || !xhci->devs[slot_id])
@@ -702,13 +796,29 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (!dev)
return;
+ if (dev->tt_info)
+ old_active_eps = dev->tt_info->active_eps;
+
for (i = 0; i < 31; ++i) {
if (dev->eps[i].ring)
xhci_ring_free(xhci, dev->eps[i].ring);
if (dev->eps[i].stream_info)
xhci_free_stream_info(xhci,
dev->eps[i].stream_info);
+ /* Endpoints on the TT/root port lists should have been removed
+ * when usb_disable_device() was called for the device.
+ * We can't drop them anyway, because the udev might have gone
+ * away by this point, and we can't tell what speed it was.
+ */
+ if (!list_empty(&dev->eps[i].bw_endpoint_list))
+ xhci_warn(xhci, "Slot %u endpoint %u "
+ "not removed from BW list!\n",
+ slot_id, i);
}
+ /* If this is a hub, free the TT(s) from the TT list */
+ xhci_free_tt_info(xhci, dev, slot_id);
+ /* If necessary, update the number of active TTs on this root port */
+ xhci_update_tt_active_eps(xhci, dev, old_active_eps);
if (dev->ring_cache) {
for (i = 0; i < dev->num_rings_cached; i++)
@@ -762,10 +872,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
for (i = 0; i < 31; i++) {
xhci_init_endpoint_timer(xhci, &dev->eps[i]);
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
+ INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
}
/* Allocate endpoint 0 ring */
- dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags);
+ dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags);
if (!dev->eps[0].ring)
goto fail;
@@ -921,9 +1032,40 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent)
/* Found device below root hub */;
- dev->port = top_dev->portnum;
+ dev->fake_port = top_dev->portnum;
+ dev->real_port = port_num;
xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num);
- xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port);
+ xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port);
+
+ /* Find the right bandwidth table that this device will be a part of.
+ * If this is a full speed device attached directly to a root port (or a
+ * decendent of one), it counts as a primary bandwidth domain, not a
+ * secondary bandwidth domain under a TT. An xhci_tt_info structure
+ * will never be created for the HS root hub.
+ */
+ if (!udev->tt || !udev->tt->hub->parent) {
+ dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table;
+ } else {
+ struct xhci_root_port_bw_info *rh_bw;
+ struct xhci_tt_bw_info *tt_bw;
+
+ rh_bw = &xhci->rh_bw[port_num - 1];
+ /* Find the right TT. */
+ list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) {
+ if (tt_bw->slot_id != udev->tt->hub->slot_id)
+ continue;
+
+ if (!dev->udev->tt->multi ||
+ (udev->tt->multi &&
+ tt_bw->ttport == dev->udev->ttport)) {
+ dev->bw_table = &tt_bw->bw_table;
+ dev->tt_info = tt_bw;
+ break;
+ }
+ }
+ if (!dev->tt_info)
+ xhci_warn(xhci, "WARN: Didn't find a matching TT\n");
+ }
/* Is this a LS/FS device under an external HS hub? */
if (udev->tt && udev->tt->hub->parent) {
@@ -1141,8 +1283,8 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
if (udev->speed == USB_SPEED_SUPER)
return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
- max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize));
- max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11;
+ max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
+ max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11;
/* A 0 in max burst means 1 transfer per ESIT */
return max_packet * (max_burst + 1);
}
@@ -1175,10 +1317,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
*/
if (usb_endpoint_xfer_isoc(&ep->desc))
virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 8, true, mem_flags);
+ xhci_ring_alloc(xhci, 8, true, true, mem_flags);
else
virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 1, true, mem_flags);
+ xhci_ring_alloc(xhci, 1, true, false, mem_flags);
if (!virt_dev->eps[ep_index].new_ring) {
/* Attempt to use the ring cache */
if (virt_dev->num_rings_cached == 0)
@@ -1187,7 +1329,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
virt_dev->ring_cache[virt_dev->num_rings_cached];
virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
virt_dev->num_rings_cached--;
- xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
+ xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring,
+ usb_endpoint_xfer_isoc(&ep->desc) ? true : false);
}
virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
@@ -1211,7 +1354,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
/* Set the max packet size and max burst */
switch (udev->speed) {
case USB_SPEED_SUPER:
- max_packet = le16_to_cpu(ep->desc.wMaxPacketSize);
+ max_packet = usb_endpoint_maxp(&ep->desc);
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet));
/* dig out max burst from ep companion desc */
max_packet = ep->ss_ep_comp.bMaxBurst;
@@ -1223,14 +1366,14 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
*/
if (usb_endpoint_xfer_isoc(&ep->desc) ||
usb_endpoint_xfer_int(&ep->desc)) {
- max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize)
+ max_burst = (usb_endpoint_maxp(&ep->desc)
& 0x1800) >> 11;
ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst));
}
/* Fall through */
case USB_SPEED_FULL:
case USB_SPEED_LOW:
- max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize));
+ max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet));
break;
default:
@@ -1286,6 +1429,70 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci,
*/
}
+void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info)
+{
+ bw_info->ep_interval = 0;
+ bw_info->mult = 0;
+ bw_info->num_packets = 0;
+ bw_info->max_packet_size = 0;
+ bw_info->type = 0;
+ bw_info->max_esit_payload = 0;
+}
+
+void xhci_update_bw_info(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_input_control_ctx *ctrl_ctx,
+ struct xhci_virt_device *virt_dev)
+{
+ struct xhci_bw_info *bw_info;
+ struct xhci_ep_ctx *ep_ctx;
+ unsigned int ep_type;
+ int i;
+
+ for (i = 1; i < 31; ++i) {
+ bw_info = &virt_dev->eps[i].bw_info;
+
+ /* We can't tell what endpoint type is being dropped, but
+ * unconditionally clearing the bandwidth info for non-periodic
+ * endpoints should be harmless because the info will never be
+ * set in the first place.
+ */
+ if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) {
+ /* Dropped endpoint */
+ xhci_clear_endpoint_bw_info(bw_info);
+ continue;
+ }
+
+ if (EP_IS_ADDED(ctrl_ctx, i)) {
+ ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, i);
+ ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
+
+ /* Ignore non-periodic endpoints */
+ if (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP &&
+ ep_type != ISOC_IN_EP &&
+ ep_type != INT_IN_EP)
+ continue;
+
+ /* Added or changed endpoint */
+ bw_info->ep_interval = CTX_TO_EP_INTERVAL(
+ le32_to_cpu(ep_ctx->ep_info));
+ /* Number of packets and mult are zero-based in the
+ * input context, but we want one-based for the
+ * interval table.
+ */
+ bw_info->mult = CTX_TO_EP_MULT(
+ le32_to_cpu(ep_ctx->ep_info)) + 1;
+ bw_info->num_packets = CTX_TO_MAX_BURST(
+ le32_to_cpu(ep_ctx->ep_info2)) + 1;
+ bw_info->max_packet_size = MAX_PACKET_DECODED(
+ le32_to_cpu(ep_ctx->ep_info2));
+ bw_info->type = ep_type;
+ bw_info->max_esit_payload = CTX_TO_MAX_ESIT_PAYLOAD(
+ le32_to_cpu(ep_ctx->tx_info));
+ }
+ }
+}
+
/* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy.
* Useful when you want to change one particular aspect of the endpoint and then
* issue a configure endpoint command.
@@ -1344,10 +1551,9 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->scratchpad)
goto fail_sp;
- xhci->scratchpad->sp_array =
- pci_alloc_consistent(to_pci_dev(dev),
+ xhci->scratchpad->sp_array = dma_alloc_coherent(dev,
num_sp * sizeof(u64),
- &xhci->scratchpad->sp_dma);
+ &xhci->scratchpad->sp_dma, flags);
if (!xhci->scratchpad->sp_array)
goto fail_sp2;
@@ -1364,8 +1570,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
- void *buf = pci_alloc_consistent(to_pci_dev(dev),
- xhci->page_size, &dma);
+ void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
+ flags);
if (!buf)
goto fail_sp5;
@@ -1378,7 +1584,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
fail_sp5:
for (i = i - 1; i >= 0; i--) {
- pci_free_consistent(to_pci_dev(dev), xhci->page_size,
+ dma_free_coherent(dev, xhci->page_size,
xhci->scratchpad->sp_buffers[i],
xhci->scratchpad->sp_dma_buffers[i]);
}
@@ -1388,7 +1594,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
kfree(xhci->scratchpad->sp_buffers);
fail_sp3:
- pci_free_consistent(to_pci_dev(dev), num_sp * sizeof(u64),
+ dma_free_coherent(dev, num_sp * sizeof(u64),
xhci->scratchpad->sp_array,
xhci->scratchpad->sp_dma);
@@ -1412,13 +1618,13 @@ static void scratchpad_free(struct xhci_hcd *xhci)
num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2);
for (i = 0; i < num_sp; i++) {
- pci_free_consistent(pdev, xhci->page_size,
+ dma_free_coherent(&pdev->dev, xhci->page_size,
xhci->scratchpad->sp_buffers[i],
xhci->scratchpad->sp_dma_buffers[i]);
}
kfree(xhci->scratchpad->sp_dma_buffers);
kfree(xhci->scratchpad->sp_buffers);
- pci_free_consistent(pdev, num_sp * sizeof(u64),
+ dma_free_coherent(&pdev->dev, num_sp * sizeof(u64),
xhci->scratchpad->sp_array,
xhci->scratchpad->sp_dma);
kfree(xhci->scratchpad);
@@ -1463,18 +1669,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
{
- int last;
-
- if (!urb_priv)
- return;
-
- last = urb_priv->length - 1;
- if (last >= 0) {
- int i;
- for (i = 0; i <= last; i++)
- kfree(urb_priv->td[i]);
+ if (urb_priv) {
+ kfree(urb_priv->td[0]);
+ kfree(urb_priv);
}
- kfree(urb_priv);
}
void xhci_free_command(struct xhci_hcd *xhci,
@@ -1489,6 +1687,8 @@ void xhci_free_command(struct xhci_hcd *xhci,
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ struct dev_info *dev_info, *next;
+ unsigned long flags;
int size;
int i;
@@ -1500,7 +1700,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
if (xhci->erst.entries)
- pci_free_consistent(pdev, size,
+ dma_free_coherent(&pdev->dev, size,
xhci->erst.entries, xhci->erst.erst_dma_addr);
xhci->erst.entries = NULL;
xhci_dbg(xhci, "Freed ERST\n");
@@ -1540,17 +1740,25 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr);
if (xhci->dcbaa)
- pci_free_consistent(pdev, sizeof(*xhci->dcbaa),
+ dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa),
xhci->dcbaa, xhci->dcbaa->dma);
xhci->dcbaa = NULL;
scratchpad_free(xhci);
+ spin_lock_irqsave(&xhci->lock, flags);
+ list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) {
+ list_del(&dev_info->list);
+ kfree(dev_info);
+ }
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
xhci->num_usb2_ports = 0;
xhci->num_usb3_ports = 0;
kfree(xhci->usb2_ports);
kfree(xhci->usb3_ports);
kfree(xhci->port_array);
+ kfree(xhci->rh_bw);
xhci->page_size = 0;
xhci->page_shift = 0;
@@ -1762,6 +1970,23 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
if (port_offset == 0 || (port_offset + port_count - 1) > num_ports)
/* WTF? "Valid values are ‘1’ to MaxPorts" */
return;
+
+ /* Check the host's USB2 LPM capability */
+ if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
+ (temp & XHCI_L1C)) {
+ xhci_dbg(xhci, "xHCI 0.96: support USB2 software lpm\n");
+ xhci->sw_lpm_support = 1;
+ }
+
+ if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) {
+ xhci_dbg(xhci, "xHCI 1.0: support USB2 software lpm\n");
+ xhci->sw_lpm_support = 1;
+ if (temp & XHCI_HLC) {
+ xhci_dbg(xhci, "xHCI 1.0: support USB2 hardware lpm\n");
+ xhci->hw_lpm_support = 1;
+ }
+ }
+
port_offset--;
for (i = port_offset; i < (port_offset + port_count); i++) {
/* Duplicate entry. Ignore the port if the revisions differ. */
@@ -1806,7 +2031,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
__le32 __iomem *addr;
u32 offset;
unsigned int num_ports;
- int i, port_index;
+ int i, j, port_index;
addr = &xhci->cap_regs->hcc_params;
offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr));
@@ -1821,6 +2046,18 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->port_array)
return -ENOMEM;
+ xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags);
+ if (!xhci->rh_bw)
+ return -ENOMEM;
+ for (i = 0; i < num_ports; i++) {
+ struct xhci_interval_bw_table *bw_table;
+
+ INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
+ bw_table = &xhci->rh_bw[i].bw_table;
+ for (j = 0; j < XHCI_MAX_INTERVAL; j++)
+ INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
+ }
+
/*
* For whatever reason, the first capability offset is from the
* capability register base, not from the HCCPARAMS register.
@@ -1959,8 +2196,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* Section 5.4.8 - doorbell array must be
* "physically contiguous and 64-byte (cache line) aligned".
*/
- xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev),
- sizeof(*xhci->dcbaa), &dma);
+ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
+ GFP_KERNEL);
if (!xhci->dcbaa)
goto fail;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
@@ -1994,14 +2231,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
dma_pool_create("xHCI 1KB stream ctx arrays",
dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
/* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
- * will be allocated with pci_alloc_consistent()
+ * will be allocated with dma_alloc_coherent()
*/
if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
goto fail;
/* Set up the command ring to have one segments for now. */
- xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags);
+ xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags);
if (!xhci->cmd_ring)
goto fail;
xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
@@ -2032,14 +2269,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* the event ring segment table (ERST). Section 4.9.3.
*/
xhci_dbg(xhci, "// Allocating event ring\n");
- xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags);
+ xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, false,
+ flags);
if (!xhci->event_ring)
goto fail;
if (xhci_check_trb_in_td_math(xhci, flags) < 0)
goto fail;
- xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev),
- sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma);
+ xhci->erst.entries = dma_alloc_coherent(dev,
+ sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
+ GFP_KERNEL);
if (!xhci->erst.entries)
goto fail;
xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n",
@@ -2102,6 +2341,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
if (xhci_setup_port_arrays(xhci, flags))
goto fail;
+ INIT_LIST_HEAD(&xhci->lpm_failed_devs);
+
return 0;
fail:
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index cb16de213f64..9f51f88cc0f5 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -51,61 +51,9 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
return 0;
}
-/* called during probe() after chip reset completes */
-static int xhci_pci_setup(struct usb_hcd *hcd)
+static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
- struct xhci_hcd *xhci;
- struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
- int retval;
- u32 temp;
-
- hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
-
- if (usb_hcd_is_primary_hcd(hcd)) {
- xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
- if (!xhci)
- return -ENOMEM;
- *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
- xhci->main_hcd = hcd;
- /* Mark the first roothub as being USB 2.0.
- * The xHCI driver will register the USB 3.0 roothub.
- */
- hcd->speed = HCD_USB2;
- hcd->self.root_hub->speed = USB_SPEED_HIGH;
- /*
- * USB 2.0 roothub under xHCI has an integrated TT,
- * (rate matching hub) as opposed to having an OHCI/UHCI
- * companion controller.
- */
- hcd->has_tt = 1;
- } else {
- /* xHCI private pointer was set in xhci_pci_probe for the second
- * registered roothub.
- */
- xhci = hcd_to_xhci(hcd);
- temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
- if (HCC_64BIT_ADDR(temp)) {
- xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
- dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
- } else {
- dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
- }
- return 0;
- }
-
- xhci->cap_regs = hcd->regs;
- xhci->op_regs = hcd->regs +
- HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
- xhci->run_regs = hcd->regs +
- (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
- /* Cache read-only capability registers */
- xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
- xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
- xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
- xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
- xhci->hci_version = HC_VERSION(xhci->hcc_params);
- xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
- xhci_print_registers(xhci);
+ struct pci_dev *pdev = to_pci_dev(dev);
/* Look for vendor-specific quirks */
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@@ -128,6 +76,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96)
+ xhci->quirks |= XHCI_AMD_0x96_HOST;
+
/* AMD PLL quirk */
if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
xhci->quirks |= XHCI_AMD_PLL_FIX;
@@ -136,39 +87,29 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
xhci->limit_active_eps = 64;
+ xhci->quirks |= XHCI_SW_BW_CHECKING;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
}
+}
- /* Make sure the HC is halted. */
- retval = xhci_halt(xhci);
- if (retval)
- goto error;
+/* called during probe() after chip reset completes */
+static int xhci_pci_setup(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci;
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ int retval;
- xhci_dbg(xhci, "Resetting HCD\n");
- /* Reset the internal HC memory state and registers. */
- retval = xhci_reset(xhci);
+ retval = xhci_gen_setup(hcd, xhci_pci_quirks);
if (retval)
- goto error;
- xhci_dbg(xhci, "Reset complete\n");
-
- temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
- if (HCC_64BIT_ADDR(temp)) {
- xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
- dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
- } else {
- dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
- }
+ return retval;
- xhci_dbg(xhci, "Calling HCD init\n");
- /* Initialize HCD and host controller data structures. */
- retval = xhci_init(hcd);
- if (retval)
- goto error;
- xhci_dbg(xhci, "Called HCD init\n");
+ xhci = hcd_to_xhci(hcd);
+ if (!usb_hcd_is_primary_hcd(hcd))
+ return 0;
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
@@ -178,7 +119,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (!retval)
return retval;
-error:
kfree(xhci);
return retval;
}
@@ -222,7 +162,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
- IRQF_DISABLED | IRQF_SHARED);
+ IRQF_SHARED);
if (retval)
goto put_usb3_hcd;
/* Roothub already marked as USB 3.0 speed */
@@ -344,6 +284,11 @@ static const struct hc_driver xhci_pci_hc_driver = {
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
+ /*
+ * call back when device connected and addressed
+ */
+ .update_device = xhci_update_device,
+ .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
};
/*-------------------------------------------------------------------------*/
@@ -375,12 +320,12 @@ static struct pci_driver xhci_pci_driver = {
#endif
};
-int xhci_register_pci(void)
+int __init xhci_register_pci(void)
{
return pci_register_driver(&xhci_pci_driver);
}
-void xhci_unregister_pci(void)
+void __exit xhci_unregister_pci(void)
{
pci_unregister_driver(&xhci_pci_driver);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 952e2ded61af..940321b3ec68 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -185,7 +185,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
* prepare_transfer()?
*/
static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
- bool consumer, bool more_trbs_coming)
+ bool consumer, bool more_trbs_coming, bool isoc)
{
u32 chain;
union xhci_trb *next;
@@ -212,11 +212,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (!chain && !more_trbs_coming)
break;
- /* If we're not dealing with 0.95 hardware,
+ /* If we're not dealing with 0.95 hardware or
+ * isoc rings on AMD 0.96 host,
* carry over the chain bit of the previous TRB
* (which may mean the chain bit is cleared).
*/
- if (!xhci_link_trb_quirk(xhci)) {
+ if (!(isoc && (xhci->quirks & XHCI_AMD_0x96_HOST))
+ && !xhci_link_trb_quirk(xhci)) {
next->link.control &=
cpu_to_le32(~TRB_CHAIN);
next->link.control |=
@@ -1329,10 +1331,8 @@ static void handle_port_status(struct xhci_hcd *xhci,
if (DEV_SUPERSPEED(temp)) {
xhci_dbg(xhci, "resume SS port %d\n", port_id);
- temp = xhci_port_state_to_neutral(temp);
- temp &= ~PORT_PLS_MASK;
- temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, port_array[faked_port_index]);
+ xhci_set_link_state(xhci, port_array, faked_port_index,
+ XDEV_U0);
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
faked_port_index);
if (!slot_id) {
@@ -1342,10 +1342,8 @@ static void handle_port_status(struct xhci_hcd *xhci,
xhci_ring_device(xhci, slot_id);
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
/* Clear PORT_PLC */
- temp = xhci_readl(xhci, port_array[faked_port_index]);
- temp = xhci_port_state_to_neutral(temp);
- temp |= PORT_PLC;
- xhci_writel(xhci, temp, port_array[faked_port_index]);
+ xhci_test_and_clear_bit(xhci, port_array,
+ faked_port_index, PORT_PLC);
} else {
xhci_dbg(xhci, "resume HS port %d\n", port_id);
bus_state->resume_done[faked_port_index] = jiffies +
@@ -1356,6 +1354,10 @@ static void handle_port_status(struct xhci_hcd *xhci,
}
}
+ if (hcd->speed != HCD_USB3)
+ xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
+ PORT_PLC);
+
cleanup:
/* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring, true);
@@ -2192,7 +2194,8 @@ cleanup:
if ((urb->actual_length != urb->transfer_buffer_length &&
(urb->transfer_flags &
URB_SHORT_NOT_OK)) ||
- status != 0)
+ (status != 0 &&
+ !usb_endpoint_xfer_isoc(&urb->ep->desc)))
xhci_dbg(xhci, "Giveback URB %p, len = %d, "
"expected = %x, status = %d\n",
urb, urb->actual_length,
@@ -2409,7 +2412,7 @@ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
* prepare_transfer()?
*/
static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
- bool consumer, bool more_trbs_coming,
+ bool consumer, bool more_trbs_coming, bool isoc,
u32 field1, u32 field2, u32 field3, u32 field4)
{
struct xhci_generic_trb *trb;
@@ -2419,7 +2422,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
trb->field[1] = cpu_to_le32(field2);
trb->field[2] = cpu_to_le32(field3);
trb->field[3] = cpu_to_le32(field4);
- inc_enq(xhci, ring, consumer, more_trbs_coming);
+ inc_enq(xhci, ring, consumer, more_trbs_coming, isoc);
}
/*
@@ -2427,7 +2430,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
* FIXME allocate segments if the ring is full.
*/
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
- u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
+ u32 ep_state, unsigned int num_trbs, bool isoc, gfp_t mem_flags)
{
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
@@ -2469,10 +2472,11 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
next = ring->enqueue;
while (last_trb(xhci, ring, ring->enq_seg, next)) {
- /* If we're not dealing with 0.95 hardware,
- * clear the chain bit.
+ /* If we're not dealing with 0.95 hardware or isoc rings
+ * on AMD 0.96 host, clear the chain bit.
*/
- if (!xhci_link_trb_quirk(xhci))
+ if (!xhci_link_trb_quirk(xhci) && !(isoc &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)))
next->link.control &= cpu_to_le32(~TRB_CHAIN);
else
next->link.control |= cpu_to_le32(TRB_CHAIN);
@@ -2505,6 +2509,7 @@ static int prepare_transfer(struct xhci_hcd *xhci,
unsigned int num_trbs,
struct urb *urb,
unsigned int td_index,
+ bool isoc,
gfp_t mem_flags)
{
int ret;
@@ -2522,7 +2527,7 @@ static int prepare_transfer(struct xhci_hcd *xhci,
ret = prepare_ring(xhci, ep_ring,
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
- num_trbs, mem_flags);
+ num_trbs, isoc, mem_flags);
if (ret)
return ret;
@@ -2711,7 +2716,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
* running_total.
*/
packets_transferred = (running_total + trb_buff_len) /
- le16_to_cpu(urb->ep->desc.wMaxPacketSize);
+ usb_endpoint_maxp(&urb->ep->desc);
return xhci_td_remainder(total_packet_count - packets_transferred);
}
@@ -2741,11 +2746,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
num_trbs = count_sg_trbs_needed(xhci, urb);
num_sgs = urb->num_sgs;
total_packet_count = roundup(urb->transfer_buffer_length,
- le16_to_cpu(urb->ep->desc.wMaxPacketSize));
+ usb_endpoint_maxp(&urb->ep->desc));
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, mem_flags);
+ num_trbs, urb, 0, false, mem_flags);
if (trb_buff_len < 0)
return trb_buff_len;
@@ -2840,7 +2845,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
more_trbs_coming = true;
else
more_trbs_coming = false;
- queue_trb(xhci, ep_ring, false, more_trbs_coming,
+ queue_trb(xhci, ep_ring, false, more_trbs_coming, false,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -2931,7 +2936,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, mem_flags);
+ num_trbs, urb, 0, false, mem_flags);
if (ret < 0)
return ret;
@@ -2948,7 +2953,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
running_total = 0;
total_packet_count = roundup(urb->transfer_buffer_length,
- le16_to_cpu(urb->ep->desc.wMaxPacketSize));
+ usb_endpoint_maxp(&urb->ep->desc));
/* How much data is in the first TRB? */
addr = (u64) urb->transfer_dma;
trb_buff_len = TRB_MAX_BUFF_SIZE -
@@ -3003,7 +3008,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
more_trbs_coming = true;
else
more_trbs_coming = false;
- queue_trb(xhci, ep_ring, false, more_trbs_coming,
+ queue_trb(xhci, ep_ring, false, more_trbs_coming, false,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -3063,7 +3068,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
num_trbs++;
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, 0, mem_flags);
+ num_trbs, urb, 0, false, mem_flags);
if (ret < 0)
return ret;
@@ -3096,7 +3101,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
}
}
- queue_trb(xhci, ep_ring, false, true,
+ queue_trb(xhci, ep_ring, false, true, false,
setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
TRB_LEN(8) | TRB_INTR_TARGET(0),
@@ -3116,7 +3121,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_DIR_IN;
- queue_trb(xhci, ep_ring, false, true,
+ queue_trb(xhci, ep_ring, false, true, false,
lower_32_bits(urb->transfer_dma),
upper_32_bits(urb->transfer_dma),
length_field,
@@ -3132,7 +3137,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
field = 0;
else
field = TRB_DIR_IN;
- queue_trb(xhci, ep_ring, false, false,
+ queue_trb(xhci, ep_ring, false, false, false,
0,
0,
TRB_INTR_TARGET(0),
@@ -3269,7 +3274,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
td_len = urb->iso_frame_desc[i].length;
td_remain_len = td_len;
total_packet_count = roundup(td_len,
- le16_to_cpu(urb->ep->desc.wMaxPacketSize));
+ usb_endpoint_maxp(&urb->ep->desc));
/* A zero-length transfer still involves at least one packet. */
if (total_packet_count == 0)
total_packet_count++;
@@ -3281,7 +3286,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
- urb->stream_id, trbs_per_td, urb, i, mem_flags);
+ urb->stream_id, trbs_per_td, urb, i, true,
+ mem_flags);
if (ret < 0) {
if (i == 0)
return ret;
@@ -3351,7 +3357,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
remainder |
TRB_INTR_TARGET(0);
- queue_trb(xhci, ep_ring, false, more_trbs_coming,
+ queue_trb(xhci, ep_ring, false, more_trbs_coming, true,
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
@@ -3433,7 +3439,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Do not insert any td of the urb to the ring if the check failed.
*/
ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
- num_trbs, mem_flags);
+ num_trbs, true, mem_flags);
if (ret)
return ret;
@@ -3492,7 +3498,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
reserved_trbs++;
ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING,
- reserved_trbs, GFP_ATOMIC);
+ reserved_trbs, false, GFP_ATOMIC);
if (ret < 0) {
xhci_err(xhci, "ERR: No room for command on command ring\n");
if (command_must_succeed)
@@ -3500,8 +3506,8 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
"unfailable commands failed.\n");
return ret;
}
- queue_trb(xhci, xhci->cmd_ring, false, false, field1, field2, field3,
- field4 | xhci->cmd_ring->cycle_state);
+ queue_trb(xhci, xhci->cmd_ring, false, false, false, field1, field2,
+ field3, field4 | xhci->cmd_ring->cycle_state);
return 0;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 3a0f695138f4..1ff95a0df576 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -175,28 +175,19 @@ int xhci_reset(struct xhci_hcd *xhci)
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
}
-/*
- * Free IRQs
- * free all IRQs request
- */
-static void xhci_free_irq(struct xhci_hcd *xhci)
+#ifdef CONFIG_PCI
+static int xhci_free_msi(struct xhci_hcd *xhci)
{
int i;
- struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- /* return if using legacy interrupt */
- if (xhci_to_hcd(xhci)->irq >= 0)
- return;
-
- if (xhci->msix_entries) {
- for (i = 0; i < xhci->msix_count; i++)
- if (xhci->msix_entries[i].vector)
- free_irq(xhci->msix_entries[i].vector,
- xhci_to_hcd(xhci));
- } else if (pdev->irq >= 0)
- free_irq(pdev->irq, xhci_to_hcd(xhci));
+ if (!xhci->msix_entries)
+ return -EINVAL;
- return;
+ for (i = 0; i < xhci->msix_count; i++)
+ if (xhci->msix_entries[i].vector)
+ free_irq(xhci->msix_entries[i].vector,
+ xhci_to_hcd(xhci));
+ return 0;
}
/*
@@ -224,6 +215,28 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
}
/*
+ * Free IRQs
+ * free all IRQs request
+ */
+static void xhci_free_irq(struct xhci_hcd *xhci)
+{
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ int ret;
+
+ /* return if using legacy interrupt */
+ if (xhci_to_hcd(xhci)->irq >= 0)
+ return;
+
+ ret = xhci_free_msi(xhci);
+ if (!ret)
+ return;
+ if (pdev->irq >= 0)
+ free_irq(pdev->irq, xhci_to_hcd(xhci));
+
+ return;
+}
+
+/*
* Set up MSI-X
*/
static int xhci_setup_msix(struct xhci_hcd *xhci)
@@ -302,6 +315,72 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
return;
}
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+ int i;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ synchronize_irq(xhci->msix_entries[i].vector);
+ }
+}
+
+static int xhci_try_enable_msi(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+ int ret;
+
+ /*
+ * Some Fresco Logic host controllers advertise MSI, but fail to
+ * generate interrupts. Don't even try to enable MSI.
+ */
+ if (xhci->quirks & XHCI_BROKEN_MSI)
+ return 0;
+
+ /* unregister the legacy interrupt */
+ if (hcd->irq)
+ free_irq(hcd->irq, hcd);
+ hcd->irq = -1;
+
+ ret = xhci_setup_msix(xhci);
+ if (ret)
+ /* fall back to msi*/
+ ret = xhci_setup_msi(xhci);
+
+ if (!ret)
+ /* hcd->irq is -1, we have MSI */
+ return 0;
+
+ /* fall back to legacy interrupt*/
+ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
+ hcd->irq_descr, hcd);
+ if (ret) {
+ xhci_err(xhci, "request interrupt %d failed\n",
+ pdev->irq);
+ return ret;
+ }
+ hcd->irq = pdev->irq;
+ return 0;
+}
+
+#else
+
+static int xhci_try_enable_msi(struct usb_hcd *hcd)
+{
+ return 0;
+}
+
+static void xhci_cleanup_msix(struct xhci_hcd *xhci)
+{
+}
+
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+}
+
+#endif
+
/*
* Initialize memory for HCD and xHC (one-time init).
*
@@ -316,7 +395,7 @@ int xhci_init(struct usb_hcd *hcd)
xhci_dbg(xhci, "xhci_init\n");
spin_lock_init(&xhci->lock);
- if (link_quirk) {
+ if (xhci->hci_version == 0x95 && link_quirk) {
xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n");
xhci->quirks |= XHCI_LINK_TRB_QUIRK;
} else {
@@ -413,9 +492,8 @@ int xhci_run(struct usb_hcd *hcd)
{
u32 temp;
u64 temp_64;
- u32 ret;
+ int ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
/* Start the xHCI host controller running only after the USB 2.0 roothub
* is setup.
@@ -426,34 +504,10 @@ int xhci_run(struct usb_hcd *hcd)
return xhci_run_finished(xhci);
xhci_dbg(xhci, "xhci_run\n");
- /* unregister the legacy interrupt */
- if (hcd->irq)
- free_irq(hcd->irq, hcd);
- hcd->irq = -1;
-
- /* Some Fresco Logic host controllers advertise MSI, but fail to
- * generate interrupts. Don't even try to enable MSI.
- */
- if (xhci->quirks & XHCI_BROKEN_MSI)
- goto legacy_irq;
- ret = xhci_setup_msix(xhci);
+ ret = xhci_try_enable_msi(hcd);
if (ret)
- /* fall back to msi*/
- ret = xhci_setup_msi(xhci);
-
- if (ret) {
-legacy_irq:
- /* fall back to legacy interrupt*/
- ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
- hcd->irq_descr, hcd);
- if (ret) {
- xhci_err(xhci, "request interrupt %d failed\n",
- pdev->irq);
- return ret;
- }
- hcd->irq = pdev->irq;
- }
+ return ret;
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
init_timer(&xhci->event_ring_timer);
@@ -694,7 +748,6 @@ int xhci_suspend(struct xhci_hcd *xhci)
int rc = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
- int i;
spin_lock_irq(&xhci->lock);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -730,10 +783,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
/* step 5: remove core well power */
/* synchronize irq when using MSI-X */
- if (xhci->msix_entries) {
- for (i = 0; i < xhci->msix_count; i++)
- synchronize_irq(xhci->msix_entries[i].vector);
- }
+ xhci_msix_sync_irqs(xhci);
return rc;
}
@@ -945,8 +995,7 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
return -ENODEV;
if (check_virt_dev) {
- if (!udev->slot_id || !xhci->devs
- || !xhci->devs[udev->slot_id]) {
+ if (!udev->slot_id || !xhci->devs[udev->slot_id]) {
printk(KERN_DEBUG "xHCI %s called with unaddressed "
"device\n", func);
return -EINVAL;
@@ -987,7 +1036,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
out_ctx = xhci->devs[slot_id]->out_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
- max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize);
+ max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc);
if (hw_max_packet_size != max_packet_size) {
xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n");
xhci_dbg(xhci, "Max packet size in usb_device = %d\n",
@@ -1035,6 +1084,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_td *buffer;
unsigned long flags;
int ret = 0;
unsigned int slot_id, ep_index;
@@ -1065,13 +1115,15 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
if (!urb_priv)
return -ENOMEM;
+ buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags);
+ if (!buffer) {
+ kfree(urb_priv);
+ return -ENOMEM;
+ }
+
for (i = 0; i < size; i++) {
- urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags);
- if (!urb_priv->td[i]) {
- urb_priv->length = i;
- xhci_urb_free_priv(xhci, urb_priv);
- return -ENOMEM;
- }
+ urb_priv->td[i] = buffer;
+ buffer++;
}
urb_priv->length = size;
@@ -1747,6 +1799,564 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci,
xhci->num_active_eps);
}
+unsigned int xhci_get_block_size(struct usb_device *udev)
+{
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return FS_BLOCK;
+ case USB_SPEED_HIGH:
+ return HS_BLOCK;
+ case USB_SPEED_SUPER:
+ return SS_BLOCK;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ default:
+ /* Should never happen */
+ return 1;
+ }
+}
+
+unsigned int xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw)
+{
+ if (interval_bw->overhead[LS_OVERHEAD_TYPE])
+ return LS_OVERHEAD;
+ if (interval_bw->overhead[FS_OVERHEAD_TYPE])
+ return FS_OVERHEAD;
+ return HS_OVERHEAD;
+}
+
+/* If we are changing a LS/FS device under a HS hub,
+ * make sure (if we are activating a new TT) that the HS bus has enough
+ * bandwidth for this new TT.
+ */
+static int xhci_check_tt_bw_table(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ struct xhci_interval_bw_table *bw_table;
+ struct xhci_tt_bw_info *tt_info;
+
+ /* Find the bandwidth table for the root port this TT is attached to. */
+ bw_table = &xhci->rh_bw[virt_dev->real_port - 1].bw_table;
+ tt_info = virt_dev->tt_info;
+ /* If this TT already had active endpoints, the bandwidth for this TT
+ * has already been added. Removing all periodic endpoints (and thus
+ * making the TT enactive) will only decrease the bandwidth used.
+ */
+ if (old_active_eps)
+ return 0;
+ if (old_active_eps == 0 && tt_info->active_eps != 0) {
+ if (bw_table->bw_used + TT_HS_OVERHEAD > HS_BW_LIMIT)
+ return -ENOMEM;
+ return 0;
+ }
+ /* Not sure why we would have no new active endpoints...
+ *
+ * Maybe because of an Evaluate Context change for a hub update or a
+ * control endpoint 0 max packet size change?
+ * FIXME: skip the bandwidth calculation in that case.
+ */
+ return 0;
+}
+
+static int xhci_check_ss_bw(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev)
+{
+ unsigned int bw_reserved;
+
+ bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100);
+ if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved))
+ return -ENOMEM;
+
+ bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100);
+ if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * This algorithm is a very conservative estimate of the worst-case scheduling
+ * scenario for any one interval. The hardware dynamically schedules the
+ * packets, so we can't tell which microframe could be the limiting factor in
+ * the bandwidth scheduling. This only takes into account periodic endpoints.
+ *
+ * Obviously, we can't solve an NP complete problem to find the minimum worst
+ * case scenario. Instead, we come up with an estimate that is no less than
+ * the worst case bandwidth used for any one microframe, but may be an
+ * over-estimate.
+ *
+ * We walk the requirements for each endpoint by interval, starting with the
+ * smallest interval, and place packets in the schedule where there is only one
+ * possible way to schedule packets for that interval. In order to simplify
+ * this algorithm, we record the largest max packet size for each interval, and
+ * assume all packets will be that size.
+ *
+ * For interval 0, we obviously must schedule all packets for each interval.
+ * The bandwidth for interval 0 is just the amount of data to be transmitted
+ * (the sum of all max ESIT payload sizes, plus any overhead per packet times
+ * the number of packets).
+ *
+ * For interval 1, we have two possible microframes to schedule those packets
+ * in. For this algorithm, if we can schedule the same number of packets for
+ * each possible scheduling opportunity (each microframe), we will do so. The
+ * remaining number of packets will be saved to be transmitted in the gaps in
+ * the next interval's scheduling sequence.
+ *
+ * As we move those remaining packets to be scheduled with interval 2 packets,
+ * we have to double the number of remaining packets to transmit. This is
+ * because the intervals are actually powers of 2, and we would be transmitting
+ * the previous interval's packets twice in this interval. We also have to be
+ * sure that when we look at the largest max packet size for this interval, we
+ * also look at the largest max packet size for the remaining packets and take
+ * the greater of the two.
+ *
+ * The algorithm continues to evenly distribute packets in each scheduling
+ * opportunity, and push the remaining packets out, until we get to the last
+ * interval. Then those packets and their associated overhead are just added
+ * to the bandwidth used.
+ */
+static int xhci_check_bw_table(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ unsigned int bw_reserved;
+ unsigned int max_bandwidth;
+ unsigned int bw_used;
+ unsigned int block_size;
+ struct xhci_interval_bw_table *bw_table;
+ unsigned int packet_size = 0;
+ unsigned int overhead = 0;
+ unsigned int packets_transmitted = 0;
+ unsigned int packets_remaining = 0;
+ unsigned int i;
+
+ if (virt_dev->udev->speed == USB_SPEED_SUPER)
+ return xhci_check_ss_bw(xhci, virt_dev);
+
+ if (virt_dev->udev->speed == USB_SPEED_HIGH) {
+ max_bandwidth = HS_BW_LIMIT;
+ /* Convert percent of bus BW reserved to blocks reserved */
+ bw_reserved = DIV_ROUND_UP(HS_BW_RESERVED * max_bandwidth, 100);
+ } else {
+ max_bandwidth = FS_BW_LIMIT;
+ bw_reserved = DIV_ROUND_UP(FS_BW_RESERVED * max_bandwidth, 100);
+ }
+
+ bw_table = virt_dev->bw_table;
+ /* We need to translate the max packet size and max ESIT payloads into
+ * the units the hardware uses.
+ */
+ block_size = xhci_get_block_size(virt_dev->udev);
+
+ /* If we are manipulating a LS/FS device under a HS hub, double check
+ * that the HS bus has enough bandwidth if we are activing a new TT.
+ */
+ if (virt_dev->tt_info) {
+ xhci_dbg(xhci, "Recalculating BW for rootport %u\n",
+ virt_dev->real_port);
+ if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) {
+ xhci_warn(xhci, "Not enough bandwidth on HS bus for "
+ "newly activated TT.\n");
+ return -ENOMEM;
+ }
+ xhci_dbg(xhci, "Recalculating BW for TT slot %u port %u\n",
+ virt_dev->tt_info->slot_id,
+ virt_dev->tt_info->ttport);
+ } else {
+ xhci_dbg(xhci, "Recalculating BW for rootport %u\n",
+ virt_dev->real_port);
+ }
+
+ /* Add in how much bandwidth will be used for interval zero, or the
+ * rounded max ESIT payload + number of packets * largest overhead.
+ */
+ bw_used = DIV_ROUND_UP(bw_table->interval0_esit_payload, block_size) +
+ bw_table->interval_bw[0].num_packets *
+ xhci_get_largest_overhead(&bw_table->interval_bw[0]);
+
+ for (i = 1; i < XHCI_MAX_INTERVAL; i++) {
+ unsigned int bw_added;
+ unsigned int largest_mps;
+ unsigned int interval_overhead;
+
+ /*
+ * How many packets could we transmit in this interval?
+ * If packets didn't fit in the previous interval, we will need
+ * to transmit that many packets twice within this interval.
+ */
+ packets_remaining = 2 * packets_remaining +
+ bw_table->interval_bw[i].num_packets;
+
+ /* Find the largest max packet size of this or the previous
+ * interval.
+ */
+ if (list_empty(&bw_table->interval_bw[i].endpoints))
+ largest_mps = 0;
+ else {
+ struct xhci_virt_ep *virt_ep;
+ struct list_head *ep_entry;
+
+ ep_entry = bw_table->interval_bw[i].endpoints.next;
+ virt_ep = list_entry(ep_entry,
+ struct xhci_virt_ep, bw_endpoint_list);
+ /* Convert to blocks, rounding up */
+ largest_mps = DIV_ROUND_UP(
+ virt_ep->bw_info.max_packet_size,
+ block_size);
+ }
+ if (largest_mps > packet_size)
+ packet_size = largest_mps;
+
+ /* Use the larger overhead of this or the previous interval. */
+ interval_overhead = xhci_get_largest_overhead(
+ &bw_table->interval_bw[i]);
+ if (interval_overhead > overhead)
+ overhead = interval_overhead;
+
+ /* How many packets can we evenly distribute across
+ * (1 << (i + 1)) possible scheduling opportunities?
+ */
+ packets_transmitted = packets_remaining >> (i + 1);
+
+ /* Add in the bandwidth used for those scheduled packets */
+ bw_added = packets_transmitted * (overhead + packet_size);
+
+ /* How many packets do we have remaining to transmit? */
+ packets_remaining = packets_remaining % (1 << (i + 1));
+
+ /* What largest max packet size should those packets have? */
+ /* If we've transmitted all packets, don't carry over the
+ * largest packet size.
+ */
+ if (packets_remaining == 0) {
+ packet_size = 0;
+ overhead = 0;
+ } else if (packets_transmitted > 0) {
+ /* Otherwise if we do have remaining packets, and we've
+ * scheduled some packets in this interval, take the
+ * largest max packet size from endpoints with this
+ * interval.
+ */
+ packet_size = largest_mps;
+ overhead = interval_overhead;
+ }
+ /* Otherwise carry over packet_size and overhead from the last
+ * time we had a remainder.
+ */
+ bw_used += bw_added;
+ if (bw_used > max_bandwidth) {
+ xhci_warn(xhci, "Not enough bandwidth. "
+ "Proposed: %u, Max: %u\n",
+ bw_used, max_bandwidth);
+ return -ENOMEM;
+ }
+ }
+ /*
+ * Ok, we know we have some packets left over after even-handedly
+ * scheduling interval 15. We don't know which microframes they will
+ * fit into, so we over-schedule and say they will be scheduled every
+ * microframe.
+ */
+ if (packets_remaining > 0)
+ bw_used += overhead + packet_size;
+
+ if (!virt_dev->tt_info && virt_dev->udev->speed == USB_SPEED_HIGH) {
+ unsigned int port_index = virt_dev->real_port - 1;
+
+ /* OK, we're manipulating a HS device attached to a
+ * root port bandwidth domain. Include the number of active TTs
+ * in the bandwidth used.
+ */
+ bw_used += TT_HS_OVERHEAD *
+ xhci->rh_bw[port_index].num_active_tts;
+ }
+
+ xhci_dbg(xhci, "Final bandwidth: %u, Limit: %u, Reserved: %u, "
+ "Available: %u " "percent\n",
+ bw_used, max_bandwidth, bw_reserved,
+ (max_bandwidth - bw_used - bw_reserved) * 100 /
+ max_bandwidth);
+
+ bw_used += bw_reserved;
+ if (bw_used > max_bandwidth) {
+ xhci_warn(xhci, "Not enough bandwidth. Proposed: %u, Max: %u\n",
+ bw_used, max_bandwidth);
+ return -ENOMEM;
+ }
+
+ bw_table->bw_used = bw_used;
+ return 0;
+}
+
+static bool xhci_is_async_ep(unsigned int ep_type)
+{
+ return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP &&
+ ep_type != ISOC_IN_EP &&
+ ep_type != INT_IN_EP);
+}
+
+static bool xhci_is_sync_in_ep(unsigned int ep_type)
+{
+ return (ep_type == ISOC_IN_EP || ep_type != INT_IN_EP);
+}
+
+static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw)
+{
+ unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK);
+
+ if (ep_bw->ep_interval == 0)
+ return SS_OVERHEAD_BURST +
+ (ep_bw->mult * ep_bw->num_packets *
+ (SS_OVERHEAD + mps));
+ return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets *
+ (SS_OVERHEAD + mps + SS_OVERHEAD_BURST),
+ 1 << ep_bw->ep_interval);
+
+}
+
+void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info)
+{
+ struct xhci_interval_bw *interval_bw;
+ int normalized_interval;
+
+ if (xhci_is_async_ep(ep_bw->type))
+ return;
+
+ if (udev->speed == USB_SPEED_SUPER) {
+ if (xhci_is_sync_in_ep(ep_bw->type))
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_in -=
+ xhci_get_ss_bw_consumed(ep_bw);
+ else
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_out -=
+ xhci_get_ss_bw_consumed(ep_bw);
+ return;
+ }
+
+ /* SuperSpeed endpoints never get added to intervals in the table, so
+ * this check is only valid for HS/FS/LS devices.
+ */
+ if (list_empty(&virt_ep->bw_endpoint_list))
+ return;
+ /* For LS/FS devices, we need to translate the interval expressed in
+ * microframes to frames.
+ */
+ if (udev->speed == USB_SPEED_HIGH)
+ normalized_interval = ep_bw->ep_interval;
+ else
+ normalized_interval = ep_bw->ep_interval - 3;
+
+ if (normalized_interval == 0)
+ bw_table->interval0_esit_payload -= ep_bw->max_esit_payload;
+ interval_bw = &bw_table->interval_bw[normalized_interval];
+ interval_bw->num_packets -= ep_bw->num_packets;
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_FULL:
+ interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_HIGH:
+ interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1;
+ break;
+ case USB_SPEED_SUPER:
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ /* Should never happen because only LS/FS/HS endpoints will get
+ * added to the endpoint list.
+ */
+ return;
+ }
+ if (tt_info)
+ tt_info->active_eps -= 1;
+ list_del_init(&virt_ep->bw_endpoint_list);
+}
+
+static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info)
+{
+ struct xhci_interval_bw *interval_bw;
+ struct xhci_virt_ep *smaller_ep;
+ int normalized_interval;
+
+ if (xhci_is_async_ep(ep_bw->type))
+ return;
+
+ if (udev->speed == USB_SPEED_SUPER) {
+ if (xhci_is_sync_in_ep(ep_bw->type))
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_in +=
+ xhci_get_ss_bw_consumed(ep_bw);
+ else
+ xhci->devs[udev->slot_id]->bw_table->ss_bw_out +=
+ xhci_get_ss_bw_consumed(ep_bw);
+ return;
+ }
+
+ /* For LS/FS devices, we need to translate the interval expressed in
+ * microframes to frames.
+ */
+ if (udev->speed == USB_SPEED_HIGH)
+ normalized_interval = ep_bw->ep_interval;
+ else
+ normalized_interval = ep_bw->ep_interval - 3;
+
+ if (normalized_interval == 0)
+ bw_table->interval0_esit_payload += ep_bw->max_esit_payload;
+ interval_bw = &bw_table->interval_bw[normalized_interval];
+ interval_bw->num_packets += ep_bw->num_packets;
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ interval_bw->overhead[LS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_FULL:
+ interval_bw->overhead[FS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_HIGH:
+ interval_bw->overhead[HS_OVERHEAD_TYPE] += 1;
+ break;
+ case USB_SPEED_SUPER:
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_WIRELESS:
+ /* Should never happen because only LS/FS/HS endpoints will get
+ * added to the endpoint list.
+ */
+ return;
+ }
+
+ if (tt_info)
+ tt_info->active_eps += 1;
+ /* Insert the endpoint into the list, largest max packet size first. */
+ list_for_each_entry(smaller_ep, &interval_bw->endpoints,
+ bw_endpoint_list) {
+ if (ep_bw->max_packet_size >=
+ smaller_ep->bw_info.max_packet_size) {
+ /* Add the new ep before the smaller endpoint */
+ list_add_tail(&virt_ep->bw_endpoint_list,
+ &smaller_ep->bw_endpoint_list);
+ return;
+ }
+ }
+ /* Add the new endpoint at the end of the list. */
+ list_add_tail(&virt_ep->bw_endpoint_list,
+ &interval_bw->endpoints);
+}
+
+void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps)
+{
+ struct xhci_root_port_bw_info *rh_bw_info;
+ if (!virt_dev->tt_info)
+ return;
+
+ rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1];
+ if (old_active_eps == 0 &&
+ virt_dev->tt_info->active_eps != 0) {
+ rh_bw_info->num_active_tts += 1;
+ rh_bw_info->bw_table.bw_used += TT_HS_OVERHEAD;
+ } else if (old_active_eps != 0 &&
+ virt_dev->tt_info->active_eps == 0) {
+ rh_bw_info->num_active_tts -= 1;
+ rh_bw_info->bw_table.bw_used -= TT_HS_OVERHEAD;
+ }
+}
+
+static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct xhci_container_ctx *in_ctx)
+{
+ struct xhci_bw_info ep_bw_info[31];
+ int i;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ int old_active_eps = 0;
+
+ if (virt_dev->tt_info)
+ old_active_eps = virt_dev->tt_info->active_eps;
+
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+
+ for (i = 0; i < 31; i++) {
+ if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
+ continue;
+
+ /* Make a copy of the BW info in case we need to revert this */
+ memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info,
+ sizeof(ep_bw_info[i]));
+ /* Drop the endpoint from the interval table if the endpoint is
+ * being dropped or changed.
+ */
+ if (EP_IS_DROPPED(ctrl_ctx, i))
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ /* Overwrite the information stored in the endpoints' bw_info */
+ xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev);
+ for (i = 0; i < 31; i++) {
+ /* Add any changed or added endpoints to the interval table */
+ if (EP_IS_ADDED(ctrl_ctx, i))
+ xhci_add_ep_to_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+
+ if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) {
+ /* Ok, this fits in the bandwidth we have.
+ * Update the number of active TTs.
+ */
+ xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
+ return 0;
+ }
+
+ /* We don't have enough bandwidth for this, revert the stored info. */
+ for (i = 0; i < 31; i++) {
+ if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
+ continue;
+
+ /* Drop the new copies of any added or changed endpoints from
+ * the interval table.
+ */
+ if (EP_IS_ADDED(ctrl_ctx, i)) {
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ /* Revert the endpoint back to its old information */
+ memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i],
+ sizeof(ep_bw_info[i]));
+ /* Add any changed or dropped endpoints back into the table */
+ if (EP_IS_DROPPED(ctrl_ctx, i))
+ xhci_add_ep_to_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ virt_dev->udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ }
+ return -ENOMEM;
+}
+
+
/* Issue a configure endpoint command or evaluate context command
* and wait for it to finish.
*/
@@ -1765,17 +2375,30 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
spin_lock_irqsave(&xhci->lock, flags);
virt_dev = xhci->devs[udev->slot_id];
- if (command) {
+
+ if (command)
in_ctx = command->in_ctx;
- if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) &&
- xhci_reserve_host_resources(xhci, in_ctx)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_warn(xhci, "Not enough host resources, "
- "active endpoint contexts = %u\n",
- xhci->num_active_eps);
- return -ENOMEM;
- }
+ else
+ in_ctx = virt_dev->in_ctx;
+
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) &&
+ xhci_reserve_host_resources(xhci, in_ctx)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "Not enough host resources, "
+ "active endpoint contexts = %u\n",
+ xhci->num_active_eps);
+ return -ENOMEM;
+ }
+ if ((xhci->quirks & XHCI_SW_BW_CHECKING) &&
+ xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) {
+ if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
+ xhci_free_host_resources(xhci, in_ctx);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_warn(xhci, "Not enough bandwidth\n");
+ return -ENOMEM;
+ }
+ if (command) {
cmd_completion = command->completion;
cmd_status = &command->status;
command->command_trb = xhci->cmd_ring->enqueue;
@@ -1789,15 +2412,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
} else {
- in_ctx = virt_dev->in_ctx;
- if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) &&
- xhci_reserve_host_resources(xhci, in_ctx)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_warn(xhci, "Not enough host resources, "
- "active endpoint contexts = %u\n",
- xhci->num_active_eps);
- return -ENOMEM;
- }
cmd_completion = &virt_dev->cmd_completion;
cmd_status = &virt_dev->cmd_status;
}
@@ -1888,6 +2502,12 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG);
ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
+
+ /* Don't issue the command if there's no endpoints to update. */
+ if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) &&
+ ctrl_ctx->drop_flags == 0)
+ return 0;
+
xhci_dbg(xhci, "New Input Control Context:\n");
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
@@ -2525,6 +3145,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
int timeleft;
int last_freed_endpoint;
struct xhci_slot_ctx *slot_ctx;
+ int old_active_eps = 0;
ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__);
if (ret <= 0)
@@ -2666,7 +3287,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
last_freed_endpoint = i;
}
- }
+ if (!list_empty(&virt_dev->eps[i].bw_endpoint_list))
+ xhci_drop_ep_from_interval_table(xhci,
+ &virt_dev->eps[i].bw_info,
+ virt_dev->bw_table,
+ udev,
+ &virt_dev->eps[i],
+ virt_dev->tt_info);
+ xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info);
+ }
+ /* If necessary, update the number of active TTs on this root port */
+ xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
+
xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
ret = 0;
@@ -2704,6 +3336,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
+ if (udev->usb2_hw_lpm_enabled) {
+ xhci_set_usb2_hardware_lpm(hcd, udev, 0);
+ udev->usb2_hw_lpm_enabled = 0;
+ }
+
spin_lock_irqsave(&xhci->lock, flags);
/* Don't disable the slot if the host controller is dead. */
state = xhci_readl(xhci, &xhci->op_regs->status);
@@ -2889,7 +3526,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
* command on a timeout.
*/
if (timeleft <= 0) {
- xhci_warn(xhci, "%s while waiting for a slot\n",
+ xhci_warn(xhci, "%s while waiting for address device command\n",
timeleft == 0 ? "Timeout" : "Signal");
/* FIXME cancel the address device command */
return -ETIME;
@@ -2957,6 +3594,254 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
return 0;
}
+#ifdef CONFIG_USB_SUSPEND
+
+/* BESL to HIRD Encoding array for USB2 LPM */
+static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000,
+ 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
+
+/* Calculate HIRD/BESL for USB2 PORTPMSC*/
+static int xhci_calculate_hird_besl(int u2del, bool use_besl)
+{
+ int hird;
+
+ if (use_besl) {
+ for (hird = 0; hird < 16; hird++) {
+ if (xhci_besl_encoding[hird] >= u2del)
+ break;
+ }
+ } else {
+ if (u2del <= 50)
+ hird = 0;
+ else
+ hird = (u2del - 51) / 75 + 1;
+
+ if (hird > 15)
+ hird = 15;
+ }
+
+ return hird;
+}
+
+static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
+ struct usb_device *udev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct dev_info *dev_info;
+ __le32 __iomem **port_array;
+ __le32 __iomem *addr, *pm_addr;
+ u32 temp, dev_id;
+ unsigned int port_num;
+ unsigned long flags;
+ int u2del, hird;
+ int ret;
+
+ if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
+ !udev->lpm_capable)
+ return -EINVAL;
+
+ /* we only support lpm for non-hub device connected to root hub yet */
+ if (!udev->parent || udev->parent->parent ||
+ udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ return -EINVAL;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Look for devices in lpm_failed_devs list */
+ dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 |
+ le16_to_cpu(udev->descriptor.idProduct);
+ list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) {
+ if (dev_info->dev_id == dev_id) {
+ ret = -EINVAL;
+ goto finish;
+ }
+ }
+
+ port_array = xhci->usb2_ports;
+ port_num = udev->portnum - 1;
+
+ if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) {
+ xhci_dbg(xhci, "invalid port number %d\n", udev->portnum);
+ ret = -EINVAL;
+ goto finish;
+ }
+
+ /*
+ * Test USB 2.0 software LPM.
+ * FIXME: some xHCI 1.0 hosts may implement a new register to set up
+ * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1
+ * in the June 2011 errata release.
+ */
+ xhci_dbg(xhci, "test port %d software LPM\n", port_num);
+ /*
+ * Set L1 Device Slot and HIRD/BESL.
+ * Check device's USB 2.0 extension descriptor to determine whether
+ * HIRD or BESL shoule be used. See USB2.0 LPM errata.
+ */
+ pm_addr = port_array[port_num] + 1;
+ u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+ if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
+ hird = xhci_calculate_hird_besl(u2del, 1);
+ else
+ hird = xhci_calculate_hird_besl(u2del, 0);
+
+ temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
+ xhci_writel(xhci, temp, pm_addr);
+
+ /* Set port link state to U2(L1) */
+ addr = port_array[port_num];
+ xhci_set_link_state(xhci, port_array, port_num, XDEV_U2);
+
+ /* wait for ACK */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Check L1 Status */
+ ret = handshake(xhci, pm_addr, PORT_L1S_MASK, PORT_L1S_SUCCESS, 125);
+ if (ret != -ETIMEDOUT) {
+ /* enter L1 successfully */
+ temp = xhci_readl(xhci, addr);
+ xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n",
+ port_num, temp);
+ ret = 0;
+ } else {
+ temp = xhci_readl(xhci, pm_addr);
+ xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n",
+ port_num, temp & PORT_L1S_MASK);
+ ret = -EINVAL;
+ }
+
+ /* Resume the port */
+ xhci_set_link_state(xhci, port_array, port_num, XDEV_U0);
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Clear PLC */
+ xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC);
+
+ /* Check PORTSC to make sure the device is in the right state */
+ if (!ret) {
+ temp = xhci_readl(xhci, addr);
+ xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp);
+ if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) ||
+ (temp & PORT_PLS_MASK) != XDEV_U0) {
+ xhci_dbg(xhci, "port L1 resume fail\n");
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret) {
+ /* Insert dev to lpm_failed_devs list */
+ xhci_warn(xhci, "device LPM test failed, may disconnect and "
+ "re-enumerate\n");
+ dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC);
+ if (!dev_info) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+ dev_info->dev_id = dev_id;
+ INIT_LIST_HEAD(&dev_info->list);
+ list_add(&dev_info->list, &xhci->lpm_failed_devs);
+ } else {
+ xhci_ring_device(xhci, udev->slot_id);
+ }
+
+finish:
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return ret;
+}
+
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ __le32 __iomem **port_array;
+ __le32 __iomem *pm_addr;
+ u32 temp;
+ unsigned int port_num;
+ unsigned long flags;
+ int u2del, hird;
+
+ if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
+ !udev->lpm_capable)
+ return -EPERM;
+
+ if (!udev->parent || udev->parent->parent ||
+ udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ return -EPERM;
+
+ if (udev->usb2_hw_lpm_capable != 1)
+ return -EPERM;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ port_array = xhci->usb2_ports;
+ port_num = udev->portnum - 1;
+ pm_addr = port_array[port_num] + 1;
+ temp = xhci_readl(xhci, pm_addr);
+
+ xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
+ enable ? "enable" : "disable", port_num);
+
+ u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+ if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
+ hird = xhci_calculate_hird_besl(u2del, 1);
+ else
+ hird = xhci_calculate_hird_besl(u2del, 0);
+
+ if (enable) {
+ temp &= ~PORT_HIRD_MASK;
+ temp |= PORT_HIRD(hird) | PORT_RWE;
+ xhci_writel(xhci, temp, pm_addr);
+ temp = xhci_readl(xhci, pm_addr);
+ temp |= PORT_HLE;
+ xhci_writel(xhci, temp, pm_addr);
+ } else {
+ temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
+ xhci_writel(xhci, temp, pm_addr);
+ }
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return 0;
+}
+
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ret;
+
+ ret = xhci_usb2_software_lpm_test(hcd, udev);
+ if (!ret) {
+ xhci_dbg(xhci, "software LPM test succeed\n");
+ if (xhci->hw_lpm_support == 1) {
+ udev->usb2_hw_lpm_capable = 1;
+ ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
+ if (!ret)
+ udev->usb2_hw_lpm_enabled = 1;
+ }
+ }
+
+ return 0;
+}
+
+#else
+
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable)
+{
+ return 0;
+}
+
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ return 0;
+}
+
+#endif /* CONFIG_USB_SUSPEND */
+
/* Once a hub descriptor is fetched for a device, we need to update the xHC's
* internal data structures for the device.
*/
@@ -2988,6 +3873,14 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
}
spin_lock_irqsave(&xhci->lock, flags);
+ if (hdev->speed == USB_SPEED_HIGH &&
+ xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) {
+ xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n");
+ xhci_free_command(xhci, config_cmd);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ENOMEM;
+ }
+
xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx);
ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx);
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
@@ -3051,22 +3944,108 @@ int xhci_get_frame(struct usb_hcd *hcd)
return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3;
}
+int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
+{
+ struct xhci_hcd *xhci;
+ struct device *dev = hcd->self.controller;
+ int retval;
+ u32 temp;
+
+ hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
+
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
+ if (!xhci)
+ return -ENOMEM;
+ *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+ xhci->main_hcd = hcd;
+ /* Mark the first roothub as being USB 2.0.
+ * The xHCI driver will register the USB 3.0 roothub.
+ */
+ hcd->speed = HCD_USB2;
+ hcd->self.root_hub->speed = USB_SPEED_HIGH;
+ /*
+ * USB 2.0 roothub under xHCI has an integrated TT,
+ * (rate matching hub) as opposed to having an OHCI/UHCI
+ * companion controller.
+ */
+ hcd->has_tt = 1;
+ } else {
+ /* xHCI private pointer was set in xhci_pci_probe for the second
+ * registered roothub.
+ */
+ xhci = hcd_to_xhci(hcd);
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ if (HCC_64BIT_ADDR(temp)) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
+ }
+ return 0;
+ }
+
+ xhci->cap_regs = hcd->regs;
+ xhci->op_regs = hcd->regs +
+ HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
+ xhci->run_regs = hcd->regs +
+ (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
+ /* Cache read-only capability registers */
+ xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
+ xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
+ xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+ xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
+ xhci->hci_version = HC_VERSION(xhci->hcc_params);
+ xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ xhci_print_registers(xhci);
+
+ get_quirks(dev, xhci);
+
+ /* Make sure the HC is halted. */
+ retval = xhci_halt(xhci);
+ if (retval)
+ goto error;
+
+ xhci_dbg(xhci, "Resetting HCD\n");
+ /* Reset the internal HC memory state and registers. */
+ retval = xhci_reset(xhci);
+ if (retval)
+ goto error;
+ xhci_dbg(xhci, "Reset complete\n");
+
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ if (HCC_64BIT_ADDR(temp)) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
+ }
+
+ xhci_dbg(xhci, "Calling HCD init\n");
+ /* Initialize HCD and host controller data structures. */
+ retval = xhci_init(hcd);
+ if (retval)
+ goto error;
+ xhci_dbg(xhci, "Called HCD init\n");
+ return 0;
+error:
+ kfree(xhci);
+ return retval;
+}
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
static int __init xhci_hcd_init(void)
{
-#ifdef CONFIG_PCI
- int retval = 0;
+ int retval;
retval = xhci_register_pci();
-
if (retval < 0) {
printk(KERN_DEBUG "Problem registering PCI driver.");
return retval;
}
-#endif
/*
* Check the compiler generated sizes of structures that must be laid
* out in specific ways for hardware access.
@@ -3091,8 +4070,6 @@ module_init(xhci_hcd_init);
static void __exit xhci_hcd_cleanup(void)
{
-#ifdef CONFIG_PCI
xhci_unregister_pci();
-#endif
}
module_exit(xhci_hcd_cleanup);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index cae8e23308bf..3c8fbd2772ea 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -272,6 +272,7 @@ struct xhci_op_regs {
*/
#define PORT_PLS_MASK (0xf << 5)
#define XDEV_U0 (0x0 << 5)
+#define XDEV_U2 (0x2 << 5)
#define XDEV_U3 (0x3 << 5)
#define XDEV_RESUME (0xf << 5)
/* true: port has power (see HCC_PPC) */
@@ -362,7 +363,13 @@ struct xhci_op_regs {
/* Bits 24:31 for port testing */
/* USB2 Protocol PORTSPMSC */
-#define PORT_RWE (1 << 0x3)
+#define PORT_L1S_MASK 7
+#define PORT_L1S_SUCCESS 1
+#define PORT_RWE (1 << 3)
+#define PORT_HIRD(p) (((p) & 0xf) << 4)
+#define PORT_HIRD_MASK (0xf << 4)
+#define PORT_L1DS(p) (((p) & 0xff) << 8)
+#define PORT_HLE (1 << 16)
/**
* struct xhci_intr_reg - Interrupt Register Set
@@ -611,11 +618,13 @@ struct xhci_ep_ctx {
#define EP_STATE_ERROR 4
/* Mult - Max number of burtst within an interval, in EP companion desc. */
#define EP_MULT(p) (((p) & 0x3) << 8)
+#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3)
/* bits 10:14 are Max Primary Streams */
/* bit 15 is Linear Stream Array */
/* Interval - period between requests to an endpoint - 125u increments. */
#define EP_INTERVAL(p) (((p) & 0xff) << 16)
#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
+#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff)
#define EP_MAXPSTREAMS_MASK (0x1f << 10)
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
@@ -640,6 +649,7 @@ struct xhci_ep_ctx {
/* bit 6 reserved */
/* bit 7 is Host Initiate Disable - for disabling stream selection */
#define MAX_BURST(p) (((p)&0xff) << 8)
+#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff)
#define MAX_PACKET(p) (((p)&0xffff) << 16)
#define MAX_PACKET_MASK (0xffff << 16)
#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff)
@@ -652,6 +662,7 @@ struct xhci_ep_ctx {
/* tx_info bitmasks */
#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff)
#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16)
+#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
/* deq bitmasks */
#define EP_CTX_CYCLE_MASK (1 << 0)
@@ -670,6 +681,11 @@ struct xhci_input_control_ctx {
__le32 rsvd2[6];
};
+#define EP_IS_ADDED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))
+#define EP_IS_DROPPED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1)))
+
/* Represents everything that is needed to issue a command on the command ring.
* It's useful to pre-allocate these for commands that cannot fail due to
* out-of-memory errors, like freeing streams.
@@ -731,6 +747,67 @@ struct xhci_stream_info {
#define SMALL_STREAM_ARRAY_SIZE 256
#define MEDIUM_STREAM_ARRAY_SIZE 1024
+/* Some Intel xHCI host controllers need software to keep track of the bus
+ * bandwidth. Keep track of endpoint info here. Each root port is allocated
+ * the full bus bandwidth. We must also treat TTs (including each port under a
+ * multi-TT hub) as a separate bandwidth domain. The direct memory interface
+ * (DMI) also limits the total bandwidth (across all domains) that can be used.
+ */
+struct xhci_bw_info {
+ /* ep_interval is zero-based */
+ unsigned int ep_interval;
+ /* mult and num_packets are one-based */
+ unsigned int mult;
+ unsigned int num_packets;
+ unsigned int max_packet_size;
+ unsigned int max_esit_payload;
+ unsigned int type;
+};
+
+/* "Block" sizes in bytes the hardware uses for different device speeds.
+ * The logic in this part of the hardware limits the number of bits the hardware
+ * can use, so must represent bandwidth in a less precise manner to mimic what
+ * the scheduler hardware computes.
+ */
+#define FS_BLOCK 1
+#define HS_BLOCK 4
+#define SS_BLOCK 16
+#define DMI_BLOCK 32
+
+/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated
+ * with each byte transferred. SuperSpeed devices have an initial overhead to
+ * set up bursts. These are in blocks, see above. LS overhead has already been
+ * translated into FS blocks.
+ */
+#define DMI_OVERHEAD 8
+#define DMI_OVERHEAD_BURST 4
+#define SS_OVERHEAD 8
+#define SS_OVERHEAD_BURST 32
+#define HS_OVERHEAD 26
+#define FS_OVERHEAD 20
+#define LS_OVERHEAD 128
+/* The TTs need to claim roughly twice as much bandwidth (94 bytes per
+ * microframe ~= 24Mbps) of the HS bus as the devices can actually use because
+ * of overhead associated with split transfers crossing microframe boundaries.
+ * 31 blocks is pure protocol overhead.
+ */
+#define TT_HS_OVERHEAD (31 + 94)
+#define TT_DMI_OVERHEAD (25 + 12)
+
+/* Bandwidth limits in blocks */
+#define FS_BW_LIMIT 1285
+#define TT_BW_LIMIT 1320
+#define HS_BW_LIMIT 1607
+#define SS_BW_LIMIT_IN 3906
+#define DMI_BW_LIMIT_IN 3906
+#define SS_BW_LIMIT_OUT 3906
+#define DMI_BW_LIMIT_OUT 3906
+
+/* Percentage of bus bandwidth reserved for non-periodic transfers */
+#define FS_BW_RESERVED 10
+#define HS_BW_RESERVED 20
+#define SS_BW_RESERVED 10
+
struct xhci_virt_ep {
struct xhci_ring *ring;
/* Related to endpoints that are configured to use stream IDs only */
@@ -772,8 +849,39 @@ struct xhci_virt_ep {
* process the missed tds on the endpoint ring.
*/
bool skip;
+ /* Bandwidth checking storage */
+ struct xhci_bw_info bw_info;
+ struct list_head bw_endpoint_list;
+};
+
+enum xhci_overhead_type {
+ LS_OVERHEAD_TYPE = 0,
+ FS_OVERHEAD_TYPE,
+ HS_OVERHEAD_TYPE,
+};
+
+struct xhci_interval_bw {
+ unsigned int num_packets;
+ /* Sorted by max packet size.
+ * Head of the list is the greatest max packet size.
+ */
+ struct list_head endpoints;
+ /* How many endpoints of each speed are present. */
+ unsigned int overhead[3];
+};
+
+#define XHCI_MAX_INTERVAL 16
+
+struct xhci_interval_bw_table {
+ unsigned int interval0_esit_payload;
+ struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL];
+ /* Includes reserved bandwidth for async endpoints */
+ unsigned int bw_used;
+ unsigned int ss_bw_in;
+ unsigned int ss_bw_out;
};
+
struct xhci_virt_device {
struct usb_device *udev;
/*
@@ -798,7 +906,32 @@ struct xhci_virt_device {
/* Status of the last command issued for this device */
u32 cmd_status;
struct list_head cmd_list;
- u8 port;
+ u8 fake_port;
+ u8 real_port;
+ struct xhci_interval_bw_table *bw_table;
+ struct xhci_tt_bw_info *tt_info;
+};
+
+/*
+ * For each roothub, keep track of the bandwidth information for each periodic
+ * interval.
+ *
+ * If a high speed hub is attached to the roothub, each TT associated with that
+ * hub is a separate bandwidth domain. The interval information for the
+ * endpoints on the devices under that TT will appear in the TT structure.
+ */
+struct xhci_root_port_bw_info {
+ struct list_head tts;
+ unsigned int num_active_tts;
+ struct xhci_interval_bw_table bw_table;
+};
+
+struct xhci_tt_bw_info {
+ struct list_head tt_list;
+ int slot_id;
+ int ttport;
+ struct xhci_interval_bw_table bw_table;
+ int active_eps;
};
@@ -1198,6 +1331,12 @@ struct s3_save {
u64 erst_dequeue;
};
+/* Use for lpm */
+struct dev_info {
+ u32 dev_id;
+ struct list_head list;
+};
+
struct xhci_bus_state {
unsigned long bus_suspended;
unsigned long next_statechange;
@@ -1261,12 +1400,16 @@ struct xhci_hcd {
struct xhci_erst erst;
/* Scratchpad */
struct xhci_scratchpad *scratchpad;
+ /* Store LPM test failed devices' information */
+ struct list_head lpm_failed_devs;
/* slot enabling and address device helpers */
struct completion addr_dev;
int slot_id;
/* Internal mirror of the HW's dcbaa */
struct xhci_virt_device *devs[MAX_HC_SLOTS];
+ /* For keeping track of bandwidth domains per roothub. */
+ struct xhci_root_port_bw_info *rh_bw;
/* DMA pools */
struct dma_pool *device_pool;
@@ -1318,6 +1461,8 @@ struct xhci_hcd {
#define XHCI_EP_LIMIT_QUIRK (1 << 5)
#define XHCI_BROKEN_MSI (1 << 6)
#define XHCI_RESET_ON_RESUME (1 << 7)
+#define XHCI_SW_BW_CHECKING (1 << 8)
+#define XHCI_AMD_0x96_HOST (1 << 9)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1330,6 +1475,10 @@ struct xhci_hcd {
/* Array of pointers to USB 2.0 PORTSC registers */
__le32 __iomem **usb2_ports;
unsigned int num_usb2_ports;
+ /* support xHCI 0.96 spec USB2 software LPM */
+ unsigned sw_lpm_support:1;
+ /* support xHCI 1.0 spec USB2 hardware LPM */
+ unsigned hw_lpm_support:1;
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -1401,9 +1550,7 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
{
- u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
- return ((HC_VERSION(temp) == 0x95) &&
- (xhci->quirks & XHCI_LINK_TRB_QUIRK));
+ return xhci->quirks & XHCI_LINK_TRB_QUIRK;
}
/* xHCI debugging */
@@ -1438,6 +1585,20 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc);
unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index);
unsigned int xhci_last_valid_endpoint(u32 added_ctxs);
void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep);
+void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
+ struct xhci_bw_info *ep_bw,
+ struct xhci_interval_bw_table *bw_table,
+ struct usb_device *udev,
+ struct xhci_virt_ep *virt_ep,
+ struct xhci_tt_bw_info *tt_info);
+void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int old_active_eps);
+void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info);
+void xhci_update_bw_info(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *in_ctx,
+ struct xhci_input_control_ctx *ctrl_ctx,
+ struct xhci_virt_device *virt_dev);
void xhci_endpoint_copy(struct xhci_hcd *xhci,
struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx,
@@ -1483,9 +1644,13 @@ void xhci_free_command(struct xhci_hcd *xhci,
/* xHCI PCI glue */
int xhci_register_pci(void);
void xhci_unregister_pci(void);
+#else
+static inline int xhci_register_pci(void) { return 0; }
+static inline void xhci_unregister_pci(void) {}
#endif
/* xHCI host controller glue */
+typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
@@ -1493,6 +1658,7 @@ int xhci_init(struct usb_hcd *hcd);
int xhci_run(struct usb_hcd *hcd);
void xhci_stop(struct usb_hcd *hcd);
void xhci_shutdown(struct usb_hcd *hcd);
+int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
#ifdef CONFIG_PM
int xhci_suspend(struct xhci_hcd *xhci);
@@ -1507,6 +1673,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_alloc_tt_info(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
unsigned int num_streams, gfp_t mem_flags);
@@ -1514,6 +1684,9 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags);
int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
+ struct usb_device *udev, int enable);
int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
@@ -1572,6 +1745,10 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
/* xHCI roothub code */
+void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 link_state);
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
+ int port_id, u32 port_bit);
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index a6afd15f6a46..fe858711651c 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -213,7 +213,7 @@ static void adu_interrupt_in_callback(struct urb *urb)
if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
if (dev->read_buffer_length <
- (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) -
+ (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) -
(urb->actual_length)) {
memcpy (dev->read_buffer_primary +
dev->read_buffer_length,
@@ -315,7 +315,7 @@ static int adu_open(struct inode *inode, struct file *file)
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
- le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ usb_endpoint_maxp(dev->interrupt_in_endpoint),
adu_interrupt_in_callback, dev,
dev->interrupt_in_endpoint->bInterval);
dev->read_urb_finished = 0;
@@ -483,7 +483,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
- le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ usb_endpoint_maxp(dev->interrupt_in_endpoint),
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
@@ -536,7 +536,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
- le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ usb_endpoint_maxp(dev->interrupt_in_endpoint),
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
@@ -622,7 +622,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
dbg(4," %s : sending, count = %Zd", __func__, count);
/* write the data into interrupt_out_buffer from userspace */
- buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint);
bytes_to_write = count > buffer_size ? buffer_size : count;
dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd",
__func__, buffer_size, count, bytes_to_write);
@@ -752,8 +752,8 @@ static int adu_probe(struct usb_interface *interface,
goto error;
}
- in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
- out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+ in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
+ out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint);
dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
if (!dev->read_buffer_primary) {
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 2f41089cd854..2dbe600fbc11 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2777,7 +2777,7 @@ static int ftdi_elan_probe(struct usb_interface *interface,
endpoint = &iface_desc->endpoint[i].desc;
if (!ftdi->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
ftdi->bulk_in_size = buffer_size;
ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index c6184b4d1695..515b67fffab1 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -359,7 +359,7 @@ static int idmouse_probe(struct usb_interface *interface,
endpoint = &iface_desc->endpoint[0].desc;
if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
- dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->orig_bi_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = 0x200; /* works _much_ faster */
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer =
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index a2190b983f52..81457904d6ba 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -803,7 +803,7 @@ static int iowarrior_probe(struct usb_interface *interface,
dev->int_out_endpoint = endpoint;
}
/* we have to check the report_size often, so remember it in the endianess suitable for our machine */
- dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize);
+ dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
(dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
/* IOWarrior56 has wMaxPacketSize different from report size */
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index cb4096201e29..48c166f0d764 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -721,7 +721,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
if (dev->interrupt_out_endpoint == NULL)
dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");
- dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
+ dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
if (!dev->ring_buffer) {
dev_err(&intf->dev, "Couldn't allocate ring_buffer\n");
@@ -737,7 +737,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");
goto error;
}
- dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) :
+ dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) :
udev->descriptor.bMaxPacketSize0;
dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
if (!dev->interrupt_out_buffer) {
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 6482c6e2e6bd..a989356f693e 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -409,7 +409,7 @@ static int tower_open (struct inode *inode, struct file *file)
dev->udev,
usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
- le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ usb_endpoint_maxp(dev->interrupt_in_endpoint),
tower_interrupt_in_callback,
dev,
dev->interrupt_in_interval);
@@ -928,7 +928,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
err("Couldn't allocate read_buffer");
goto error;
}
- dev->interrupt_in_buffer = kmalloc (le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL);
+ dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
if (!dev->interrupt_in_buffer) {
err("Couldn't allocate interrupt_in_buffer");
goto error;
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 51648154bb44..1871cdf10da3 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#define DRIVER_VERSION "USBLCD Driver Version 1.05"
@@ -34,22 +34,29 @@ static const struct usb_device_id id_table[] = {
{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
{ },
};
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE(usb, id_table);
static DEFINE_MUTEX(open_disc_mutex);
struct usb_lcd {
- struct usb_device * udev; /* init: probe_lcd */
- struct usb_interface * interface; /* the interface for this device */
- unsigned char * bulk_in_buffer; /* the buffer to receive data */
- size_t bulk_in_size; /* the size of the receive buffer */
- __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
- __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
+ struct usb_device *udev; /* init: probe_lcd */
+ struct usb_interface *interface; /* the interface for
+ this device */
+ unsigned char *bulk_in_buffer; /* the buffer to receive
+ data */
+ size_t bulk_in_size; /* the size of the
+ receive buffer */
+ __u8 bulk_in_endpointAddr; /* the address of the
+ bulk in endpoint */
+ __u8 bulk_out_endpointAddr; /* the address of the
+ bulk out endpoint */
struct kref kref;
- struct semaphore limit_sem; /* to stop writes at full throttle from
- * using up all RAM */
- struct usb_anchor submitted; /* URBs to wait for before suspend */
+ struct semaphore limit_sem; /* to stop writes at
+ full throttle from
+ using up all RAM */
+ struct usb_anchor submitted; /* URBs to wait for
+ before suspend */
};
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
@@ -63,8 +70,8 @@ static void lcd_delete(struct kref *kref)
struct usb_lcd *dev = to_lcd_dev(kref);
usb_put_dev(dev->udev);
- kfree (dev->bulk_in_buffer);
- kfree (dev);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev);
}
@@ -80,7 +87,7 @@ static int lcd_open(struct inode *inode, struct file *file)
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
mutex_unlock(&lcd_mutex);
- err ("USBLCD: %s - error, can't find device for minor %d",
+ err("USBLCD: %s - error, can't find device for minor %d",
__func__, subminor);
return -ENODEV;
}
@@ -126,7 +133,8 @@ static int lcd_release(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t lcd_read(struct file *file, char __user * buffer,
+ size_t count, loff_t *ppos)
{
struct usb_lcd *dev;
int retval = 0;
@@ -135,8 +143,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l
dev = file->private_data;
/* do a blocking bulk read to get data from the device */
- retval = usb_bulk_msg(dev->udev,
- usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+ retval = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&bytes_read, 10000);
@@ -161,23 +170,23 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
-
+
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
mutex_lock(&lcd_mutex);
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
- sprintf(buf,"%1d%1d.%1d%1d",
+ sprintf(buf, "%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
mutex_unlock(&lcd_mutex);
- if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
+ if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
return -EFAULT;
break;
case IOCTL_GET_DRV_VERSION:
- sprintf(buf,DRIVER_VERSION);
- if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
+ sprintf(buf, DRIVER_VERSION);
+ if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
return -EFAULT;
break;
default:
@@ -199,7 +208,7 @@ static void lcd_write_bulk_callback(struct urb *urb)
if (status &&
!(status == -ENOENT ||
status == -ECONNRESET ||
- status == -ESHUTDOWN)) {
+ status == -ESHUTDOWN)) {
dbg("USBLCD: %s - nonzero write bulk status received: %d",
__func__, status);
}
@@ -210,15 +219,16 @@ static void lcd_write_bulk_callback(struct urb *urb)
up(&dev->limit_sem);
}
-static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos)
+static ssize_t lcd_write(struct file *file, const char __user * user_buffer,
+ size_t count, loff_t *ppos)
{
struct usb_lcd *dev;
- int retval = 0, r;
+ int retval = 0, r;
struct urb *urb = NULL;
char *buf = NULL;
-
+
dev = file->private_data;
-
+
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
@@ -233,34 +243,38 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
retval = -ENOMEM;
goto err_no_buf;
}
-
- buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
+
+ buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
+ &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
-
+
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
-
+
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
- usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out_endpointAddr),
buf, count, lcd_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
-
+
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
- err("USBLCD: %s - failed submitting write urb, error %d", __func__, retval);
+ err("USBLCD: %s - failed submitting write urb, error %d",
+ __func__, retval);
goto error_unanchor;
}
-
- /* release our reference to this urb, the USB core will eventually free it entirely */
+
+ /* release our reference to this urb,
+ the USB core will eventually free it entirely */
usb_free_urb(urb);
exit:
@@ -276,13 +290,13 @@ err_no_buf:
}
static const struct file_operations lcd_fops = {
- .owner = THIS_MODULE,
- .read = lcd_read,
- .write = lcd_write,
- .open = lcd_open,
+ .owner = THIS_MODULE,
+ .read = lcd_read,
+ .write = lcd_write,
+ .open = lcd_open,
.unlocked_ioctl = lcd_ioctl,
- .release = lcd_release,
- .llseek = noop_llseek,
+ .release = lcd_release,
+ .llseek = noop_llseek,
};
/*
@@ -290,12 +304,13 @@ static const struct file_operations lcd_fops = {
* and to have the device registered with the driver core
*/
static struct usb_class_driver lcd_class = {
- .name = "lcd%d",
- .fops = &lcd_fops,
- .minor_base = USBLCD_MINOR,
+ .name = "lcd%d",
+ .fops = &lcd_fops,
+ .minor_base = USBLCD_MINOR,
};
-static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id)
+static int lcd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
{
struct usb_lcd *dev = NULL;
struct usb_host_interface *iface_desc;
@@ -322,7 +337,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
retval = -ENODEV;
goto error;
}
-
+
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
@@ -332,7 +347,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -369,7 +384,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
"at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
- (i & 0xF0)>>4,(i & 0xF), dev->udev->devnum);
+ (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum);
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
@@ -401,7 +416,7 @@ static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
}
-static int lcd_resume (struct usb_interface *intf)
+static int lcd_resume(struct usb_interface *intf)
{
return 0;
}
@@ -409,16 +424,16 @@ static int lcd_resume (struct usb_interface *intf)
static void lcd_disconnect(struct usb_interface *interface)
{
struct usb_lcd *dev;
- int minor = interface->minor;
+ int minor = interface->minor;
mutex_lock(&open_disc_mutex);
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
mutex_unlock(&open_disc_mutex);
- /* give back our minor */
- usb_deregister_dev(interface, &lcd_class);
-
+ /* give back our minor */
+ usb_deregister_dev(interface, &lcd_class);
+
/* decrement our usage count */
kref_put(&dev->kref, lcd_delete);
@@ -438,7 +453,7 @@ static struct usb_driver lcd_driver = {
static int __init usb_lcd_init(void)
{
int result;
-
+
result = usb_register(&lcd_driver);
if (result)
err("usb_register failed. Error number %d", result);
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
index 1616ad1793a4..43f84e50d514 100644
--- a/drivers/usb/misc/usbled.c
+++ b/drivers/usb/misc/usbled.c
@@ -33,10 +33,10 @@ static const struct usb_device_id id_table[] = {
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
{ },
};
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE(usb, id_table);
struct usb_led {
- struct usb_device * udev;
+ struct usb_device *udev;
unsigned char blue;
unsigned char red;
unsigned char green;
@@ -113,14 +113,16 @@ static void change_color(struct usb_led *led)
}
#define show_set(value) \
-static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
+ char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
\
return sprintf(buf, "%d\n", led->value); \
} \
-static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
+ const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
@@ -135,7 +137,8 @@ show_set(blue);
show_set(red);
show_set(green);
-static int led_probe(struct usb_interface *interface, const struct usb_device_id *id)
+static int led_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_led *dev = NULL;
@@ -150,7 +153,7 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
- usb_set_intfdata (interface, dev);
+ usb_set_intfdata(interface, dev);
retval = device_create_file(&interface->dev, &dev_attr_blue);
if (retval)
@@ -194,7 +197,7 @@ error:
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
- usb_set_intfdata (interface, NULL);
+ usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
@@ -205,14 +208,14 @@ static void led_disconnect(struct usb_interface *interface)
{
struct usb_led *dev;
- dev = usb_get_intfdata (interface);
+ dev = usb_get_intfdata(interface);
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
/* first remove the files, then set the pointer to NULL */
- usb_set_intfdata (interface, NULL);
+ usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
@@ -243,8 +246,8 @@ static void __exit usb_led_exit(void)
usb_deregister(&led_driver);
}
-module_init (usb_led_init);
-module_exit (usb_led_exit);
+module_init(usb_led_init);
+module_exit(usb_led_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index bb10846affc3..bd6d00802eab 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -359,8 +359,10 @@ static int simple_io(
urb->context = &completion;
while (retval == 0 && iterations-- > 0) {
init_completion(&completion);
- if (usb_pipeout(urb->pipe))
+ if (usb_pipeout(urb->pipe)) {
simple_fill_buf(urb);
+ urb->transfer_flags |= URB_ZERO_PACKET;
+ }
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval != 0)
break;
@@ -1583,8 +1585,8 @@ static struct urb *iso_alloc_urb(
if (bytes < 0 || !desc)
return NULL;
- maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
- maxp *= 1 + (0x3 & (le16_to_cpu(desc->wMaxPacketSize) >> 11));
+ maxp = 0x7ff & usb_endpoint_maxp(desc);
+ maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11));
packets = DIV_ROUND_UP(bytes, maxp);
urb = usb_alloc_urb(packets, GFP_KERNEL);
@@ -1654,7 +1656,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
"... iso period %d %sframes, wMaxPacket %04x\n",
1 << (desc->bInterval - 1),
(udev->speed == USB_SPEED_HIGH) ? "micro" : "",
- le16_to_cpu(desc->wMaxPacketSize));
+ usb_endpoint_maxp(desc));
for (i = 0; i < param->sglen; i++) {
urbs[i] = iso_alloc_urb(udev, pipe, desc,
@@ -2298,25 +2300,8 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_set_intfdata(intf, dev);
dev_info(&intf->dev, "%s\n", info->name);
- dev_info(&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n",
- ({ char *tmp;
- switch (udev->speed) {
- case USB_SPEED_LOW:
- tmp = "low";
- break;
- case USB_SPEED_FULL:
- tmp = "full";
- break;
- case USB_SPEED_HIGH:
- tmp = "high";
- break;
- case USB_SPEED_SUPER:
- tmp = "super";
- break;
- default:
- tmp = "unknown";
- break;
- }; tmp; }),
+ dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n",
+ usb_speed_string(udev->speed),
info->ctrl_out ? " in/out" : "",
rtest, wtest,
irtest, iwtest,
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index a09dbd243eb3..a04b2ff9dd83 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1101,7 +1101,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg
nevents = mon_bin_queued(rp);
sp = (struct mon_bin_stats __user *)arg;
- if (put_user(rp->cnt_lost, &sp->dropped))
+ if (put_user(ndropped, &sp->dropped))
return -EFAULT;
if (put_user(nevents, &sp->queued))
return -EFAULT;
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 8bdf25a8b023..f9a3f62a83b5 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -35,7 +35,7 @@
#include <mach/hardware.h>
#include <mach/memory.h>
-#include <mach/gpio.h>
+#include <asm/gpio.h>
#include <mach/cputype.h>
#include <asm/mach-types.h>
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index b0176e4569e0..61f4ee466df7 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -41,12 +41,6 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#ifdef CONFIG_ARM
-#include <mach/hardware.h>
-#include <mach/memory.h>
-#include <asm/mach-types.h>
-#endif
-
#include <asm/uaccess.h>
#include "musb_core.h"
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index e81820370d6f..ae4a20acef6c 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -634,6 +634,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
u16 len;
u16 csr = musb_readw(epio, MUSB_RXCSR);
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
+ u8 use_mode_1;
if (hw_ep->is_shared_fifo)
musb_ep = &hw_ep->ep_in;
@@ -683,6 +684,18 @@ static void rxstate(struct musb *musb, struct musb_request *req)
if (csr & MUSB_RXCSR_RXPKTRDY) {
len = musb_readw(epio, MUSB_RXCOUNT);
+
+ /*
+ * Enable Mode 1 on RX transfers only when short_not_ok flag
+ * is set. Currently short_not_ok flag is set only from
+ * file_storage and f_mass_storage drivers
+ */
+
+ if (request->short_not_ok && len == musb_ep->packet_sz)
+ use_mode_1 = 1;
+ else
+ use_mode_1 = 0;
+
if (request->actual < request->length) {
#ifdef CONFIG_USB_INVENTRA_DMA
if (is_buffer_mapped(req)) {
@@ -704,7 +717,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* most these gadgets, end of is signified either by a short packet,
* or filling the last byte of the buffer. (Sending extra data in
* that last pckate should trigger an overflow fault.) But in mode 1,
- * we don't get DMA completion interrrupt for short packets.
+ * we don't get DMA completion interrupt for short packets.
*
* Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1),
* to get endpoint interrupt on every DMA req, but that didn't seem
@@ -714,37 +727,41 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* then becomes usable as a runtime "use mode 1" hint...
*/
- csr |= MUSB_RXCSR_DMAENAB;
-#ifdef USE_MODE1
- csr |= MUSB_RXCSR_AUTOCLEAR;
- /* csr |= MUSB_RXCSR_DMAMODE; */
-
- /* this special sequence (enabling and then
- * disabling MUSB_RXCSR_DMAMODE) is required
- * to get DMAReq to activate
- */
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
-#else
- if (!musb_ep->hb_mult &&
- musb_ep->hw_ep->rx_double_buffered)
+ /* Experimental: Mode1 works with mass storage use cases */
+ if (use_mode_1) {
csr |= MUSB_RXCSR_AUTOCLEAR;
-#endif
- musb_writew(epio, MUSB_RXCSR, csr);
+ musb_writew(epio, MUSB_RXCSR, csr);
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio, MUSB_RXCSR, csr);
+
+ /*
+ * this special sequence (enabling and then
+ * disabling MUSB_RXCSR_DMAMODE) is required
+ * to get DMAReq to activate
+ */
+ musb_writew(epio, MUSB_RXCSR,
+ csr | MUSB_RXCSR_DMAMODE);
+ musb_writew(epio, MUSB_RXCSR, csr);
+
+ } else {
+ if (!musb_ep->hb_mult &&
+ musb_ep->hw_ep->rx_double_buffered)
+ csr |= MUSB_RXCSR_AUTOCLEAR;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio, MUSB_RXCSR, csr);
+ }
if (request->actual < request->length) {
int transfer_size = 0;
-#ifdef USE_MODE1
- transfer_size = min(request->length - request->actual,
- channel->max_len);
-#else
- transfer_size = min(request->length - request->actual,
- (unsigned)len);
-#endif
- if (transfer_size <= musb_ep->packet_sz)
- musb_ep->dma->desired_mode = 0;
- else
+ if (use_mode_1) {
+ transfer_size = min(request->length - request->actual,
+ channel->max_len);
musb_ep->dma->desired_mode = 1;
+ } else {
+ transfer_size = min(request->length - request->actual,
+ (unsigned)len);
+ musb_ep->dma->desired_mode = 0;
+ }
use_dma = c->channel_program(
channel,
@@ -1020,7 +1037,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
goto fail;
/* REVISIT this rules out high bandwidth periodic transfers */
- tmp = le16_to_cpu(desc->wMaxPacketSize);
+ tmp = usb_endpoint_maxp(desc);
if (tmp & ~0x07ff) {
int ok;
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 9378b359c1f0..6a0d0467ec74 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -679,6 +679,14 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
musb_readb(mbase, MUSB_FADDR),
decode_ep0stage(musb->ep0_state));
+ if (csr & MUSB_CSR0_P_DATAEND) {
+ /*
+ * If DATAEND is set we should not call the callback,
+ * hence the status stage is not complete.
+ */
+ return IRQ_HANDLED;
+ }
+
/* I sent a stall.. need to acknowledge it now.. */
if (csr & MUSB_CSR0_P_SENTSTALL) {
musb_writew(regs, MUSB_CSR0,
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 8b2473fa0f47..60ddba8066ea 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -1932,7 +1932,7 @@ static int musb_urb_enqueue(
INIT_LIST_HEAD(&qh->ring);
qh->is_ready = 1;
- qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize);
+ qh->maxpacket = usb_endpoint_maxp(epd);
qh->type = usb_endpoint_type(epd);
/* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier.
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index f70c5a577736..57a608584e16 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -408,7 +408,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
controller->controller.channel_program = dma_channel_program;
controller->controller.channel_abort = dma_channel_abort;
- if (request_irq(irq, dma_controller_irq, IRQF_DISABLED,
+ if (request_irq(irq, dma_controller_irq, 0,
dev_name(musb->controller), &controller->controller)) {
dev_err(dev, "request_irq %d failed!\n", irq);
dma_controller_destroy(&controller->controller);
diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c
index ca9b690a7e40..8c86787c2f09 100644
--- a/drivers/usb/otg/isp1301_omap.c
+++ b/drivers/usb/otg/isp1301_omap.c
@@ -899,7 +899,7 @@ static int otg_bind(struct isp1301 *isp)
if (otg_dev)
status = request_irq(otg_dev->resource[1].start, omap_otg_irq,
- IRQF_DISABLED, DRIVER_NAME, isp);
+ 0, DRIVER_NAME, isp);
else
status = -ENODEV;
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index b4d2c0972b3d..ed2b26cfe814 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -137,22 +137,6 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
return ret;
}
-/*-------------------------------------------------------------------------*/
-static int twl6030_set_phy_clk(struct otg_transceiver *x, int on)
-{
- struct twl6030_usb *twl;
- struct device *dev;
- struct twl4030_usb_data *pdata;
-
- twl = xceiv_to_twl(x);
- dev = twl->dev;
- pdata = dev->platform_data;
-
- pdata->phy_set_clock(twl->dev, on);
-
- return 0;
-}
-
static int twl6030_phy_init(struct otg_transceiver *x)
{
struct twl6030_usb *twl;
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
index 286cbf1ca7de..6f4afa436381 100644
--- a/drivers/usb/renesas_usbhs/Kconfig
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -4,7 +4,7 @@
config USB_RENESAS_USBHS
tristate 'Renesas USBHS controller'
- depends on SUPERH || ARCH_SHMOBILE
+ depends on USB && USB_GADGET
default n
help
Renesas USBHS is a discrete USB host and peripheral controller chip
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index ce08345fa15a..bc8aef4311a1 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -6,4 +6,10 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
renesas_usbhs-y := common.o mod.o pipe.o fifo.o
-renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o
+ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
+ renesas_usbhs-y += mod_host.o
+endif
+
+ifneq ($(CONFIG_USB_RENESAS_USBHS_UDC),)
+ renesas_usbhs-y += mod_gadget.o
+endif
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index d8239e5efa66..d2e2efaba658 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -61,8 +61,8 @@
*/
#define usbhs_platform_call(priv, func, args...)\
(!(priv) ? -ENODEV : \
- !((priv)->pfunc->func) ? 0 : \
- (priv)->pfunc->func(args))
+ !((priv)->pfunc.func) ? 0 : \
+ (priv)->pfunc.func(args))
/*
* common functions
@@ -114,6 +114,10 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
{
u16 mask = DCFM | DRPD | DPRPU;
u16 val = DCFM | DRPD;
+ int has_otg = usbhs_get_dparam(priv, has_otg);
+
+ if (has_otg)
+ usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN));
/*
* if enable
@@ -147,19 +151,133 @@ int usbhs_frame_get_num(struct usbhs_priv *priv)
}
/*
+ * usb request functions
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ u16 val;
+
+ val = usbhs_read(priv, USBREQ);
+ req->bRequest = (val >> 8) & 0xFF;
+ req->bRequestType = (val >> 0) & 0xFF;
+
+ req->wValue = usbhs_read(priv, USBVAL);
+ req->wIndex = usbhs_read(priv, USBINDX);
+ req->wLength = usbhs_read(priv, USBLENG);
+}
+
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType);
+ usbhs_write(priv, USBVAL, req->wValue);
+ usbhs_write(priv, USBINDX, req->wIndex);
+ usbhs_write(priv, USBLENG, req->wLength);
+
+ usbhs_bset(priv, DCPCTR, SUREQ, SUREQ);
+}
+
+/*
+ * bus/vbus functions
+ */
+void usbhs_bus_send_sof_enable(struct usbhs_priv *priv)
+{
+ u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT);
+
+ if (status != USBRST) {
+ struct device *dev = usbhs_priv_to_dev(priv);
+ dev_err(dev, "usbhs should be reset\n");
+ }
+
+ usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT);
+}
+
+void usbhs_bus_send_reset(struct usbhs_priv *priv)
+{
+ usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST);
+}
+
+int usbhs_bus_get_speed(struct usbhs_priv *priv)
+{
+ u16 dvstctr = usbhs_read(priv, DVSTCTR);
+
+ switch (RHST & dvstctr) {
+ case RHST_LOW_SPEED:
+ return USB_SPEED_LOW;
+ case RHST_FULL_SPEED:
+ return USB_SPEED_FULL;
+ case RHST_HIGH_SPEED:
+ return USB_SPEED_HIGH;
+ }
+
+ return USB_SPEED_UNKNOWN;
+}
+
+int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable)
+{
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+ return usbhs_platform_call(priv, set_vbus, pdev, enable);
+}
+
+static void usbhsc_bus_init(struct usbhs_priv *priv)
+{
+ usbhs_write(priv, DVSTCTR, 0);
+
+ usbhs_vbus_ctrl(priv, 0);
+}
+
+/*
+ * device configuration
+ */
+int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum,
+ u16 upphub, u16 hubport, u16 speed)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+ u16 usbspd = 0;
+ u32 reg = DEVADD0 + (2 * devnum);
+
+ if (devnum > 10) {
+ dev_err(dev, "cannot set speed to unknown device %d\n", devnum);
+ return -EIO;
+ }
+
+ if (upphub > 0xA) {
+ dev_err(dev, "unsupported hub number %d\n", upphub);
+ return -EIO;
+ }
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ usbspd = USBSPD_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ usbspd = USBSPD_SPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ usbspd = USBSPD_SPEED_HIGH;
+ break;
+ default:
+ dev_err(dev, "unsupported speed %d\n", speed);
+ return -EIO;
+ }
+
+ usbhs_write(priv, reg, UPPHUB(upphub) |
+ HUBPORT(hubport)|
+ USBSPD(usbspd));
+
+ return 0;
+}
+
+/*
* local functions
*/
-static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
+static void usbhsc_set_buswait(struct usbhs_priv *priv)
{
int wait = usbhs_get_dparam(priv, buswait_bwait);
- u16 data = 0;
- if (enable) {
- /* set bus wait if platform have */
- if (wait)
- usbhs_bset(priv, BUSWAIT, 0x000F, wait);
- }
- usbhs_write(priv, DVSTCTR, data);
+ /* set bus wait if platform have */
+ if (wait)
+ usbhs_bset(priv, BUSWAIT, 0x000F, wait);
}
/*
@@ -191,10 +309,8 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
/* USB on */
usbhs_sys_clock_ctrl(priv, enable);
- usbhsc_bus_ctrl(priv, enable);
} else {
/* USB off */
- usbhsc_bus_ctrl(priv, enable);
usbhs_sys_clock_ctrl(priv, enable);
/* disable PM */
@@ -203,13 +319,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
}
/*
- * notify hotplug
+ * hotplug
*/
-static void usbhsc_notify_hotplug(struct work_struct *work)
+static void usbhsc_hotplug(struct usbhs_priv *priv)
{
- struct usbhs_priv *priv = container_of(work,
- struct usbhs_priv,
- notify_hotplug_work.work);
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
int id;
@@ -237,6 +350,10 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
usbhsc_power_ctrl(priv, enable);
+ /* bus init */
+ usbhsc_set_buswait(priv);
+ usbhsc_bus_init(priv);
+
/* module start */
usbhs_mod_call(priv, start, priv);
@@ -246,6 +363,9 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
/* module stop */
usbhs_mod_call(priv, stop, priv);
+ /* bus init */
+ usbhsc_bus_init(priv);
+
/* power off */
if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
usbhsc_power_ctrl(priv, enable);
@@ -257,6 +377,17 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
}
}
+/*
+ * notify hotplug
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+ struct usbhs_priv *priv = container_of(work,
+ struct usbhs_priv,
+ notify_hotplug_work.work);
+ usbhsc_hotplug(priv);
+}
+
int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
@@ -315,24 +446,28 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
/*
* care platform info
*/
- priv->pfunc = &info->platform_callback;
- priv->dparam = &info->driver_param;
+ memcpy(&priv->pfunc,
+ &info->platform_callback,
+ sizeof(struct renesas_usbhs_platform_callback));
+ memcpy(&priv->dparam,
+ &info->driver_param,
+ sizeof(struct renesas_usbhs_driver_param));
/* set driver callback functions for platform */
dfunc = &info->driver_callback;
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
/* set default param if platform doesn't have */
- if (!priv->dparam->pipe_type) {
- priv->dparam->pipe_type = usbhsc_default_pipe_type;
- priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
+ if (!priv->dparam.pipe_type) {
+ priv->dparam.pipe_type = usbhsc_default_pipe_type;
+ priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
}
- if (!priv->dparam->pio_dma_border)
- priv->dparam->pio_dma_border = 64; /* 64byte */
+ if (!priv->dparam.pio_dma_border)
+ priv->dparam.pio_dma_border = 64; /* 64byte */
/* FIXME */
/* runtime power control ? */
- if (priv->pfunc->get_vbus)
+ if (priv->pfunc.get_vbus)
usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL);
/*
@@ -443,9 +578,60 @@ static int __devexit usbhs_remove(struct platform_device *pdev)
return 0;
}
+static int usbhsc_suspend(struct device *dev)
+{
+ struct usbhs_priv *priv = dev_get_drvdata(dev);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+ if (mod) {
+ usbhs_mod_call(priv, stop, priv);
+ usbhs_mod_change(priv, -1);
+ }
+
+ if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, 0);
+
+ return 0;
+}
+
+static int usbhsc_resume(struct device *dev)
+{
+ struct usbhs_priv *priv = dev_get_drvdata(dev);
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, 1);
+
+ usbhsc_hotplug(priv);
+
+ return 0;
+}
+
+static int usbhsc_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static const struct dev_pm_ops usbhsc_pm_ops = {
+ .suspend = usbhsc_suspend,
+ .resume = usbhsc_resume,
+ .runtime_suspend = usbhsc_runtime_nop,
+ .runtime_resume = usbhsc_runtime_nop,
+};
+
static struct platform_driver renesas_usbhs_driver = {
.driver = {
.name = "renesas_usbhs",
+ .pm = &usbhsc_pm_ops,
},
.probe = usbhs_probe,
.remove = __devexit_p(usbhs_remove),
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index b410463a1212..8729da5c3be6 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -90,6 +90,17 @@ struct usbhs_priv;
#define PIPE9TRN 0x00BA
#define PIPEATRE 0x00BC
#define PIPEATRN 0x00BE
+#define DEVADD0 0x00D0 /* Device address n configuration */
+#define DEVADD1 0x00D2
+#define DEVADD2 0x00D4
+#define DEVADD3 0x00D6
+#define DEVADD4 0x00D8
+#define DEVADD5 0x00DA
+#define DEVADD6 0x00DC
+#define DEVADD7 0x00DE
+#define DEVADD8 0x00E0
+#define DEVADD9 0x00E2
+#define DEVADDA 0x00E4
/* SYSCFG */
#define SCKE (1 << 10) /* USB Module Clock Enable */
@@ -102,6 +113,8 @@ struct usbhs_priv;
/* DVSTCTR */
#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */
#define PWEN (1 << 9) /* Controls the PWEN pin output state */
+#define USBRST (1 << 6) /* Bus Reset Output */
+#define UACT (1 << 4) /* USB Bus Enable */
#define RHST (0x7) /* Reset Handshake */
#define RHST_LOW_SPEED 1 /* Low-speed connection */
#define RHST_FULL_SPEED 2 /* Full-speed connection */
@@ -159,6 +172,15 @@ struct usbhs_priv;
#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */
#define SEQUENCE_ERROR 6 /* Control transfer sequence error */
+/* INTSTS1 */
+#define OVRCR (1 << 15) /* OVRCR Interrupt Status */
+#define BCHG (1 << 14) /* USB Bus Change Interrupt Status */
+#define DTCH (1 << 12) /* USB Disconnection Detect Interrupt Status */
+#define ATTCH (1 << 11) /* ATTCH Interrupt Status */
+#define EOFERR (1 << 6) /* EOF Error Detect Interrupt Status */
+#define SIGN (1 << 5) /* Setup Transaction Error Interrupt Status */
+#define SACK (1 << 4) /* Setup Transaction ACK Response Interrupt Status */
+
/* PIPECFG */
/* DCPCFG */
#define TYPE_NONE (0 << 14) /* Transfer Type */
@@ -183,9 +205,11 @@ struct usbhs_priv;
/* PIPEnCTR */
/* DCPCTR */
#define BSTS (1 << 15) /* Buffer Status */
+#define SUREQ (1 << 14) /* Sending SETUP Token */
#define CSSTS (1 << 12) /* CSSTS Status */
-#define SQCLR (1 << 8) /* Toggle Bit Clear */
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
+#define SQCLR (1 << 8) /* Toggle Bit Clear */
+#define SQSET (1 << 7) /* Toggle Bit Set */
#define PBUSY (1 << 5) /* Pipe Busy */
#define PID_MASK (0x3) /* Response PID */
#define PID_NAK 0
@@ -202,6 +226,14 @@ struct usbhs_priv;
/* FRMNUM */
#define FRNM_MASK (0x7FF)
+/* DEVADDn */
+#define UPPHUB(x) (((x) & 0xF) << 11) /* HUB Register */
+#define HUBPORT(x) (((x) & 0x7) << 8) /* HUB Port for Target Device */
+#define USBSPD(x) (((x) & 0x3) << 6) /* Device Transfer Rate */
+#define USBSPD_SPEED_LOW 0x1
+#define USBSPD_SPEED_FULL 0x2
+#define USBSPD_SPEED_HIGH 0x3
+
/*
* struct
*/
@@ -210,8 +242,8 @@ struct usbhs_priv {
void __iomem *base;
unsigned int irq;
- struct renesas_usbhs_platform_callback *pfunc;
- struct renesas_usbhs_driver_param *dparam;
+ struct renesas_usbhs_platform_callback pfunc;
+ struct renesas_usbhs_driver_param dparam;
struct delayed_work notify_hotplug_work;
struct platform_device *pdev;
@@ -258,15 +290,35 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
/*
+ * usb request
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+
+/*
+ * bus
+ */
+void usbhs_bus_send_sof_enable(struct usbhs_priv *priv);
+void usbhs_bus_send_reset(struct usbhs_priv *priv);
+int usbhs_bus_get_speed(struct usbhs_priv *priv);
+int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable);
+
+/*
* frame
*/
int usbhs_frame_get_num(struct usbhs_priv *priv);
/*
+ * device config
+ */
+int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub,
+ u16 hubport, u16 speed);
+
+/*
* data
*/
struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev);
-#define usbhs_get_dparam(priv, param) (priv->dparam->param)
+#define usbhs_get_dparam(priv, param) (priv->dparam.param)
#define usbhs_priv_to_pdev(priv) (priv->pdev)
#define usbhs_priv_to_dev(priv) (&priv->pdev->dev)
#define usbhs_priv_to_lock(priv) (&priv->lock)
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index a34430f55fb7..8da685e796d1 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -54,35 +54,45 @@ static struct usbhs_pkt_handle usbhsf_null_handler = {
};
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
- struct usbhs_pkt_handle *handler,
+ void (*done)(struct usbhs_priv *priv,
+ struct usbhs_pkt *pkt),
void *buf, int len, int zero)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv);
unsigned long flags;
+ if (!done) {
+ dev_err(dev, "no done function\n");
+ return;
+ }
+
/******************** spin lock ********************/
usbhs_lock(priv, flags);
- if (!handler) {
+ if (!pipe->handler) {
dev_err(dev, "no handler function\n");
- handler = &usbhsf_null_handler;
+ pipe->handler = &usbhsf_null_handler;
}
list_del_init(&pkt->node);
list_add_tail(&pkt->node, &pipe->list);
+ /*
+ * each pkt must hold own handler.
+ * because handler might be changed by its situation.
+ * dma handler -> pio handler.
+ */
pkt->pipe = pipe;
pkt->buf = buf;
- pkt->handler = handler;
+ pkt->handler = pipe->handler;
pkt->length = len;
pkt->zero = zero;
pkt->actual = 0;
+ pkt->done = done;
usbhs_unlock(priv, flags);
/******************** spin unlock ******************/
-
- usbhs_pkt_start(pipe);
}
static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
@@ -118,10 +128,15 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
return pkt;
}
-int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type)
+enum {
+ USBHSF_PKT_PREPARE,
+ USBHSF_PKT_TRY_RUN,
+ USBHSF_PKT_DMA_DONE,
+};
+
+static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
- struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct usbhs_pkt *pkt;
struct device *dev = usbhs_priv_to_dev(priv);
int (*func)(struct usbhs_pkt *pkt, int *is_done);
@@ -161,13 +176,18 @@ __usbhs_pkt_handler_end:
/******************** spin unlock ******************/
if (is_done) {
- info->done(pkt);
+ pkt->done(priv, pkt);
usbhs_pkt_start(pipe);
}
return ret;
}
+void usbhs_pkt_start(struct usbhs_pipe *pipe)
+{
+ usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE);
+}
+
/*
* irq enable/disable function
*/
@@ -276,9 +296,13 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
usbhsf_fifo_is_busy(fifo))
return -EBUSY;
- if (usbhs_pipe_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe)) {
base |= (1 == write) << 5; /* ISEL */
+ if (usbhs_mod_is_host(priv))
+ usbhs_dcp_dir_for_host(pipe, write);
+ }
+
/* "base" will be used below */
usbhs_write(priv, fifo->sel, base | MBW_32);
@@ -297,6 +321,151 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
}
/*
+ * DCP status stage
+ */
+static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret;
+
+ usbhs_pipe_disable(pipe);
+
+ ret = usbhsf_fifo_select(pipe, fifo, 1);
+ if (ret < 0) {
+ dev_err(dev, "%s() faile\n", __func__);
+ return ret;
+ }
+
+ usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+ usbhsf_fifo_clear(pipe, fifo);
+ usbhsf_send_terminator(pipe, fifo);
+
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ usbhsf_tx_irq_ctrl(pipe, 1);
+ usbhs_pipe_enable(pipe);
+
+ return ret;
+}
+
+static int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret;
+
+ usbhs_pipe_disable(pipe);
+
+ ret = usbhsf_fifo_select(pipe, fifo, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s() fail\n", __func__);
+ return ret;
+ }
+
+ usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+ usbhsf_fifo_clear(pipe, fifo);
+
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ usbhsf_rx_irq_ctrl(pipe, 1);
+ usbhs_pipe_enable(pipe);
+
+ return ret;
+
+}
+
+static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+
+ if (pkt->handler == &usbhs_dcp_status_stage_in_handler)
+ usbhsf_tx_irq_ctrl(pipe, 0);
+ else
+ usbhsf_rx_irq_ctrl(pipe, 0);
+
+ pkt->actual = pkt->length;
+ *is_done = 1;
+
+ return 0;
+}
+
+struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = {
+ .prepare = usbhs_dcp_dir_switch_to_write,
+ .try_run = usbhs_dcp_dir_switch_done,
+};
+
+struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = {
+ .prepare = usbhs_dcp_dir_switch_to_read,
+ .try_run = usbhs_dcp_dir_switch_done,
+};
+
+/*
+ * DCP data stage (push)
+ */
+static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+
+ usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+ /*
+ * change handler to PIO push
+ */
+ pkt->handler = &usbhs_fifo_pio_push_handler;
+
+ return pkt->handler->prepare(pkt, is_done);
+}
+
+struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = {
+ .prepare = usbhsf_dcp_data_stage_try_push,
+};
+
+/*
+ * DCP data stage (pop)
+ */
+static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt,
+ int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
+
+ if (usbhs_pipe_is_busy(pipe))
+ return 0;
+
+ /*
+ * prepare pop for DCP should
+ * - change DCP direction,
+ * - clear fifo
+ * - DATA1
+ */
+ usbhs_pipe_disable(pipe);
+
+ usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+ usbhsf_fifo_select(pipe, fifo, 0);
+ usbhsf_fifo_clear(pipe, fifo);
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ /*
+ * change handler to PIO pop
+ */
+ pkt->handler = &usbhs_fifo_pio_pop_handler;
+
+ return pkt->handler->prepare(pkt, is_done);
+}
+
+struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = {
+ .prepare = usbhsf_dcp_data_stage_prepare_pop,
+};
+
+/*
* PIO push handler
*/
static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
@@ -452,6 +621,20 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
total_len = len;
/*
+ * update actual length first here to decide disable pipe.
+ * if this pipe keeps BUF status and all data were popped,
+ * then, next interrupt/token will be issued again
+ */
+ pkt->actual += total_len;
+
+ if ((pkt->actual == pkt->length) || /* receive all data */
+ (total_len < maxp)) { /* short packet */
+ *is_done = 1;
+ usbhsf_rx_irq_ctrl(pipe, 0);
+ usbhs_pipe_disable(pipe); /* disable pipe first */
+ }
+
+ /*
* Buffer clear if Zero-Length packet
*
* see
@@ -481,16 +664,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
}
- pkt->actual += total_len;
-
usbhs_fifo_read_end:
- if ((pkt->actual == pkt->length) || /* receive all data */
- (total_len < maxp)) { /* short packet */
- *is_done = 1;
- usbhsf_rx_irq_ctrl(pipe, 0);
- usbhs_pipe_disable(pipe);
- }
-
dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
usbhs_pipe_number(pipe),
pkt->length, pkt->actual, *is_done, pkt->zero);
@@ -646,7 +820,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
if (len % 4) /* 32bit alignment */
goto usbhsf_pio_prepare_push;
- if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+ if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_push;
/* get enable DMA fifo */
@@ -723,7 +897,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
if (!fifo)
goto usbhsf_pio_prepare_pop;
- if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+ if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_pop;
ret = usbhsf_fifo_select(pipe, fifo, 0);
@@ -884,7 +1058,7 @@ static int usbhsf_irq_empty(struct usbhs_priv *priv,
if (!(irq_state->bempsts & (1 << i)))
continue;
- ret = usbhs_pkt_run(pipe);
+ ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
if (ret < 0)
dev_err(dev, "irq_empty run_error %d : %d\n", i, ret);
}
@@ -914,7 +1088,7 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv,
if (!(irq_state->brdysts & (1 << i)))
continue;
- ret = usbhs_pkt_run(pipe);
+ ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
if (ret < 0)
dev_err(dev, "irq_ready run_error %d : %d\n", i, ret);
}
@@ -929,7 +1103,7 @@ static void usbhsf_dma_complete(void *arg)
struct device *dev = usbhs_priv_to_dev(priv);
int ret;
- ret = usbhs_pkt_dmadone(pipe);
+ ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE);
if (ret < 0)
dev_err(dev, "dma_complete run_error %d : %d\n",
usbhs_pipe_number(pipe), ret);
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
index ed6d8e56c13c..32a7b246b28d 100644
--- a/drivers/usb/renesas_usbhs/fifo.h
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -51,6 +51,8 @@ struct usbhs_pkt {
struct list_head node;
struct usbhs_pipe *pipe;
struct usbhs_pkt_handle *handler;
+ void (*done)(struct usbhs_priv *priv,
+ struct usbhs_pkt *pkt);
dma_addr_t dma;
void *buf;
int length;
@@ -76,12 +78,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv);
/*
* packet info
*/
-enum {
- USBHSF_PKT_PREPARE,
- USBHSF_PKT_TRY_RUN,
- USBHSF_PKT_DMA_DONE,
-};
-
extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler;
extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler;
extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler;
@@ -89,16 +85,18 @@ extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler;
extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler;
extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler;
+extern struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler;
+extern struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler;
+
+extern struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler;
+extern struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler;
void usbhs_pkt_init(struct usbhs_pkt *pkt);
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
- struct usbhs_pkt_handle *handler,
+ void (*done)(struct usbhs_priv *priv,
+ struct usbhs_pkt *pkt),
void *buf, int len, int zero);
struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt);
-int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type);
-
-#define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE)
-#define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN)
-#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE)
+void usbhs_pkt_start(struct usbhs_pipe *pipe);
#endif /* RENESAS_USB_FIFO_H */
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index a577f8f4064c..053f86d70009 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -58,7 +58,7 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv)
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
info->irq_vbus = usbhsm_autonomy_irq_vbus;
- priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus;
+ priv->pfunc.get_vbus = usbhsm_autonomy_get_vbus;
usbhs_irq_callback_update(priv, NULL);
}
@@ -93,8 +93,9 @@ struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
return ret;
}
-int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod)
+int usbhs_mod_is_host(struct usbhs_priv *priv)
{
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
if (!mod)
@@ -139,13 +140,17 @@ int usbhs_mod_probe(struct usbhs_priv *priv)
/*
* install host/gadget driver
*/
- ret = usbhs_mod_gadget_probe(priv);
+ ret = usbhs_mod_host_probe(priv);
if (ret < 0)
return ret;
+ ret = usbhs_mod_gadget_probe(priv);
+ if (ret < 0)
+ goto mod_init_host_err;
+
/* irq settings */
ret = request_irq(priv->irq, usbhs_interrupt,
- IRQF_DISABLED, dev_name(dev), priv);
+ 0, dev_name(dev), priv);
if (ret) {
dev_err(dev, "irq request err\n");
goto mod_init_gadget_err;
@@ -155,12 +160,15 @@ int usbhs_mod_probe(struct usbhs_priv *priv)
mod_init_gadget_err:
usbhs_mod_gadget_remove(priv);
+mod_init_host_err:
+ usbhs_mod_host_remove(priv);
return ret;
}
void usbhs_mod_remove(struct usbhs_priv *priv)
{
+ usbhs_mod_host_remove(priv);
usbhs_mod_gadget_remove(priv);
free_irq(priv->irq, priv);
}
@@ -168,20 +176,6 @@ void usbhs_mod_remove(struct usbhs_priv *priv)
/*
* status functions
*/
-int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state)
-{
- switch (irq_state->dvstctr & RHST) {
- case RHST_LOW_SPEED:
- return USB_SPEED_LOW;
- case RHST_FULL_SPEED:
- return USB_SPEED_FULL;
- case RHST_HIGH_SPEED:
- return USB_SPEED_HIGH;
- }
-
- return USB_SPEED_UNKNOWN;
-}
-
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
{
int state = irq_state->intsts0 & DVSQ_MASK;
@@ -221,8 +215,6 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
state->intsts0 = usbhs_read(priv, INTSTS0);
state->intsts1 = usbhs_read(priv, INTSTS1);
- state->dvstctr = usbhs_read(priv, DVSTCTR);
-
/* mask */
if (mod) {
state->brdysts = usbhs_read(priv, BRDYSTS);
@@ -269,6 +261,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
* see also
* usbhs_irq_setting_update
*/
+
+ /* INTSTS0 */
if (irq_state.intsts0 & VBINT)
usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state);
@@ -284,15 +278,38 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
if (irq_state.intsts0 & BRDY)
usbhs_mod_call(priv, irq_ready, priv, &irq_state);
+ /* INTSTS1 */
+ if (irq_state.intsts1 & ATTCH)
+ usbhs_mod_call(priv, irq_attch, priv, &irq_state);
+
+ if (irq_state.intsts1 & DTCH)
+ usbhs_mod_call(priv, irq_dtch, priv, &irq_state);
+
+ if (irq_state.intsts1 & SIGN)
+ usbhs_mod_call(priv, irq_sign, priv, &irq_state);
+
+ if (irq_state.intsts1 & SACK)
+ usbhs_mod_call(priv, irq_sack, priv, &irq_state);
+
return IRQ_HANDLED;
}
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
{
u16 intenb0 = 0;
+ u16 intenb1 = 0;
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+ /*
+ * BEMPENB/BRDYENB are picky.
+ * below method is required
+ *
+ * - clear INTSTS0
+ * - update BEMPENB/BRDYENB
+ * - update INTSTS0
+ */
usbhs_write(priv, INTENB0, 0);
+ usbhs_write(priv, INTENB1, 0);
usbhs_write(priv, BEMPENB, 0);
usbhs_write(priv, BRDYENB, 0);
@@ -310,6 +327,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
intenb0 |= VBSE;
if (mod) {
+ /*
+ * INTSTS0
+ */
if (mod->irq_ctrl_stage)
intenb0 |= CTRE;
@@ -322,7 +342,26 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
usbhs_write(priv, BRDYENB, mod->irq_brdysts);
intenb0 |= BRDYE;
}
+
+ /*
+ * INTSTS1
+ */
+ if (mod->irq_attch)
+ intenb1 |= ATTCHE;
+
+ if (mod->irq_attch)
+ intenb1 |= DTCHE;
+
+ if (mod->irq_sign)
+ intenb1 |= SIGNE;
+
+ if (mod->irq_sack)
+ intenb1 |= SACKE;
}
- usbhs_write(priv, INTENB0, intenb0);
+ if (intenb0)
+ usbhs_write(priv, INTENB0, intenb0);
+
+ if (intenb1)
+ usbhs_write(priv, INTENB1, intenb1);
}
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
index 5c845a28a21c..8ae3733031cd 100644
--- a/drivers/usb/renesas_usbhs/mod.h
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -30,7 +30,6 @@ struct usbhs_irq_state {
u16 brdysts;
u16 nrdysts;
u16 bempsts;
- u16 dvstctr;
};
struct usbhs_mod {
@@ -42,26 +41,48 @@ struct usbhs_mod {
int (*start)(struct usbhs_priv *priv);
int (*stop)(struct usbhs_priv *priv);
- /* INTSTS0 :: DVST (DVSQ) */
+ /*
+ * INTSTS0
+ */
+
+ /* DVST (DVSQ) */
int (*irq_dev_state)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
- /* INTSTS0 :: CTRT (CTSQ) */
+ /* CTRT (CTSQ) */
int (*irq_ctrl_stage)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
- /* INTSTS0 :: BEMP */
- /* BEMPSTS */
+ /* BEMP / BEMPSTS */
int (*irq_empty)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
u16 irq_bempsts;
- /* INTSTS0 :: BRDY */
- /* BRDYSTS */
+ /* BRDY / BRDYSTS */
int (*irq_ready)(struct usbhs_priv *priv,
struct usbhs_irq_state *irq_state);
u16 irq_brdysts;
+ /*
+ * INTSTS1
+ */
+
+ /* ATTCHE */
+ int (*irq_attch)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+
+ /* DTCHE */
+ int (*irq_dtch)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+
+ /* SIGN */
+ int (*irq_sign)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+
+ /* SACK */
+ int (*irq_sack)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+
struct usbhs_priv *priv;
};
@@ -89,7 +110,7 @@ struct usbhs_mod_info {
struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
-int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod);
+int usbhs_mod_is_host(struct usbhs_priv *priv);
int usbhs_mod_change(struct usbhs_priv *priv, int id);
int usbhs_mod_probe(struct usbhs_priv *priv);
void usbhs_mod_remove(struct usbhs_priv *priv);
@@ -99,7 +120,6 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv);
/*
* status functions
*/
-int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state);
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
@@ -119,9 +139,24 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
})
/*
- * gadget control
+ * host / gadget control
*/
-#ifdef CONFIG_USB_RENESAS_USBHS_UDC
+#if defined(CONFIG_USB_RENESAS_USBHS_HCD) || \
+ defined(CONFIG_USB_RENESAS_USBHS_HCD_MODULE)
+extern int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv);
+extern int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv);
+#else
+static inline int usbhs_mod_host_probe(struct usbhs_priv *priv)
+{
+ return 0;
+}
+static inline void usbhs_mod_host_remove(struct usbhs_priv *priv)
+{
+}
+#endif
+
+#if defined(CONFIG_USB_RENESAS_USBHS_UDC) || \
+ defined(CONFIG_USB_RENESAS_USBHS_UDC_MODULE)
extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv);
extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv);
#else
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index cb2d451d511e..4cc7ee0babc6 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -39,7 +39,6 @@ struct usbhsg_uep {
char ep_name[EP_NAME_SIZE];
struct usbhsg_gpriv *gpriv;
- struct usbhs_pkt_handle *handler;
};
struct usbhsg_gpriv {
@@ -128,25 +127,6 @@ LIST_HEAD(the_controller_link);
/*
* queue push/pop
*/
-static void usbhsg_queue_push(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
-{
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
- struct usb_request *req = &ureq->req;
-
- req->actual = 0;
- req->status = -EINPROGRESS;
- usbhs_pkt_push(pipe, pkt, uep->handler,
- req->buf, req->length, req->zero);
-
- dev_dbg(dev, "pipe %d : queue push (%d)\n",
- usbhs_pipe_number(pipe),
- req->length);
-}
-
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhsg_request *ureq,
int status)
@@ -161,7 +141,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep,
ureq->req.complete(&uep->ep, &ureq->req);
}
-static void usbhsg_queue_done(struct usbhs_pkt *pkt)
+static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
{
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
@@ -172,6 +152,26 @@ static void usbhsg_queue_done(struct usbhs_pkt *pkt)
usbhsg_queue_pop(uep, ureq, 0);
}
+static void usbhsg_queue_push(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
+ struct usb_request *req = &ureq->req;
+
+ req->actual = 0;
+ req->status = -EINPROGRESS;
+ usbhs_pkt_push(pipe, pkt, usbhsg_queue_done,
+ req->buf, req->length, req->zero);
+ usbhs_pkt_start(pipe);
+
+ dev_dbg(dev, "pipe %d : queue push (%d)\n",
+ usbhs_pipe_number(pipe),
+ req->length);
+}
+
/*
* dma map/unmap
*/
@@ -265,7 +265,7 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv,
if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) {
usbhs_pipe_disable(pipe);
- usbhs_pipe_clear_sequence(pipe);
+ usbhs_pipe_sequence_data0(pipe);
usbhs_pipe_enable(pipe);
}
@@ -355,7 +355,7 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state);
+ gpriv->gadget.speed = usbhs_bus_get_speed(priv);
dev_dbg(dev, "state = %x : speed : %d\n",
usbhs_status_get_device_state(irq_state),
@@ -389,13 +389,13 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
switch (stage) {
case READ_DATA_STAGE:
- dcp->handler = &usbhs_fifo_pio_push_handler;
+ pipe->handler = &usbhs_fifo_pio_push_handler;
break;
case WRITE_DATA_STAGE:
- dcp->handler = &usbhs_fifo_pio_pop_handler;
+ pipe->handler = &usbhs_fifo_pio_pop_handler;
break;
case NODATA_STATUS_STAGE:
- dcp->handler = &usbhs_ctrl_stage_end_handler;
+ pipe->handler = &usbhs_ctrl_stage_end_handler;
break;
default:
return ret;
@@ -479,24 +479,31 @@ static int usbhsg_ep_enable(struct usb_ep *ep,
*/
if (uep->pipe) {
usbhs_pipe_clear(uep->pipe);
- usbhs_pipe_clear_sequence(uep->pipe);
+ usbhs_pipe_sequence_data0(uep->pipe);
return 0;
}
- pipe = usbhs_pipe_malloc(priv, desc);
+ pipe = usbhs_pipe_malloc(priv,
+ usb_endpoint_type(desc),
+ usb_endpoint_dir_in(desc));
if (pipe) {
uep->pipe = pipe;
pipe->mod_private = uep;
+ /* set epnum / maxp */
+ usbhs_pipe_config_update(pipe, 0,
+ usb_endpoint_num(desc),
+ usb_endpoint_maxp(desc));
+
/*
* usbhs_fifo_dma_push/pop_handler try to
* use dmaengine if possible.
* It will use pio handler if impossible.
*/
if (usb_endpoint_dir_in(desc))
- uep->handler = &usbhs_fifo_dma_push_handler;
+ pipe->handler = &usbhs_fifo_dma_push_handler;
else
- uep->handler = &usbhs_fifo_dma_pop_handler;
+ pipe->handler = &usbhs_fifo_dma_pop_handler;
ret = 0;
}
@@ -659,7 +666,6 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
* pipe initialize and enable DCP
*/
usbhs_pipe_init(priv,
- usbhsg_queue_done,
usbhsg_dma_map_ctrl);
usbhs_fifo_init(priv);
usbhsg_uep_init(gpriv);
@@ -667,6 +673,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
/* dcp init */
dcp->pipe = usbhs_dcp_malloc(priv);
dcp->pipe->mod_private = dcp;
+ usbhs_pipe_config_update(dcp->pipe, 0, 0, 64);
/*
* system config enble
@@ -730,10 +737,6 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
usbhsg_pipe_disable(dcp);
- if (gpriv->driver &&
- gpriv->driver->disconnect)
- gpriv->driver->disconnect(&gpriv->gadget);
-
dev_dbg(dev, "stop gadget\n");
return 0;
@@ -744,31 +747,19 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
* linux usb function
*
*/
-static int usbhsg_gadget_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int usbhsg_gadget_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct usbhsg_gpriv *gpriv;
+ struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
struct usbhs_priv *priv;
struct device *dev;
int ret;
- if (!bind ||
- !driver ||
+ if (!driver ||
!driver->setup ||
driver->speed != USB_SPEED_HIGH)
return -EINVAL;
- /*
- * find unused controller
- */
- usbhsg_for_each_controller(gpriv) {
- if (!gpriv->driver)
- goto find_unused_controller;
- }
- return -ENODEV;
-
-find_unused_controller:
-
dev = usbhsg_gpriv_to_dev(gpriv);
priv = usbhsg_gpriv_to_priv(gpriv);
@@ -782,19 +773,8 @@ find_unused_controller:
goto add_fail;
}
- ret = bind(&gpriv->gadget);
- if (ret) {
- dev_err(dev, "bind to driver %s error %d\n",
- driver->driver.name, ret);
- goto bind_fail;
- }
-
- dev_dbg(dev, "bind %s\n", driver->driver.name);
-
return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
-bind_fail:
- device_del(&gpriv->gadget.dev);
add_fail:
gpriv->driver = NULL;
gpriv->gadget.dev.driver = NULL;
@@ -802,9 +782,10 @@ add_fail:
return ret;
}
-static int usbhsg_gadget_stop(struct usb_gadget_driver *driver)
+static int usbhsg_gadget_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct usbhsg_gpriv *gpriv;
+ struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
struct usbhs_priv *priv;
struct device *dev;
@@ -812,17 +793,6 @@ static int usbhsg_gadget_stop(struct usb_gadget_driver *driver)
!driver->unbind)
return -EINVAL;
- /*
- * find controller
- */
- usbhsg_for_each_controller(gpriv) {
- if (gpriv->driver == driver)
- goto find_matching_controller;
- }
- return -ENODEV;
-
-find_matching_controller:
-
dev = usbhsg_gpriv_to_dev(gpriv);
priv = usbhsg_gpriv_to_priv(gpriv);
@@ -830,12 +800,6 @@ find_matching_controller:
device_del(&gpriv->gadget.dev);
gpriv->driver = NULL;
- if (driver->disconnect)
- driver->disconnect(&gpriv->gadget);
-
- driver->unbind(&gpriv->gadget);
- dev_dbg(dev, "unbind %s\n", driver->driver.name);
-
return 0;
}
@@ -852,8 +816,8 @@ static int usbhsg_get_frame(struct usb_gadget *gadget)
static struct usb_gadget_ops usbhsg_gadget_ops = {
.get_frame = usbhsg_get_frame,
- .start = usbhsg_gadget_start,
- .stop = usbhsg_gadget_stop,
+ .udc_start = usbhsg_gadget_start,
+ .udc_stop = usbhsg_gadget_stop,
};
static int usbhsg_start(struct usbhs_priv *priv)
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
new file mode 100644
index 000000000000..1a7208a50afc
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -0,0 +1,1312 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "common.h"
+
+/*
+ *** HARDWARE LIMITATION ***
+ *
+ * 1) renesas_usbhs has a limited number of controllable devices.
+ * it can control only 9 devices in generally.
+ * see DEVADDn / DCPMAXP / PIPEMAXP.
+ *
+ * 2) renesas_usbhs pipe number is limited.
+ * the pipe will be re-used for each devices.
+ * so, software should control DATA0/1 sequence of each devices.
+ */
+
+
+/*
+ * image of mod_host
+ *
+ * +--------+
+ * | udev 0 | --> it is used when set address
+ * +--------+
+ *
+ * +--------+ pipes are reused for each uep.
+ * | udev 1 |-+- [uep 0 (dcp) ] --+ pipe will be switched when
+ * +--------+ | | target device was changed
+ * +- [uep 1 (bulk)] --|---+ +--------------+
+ * | +--------------> | pipe0 (dcp) |
+ * +- [uep 2 (bulk)] --|---|---+ +--------------+
+ * | | | | pipe1 (isoc) |
+ * +--------+ | | | +--------------+
+ * | udev 2 |-+- [uep 0 (dcp) ] --+ +-- |------> | pipe2 (bulk) |
+ * +--------+ | | | | +--------------+
+ * +- [uep 1 (int) ] --|-+ | +------> | pipe3 (bulk) |
+ * | | | | +--------------+
+ * +--------+ | +-|---|------> | pipe4 (int) |
+ * | udev 3 |-+- [uep 0 (dcp) ] --+ | | +--------------+
+ * +--------+ | | | | .... |
+ * +- [uep 1 (bulk)] ------+ | | .... |
+ * | |
+ * +- [uep 2 (bulk)]-----------+
+ */
+
+
+/*
+ * struct
+ */
+struct usbhsh_pipe_info {
+ unsigned int usr_cnt; /* see usbhsh_endpoint_alloc() */
+};
+
+struct usbhsh_request {
+ struct urb *urb;
+ struct usbhs_pkt pkt;
+ struct list_head ureq_link; /* see hpriv :: ureq_link_xxx */
+};
+
+struct usbhsh_device {
+ struct usb_device *usbv;
+ struct list_head ep_list_head; /* list of usbhsh_ep */
+};
+
+struct usbhsh_ep {
+ struct usbhs_pipe *pipe;
+ struct usbhsh_device *udev; /* attached udev */
+ struct list_head ep_list; /* list to usbhsh_device */
+
+ int maxp;
+};
+
+#define USBHSH_DEVICE_MAX 10 /* see DEVADDn / DCPMAXP / PIPEMAXP */
+#define USBHSH_PORT_MAX 7 /* see DEVADDn :: HUBPORT */
+struct usbhsh_hpriv {
+ struct usbhs_mod mod;
+ struct usbhs_pipe *dcp;
+
+ struct usbhsh_device udev[USBHSH_DEVICE_MAX];
+
+ struct usbhsh_pipe_info *pipe_info;
+ int pipe_size;
+
+ u32 port_stat; /* USB_PORT_STAT_xxx */
+
+ struct completion *done;
+
+ /* see usbhsh_req_alloc/free */
+ struct list_head ureq_link_active;
+ struct list_head ureq_link_free;
+};
+
+
+static const char usbhsh_hcd_name[] = "renesas_usbhs host";
+
+/*
+ * macro
+ */
+#define usbhsh_priv_to_hpriv(priv) \
+ container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod)
+
+#define __usbhsh_for_each_hpipe(start, pos, h, i) \
+ for (i = start, pos = (h)->hpipe + i; \
+ i < (h)->hpipe_size; \
+ i++, pos = (h)->hpipe + i)
+
+#define usbhsh_for_each_hpipe(pos, hpriv, i) \
+ __usbhsh_for_each_hpipe(1, pos, hpriv, i)
+
+#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i) \
+ __usbhsh_for_each_hpipe(0, pos, hpriv, i)
+
+#define __usbhsh_for_each_udev(start, pos, h, i) \
+ for (i = start, pos = (h)->udev + i; \
+ i < USBHSH_DEVICE_MAX; \
+ i++, pos = (h)->udev + i)
+
+#define usbhsh_for_each_udev(pos, hpriv, i) \
+ __usbhsh_for_each_udev(1, pos, hpriv, i)
+
+#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i) \
+ __usbhsh_for_each_udev(0, pos, hpriv, i)
+
+#define usbhsh_hcd_to_hpriv(h) (struct usbhsh_hpriv *)((h)->hcd_priv)
+#define usbhsh_hcd_to_dev(h) ((h)->self.controller)
+
+#define usbhsh_hpriv_to_priv(h) ((h)->mod.priv)
+#define usbhsh_hpriv_to_dcp(h) ((h)->dcp)
+#define usbhsh_hpriv_to_hcd(h) \
+ container_of((void *)h, struct usb_hcd, hcd_priv)
+
+#define usbhsh_ep_to_uep(u) ((u)->hcpriv)
+#define usbhsh_uep_to_pipe(u) ((u)->pipe)
+#define usbhsh_uep_to_udev(u) ((u)->udev)
+#define usbhsh_urb_to_ureq(u) ((u)->hcpriv)
+#define usbhsh_urb_to_usbv(u) ((u)->dev)
+
+#define usbhsh_usbv_to_udev(d) dev_get_drvdata(&(d)->dev)
+
+#define usbhsh_udev_to_usbv(h) ((h)->usbv)
+
+#define usbhsh_pipe_info(p) ((p)->mod_private)
+
+#define usbhsh_device_number(h, d) ((int)((d) - (h)->udev))
+#define usbhsh_device_nth(h, d) ((h)->udev + d)
+#define usbhsh_device0(h) usbhsh_device_nth(h, 0)
+
+#define usbhsh_port_stat_init(h) ((h)->port_stat = 0)
+#define usbhsh_port_stat_set(h, s) ((h)->port_stat |= (s))
+#define usbhsh_port_stat_clear(h, s) ((h)->port_stat &= ~(s))
+#define usbhsh_port_stat_get(h) ((h)->port_stat)
+
+#define usbhsh_pkt_to_req(p) \
+ container_of((void *)p, struct usbhsh_request, pkt)
+
+/*
+ * req alloc/free
+ */
+static void usbhsh_req_list_init(struct usbhsh_hpriv *hpriv)
+{
+ INIT_LIST_HEAD(&hpriv->ureq_link_active);
+ INIT_LIST_HEAD(&hpriv->ureq_link_free);
+}
+
+static void usbhsh_req_list_quit(struct usbhsh_hpriv *hpriv)
+{
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+ struct usbhsh_request *ureq, *next;
+
+ /* kfree all active ureq */
+ list_for_each_entry_safe(ureq, next,
+ &hpriv->ureq_link_active,
+ ureq_link) {
+ dev_err(dev, "active ureq (%p) is force freed\n", ureq);
+ kfree(ureq);
+ }
+
+ /* kfree all free ureq */
+ list_for_each_entry_safe(ureq, next, &hpriv->ureq_link_free, ureq_link)
+ kfree(ureq);
+}
+
+static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct usbhsh_request *ureq;
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ if (list_empty(&hpriv->ureq_link_free)) {
+ /*
+ * create new one if there is no free ureq
+ */
+ ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
+ if (ureq)
+ INIT_LIST_HEAD(&ureq->ureq_link);
+ } else {
+ /*
+ * reuse "free" ureq if exist
+ */
+ ureq = list_entry(hpriv->ureq_link_free.next,
+ struct usbhsh_request,
+ ureq_link);
+ if (ureq)
+ list_del_init(&ureq->ureq_link);
+ }
+
+ if (!ureq) {
+ dev_err(dev, "ureq alloc fail\n");
+ return NULL;
+ }
+
+ usbhs_pkt_init(&ureq->pkt);
+
+ /*
+ * push it to "active" list
+ */
+ list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_active);
+ ureq->urb = urb;
+
+ return ureq;
+}
+
+static void usbhsh_req_free(struct usbhsh_hpriv *hpriv,
+ struct usbhsh_request *ureq)
+{
+ struct usbhs_pkt *pkt = &ureq->pkt;
+
+ usbhs_pkt_init(pkt);
+
+ /*
+ * removed from "active" list,
+ * and push it to "free" list
+ */
+ ureq->urb = NULL;
+ list_del_init(&ureq->ureq_link);
+ list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_free);
+}
+
+/*
+ * device control
+ */
+
+static int usbhsh_device_has_endpoint(struct usbhsh_device *udev)
+{
+ return !list_empty(&udev->ep_list_head);
+}
+
+static struct usbhsh_device *usbhsh_device_alloc(struct usbhsh_hpriv *hpriv,
+ struct urb *urb)
+{
+ struct usbhsh_device *udev = NULL;
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+ struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ int i;
+
+ /*
+ * device 0
+ */
+ if (0 == usb_pipedevice(urb->pipe)) {
+ udev = usbhsh_device0(hpriv);
+ goto usbhsh_device_find;
+ }
+
+ /*
+ * find unused device
+ */
+ usbhsh_for_each_udev(udev, hpriv, i) {
+ if (usbhsh_udev_to_usbv(udev))
+ continue;
+ goto usbhsh_device_find;
+ }
+
+ dev_err(dev, "no free usbhsh_device\n");
+
+ return NULL;
+
+usbhsh_device_find:
+ if (usbhsh_device_has_endpoint(udev))
+ dev_warn(dev, "udev have old endpoint\n");
+
+ /* uep will be attached */
+ INIT_LIST_HEAD(&udev->ep_list_head);
+
+ /*
+ * usbhsh_usbv_to_udev()
+ * usbhsh_udev_to_usbv()
+ * will be enable
+ */
+ dev_set_drvdata(&usbv->dev, udev);
+ udev->usbv = usbv;
+
+ /* set device config */
+ usbhs_set_device_speed(priv,
+ usbhsh_device_number(hpriv, udev),
+ usbhsh_device_number(hpriv, udev),
+ 0, /* FIXME no parent */
+ usbv->speed);
+
+ dev_dbg(dev, "%s [%d](%p)\n", __func__,
+ usbhsh_device_number(hpriv, udev), udev);
+
+ return udev;
+}
+
+static void usbhsh_device_free(struct usbhsh_hpriv *hpriv,
+ struct usbhsh_device *udev)
+{
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+ struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
+
+ dev_dbg(dev, "%s [%d](%p)\n", __func__,
+ usbhsh_device_number(hpriv, udev), udev);
+
+ if (usbhsh_device_has_endpoint(udev))
+ dev_warn(dev, "udev still have endpoint\n");
+
+ /*
+ * usbhsh_usbv_to_udev()
+ * usbhsh_udev_to_usbv()
+ * will be disable
+ */
+ dev_set_drvdata(&usbv->dev, NULL);
+ udev->usbv = NULL;
+}
+
+/*
+ * end-point control
+ */
+struct usbhsh_ep *usbhsh_endpoint_alloc(struct usbhsh_hpriv *hpriv,
+ struct usbhsh_device *udev,
+ struct usb_host_endpoint *ep,
+ gfp_t mem_flags)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct usbhsh_ep *uep;
+ struct usbhsh_pipe_info *info;
+ struct usbhs_pipe *pipe, *best_pipe;
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+ struct usb_endpoint_descriptor *desc = &ep->desc;
+ int type, i;
+ unsigned int min_usr;
+
+ uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
+ if (!uep) {
+ dev_err(dev, "usbhsh_ep alloc fail\n");
+ return NULL;
+ }
+ type = usb_endpoint_type(desc);
+
+ /*
+ * find best pipe for endpoint
+ * see
+ * HARDWARE LIMITATION
+ */
+ min_usr = ~0;
+ best_pipe = NULL;
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (!usbhs_pipe_type_is(pipe, type))
+ continue;
+
+ info = usbhsh_pipe_info(pipe);
+
+ if (min_usr > info->usr_cnt) {
+ min_usr = info->usr_cnt;
+ best_pipe = pipe;
+ }
+ }
+
+ if (unlikely(!best_pipe)) {
+ dev_err(dev, "couldn't find best pipe\n");
+ kfree(uep);
+ return NULL;
+ }
+
+ /*
+ * init uep
+ */
+ uep->pipe = best_pipe;
+ uep->maxp = usb_endpoint_maxp(desc);
+ usbhsh_uep_to_udev(uep) = udev;
+ usbhsh_ep_to_uep(ep) = uep;
+
+ /*
+ * update pipe user count
+ */
+ info = usbhsh_pipe_info(best_pipe);
+ info->usr_cnt++;
+
+ /* init this endpoint, and attach it to udev */
+ INIT_LIST_HEAD(&uep->ep_list);
+ list_add_tail(&uep->ep_list, &udev->ep_list_head);
+
+ /*
+ * usbhs_pipe_config_update() should be called after
+ * usbhs_device_config()
+ * see
+ * DCPMAXP/PIPEMAXP
+ */
+ usbhs_pipe_config_update(uep->pipe,
+ usbhsh_device_number(hpriv, udev),
+ usb_endpoint_num(desc),
+ uep->maxp);
+
+ dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+ usbhsh_device_number(hpriv, udev),
+ usbhs_pipe_name(pipe), uep);
+
+ return uep;
+}
+
+void usbhsh_endpoint_free(struct usbhsh_hpriv *hpriv,
+ struct usb_host_endpoint *ep)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
+ struct usbhsh_pipe_info *info;
+
+ if (!uep)
+ return;
+
+ dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+ usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
+ usbhs_pipe_name(uep->pipe), uep);
+
+ info = usbhsh_pipe_info(uep->pipe);
+ info->usr_cnt--;
+
+ /* remove this endpoint from udev */
+ list_del_init(&uep->ep_list);
+
+ usbhsh_uep_to_udev(uep) = NULL;
+ usbhsh_ep_to_uep(ep) = NULL;
+
+ kfree(uep);
+}
+
+/*
+ * queue push/pop
+ */
+static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
+{
+ struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt);
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct urb *urb = ureq->urb;
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (!urb) {
+ dev_warn(dev, "pkt doesn't have urb\n");
+ return;
+ }
+
+ urb->actual_length = pkt->actual;
+ usbhsh_req_free(hpriv, ureq);
+ usbhsh_urb_to_ureq(urb) = NULL;
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ usb_hcd_giveback_urb(hcd, urb, 0);
+}
+
+static int usbhsh_queue_push(struct usb_hcd *hcd,
+ struct usbhs_pipe *pipe,
+ struct urb *urb)
+{
+ struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb);
+ struct usbhs_pkt *pkt = &ureq->pkt;
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+ void *buf;
+ int len;
+
+ if (usb_pipeisoc(urb->pipe)) {
+ dev_err(dev, "pipe iso is not supported now\n");
+ return -EIO;
+ }
+
+ if (usb_pipein(urb->pipe))
+ pipe->handler = &usbhs_fifo_pio_pop_handler;
+ else
+ pipe->handler = &usbhs_fifo_pio_push_handler;
+
+ buf = (void *)(urb->transfer_buffer + urb->actual_length);
+ len = urb->transfer_buffer_length - urb->actual_length;
+
+ dev_dbg(dev, "%s\n", __func__);
+ usbhs_pkt_push(pipe, pkt, usbhsh_queue_done,
+ buf, len, (urb->transfer_flags & URB_ZERO_PACKET));
+ usbhs_pkt_start(pipe);
+
+ return 0;
+}
+
+/*
+ * DCP setup stage
+ */
+static int usbhsh_is_request_address(struct urb *urb)
+{
+ struct usb_ctrlrequest *cmd;
+
+ cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+
+ if ((DeviceOutRequest == cmd->bRequestType << 8) &&
+ (USB_REQ_SET_ADDRESS == cmd->bRequest))
+ return 1;
+ else
+ return 0;
+}
+
+static void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv,
+ struct urb *urb,
+ struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct usb_ctrlrequest req;
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ /*
+ * wait setup packet ACK
+ * see
+ * usbhsh_irq_setup_ack()
+ * usbhsh_irq_setup_err()
+ */
+ DECLARE_COMPLETION(done);
+ hpriv->done = &done;
+
+ /* copy original request */
+ memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest));
+
+ /*
+ * renesas_usbhs can not use original usb address.
+ * see HARDWARE LIMITATION.
+ * modify usb address here.
+ */
+ if (usbhsh_is_request_address(urb)) {
+ /* FIXME */
+ req.wValue = 1;
+ dev_dbg(dev, "create new address - %d\n", req.wValue);
+ }
+
+ /* set request */
+ usbhs_usbreq_set_val(priv, &req);
+
+ /*
+ * wait setup packet ACK
+ */
+ wait_for_completion(&done);
+ hpriv->done = NULL;
+
+ dev_dbg(dev, "%s done\n", __func__);
+}
+
+/*
+ * DCP data stage
+ */
+static void usbhsh_data_stage_packet_done(struct usbhs_priv *priv,
+ struct usbhs_pkt *pkt)
+{
+ struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt);
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct urb *urb = ureq->urb;
+
+ /* this ureq was connected to urb when usbhsh_urb_enqueue() */
+
+ usbhsh_req_free(hpriv, ureq);
+ usbhsh_urb_to_ureq(urb) = NULL;
+}
+
+static void usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv,
+ struct urb *urb,
+ struct usbhs_pipe *pipe)
+{
+ struct usbhsh_request *ureq;
+ struct usbhs_pkt *pkt;
+
+ /*
+ * FIXME
+ *
+ * data stage uses ureq which is connected to urb
+ * see usbhsh_urb_enqueue() :: alloc new request.
+ * it will be freed in usbhsh_data_stage_packet_done()
+ */
+ ureq = usbhsh_urb_to_ureq(urb);
+ pkt = &ureq->pkt;
+
+ if (usb_pipein(urb->pipe))
+ pipe->handler = &usbhs_dcp_data_stage_in_handler;
+ else
+ pipe->handler = &usbhs_dcp_data_stage_out_handler;
+
+ usbhs_pkt_push(pipe, pkt,
+ usbhsh_data_stage_packet_done,
+ urb->transfer_buffer,
+ urb->transfer_buffer_length,
+ (urb->transfer_flags & URB_ZERO_PACKET));
+}
+
+/*
+ * DCP status stage
+ */
+static void usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv,
+ struct urb *urb,
+ struct usbhs_pipe *pipe)
+{
+ struct usbhsh_request *ureq;
+ struct usbhs_pkt *pkt;
+
+ /*
+ * FIXME
+ *
+ * status stage uses allocated ureq.
+ * it will be freed on usbhsh_queue_done()
+ */
+ ureq = usbhsh_req_alloc(hpriv, urb, GFP_KERNEL);
+ pkt = &ureq->pkt;
+
+ if (usb_pipein(urb->pipe))
+ pipe->handler = &usbhs_dcp_status_stage_in_handler;
+ else
+ pipe->handler = &usbhs_dcp_status_stage_out_handler;
+
+ usbhs_pkt_push(pipe, pkt,
+ usbhsh_queue_done,
+ NULL,
+ urb->transfer_buffer_length,
+ 0);
+}
+
+static int usbhsh_dcp_queue_push(struct usb_hcd *hcd,
+ struct usbhsh_hpriv *hpriv,
+ struct usbhs_pipe *pipe,
+ struct urb *urb)
+{
+ struct device *dev = usbhsh_hcd_to_dev(hcd);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /*
+ * setup stage
+ *
+ * usbhsh_send_setup_stage_packet() wait SACK/SIGN
+ */
+ usbhsh_setup_stage_packet_push(hpriv, urb, pipe);
+
+ /*
+ * data stage
+ *
+ * It is pushed only when urb has buffer.
+ */
+ if (urb->transfer_buffer_length)
+ usbhsh_data_stage_packet_push(hpriv, urb, pipe);
+
+ /*
+ * status stage
+ */
+ usbhsh_status_stage_packet_push(hpriv, urb, pipe);
+
+ /*
+ * start pushed packets
+ */
+ usbhs_pkt_start(pipe);
+
+ return 0;
+}
+
+/*
+ * dma map functions
+ */
+static int usbhsh_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
+{
+ return 0;
+}
+
+/*
+ * for hc_driver
+ */
+static int usbhsh_host_start(struct usb_hcd *hcd)
+{
+ return 0;
+}
+
+static void usbhsh_host_stop(struct usb_hcd *hcd)
+{
+}
+
+static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usbhsh_request *ureq;
+ struct usbhsh_device *udev, *new_udev = NULL;
+ struct usbhs_pipe *pipe;
+ struct usbhsh_ep *uep;
+
+ int ret;
+
+ dev_dbg(dev, "%s (%s)\n",
+ __func__, usb_pipein(urb->pipe) ? "in" : "out");
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto usbhsh_urb_enqueue_error_not_linked;
+
+ /*
+ * get udev
+ */
+ udev = usbhsh_usbv_to_udev(usbv);
+ if (!udev) {
+ new_udev = usbhsh_device_alloc(hpriv, urb);
+ if (!new_udev)
+ goto usbhsh_urb_enqueue_error_not_linked;
+
+ udev = new_udev;
+ }
+
+ /*
+ * get uep
+ */
+ uep = usbhsh_ep_to_uep(ep);
+ if (!uep) {
+ uep = usbhsh_endpoint_alloc(hpriv, udev, ep, mem_flags);
+ if (!uep)
+ goto usbhsh_urb_enqueue_error_free_device;
+ }
+ pipe = usbhsh_uep_to_pipe(uep);
+
+ /*
+ * alloc new request
+ */
+ ureq = usbhsh_req_alloc(hpriv, urb, mem_flags);
+ if (unlikely(!ureq)) {
+ ret = -ENOMEM;
+ goto usbhsh_urb_enqueue_error_free_endpoint;
+ }
+ usbhsh_urb_to_ureq(urb) = ureq;
+
+ /*
+ * push packet
+ */
+ if (usb_pipecontrol(urb->pipe))
+ usbhsh_dcp_queue_push(hcd, hpriv, pipe, urb);
+ else
+ usbhsh_queue_push(hcd, pipe, urb);
+
+ return 0;
+
+usbhsh_urb_enqueue_error_free_endpoint:
+ usbhsh_endpoint_free(hpriv, ep);
+usbhsh_urb_enqueue_error_free_device:
+ if (new_udev)
+ usbhsh_device_free(hpriv, new_udev);
+usbhsh_urb_enqueue_error_not_linked:
+
+ dev_dbg(dev, "%s error\n", __func__);
+
+ return ret;
+}
+
+static int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+ struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb);
+
+ if (ureq) {
+ usbhsh_req_free(hpriv, ureq);
+ usbhsh_urb_to_ureq(urb) = NULL;
+ }
+
+ return 0;
+}
+
+static void usbhsh_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
+ struct usbhsh_device *udev;
+ struct usbhsh_hpriv *hpriv;
+
+ /*
+ * this function might be called manytimes by same hcd/ep
+ * in-endpoitn == out-endpoint if ep == dcp.
+ */
+ if (!uep)
+ return;
+
+ udev = usbhsh_uep_to_udev(uep);
+ hpriv = usbhsh_hcd_to_hpriv(hcd);
+
+ usbhsh_endpoint_free(hpriv, ep);
+ ep->hcpriv = NULL;
+
+ /*
+ * if there is no endpoint,
+ * free device
+ */
+ if (!usbhsh_device_has_endpoint(udev))
+ usbhsh_device_free(hpriv, udev);
+}
+
+static int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int roothub_id = 1; /* only 1 root hub */
+
+ /*
+ * does port stat was changed ?
+ * check USB_PORT_STAT_C_xxx << 16
+ */
+ if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000)
+ *buf = (1 << roothub_id);
+ else
+ *buf = 0;
+
+ dev_dbg(dev, "%s (%02x)\n", __func__, *buf);
+
+ return !!(*buf);
+}
+
+static int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv,
+ u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ dev_dbg(dev, "%s :: C_HUB_xx\n", __func__);
+ return 0;
+ }
+
+ return -EPIPE;
+}
+
+static int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv,
+ u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int enable = (typeReq == SetPortFeature);
+ int speed, i, timeout = 128;
+ int roothub_id = 1; /* only 1 root hub */
+
+ /* common error */
+ if (wIndex > roothub_id || wLength != 0)
+ return -EPIPE;
+
+ /* check wValue */
+ switch (wValue) {
+ case USB_PORT_FEAT_POWER:
+ usbhs_vbus_ctrl(priv, enable);
+ dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__);
+ break;
+
+ case USB_PORT_FEAT_ENABLE:
+ case USB_PORT_FEAT_SUSPEND:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__);
+ break;
+
+ case USB_PORT_FEAT_RESET:
+ if (!enable)
+ break;
+
+ usbhsh_port_stat_clear(hpriv,
+ USB_PORT_STAT_HIGH_SPEED |
+ USB_PORT_STAT_LOW_SPEED);
+
+ usbhs_bus_send_reset(priv);
+ msleep(20);
+ usbhs_bus_send_sof_enable(priv);
+
+ for (i = 0; i < timeout ; i++) {
+ switch (usbhs_bus_get_speed(priv)) {
+ case USB_SPEED_LOW:
+ speed = USB_PORT_STAT_LOW_SPEED;
+ goto got_usb_bus_speed;
+ case USB_SPEED_HIGH:
+ speed = USB_PORT_STAT_HIGH_SPEED;
+ goto got_usb_bus_speed;
+ case USB_SPEED_FULL:
+ speed = 0;
+ goto got_usb_bus_speed;
+ }
+
+ msleep(20);
+ }
+ return -EPIPE;
+
+got_usb_bus_speed:
+ usbhsh_port_stat_set(hpriv, speed);
+ usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE);
+
+ dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n",
+ __func__, speed);
+
+ /* status change is not needed */
+ return 0;
+
+ default:
+ return -EPIPE;
+ }
+
+ /* set/clear status */
+ if (enable)
+ usbhsh_port_stat_set(hpriv, (1 << wValue));
+ else
+ usbhsh_port_stat_clear(hpriv, (1 << wValue));
+
+ return 0;
+}
+
+static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv,
+ u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int roothub_id = 1; /* only 1 root hub */
+
+ switch (typeReq) {
+ case GetHubStatus:
+ dev_dbg(dev, "%s :: GetHubStatus\n", __func__);
+
+ *buf = 0x00;
+ break;
+
+ case GetPortStatus:
+ if (wIndex != roothub_id)
+ return -EPIPE;
+
+ dev_dbg(dev, "%s :: GetPortStatus\n", __func__);
+ *(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv));
+ break;
+
+ case GetHubDescriptor:
+ desc->bDescriptorType = 0x29;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = roothub_id;
+ desc->bDescLength = 9;
+ desc->bPwrOn2PwrGood = 0;
+ desc->wHubCharacteristics = cpu_to_le16(0x0011);
+ desc->u.hs.DeviceRemovable[0] = (roothub_id << 1);
+ desc->u.hs.DeviceRemovable[1] = ~0;
+ dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__);
+ break;
+ }
+
+ return 0;
+}
+
+static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret = -EPIPE;
+
+ switch (typeReq) {
+
+ /* Hub Feature */
+ case ClearHubFeature:
+ case SetHubFeature:
+ ret = __usbhsh_hub_hub_feature(hpriv, typeReq,
+ wValue, wIndex, buf, wLength);
+ break;
+
+ /* Port Feature */
+ case SetPortFeature:
+ case ClearPortFeature:
+ ret = __usbhsh_hub_port_feature(hpriv, typeReq,
+ wValue, wIndex, buf, wLength);
+ break;
+
+ /* Get status */
+ case GetHubStatus:
+ case GetPortStatus:
+ case GetHubDescriptor:
+ ret = __usbhsh_hub_get_status(hpriv, typeReq,
+ wValue, wIndex, buf, wLength);
+ break;
+ }
+
+ dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n",
+ typeReq, ret, usbhsh_port_stat_get(hpriv));
+
+ return ret;
+}
+
+static struct hc_driver usbhsh_driver = {
+ .description = usbhsh_hcd_name,
+ .hcd_priv_size = sizeof(struct usbhsh_hpriv),
+
+ /*
+ * generic hardware linkage
+ */
+ .flags = HCD_USB2,
+
+ .start = usbhsh_host_start,
+ .stop = usbhsh_host_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = usbhsh_urb_enqueue,
+ .urb_dequeue = usbhsh_urb_dequeue,
+ .endpoint_disable = usbhsh_endpoint_disable,
+
+ /*
+ * root hub
+ */
+ .hub_status_data = usbhsh_hub_status_data,
+ .hub_control = usbhsh_hub_control,
+};
+
+/*
+ * interrupt functions
+ */
+static int usbhsh_irq_attch(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_dbg(dev, "device attached\n");
+
+ usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION);
+ usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
+
+ return 0;
+}
+
+static int usbhsh_irq_dtch(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_dbg(dev, "device detached\n");
+
+ usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION);
+ usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
+
+ return 0;
+}
+
+static int usbhsh_irq_setup_ack(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_dbg(dev, "setup packet OK\n");
+
+ if (unlikely(!hpriv->done))
+ dev_err(dev, "setup ack happen without necessary data\n");
+ else
+ complete(hpriv->done); /* see usbhsh_urb_enqueue() */
+
+ return 0;
+}
+
+static int usbhsh_irq_setup_err(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_dbg(dev, "setup packet Err\n");
+
+ if (unlikely(!hpriv->done))
+ dev_err(dev, "setup err happen without necessary data\n");
+ else
+ complete(hpriv->done); /* see usbhsh_urb_enqueue() */
+
+ return 0;
+}
+
+/*
+ * module start/stop
+ */
+static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct usbhsh_pipe_info *pipe_info = hpriv->pipe_info;
+ struct usbhs_pipe *pipe;
+ u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int old_type, dir_in, i;
+
+ /* init all pipe */
+ old_type = USB_ENDPOINT_XFER_CONTROL;
+ for (i = 0; i < pipe_size; i++) {
+ pipe_info[i].usr_cnt = 0;
+
+ /*
+ * data "output" will be finished as soon as possible,
+ * but there is no guaranty at data "input" case.
+ *
+ * "input" needs "standby" pipe.
+ * So, "input" direction pipe > "output" direction pipe
+ * is good idea.
+ *
+ * 1st USB_ENDPOINT_XFER_xxx will be output direction,
+ * and the other will be input direction here.
+ *
+ * ex)
+ * ...
+ * USB_ENDPOINT_XFER_ISOC -> dir out
+ * USB_ENDPOINT_XFER_ISOC -> dir in
+ * USB_ENDPOINT_XFER_BULK -> dir out
+ * USB_ENDPOINT_XFER_BULK -> dir in
+ * USB_ENDPOINT_XFER_BULK -> dir in
+ * ...
+ */
+ dir_in = (pipe_type[i] == old_type);
+ old_type = pipe_type[i];
+
+ if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) {
+ pipe = usbhs_dcp_malloc(priv);
+ usbhsh_hpriv_to_dcp(hpriv) = pipe;
+ } else {
+ pipe = usbhs_pipe_malloc(priv,
+ pipe_type[i],
+ dir_in);
+ }
+
+ pipe->mod_private = pipe_info + i;
+ }
+}
+
+static int usbhsh_start(struct usbhs_priv *priv)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret;
+
+ /* add hcd */
+ ret = usb_add_hcd(hcd, 0, 0);
+ if (ret < 0)
+ return 0;
+
+ /*
+ * pipe initialize and enable DCP
+ */
+ usbhs_pipe_init(priv,
+ usbhsh_dma_map_ctrl);
+ usbhs_fifo_init(priv);
+ usbhsh_pipe_init_for_host(priv);
+
+ /*
+ * system config enble
+ * - HI speed
+ * - host
+ * - usb module
+ */
+ usbhs_sys_hispeed_ctrl(priv, 1);
+ usbhs_sys_host_ctrl(priv, 1);
+ usbhs_sys_usb_ctrl(priv, 1);
+
+ /*
+ * enable irq callback
+ */
+ mod->irq_attch = usbhsh_irq_attch;
+ mod->irq_dtch = usbhsh_irq_dtch;
+ mod->irq_sack = usbhsh_irq_setup_ack;
+ mod->irq_sign = usbhsh_irq_setup_err;
+ usbhs_irq_callback_update(priv, mod);
+
+ dev_dbg(dev, "start host\n");
+
+ return ret;
+}
+
+static int usbhsh_stop(struct usbhs_priv *priv)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ usb_remove_hcd(hcd);
+
+ /* disable sys */
+ usbhs_sys_hispeed_ctrl(priv, 0);
+ usbhs_sys_host_ctrl(priv, 0);
+ usbhs_sys_usb_ctrl(priv, 0);
+
+ dev_dbg(dev, "quit host\n");
+
+ return 0;
+}
+
+int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv)
+{
+ struct usbhsh_hpriv *hpriv;
+ struct usb_hcd *hcd;
+ struct usbhsh_pipe_info *pipe_info;
+ struct usbhsh_device *udev;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int i;
+
+ /* initialize hcd */
+ hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name);
+ if (!hcd) {
+ dev_err(dev, "Failed to create hcd\n");
+ return -ENOMEM;
+ }
+
+ pipe_info = kzalloc(sizeof(*pipe_info) * pipe_size, GFP_KERNEL);
+ if (!pipe_info) {
+ dev_err(dev, "Could not allocate pipe_info\n");
+ goto usbhs_mod_host_probe_err;
+ }
+
+ /*
+ * CAUTION
+ *
+ * There is no guarantee that it is possible to access usb module here.
+ * Don't accesses to it.
+ * The accesse will be enable after "usbhsh_start"
+ */
+
+ hpriv = usbhsh_hcd_to_hpriv(hcd);
+
+ /*
+ * register itself
+ */
+ usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST);
+
+ /* init hpriv */
+ hpriv->mod.name = "host";
+ hpriv->mod.start = usbhsh_start;
+ hpriv->mod.stop = usbhsh_stop;
+ hpriv->pipe_info = pipe_info;
+ hpriv->pipe_size = pipe_size;
+ hpriv->done = NULL;
+ usbhsh_req_list_init(hpriv);
+ usbhsh_port_stat_init(hpriv);
+
+ /* init all device */
+ usbhsh_for_each_udev_with_dev0(udev, hpriv, i) {
+ udev->usbv = NULL;
+ INIT_LIST_HEAD(&udev->ep_list_head);
+ }
+
+ dev_info(dev, "host probed\n");
+
+ return 0;
+
+usbhs_mod_host_probe_err:
+ usb_put_hcd(hcd);
+
+ return -ENOMEM;
+}
+
+int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv)
+{
+ struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+ struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+
+ usbhsh_req_list_quit(hpriv);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 1b14cae45704..c74389ce2177 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -29,9 +29,6 @@
#define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f)
#define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0)
-#define usbhsp_type(p) ((p)->pipe_type)
-#define usbhsp_type_is(p, t) ((p)->pipe_type == t)
-
/*
* for debug
*/
@@ -42,28 +39,9 @@ static char *usbhsp_pipe_name[] = {
[USB_ENDPOINT_XFER_ISOC] = "ISO",
};
-/*
- * usb request functions
- */
-void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+char *usbhs_pipe_name(struct usbhs_pipe *pipe)
{
- u16 val;
-
- val = usbhs_read(priv, USBREQ);
- req->bRequest = (val >> 8) & 0xFF;
- req->bRequestType = (val >> 0) & 0xFF;
-
- req->wValue = usbhs_read(priv, USBVAL);
- req->wIndex = usbhs_read(priv, USBINDX);
- req->wLength = usbhs_read(priv, USBLENG);
-}
-
-void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
-{
- usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType);
- usbhs_write(priv, USBVAL, req->wValue);
- usbhs_write(priv, USBINDX, req->wIndex);
- usbhs_write(priv, USBLENG, req->wLength);
+ return usbhsp_pipe_name[usbhs_pipe_type(pipe)];
}
/*
@@ -106,17 +84,6 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
usbhs_bset(priv, pipe_reg, mask, val);
}
-static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
- u16 dcp_reg, u16 pipe_reg)
-{
- struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
-
- if (usbhs_pipe_is_dcp(pipe))
- return usbhs_read(priv, dcp_reg);
- else
- return usbhs_read(priv, pipe_reg);
-}
-
/*
* DCPCFG/PIPECFG functions
*/
@@ -144,11 +111,6 @@ static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
__usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val);
}
-static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
-{
- return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP);
-}
-
/*
* pipe control functions
*/
@@ -303,16 +265,16 @@ static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
/*
* only ISO / BULK pipe can use double buffer
*/
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
- usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
+ usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
return 1;
return 0;
}
static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
- const struct usb_endpoint_descriptor *desc,
- int is_host)
+ int is_host,
+ int dir_in)
{
u16 type = 0;
u16 bfre = 0;
@@ -341,40 +303,40 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
*/
/* TYPE */
- type = type_array[usbhsp_type(pipe)];
+ type = type_array[usbhs_pipe_type(pipe)];
/* BFRE */
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
- usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
bfre = 0; /* FIXME */
/* DBLB */
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
- usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
dblb = (is_double) ? DBLB : 0;
/* CNTMD */
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
cntmd = 0; /* FIXME */
/* DIR */
- if (usb_endpoint_dir_in(desc))
+ if (dir_in)
usbhsp_flags_set(pipe, IS_DIR_HOST);
- if ((is_host && usb_endpoint_dir_out(desc)) ||
- (!is_host && usb_endpoint_dir_in(desc)))
+ if ((is_host && !dir_in) ||
+ (!is_host && dir_in))
dir |= DIR_OUT;
if (!dir)
usbhsp_flags_set(pipe, IS_DIR_IN);
/* SHTNAK */
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
!dir)
shtnak = SHTNAK;
/* EPNUM */
- epnum = 0xF & usb_endpoint_num(desc);
+ epnum = 0; /* see usbhs_pipe_config_update() */
return type |
bfre |
@@ -385,19 +347,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
epnum;
}
-static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe,
- const struct usb_endpoint_descriptor *desc,
- int is_host)
-{
- /* host should set DEVSEL */
-
- /* reutn MXPS */
- return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize);
-}
-
-static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
- const struct usb_endpoint_descriptor *desc,
- int is_host)
+static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
@@ -441,9 +391,9 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
* INT : 64 byte
* ISOC: 512 byte
*/
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
buff_size = 256;
- else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
buff_size = 64;
else
buff_size = 512;
@@ -453,7 +403,7 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
/* BUFNMB has been reserved for INT pipe
* see above */
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
bufnmb = pipe_num - 2;
} else {
bufnmb = info->bufnmb_last;
@@ -473,16 +423,42 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
(0xff & bufnmb) << 0;
}
+void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
+ u16 epnum, u16 maxp)
+{
+ if (devsel > 0xA) {
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_err(dev, "devsel error %d\n", devsel);
+
+ devsel = 0;
+ }
+
+ usbhsp_pipe_barrier(pipe);
+
+ pipe->maxp = maxp;
+
+ usbhsp_pipe_select(pipe);
+ usbhsp_pipe_maxp_set(pipe, 0xFFFF,
+ (devsel << 12) |
+ maxp);
+
+ if (!usbhs_pipe_is_dcp(pipe))
+ usbhsp_pipe_cfg_set(pipe, 0x000F, epnum);
+}
+
/*
* pipe control
*/
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
{
- u16 mask = usbhs_pipe_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
-
- usbhsp_pipe_select(pipe);
-
- return (int)(usbhsp_pipe_maxp_get(pipe) & mask);
+ /*
+ * see
+ * usbhs_pipe_config_update()
+ * usbhs_dcp_malloc()
+ */
+ return pipe->maxp;
}
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
@@ -495,9 +471,12 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe)
return usbhsp_flags_has(pipe, IS_DIR_HOST);
}
-void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
+void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data)
{
- usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
+ u16 mask = (SQCLR | SQSET);
+ u16 val = (data) ? SQSET : SQCLR;
+
+ usbhsp_pipectrl_set(pipe, mask, val);
}
void usbhs_pipe_clear(struct usbhs_pipe *pipe)
@@ -516,7 +495,7 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
*/
pipe = NULL;
usbhs_for_each_pipe_with_dcp(pos, priv, i) {
- if (!usbhsp_type_is(pos, type))
+ if (!usbhs_pipe_type_is(pos, type))
continue;
if (usbhsp_flags_has(pos, IS_USED))
continue;
@@ -538,19 +517,12 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
}
void usbhs_pipe_init(struct usbhs_priv *priv,
- void (*done)(struct usbhs_pkt *pkt),
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
{
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
- struct device *dev = usbhs_priv_to_dev(priv);
struct usbhs_pipe *pipe;
int i;
- if (!done) {
- dev_err(dev, "no done function\n");
- return;
- }
-
/*
* FIXME
*
@@ -565,7 +537,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
*/
info->bufnmb_last = 4;
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
- if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
info->bufnmb_last++;
usbhsp_flags_init(pipe);
@@ -577,24 +549,23 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
usbhs_pipe_clear(pipe);
}
- info->done = done;
info->dma_map_ctrl = dma_map_ctrl;
}
struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
- const struct usb_endpoint_descriptor *desc)
+ int endpoint_type,
+ int dir_in)
{
struct device *dev = usbhs_priv_to_dev(priv);
- struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct usbhs_pipe *pipe;
- int is_host = usbhs_mod_is_host(priv, mod);
+ int is_host = usbhs_mod_is_host(priv);
int ret;
- u16 pipecfg, pipebuf, pipemaxp;
+ u16 pipecfg, pipebuf;
- pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc));
+ pipe = usbhsp_get_pipe(priv, endpoint_type);
if (!pipe) {
dev_err(dev, "can't get pipe (%s)\n",
- usbhsp_pipe_name[usb_endpoint_type(desc)]);
+ usbhsp_pipe_name[endpoint_type]);
return NULL;
}
@@ -609,22 +580,25 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
return NULL;
}
- pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host);
- pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host);
- pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
+ pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in);
+ pipebuf = usbhsp_setup_pipebuff(pipe);
usbhsp_pipe_select(pipe);
usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
- usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp);
- usbhs_pipe_clear_sequence(pipe);
+ usbhs_pipe_sequence_data0(pipe);
dev_dbg(dev, "enable pipe %d : %s (%s)\n",
usbhs_pipe_number(pipe),
- usbhsp_pipe_name[usb_endpoint_type(desc)],
+ usbhs_pipe_name(pipe),
usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
+ /*
+ * epnum / maxp are still not set to this pipe.
+ * call usbhs_pipe_config_update() after this function !!
+ */
+
return pipe;
}
@@ -651,25 +625,31 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
if (!pipe)
return NULL;
+ INIT_LIST_HEAD(&pipe->list);
+
/*
- * dcpcfg : default
- * dcpmaxp : default
- * pipebuf : nothing to do
+ * call usbhs_pipe_config_update() after this function !!
*/
- usbhsp_pipe_select(pipe);
- usbhs_pipe_clear_sequence(pipe);
- INIT_LIST_HEAD(&pipe->list);
-
return pipe;
}
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
WARN_ON(!usbhs_pipe_is_dcp(pipe));
usbhs_pipe_enable(pipe);
- usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+
+ if (!usbhs_mod_is_host(priv)) /* funconly */
+ usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+}
+
+void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out)
+{
+ usbhsp_pipe_cfg_set(pipe, DIR_OUT,
+ dir_out ? DIR_OUT : 0);
}
/*
@@ -703,7 +683,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
*/
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
pipe->priv = priv;
- usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
+
+ usbhs_pipe_type(pipe) =
+ pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
dev_dbg(dev, "pipe %x\t: %s\n",
i, usbhsp_pipe_name[pipe_type[i]]);
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 41534cb0e734..6334fc644cc0 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -30,11 +30,15 @@ struct usbhs_pipe {
struct usbhs_fifo *fifo;
struct list_head list;
+ int maxp;
+
u32 flags;
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
#define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2)
+ struct usbhs_pkt_handle *handler;
+
void *mod_private;
};
@@ -43,7 +47,6 @@ struct usbhs_pipe_info {
int size; /* array size of "pipe" */
int bufnmb_last; /* FIXME : driver needs good allocator */
- void (*done)(struct usbhs_pkt *pkt);
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
};
@@ -67,32 +70,30 @@ struct usbhs_pipe_info {
#define usbhs_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
/*
- * usb request
- */
-void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
-void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
-
-/*
* pipe control
*/
+char *usbhs_pipe_name(struct usbhs_pipe *pipe);
struct usbhs_pipe
-*usbhs_pipe_malloc(struct usbhs_priv *priv,
- const struct usb_endpoint_descriptor *desc);
+*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in);
int usbhs_pipe_probe(struct usbhs_priv *priv);
void usbhs_pipe_remove(struct usbhs_priv *priv);
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe);
void usbhs_pipe_init(struct usbhs_priv *priv,
- void (*done)(struct usbhs_pkt *pkt),
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
-void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
void usbhs_pipe_clear(struct usbhs_pipe *pipe);
int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
void usbhs_pipe_enable(struct usbhs_pipe *pipe);
void usbhs_pipe_disable(struct usbhs_pipe *pipe);
void usbhs_pipe_stall(struct usbhs_pipe *pipe);
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
+void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
+ u16 epnum, u16 maxp);
+
+#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0)
+#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1)
+void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data);
#define usbhs_pipe_to_priv(p) ((p)->priv)
#define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe)
@@ -100,10 +101,14 @@ void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
#define usbhs_pipe_to_fifo(p) ((p)->fifo)
#define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p)
+#define usbhs_pipe_type(p) ((p)->pipe_type)
+#define usbhs_pipe_type_is(p, t) ((p)->pipe_type == t)
+
/*
* dcp control
*/
struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
+void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out);
#endif /* RENESAS_USB_PIPE_H */
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index b71e309116a3..677f577c0243 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -252,6 +252,7 @@ config USB_SERIAL_GARMIN
config USB_SERIAL_IPW
tristate "USB IPWireless (3G UMTS TDD) Driver"
+ select USB_SERIAL_WWAN
help
Say Y here if you want to use a IPWireless USB modem such as
the ones supplied by Axity3G/Sentech South Africa.
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 86fbba6336c9..e92cbefc0f88 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -227,7 +227,7 @@
* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to
* recheck the condition they are sleeping on. This is defensive,
* in case a wake up is lost.
-* - Following Documentation/DocBook/kernel-locking.pdf no spin locks
+* - Following Documentation/DocBook/kernel-locking.tmpl no spin locks
* are held when calling copy_to/from_user or printk.
*/
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 5fc13e717911..8fe034d2d3e7 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -73,6 +73,7 @@ struct ftdi_private {
*/
int flags; /* some ASYNC_xxxx flags are supported */
unsigned long last_dtr_rts; /* saved modem control outputs */
+ struct async_icount icount;
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
char prev_status, diff_status; /* Used for TIOCMIWAIT */
char transmit_empty; /* If transmitter is empty or not */
@@ -207,6 +208,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) },
{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) },
@@ -745,6 +748,8 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
@@ -885,6 +890,8 @@ static void ftdi_set_termios(struct tty_struct *tty,
static int ftdi_tiocmget(struct tty_struct *tty);
static int ftdi_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
+static int ftdi_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount);
static int ftdi_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
@@ -919,6 +926,7 @@ static struct usb_serial_driver ftdi_sio_device = {
.prepare_write_buffer = ftdi_prepare_write_buffer,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
+ .get_icount = ftdi_get_icount,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
@@ -1486,7 +1494,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port)
}
/* set max packet size based on descriptor */
- priv->max_packet_size = le16_to_cpu(ep_desc->wMaxPacketSize);
+ priv->max_packet_size = usb_endpoint_maxp(ep_desc);
dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
}
@@ -1646,6 +1654,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
kref_init(&priv->kref);
mutex_init(&priv->cfg_lock);
+ memset(&priv->icount, 0x00, sizeof(priv->icount));
init_waitqueue_head(&priv->delta_msr_wait);
priv->flags = ASYNC_LOW_LATENCY;
@@ -1909,6 +1918,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
c = kfifo_out(&port->write_fifo, &buffer[i + 1], len);
if (!c)
break;
+ priv->icount.tx += c;
buffer[i] = (c << 2) + 1;
count += c + 1;
}
@@ -1916,6 +1926,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
} else {
count = kfifo_out_locked(&port->write_fifo, dest, size,
&port->lock);
+ priv->icount.tx += count;
}
return count;
@@ -1943,6 +1954,14 @@ static int ftdi_process_packet(struct tty_struct *tty,
N.B. packet may be processed more than once, but differences
are only processed once. */
status = packet[0] & FTDI_STATUS_B0_MASK;
+ if (status & FTDI_RS0_CTS)
+ priv->icount.cts++;
+ if (status & FTDI_RS0_DSR)
+ priv->icount.dsr++;
+ if (status & FTDI_RS0_RI)
+ priv->icount.rng++;
+ if (status & FTDI_RS0_RLSD)
+ priv->icount.dcd++;
if (status != priv->prev_status) {
priv->diff_status |= status ^ priv->prev_status;
wake_up_interruptible(&priv->delta_msr_wait);
@@ -1955,15 +1974,20 @@ static int ftdi_process_packet(struct tty_struct *tty,
* over framing errors */
if (packet[1] & FTDI_RS_BI) {
flag = TTY_BREAK;
+ priv->icount.brk++;
usb_serial_handle_break(port);
} else if (packet[1] & FTDI_RS_PE) {
flag = TTY_PARITY;
+ priv->icount.parity++;
} else if (packet[1] & FTDI_RS_FE) {
flag = TTY_FRAME;
+ priv->icount.frame++;
}
/* Overrun is special, not associated with a char */
- if (packet[1] & FTDI_RS_OE)
+ if (packet[1] & FTDI_RS_OE) {
+ priv->icount.overrun++;
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ }
}
/* save if the transmitter is empty or not */
@@ -1975,6 +1999,7 @@ static int ftdi_process_packet(struct tty_struct *tty,
len -= 2;
if (!len)
return 0; /* status only */
+ priv->icount.rx += len;
ch = packet + 2;
if (port->port.console && port->sysrq) {
@@ -2277,11 +2302,34 @@ static int ftdi_tiocmset(struct tty_struct *tty,
return update_mctrl(port, set, clear);
}
+static int ftdi_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct async_icount *ic = &priv->icount;
+
+ icount->cts = ic->cts;
+ icount->dsr = ic->dsr;
+ icount->rng = ic->rng;
+ icount->dcd = ic->dcd;
+ icount->tx = ic->tx;
+ icount->rx = ic->rx;
+ icount->frame = ic->frame;
+ icount->parity = ic->parity;
+ icount->overrun = ic->overrun;
+ icount->brk = ic->brk;
+ icount->buf_overrun = ic->buf_overrun;
+ return 0;
+}
+
static int ftdi_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct async_icount cnow;
+ struct async_icount cprev;
dbg("%s cmd 0x%04x", __func__, cmd);
@@ -2301,41 +2349,30 @@ static int ftdi_ioctl(struct tty_struct *tty,
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was.
- * (except that the driver doesn't support it !)
*
* This code is borrowed from linux/drivers/char/serial.c
*/
case TIOCMIWAIT:
- while (priv != NULL) {
+ cprev = priv->icount;
+ while (1) {
interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
- else {
- char diff = priv->diff_status;
-
- if (diff == 0)
- return -EIO; /* no change => error */
-
- /* Consume all events */
- priv->diff_status = 0;
-
- /* Return 0 if caller wanted to know about
- these bits */
- if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) ||
- ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) ||
- ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) ||
- ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) {
- return 0;
- }
- /*
- * Otherwise caller can't care less about what
- * happened,and so we continue to wait for more
- * events.
- */
+ cnow = priv->icount;
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+ return 0;
}
+ cprev = cnow;
}
- return 0;
+ /* not reached */
+ break;
case TIOCSERGETLSR:
return get_lsr_info(port, (struct serial_struct __user *)arg);
break;
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index bf5227ad3ef7..571fa96b49c7 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -54,6 +54,7 @@
/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */
#define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8
#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9
+#define LMI_LM3S_ICDI_BOARD_PID 0xbcda
#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */
@@ -420,9 +421,11 @@
#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */
/*
- * DSS-20 Sync Station for Sony Ericsson P800
+ * Sony Ericsson product ids
*/
-#define FTDI_DSS20_PID 0xFC82
+#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */
+#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */
+#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */
/* www.irtrans.de device */
#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index abf095be5753..2ee807523f53 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -3042,7 +3042,7 @@ static int edge_startup(struct usb_serial *serial)
endpoint = &serial->interface->altsetting[0].
endpoint[i].desc;
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
if (!interrupt_in_found &&
(usb_endpoint_is_int_in(endpoint))) {
/* we found a interrupt in endpoint */
@@ -3107,7 +3107,7 @@ static int edge_startup(struct usb_serial *serial)
usb_rcvbulkpipe(dev,
endpoint->bEndpointAddress),
edge_serial->bulk_in_buffer,
- le16_to_cpu(endpoint->wMaxPacketSize),
+ usb_endpoint_maxp(endpoint),
edge_bulk_in_callback,
edge_serial);
bulk_in_found = true;
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index ca77e88836bd..5170799d6e94 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
+#include "usb-wwan.h"
/*
* Version Information
@@ -185,7 +186,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
/*--2: Start reading from the device */
dbg("%s: setting up bulk read callback", __func__);
- usb_serial_generic_open(tty, port);
+ usb_wwan_open(tty, port);
/*--3: Tell the modem to open the floodgates on the rx bulk channel */
dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__);
@@ -219,6 +220,29 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
return 0;
}
+/* fake probe - only to allocate data structures */
+static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id)
+{
+ struct usb_wwan_intf_private *data;
+
+ data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spin_lock_init(&data->susp_lock);
+ usb_set_serial_data(serial, data);
+ return 0;
+}
+
+static void ipw_release(struct usb_serial *serial)
+{
+ struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
+
+ usb_wwan_release(serial);
+ usb_set_serial_data(serial, NULL);
+ kfree(data);
+}
+
static void ipw_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_device *dev = port->serial->dev;
@@ -285,7 +309,7 @@ static void ipw_close(struct usb_serial_port *port)
dev_err(&port->dev,
"Disabling bulk RxRead failed (error = %d)\n", result);
- usb_serial_generic_close(port);
+ usb_wwan_close(port);
}
static struct usb_serial_driver ipw_device = {
@@ -297,9 +321,14 @@ static struct usb_serial_driver ipw_device = {
.usb_driver = &usb_ipw_driver,
.id_table = usb_ipw_ids,
.num_ports = 1,
+ .disconnect = usb_wwan_disconnect,
.open = ipw_open,
.close = ipw_close,
+ .probe = ipw_probe,
+ .attach = usb_wwan_startup,
+ .release = ipw_release,
.dtr_rts = ipw_dtr_rts,
+ .write = usb_wwan_write,
};
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 40abedbc5943..3524a105d042 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -2006,7 +2006,6 @@ static int mos7720_ioctl(struct tty_struct *tty,
dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
return get_lsr_info(tty, mos7720_port,
(unsigned int __user *)arg);
- return 0;
/* FIXME: These should be using the mode methods */
case TIOCMBIS:
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 7b50aa122752..c72abd524983 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -2263,7 +2263,6 @@ static int mos7840_ioctl(struct tty_struct *tty,
case TIOCSERGETLSR:
dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
return mos7840_get_lsr_info(tty, argp);
- return 0;
case TIOCGSERIAL:
dbg("%s (%d) TIOCGSERIAL", __func__, port->number);
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 96423f3c8ef3..c248a9147439 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -523,7 +523,7 @@ static int opticon_startup(struct usb_serial *serial)
goto error;
}
- priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2;
+ priv->buffer_size = usb_endpoint_maxp(endpoint) * 2;
priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
if (!priv->bulk_in_buffer) {
dev_err(&priv->udev->dev, "out of memory\n");
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index fe22e90bc879..89ae1f65e1b1 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -475,31 +475,54 @@ enum option_blacklist_reason {
OPTION_BLACKLIST_RESERVED_IF = 2
};
+#define MAX_BL_NUM 8
struct option_blacklist_info {
- const u32 infolen; /* number of interface numbers on blacklist */
- const u8 *ifaceinfo; /* pointer to the array holding the numbers */
- enum option_blacklist_reason reason;
+ /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */
+ const unsigned long sendsetup;
+ /* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */
+ const unsigned long reserved;
};
-static const u8 four_g_w14_no_sendsetup[] = { 0, 1 };
static const struct option_blacklist_info four_g_w14_blacklist = {
- .infolen = ARRAY_SIZE(four_g_w14_no_sendsetup),
- .ifaceinfo = four_g_w14_no_sendsetup,
- .reason = OPTION_BLACKLIST_SENDSETUP
+ .sendsetup = BIT(0) | BIT(1),
};
-static const u8 alcatel_x200_no_sendsetup[] = { 0, 1 };
static const struct option_blacklist_info alcatel_x200_blacklist = {
- .infolen = ARRAY_SIZE(alcatel_x200_no_sendsetup),
- .ifaceinfo = alcatel_x200_no_sendsetup,
- .reason = OPTION_BLACKLIST_SENDSETUP
+ .sendsetup = BIT(0) | BIT(1),
+};
+
+static const struct option_blacklist_info zte_0037_blacklist = {
+ .sendsetup = BIT(0) | BIT(1),
};
-static const u8 zte_k3765_z_no_sendsetup[] = { 0, 1, 2 };
static const struct option_blacklist_info zte_k3765_z_blacklist = {
- .infolen = ARRAY_SIZE(zte_k3765_z_no_sendsetup),
- .ifaceinfo = zte_k3765_z_no_sendsetup,
- .reason = OPTION_BLACKLIST_SENDSETUP
+ .sendsetup = BIT(0) | BIT(1) | BIT(2),
+ .reserved = BIT(4),
+};
+
+static const struct option_blacklist_info huawei_cdc12_blacklist = {
+ .reserved = BIT(1) | BIT(2),
+};
+
+static const struct option_blacklist_info net_intf1_blacklist = {
+ .reserved = BIT(1),
+};
+
+static const struct option_blacklist_info net_intf3_blacklist = {
+ .reserved = BIT(3),
+};
+
+static const struct option_blacklist_info net_intf4_blacklist = {
+ .reserved = BIT(4),
+};
+
+static const struct option_blacklist_info net_intf5_blacklist = {
+ .reserved = BIT(5),
+};
+
+static const struct option_blacklist_info zte_mf626_blacklist = {
+ .sendsetup = BIT(0) | BIT(1),
+ .reserved = BIT(4),
};
static const struct usb_device_id option_ids[] = {
@@ -599,12 +622,15 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
@@ -705,7 +731,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) },
@@ -720,51 +747,62 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
/* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff,
- 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist },
+ 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&zte_0037_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
/* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) },
@@ -779,11 +817,13 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) },
@@ -1214,10 +1254,35 @@ static void __exit option_exit(void)
module_init(option_init);
module_exit(option_exit);
+static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason,
+ const struct option_blacklist_info *blacklist)
+{
+ unsigned long num;
+ const unsigned long *intf_list;
+
+ if (blacklist) {
+ if (reason == OPTION_BLACKLIST_SENDSETUP)
+ intf_list = &blacklist->sendsetup;
+ else if (reason == OPTION_BLACKLIST_RESERVED_IF)
+ intf_list = &blacklist->reserved;
+ else {
+ BUG_ON(reason);
+ return false;
+ }
+
+ for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) {
+ if (num == ifnum)
+ return true;
+ }
+ }
+ return false;
+}
+
static int option_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct usb_wwan_intf_private *data;
+
/* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
@@ -1230,14 +1295,14 @@ static int option_probe(struct usb_serial *serial,
serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
return -ENODEV;
- /* Don't bind network interfaces on Huawei K3765, K4505 & K4605 */
- if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID &&
- (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 ||
- serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505 ||
- serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4605) &&
- (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1 ||
- serial->interface->cur_altsetting->desc.bInterfaceNumber == 2))
- return -ENODEV;
+ /* Don't bind reserved interfaces (like network ones) which often have
+ * the same class/subclass/protocol as the serial interfaces. Look at
+ * the Windows driver .INF files for reserved interface numbers.
+ */
+ if (is_blacklisted(
+ serial->interface->cur_altsetting->desc.bInterfaceNumber,
+ OPTION_BLACKLIST_RESERVED_IF,
+ (const struct option_blacklist_info *) id->driver_info))
/* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */
if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID &&
@@ -1246,7 +1311,6 @@ static int option_probe(struct usb_serial *serial,
return -ENODEV;
data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
-
if (!data)
return -ENOMEM;
data->send_setup = option_send_setup;
@@ -1255,23 +1319,6 @@ static int option_probe(struct usb_serial *serial,
return 0;
}
-static enum option_blacklist_reason is_blacklisted(const u8 ifnum,
- const struct option_blacklist_info *blacklist)
-{
- const u8 *info;
- int i;
-
- if (blacklist) {
- info = blacklist->ifaceinfo;
-
- for (i = 0; i < blacklist->infolen; i++) {
- if (info[i] == ifnum)
- return blacklist->reason;
- }
- }
- return OPTION_BLACKLIST_NONE;
-}
-
static void option_instat_callback(struct urb *urb)
{
int err;
@@ -1343,9 +1390,8 @@ static int option_send_setup(struct usb_serial_port *port)
int val = 0;
dbg("%s", __func__);
- if (is_blacklisted(ifNum,
- (struct option_blacklist_info *) intfdata->private)
- == OPTION_BLACKLIST_SENDSETUP) {
+ if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP,
+ (struct option_blacklist_info *) intfdata->private)) {
dbg("No send_setup on blacklisted interface #%d\n", ifNum);
return -EIO;
}
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 1d33260de014..9083d1e616b4 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -92,6 +92,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
{ USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) },
+ { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
{ } /* Terminating entry */
};
@@ -360,9 +361,6 @@ static void pl2303_set_termios(struct tty_struct *tty,
tmp >>= 2;
buf[1] <<= 1;
}
- if (tmp > 256) {
- tmp %= 256;
- }
buf[0] = tmp;
}
}
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index ca0d237683b3..3d10d7f02072 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -148,3 +148,8 @@
/* WinChipHead USB->RS 232 adapter */
#define WINCHIPHEAD_VENDOR_ID 0x4348
#define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523
+
+/* SMART USB Serial Adapter */
+#define SMART_VENDOR_ID 0x0b8c
+#define SMART_PRODUCT_ID 0x2303
+
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index aeccc7f0a93c..b9bb24729c99 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -28,6 +28,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */
+ {USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
{USB_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
{USB_DEVICE(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */
{USB_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */
@@ -84,6 +85,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */
{USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
+ {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index d5d136a53b61..b18179bda0d8 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -1009,7 +1009,7 @@ static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
struct sierra_intf_private *intfdata;
int b;
- if (message.event & PM_EVENT_AUTO) {
+ if (PMSG_IS_AUTO(message)) {
intfdata = serial->private;
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index d9457bd4fe10..7096f799b071 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -226,7 +226,7 @@ static int symbol_startup(struct usb_serial *serial)
goto error;
}
- priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2;
+ priv->buffer_size = usb_endpoint_maxp(endpoint) * 2;
priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
if (!priv->int_buffer) {
dev_err(&priv->udev->dev, "out of memory\n");
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 1c031309ab25..cc274fdf2627 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -912,7 +912,7 @@ int usb_serial_probe(struct usb_interface *interface,
goto probe_error;
}
buffer_size = max_t(int, serial->type->bulk_in_size,
- le16_to_cpu(endpoint->wMaxPacketSize));
+ usb_endpoint_maxp(endpoint));
port->bulk_in_size = buffer_size;
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -942,7 +942,7 @@ int usb_serial_probe(struct usb_interface *interface,
goto probe_error;
buffer_size = serial->type->bulk_out_size;
if (!buffer_size)
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
port->bulk_out_size = buffer_size;
port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -990,7 +990,7 @@ int usb_serial_probe(struct usb_interface *interface,
"No free urbs available\n");
goto probe_error;
}
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
port->interrupt_in_endpointAddress =
endpoint->bEndpointAddress;
port->interrupt_in_buffer = kmalloc(buffer_size,
@@ -1021,7 +1021,7 @@ int usb_serial_probe(struct usb_interface *interface,
"No free urbs available\n");
goto probe_error;
}
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
port->interrupt_out_size = buffer_size;
port->interrupt_out_endpointAddress =
endpoint->bEndpointAddress;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index e4fad5e643d7..d555ca9567b8 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -651,7 +651,7 @@ int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
dbg("%s entered", __func__);
- if (message.event & PM_EVENT_AUTO) {
+ if (PMSG_IS_AUTO(message)) {
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;
spin_unlock_irq(&intfdata->susp_lock);
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index bedc4b9f2ac4..fe2d803a6347 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -42,7 +42,7 @@ config USB_STORAGE_REALTEK
config REALTEK_AUTOPM
bool "Realtek Card Reader autosuspend support"
- depends on USB_STORAGE_REALTEK && CONFIG_PM_RUNTIME
+ depends on USB_STORAGE_REALTEK && PM_RUNTIME
default y
config USB_STORAGE_DATAFAB
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index 34adc4b42ceb..0ce5f79197e7 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -25,7 +25,6 @@
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/version.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -293,6 +292,52 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
return USB_STOR_TRANSPORT_ERROR;
}
+static int rts51x_bulk_transport_special(struct us_data *us, u8 lun,
+ u8 *cmd, int cmd_len, u8 *buf, int buf_len,
+ enum dma_data_direction dir, int *act_len)
+{
+ struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
+ int result;
+ unsigned int cswlen;
+ unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
+
+ /* set up the command wrapper */
+ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ bcb->DataTransferLength = cpu_to_le32(buf_len);
+ bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0;
+ bcb->Tag = ++us->tag;
+ bcb->Lun = lun;
+ bcb->Length = cmd_len;
+
+ /* copy the command payload */
+ memset(bcb->CDB, 0, sizeof(bcb->CDB));
+ memcpy(bcb->CDB, cmd, bcb->Length);
+
+ /* send it to out endpoint */
+ result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ bcb, cbwlen, NULL);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ /* DATA STAGE */
+ /* send/receive data payload, if there is any */
+
+ if (buf && buf_len) {
+ unsigned int pipe = (dir == DMA_FROM_DEVICE) ?
+ us->recv_bulk_pipe : us->send_bulk_pipe;
+ result = usb_stor_bulk_transfer_buf(us, pipe,
+ buf, buf_len, NULL);
+ if (result == USB_STOR_XFER_ERROR)
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* get CSW for device status */
+ result = usb_bulk_msg(us->pusb_dev, us->recv_bulk_pipe, bcs,
+ US_BULK_CS_WRAP_LEN, &cswlen, 250);
+ return result;
+}
+
/* Determine what the maximum LUN supported is */
static int rts51x_get_max_lun(struct us_data *us)
{
@@ -320,6 +365,11 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
{
int retval;
u8 cmnd[12] = { 0 };
+ u8 *buf;
+
+ buf = kmalloc(len, GFP_NOIO);
+ if (buf == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
@@ -331,10 +381,14 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
- data, len, DMA_FROM_DEVICE, NULL);
- if (retval != USB_STOR_TRANSPORT_GOOD)
+ buf, len, DMA_FROM_DEVICE, NULL);
+ if (retval != USB_STOR_TRANSPORT_GOOD) {
+ kfree(buf);
return -EIO;
+ }
+ memcpy(data, buf, len);
+ kfree(buf);
return 0;
}
@@ -342,6 +396,12 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
{
int retval;
u8 cmnd[12] = { 0 };
+ u8 *buf;
+
+ buf = kmalloc(len, GFP_NOIO);
+ if (buf == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
+ memcpy(buf, data, len);
US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
@@ -353,7 +413,8 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
- data, len, DMA_TO_DEVICE, NULL);
+ buf, len, DMA_TO_DEVICE, NULL);
+ kfree(buf);
if (retval != USB_STOR_TRANSPORT_GOOD)
return -EIO;
@@ -365,6 +426,11 @@ static int rts51x_read_status(struct us_data *us,
{
int retval;
u8 cmnd[12] = { 0 };
+ u8 *buf;
+
+ buf = kmalloc(len, GFP_NOIO);
+ if (buf == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
US_DEBUGP("%s, lun = %d\n", __func__, lun);
@@ -372,10 +438,14 @@ static int rts51x_read_status(struct us_data *us,
cmnd[1] = 0x09;
retval = rts51x_bulk_transport(us, lun, cmnd, 12,
- status, len, DMA_FROM_DEVICE, actlen);
- if (retval != USB_STOR_TRANSPORT_GOOD)
+ buf, len, DMA_FROM_DEVICE, actlen);
+ if (retval != USB_STOR_TRANSPORT_GOOD) {
+ kfree(buf);
return -EIO;
+ }
+ memcpy(status, buf, len);
+ kfree(buf);
return 0;
}
@@ -434,6 +504,29 @@ static int enable_oscillator(struct us_data *us)
return 0;
}
+static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len)
+{
+ int retval;
+ u16 addr = 0xFE47;
+ u8 cmnd[12] = {0};
+
+ US_DEBUGP("%s, addr = 0x%x, len = %d\n", __FUNCTION__, addr, len);
+
+ cmnd[0] = 0xF0;
+ cmnd[1] = 0x0E;
+ cmnd[2] = (u8)(addr >> 8);
+ cmnd[3] = (u8)addr;
+ cmnd[4] = (u8)(len >> 8);
+ cmnd[5] = (u8)len;
+
+ retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, data, len, DMA_TO_DEVICE, NULL);
+ if (retval != USB_STOR_TRANSPORT_GOOD) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int do_config_autodelink(struct us_data *us, int enable, int force)
{
int retval;
@@ -454,7 +547,8 @@ static int do_config_autodelink(struct us_data *us, int enable, int force)
US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value);
- retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
+ retval = __do_config_autodelink(us, &value, 1);
if (retval < 0)
return -EIO;
@@ -486,7 +580,8 @@ static int config_autodelink_after_power_on(struct us_data *us)
SET_BIT(value, 7);
- retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
+ retval = __do_config_autodelink(us, &value, 1);
if (retval < 0)
return -EIO;
@@ -507,7 +602,8 @@ static int config_autodelink_after_power_on(struct us_data *us)
CLR_BIT(value, 7);
}
- retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
+ retval = __do_config_autodelink(us, &value, 1);
if (retval < 0)
return -EIO;
@@ -584,7 +680,8 @@ static int config_autodelink_before_power_down(struct us_data *us)
if (CHECK_ID(chip, 0x0138, 0x3882))
SET_BIT(value, 2);
- retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
+ retval = __do_config_autodelink(us, &value, 1);
if (retval < 0)
return -EIO;
}
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index e8ae21b2d387..ff32390d61e5 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -691,6 +691,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
int temp_result;
struct scsi_eh_save ses;
int sense_size = US_SENSE_SIZE;
+ struct scsi_sense_hdr sshdr;
+ const u8 *scdd;
+ u8 fm_ili;
/* device supports and needs bigger sense buffer */
if (us->fflags & US_FL_SANE_SENSE)
@@ -774,32 +777,30 @@ Retry_Sense:
srb->sense_buffer[7] = (US_SENSE_SIZE - 8);
}
+ scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ &sshdr);
+
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
- srb->sense_buffer[0],
- srb->sense_buffer[2] & 0xf,
- srb->sense_buffer[12],
- srb->sense_buffer[13]);
+ sshdr.response_code, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
#ifdef CONFIG_USB_STORAGE_DEBUG
- usb_stor_show_sense(
- srb->sense_buffer[2] & 0xf,
- srb->sense_buffer[12],
- srb->sense_buffer[13]);
+ usb_stor_show_sense(sshdr.sense_key, sshdr.asc, sshdr.ascq);
#endif
/* set the result so the higher layers expect this data */
srb->result = SAM_STAT_CHECK_CONDITION;
+ scdd = scsi_sense_desc_find(srb->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, 4);
+ fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0;
+
/* We often get empty sense data. This could indicate that
* everything worked or that there was an unspecified
* problem. We have to decide which.
*/
- if ( /* Filemark 0, ignore EOM, ILI 0, no sense */
- (srb->sense_buffer[2] & 0xaf) == 0 &&
- /* No ASC or ASCQ */
- srb->sense_buffer[12] == 0 &&
- srb->sense_buffer[13] == 0) {
-
+ if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 &&
+ fm_ili == 0) {
/* If things are really okay, then let's show that.
* Zero out the sense buffer so the higher layers
* won't realize we did an unsolicited auto-sense.
@@ -814,7 +815,10 @@ Retry_Sense:
*/
} else {
srb->result = DID_ERROR << 16;
- srb->sense_buffer[2] = HARDWARE_ERROR;
+ if ((sshdr.response_code & 0x72) == 0x72)
+ srb->sense_buffer[1] = HARDWARE_ERROR;
+ else
+ srb->sense_buffer[2] = HARDWARE_ERROR;
}
}
}
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 0ca095820f3e..c325e69415a1 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -831,12 +831,22 @@ static int usb_stor_scan_thread(void * __us)
dev_dbg(dev, "device found\n");
- set_freezable();
- /* Wait for the timeout to expire or for a disconnect */
+ set_freezable_with_signal();
+ /*
+ * Wait for the timeout to expire or for a disconnect
+ *
+ * We can't freeze in this thread or we risk causing khubd to
+ * fail to freeze, but we can't be non-freezable either. Nor can
+ * khubd freeze while waiting for scanning to complete as it may
+ * hold the device lock, causing a hang when suspending devices.
+ * So we request a fake signal when freezing and use
+ * interruptible sleep to kick us out of our wait early when
+ * freezing happens.
+ */
if (delay_use > 0) {
dev_dbg(dev, "waiting for device to settle "
"before scanning\n");
- wait_event_freezable_timeout(us->delay_wait,
+ wait_event_interruptible_timeout(us->delay_wait,
test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
delay_use * HZ);
}
diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c
new file mode 100644
index 000000000000..d29503e954ab
--- /dev/null
+++ b/drivers/usb/usb-common.c
@@ -0,0 +1,35 @@
+/*
+ * Provides code common for host and device side USB.
+ *
+ * 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, version 2.
+ *
+ * If either host side (ie. CONFIG_USB=y) or device side USB stack
+ * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is
+ * compiled-in as well. Otherwise, if either of the two stacks is
+ * compiled as module, this file is compiled as module as well.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/ch9.h>
+
+const char *usb_speed_string(enum usb_device_speed speed)
+{
+ static const char *const names[] = {
+ [USB_SPEED_UNKNOWN] = "UNKNOWN",
+ [USB_SPEED_LOW] = "low-speed",
+ [USB_SPEED_FULL] = "full-speed",
+ [USB_SPEED_HIGH] = "high-speed",
+ [USB_SPEED_WIRELESS] = "wireless",
+ [USB_SPEED_SUPER] = "super-speed",
+ };
+
+ if (speed < 0 || speed >= ARRAY_SIZE(names))
+ speed = USB_SPEED_UNKNOWN;
+ return names[speed];
+}
+EXPORT_SYMBOL_GPL(usb_speed_string);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index e24ce3123071..32d6fc953904 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -555,7 +555,7 @@ static int skel_probe(struct usb_interface *interface,
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 59a748a0e5da..0d1863c9edde 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -43,7 +43,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
/* Fill up Data Transfer EP pointers */
wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
- wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize);
+ wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
if (wa->xfer_result == NULL)
goto error_xfer_result_alloc;