mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (260 commits) usb: renesas_usbhs: fixup inconsistent return from usbhs_pkt_push() usb/isp1760: Allow to optionally trigger low-level chip reset via GPIOLIB. USB: gadget: midi: memory leak in f_midi_bind_config() USB: gadget: midi: fix range check in f_midi_out_open() QE/FHCI: fixed the CONTROL bug usb: renesas_usbhs: tidyup for smatch warnings USB: Fix USB Kconfig dependency problem on 85xx/QoirQ platforms EHCI: workaround for MosChip controller bug usb: gadget: file_storage: fix race on unloading USB: ftdi_sio.c: Use ftdi async_icount structure for TIOCMIWAIT, as in other drivers USB: ftdi_sio.c:Fill MSR fields of the ftdi async_icount structure USB: ftdi_sio.c: Fill LSR fields of the ftdi async_icount structure USB: ftdi_sio.c:Fill TX field of the ftdi async_icount structure USB: ftdi_sio.c: Fill the RX field of the ftdi async_icount structure USB: ftdi_sio.c: Basic icount infrastructure for ftdi_sio usb/isp1760: Let OF bindings depend on general CONFIG_OF instead of PPC_OF . USB: ftdi_sio: Support TI/Luminary Micro Stellaris BD-ICDI Board USB: Fix runtime wakeup on OHCI xHCI/USB: Make xHCI driver have a BOS descriptor. usb: gadget: add new usb gadget for ACM and mass storage ...
This commit is contained in:
commit
1be025d3cb
46
Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd
Normal file
46
Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd
Normal file
@ -0,0 +1,46 @@
|
||||
What: /sys/bus/pci/drivers/ehci_hcd/.../companion
|
||||
/sys/bus/usb/devices/usbN/../companion
|
||||
Date: January 2007
|
||||
KernelVersion: 2.6.21
|
||||
Contact: Alan Stern <stern@rowland.harvard.edu>
|
||||
Description:
|
||||
PCI-based EHCI USB controllers (i.e., high-speed USB-2.0
|
||||
controllers) are often implemented along with a set of
|
||||
"companion" full/low-speed USB-1.1 controllers. When a
|
||||
high-speed device is plugged in, the connection is routed
|
||||
to the EHCI controller; when a full- or low-speed device
|
||||
is plugged in, the connection is routed to the companion
|
||||
controller.
|
||||
|
||||
Sometimes you want to force a high-speed device to connect
|
||||
at full speed, which can be accomplished by forcing the
|
||||
connection to be routed to the companion controller.
|
||||
That's what this file does. Writing a port number to the
|
||||
file causes connections on that port to be routed to the
|
||||
companion controller, and writing the negative of a port
|
||||
number returns the port to normal operation.
|
||||
|
||||
For example: To force the high-speed device attached to
|
||||
port 4 on bus 2 to run at full speed:
|
||||
|
||||
echo 4 >/sys/bus/usb/devices/usb2/../companion
|
||||
|
||||
To return the port to high-speed operation:
|
||||
|
||||
echo -4 >/sys/bus/usb/devices/usb2/../companion
|
||||
|
||||
Reading the file gives the list of ports currently forced
|
||||
to the companion controller.
|
||||
|
||||
Note: Some EHCI controllers do not have companions; they
|
||||
may contain an internal "transaction translator" or they
|
||||
may be attached directly to a "rate-matching hub". This
|
||||
mechanism will not work with such controllers. Also, it
|
||||
cannot be used to force a port on a high-speed hub to
|
||||
connect at full speed.
|
||||
|
||||
Note: When this file was first added, it appeared in a
|
||||
different sysfs directory. The location given above is
|
||||
correct for 2.6.35 (and probably several earlier kernel
|
||||
versions as well).
|
||||
|
@ -142,3 +142,18 @@ Description:
|
||||
such devices.
|
||||
Users:
|
||||
usb_modeswitch
|
||||
|
||||
What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm
|
||||
Date: September 2011
|
||||
Contact: Andiry Xu <andiry.xu@amd.com>
|
||||
Description:
|
||||
If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device
|
||||
is plugged in to a xHCI host which support link PM, it will
|
||||
perform a LPM test; if the test is passed and host supports
|
||||
USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will
|
||||
be enabled for the device and the USB device directory will
|
||||
contain a file named power/usb2_hardware_lpm. The file holds
|
||||
a string value (enable or disable) indicating whether or not
|
||||
USB2 hardware LPM is enabled for the device. Developer can
|
||||
write y/Y/1 or n/N/0 to the file to enable/disable the
|
||||
feature.
|
||||
|
45
Documentation/usb/dwc3.txt
Normal file
45
Documentation/usb/dwc3.txt
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
TODO
|
||||
~~~~~~
|
||||
Please pick something while reading :)
|
||||
|
||||
- Convert interrupt handler to per-ep-thread-irq
|
||||
|
||||
As it turns out some DWC3-commands ~1ms to complete. Currently we spin
|
||||
until the command completes which is bad.
|
||||
|
||||
Implementation idea:
|
||||
- dwc core implements a demultiplexing irq chip for interrupts per
|
||||
endpoint. The interrupt numbers are allocated during probe and belong
|
||||
to the device. If MSI provides per-endpoint interrupt this dummy
|
||||
interrupt chip can be replaced with "real" interrupts.
|
||||
- interrupts are requested / allocated on usb_ep_enable() and removed on
|
||||
usb_ep_disable(). Worst case are 32 interrupts, the lower limit is two
|
||||
for ep0/1.
|
||||
- dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout()
|
||||
until the command completes.
|
||||
- the interrupt handler is split into the following pieces:
|
||||
- primary handler of the device
|
||||
goes through every event and calls generic_handle_irq() for event
|
||||
it. On return from generic_handle_irq() in acknowledges the event
|
||||
counter so interrupt goes away (eventually).
|
||||
|
||||
- threaded handler of the device
|
||||
none
|
||||
|
||||
- primary handler of the EP-interrupt
|
||||
reads the event and tries to process it. Everything that requries
|
||||
sleeping is handed over to the Thread. The event is saved in an
|
||||
per-endpoint data-structure.
|
||||
We probably have to pay attention not to process events once we
|
||||
handed something to thread so we don't process event X prio Y
|
||||
where X > Y.
|
||||
|
||||
- threaded handler of the EP-interrupt
|
||||
handles the remaining EP work which might sleep such as waiting
|
||||
for command completion.
|
||||
|
||||
Latency:
|
||||
There should be no increase in latency since the interrupt-thread has a
|
||||
high priority and will be run before an average task in user land
|
||||
(except the user changed priorities).
|
@ -487,3 +487,29 @@ succeed, it may still remain active and thus cause the system to
|
||||
resume as soon as the system suspend is complete. Or the remote
|
||||
wakeup may fail and get lost. Which outcome occurs depends on timing
|
||||
and on the hardware and firmware design.
|
||||
|
||||
|
||||
xHCI hardware link PM
|
||||
---------------------
|
||||
|
||||
xHCI host controller provides hardware link power management to usb2.0
|
||||
(xHCI 1.0 feature) and usb3.0 devices which support link PM. By
|
||||
enabling hardware LPM, the host can automatically put the device into
|
||||
lower power state(L1 for usb2.0 devices, or U1/U2 for usb3.0 devices),
|
||||
which state device can enter and resume very quickly.
|
||||
|
||||
The user interface for controlling USB2 hardware LPM is located in the
|
||||
power/ subdirectory of each USB device's sysfs directory, that is, in
|
||||
/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The
|
||||
relevant attribute files is usb2_hardware_lpm.
|
||||
|
||||
power/usb2_hardware_lpm
|
||||
|
||||
When a USB2 device which support LPM is plugged to a
|
||||
xHCI host root hub which support software LPM, the
|
||||
host will run a software LPM test for it; if the device
|
||||
enters L1 state and resume successfully and the host
|
||||
supports USB2 hardware LPM, this file will show up and
|
||||
driver will enable hardware LPM for the device. You
|
||||
can write y/Y/1 or n/N/0 to the file to enable/disable
|
||||
USB2 hardware LPM manually. This is for test purpose mainly.
|
||||
|
@ -2136,6 +2136,14 @@ M: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell-wmi.c
|
||||
|
||||
DESIGNWARE USB3 DRD IP DRIVER
|
||||
M: Felipe Balbi <balbi@ti.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
L: linux-omap@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
|
||||
S: Maintained
|
||||
F: drivers/usb/dwc3/
|
||||
|
||||
DEVICE NUMBER REGISTRY
|
||||
M: Torben Mathiasen <device@lanana.org>
|
||||
W: http://lanana.org/docs/device-list/index.html
|
||||
|
@ -35,6 +35,13 @@ extern struct pxa_device_desc pxa168_device_fb;
|
||||
extern struct pxa_device_desc pxa168_device_keypad;
|
||||
extern struct pxa_device_desc pxa168_device_eth;
|
||||
|
||||
struct pxa168_usb_pdata {
|
||||
/* If NULL, default phy init routine for PXA168 would be called */
|
||||
int (*phy_init)(void __iomem *usb_phy_reg_base);
|
||||
};
|
||||
/* pdata can be NULL */
|
||||
int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata);
|
||||
|
||||
static inline int pxa168_add_uart(int id)
|
||||
{
|
||||
struct pxa_device_desc *d = NULL;
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include <mach/dma.h>
|
||||
#include <mach/devices.h>
|
||||
#include <mach/mfp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <mach/pxa168.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "clock.h"
|
||||
@ -83,6 +86,7 @@ static APBC_CLK(keypad, PXA168_KPC, 0, 32000);
|
||||
static APMU_CLK(nand, NAND, 0x19b, 156000000);
|
||||
static APMU_CLK(lcd, LCD, 0x7f, 312000000);
|
||||
static APMU_CLK(eth, ETH, 0x09, 0);
|
||||
static APMU_CLK(usb, USB, 0x12, 0);
|
||||
|
||||
/* device and clock bindings */
|
||||
static struct clk_lookup pxa168_clkregs[] = {
|
||||
@ -104,6 +108,7 @@ static struct clk_lookup pxa168_clkregs[] = {
|
||||
INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL),
|
||||
INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL),
|
||||
INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"),
|
||||
INIT_CLKREG(&clk_usb, "pxa168-ehci", "PXA168-USBCLK"),
|
||||
};
|
||||
|
||||
static int __init pxa168_init(void)
|
||||
@ -169,3 +174,44 @@ PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
|
||||
PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8);
|
||||
PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c);
|
||||
PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
|
||||
|
||||
struct resource pxa168_usb_host_resources[] = {
|
||||
/* USB Host conroller register base */
|
||||
[0] = {
|
||||
.start = 0xd4209000,
|
||||
.end = 0xd4209000 + 0x200,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.name = "pxa168-usb-host",
|
||||
},
|
||||
/* USB PHY register base */
|
||||
[1] = {
|
||||
.start = 0xd4206000,
|
||||
.end = 0xd4206000 + 0xff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
.name = "pxa168-usb-phy",
|
||||
},
|
||||
[2] = {
|
||||
.start = IRQ_PXA168_USB2,
|
||||
.end = IRQ_PXA168_USB2,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static u64 pxa168_usb_host_dmamask = DMA_BIT_MASK(32);
|
||||
struct platform_device pxa168_device_usb_host = {
|
||||
.name = "pxa168-ehci",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &pxa168_usb_host_dmamask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
|
||||
.num_resources = ARRAY_SIZE(pxa168_usb_host_resources),
|
||||
.resource = pxa168_usb_host_resources,
|
||||
};
|
||||
|
||||
int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata)
|
||||
{
|
||||
pxa168_device_usb_host.dev.platform_data = pdata;
|
||||
return platform_device_register(&pxa168_device_usb_host);
|
||||
}
|
||||
|
@ -242,14 +242,11 @@ obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o \
|
||||
obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \
|
||||
hsmmc.o
|
||||
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
|
||||
hsmmc.o \
|
||||
omap_phy_internal.o
|
||||
hsmmc.o
|
||||
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \
|
||||
hsmmc.o \
|
||||
omap_phy_internal.o
|
||||
hsmmc.o
|
||||
|
||||
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \
|
||||
omap_phy_internal.o \
|
||||
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o
|
||||
|
||||
obj-$(CONFIG_MACH_CRANEBOARD) += board-am3517crane.o
|
||||
|
||||
@ -260,6 +257,8 @@ obj-$(CONFIG_MACH_TI8168EVM) += board-ti8168evm.o
|
||||
usbfs-$(CONFIG_ARCH_OMAP_OTG) := usb-fs.o
|
||||
obj-y += $(usbfs-m) $(usbfs-y)
|
||||
obj-y += usb-musb.o
|
||||
obj-y += omap_phy_internal.o
|
||||
|
||||
obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o
|
||||
obj-y += usb-host.o
|
||||
|
||||
|
@ -810,6 +810,7 @@ static struct usbhs_private usbhs1_private = {
|
||||
},
|
||||
.driver_param = {
|
||||
.buswait_bwait = 4,
|
||||
.has_otg = 1,
|
||||
.pipe_type = usbhs1_pipe_cfg,
|
||||
.pipe_size = ARRAY_SIZE(usbhs1_pipe_cfg),
|
||||
.d0_tx_id = SHDMA_SLAVE_USB1_TX,
|
||||
|
@ -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 SOC_AU1200
|
||||
default y if ARCH_IXP4XX
|
||||
@ -69,6 +75,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.
|
||||
@ -110,6 +122,8 @@ config USB
|
||||
|
||||
source "drivers/usb/core/Kconfig"
|
||||
|
||||
source "drivers/usb/dwc3/Kconfig"
|
||||
|
||||
source "drivers/usb/mon/Kconfig"
|
||||
|
||||
source "drivers/usb/wusbcore/Kconfig"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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(¤t_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 = ¤t_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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1583,7 +1583,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 +1700,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 = {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
@ -2429,7 +2443,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 +2492,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:
|
||||
|
@ -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;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* bomb out completely if the connection bounced */
|
||||
if ((portchange & USB_PORT_STAT_C_CONNECTION))
|
||||
return -ENOTCONN;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
/* 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,47 +2230,13 @@ 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);
|
||||
done:
|
||||
if (!warm)
|
||||
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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if a port is power on */
|
||||
static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
|
||||
{
|
||||
@ -2347,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@ -2558,7 +2607,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;
|
||||
}
|
||||
|
||||
@ -2798,7 +2852,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 +2873,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 +2912,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 +2994,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 +3068,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 +3092,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 +3624,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)
|
||||
|
@ -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;
|
||||
|
@ -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 },
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
25
drivers/usb/dwc3/Kconfig
Normal file
25
drivers/usb/dwc3/Kconfig
Normal file
@ -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
|
36
drivers/usb/dwc3/Makefile
Normal file
36
drivers/usb/dwc3/Makefile
Normal file
@ -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
|
||||
|
484
drivers/usb/dwc3/core.c
Normal file
484
drivers/usb/dwc3/core.c
Normal file
@ -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);
|
768
drivers/usb/dwc3/core.h
Normal file
768
drivers/usb/dwc3/core.h
Normal file
@ -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 */
|
50
drivers/usb/dwc3/debug.h
Normal file
50
drivers/usb/dwc3/debug.h
Normal file
@ -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
|
||||
|
441
drivers/usb/dwc3/debugfs.c
Normal file
441
drivers/usb/dwc3/debugfs.c
Normal file
@ -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;
|
||||
}
|
401
drivers/usb/dwc3/dwc3-omap.c
Normal file
401
drivers/usb/dwc3/dwc3-omap.c
Normal file
@ -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);
|
219
drivers/usb/dwc3/dwc3-pci.c
Normal file
219
drivers/usb/dwc3/dwc3-pci.c
Normal file
@ -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);
|
804
drivers/usb/dwc3/ep0.c
Normal file
804
drivers/usb/dwc3/ep0.c
Normal file
@ -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(¶ms, 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, ¶ms);
|
||||
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;
|
||||
}
|
||||
}
|
2104
drivers/usb/dwc3/gadget.c
Normal file
2104
drivers/usb/dwc3/gadget.c
Normal file
File diff suppressed because it is too large
Load Diff
211
drivers/usb/dwc3/gadget.h
Normal file
211
drivers/usb/dwc3/gadget.h
Normal file
@ -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 */
|
54
drivers/usb/dwc3/io.h
Normal file
54
drivers/usb/dwc3/io.h
Normal file
@ -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 */
|
@ -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
|
||||
|
@ -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
|
||||
|
256
drivers/usb/gadget/acm_ms.c
Normal file
256
drivers/usb/gadget/acm_ms.c
Normal file
@ -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);
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -4,7 +4,6 @@
|
||||
* Copyright (C) 2010 Stephane Duverger
|
||||
*
|
||||
* Released under the GPLv2.
|
||||
*
|
||||
*/
|
||||
|
||||
/* verbose messages */
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
998
drivers/usb/gadget/f_midi.c
Normal file
998
drivers/usb/gadget/f_midi.c
Normal file
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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" :
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
@ -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__);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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__
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
@ -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_pullup(struct usb_gadget *gadget, int is_on)
|
||||
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);
|
||||
|
||||
udc->softconnect = (is_on != 0);
|
||||
if (udc->driver && udc->softconnect)
|
||||
udc_start(udc);
|
||||
else
|
||||
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 0;
|
||||
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->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 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 (udc == NULL) {
|
||||
dev_err(&dev->dev, "failed to allocate memory for udc\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
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");
|
||||
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",
|
||||
|
@ -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
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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];
|
||||
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user