summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 10:08:32 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 10:08:32 +0400
commitf549953c15deab4c54708b39af86d4edecc6cddc (patch)
treef0412f989b77cdceab34c18aa85a8a25d5942a1f /drivers/usb
parentf0deb97ab13ad1f89cd0993f7339655d59788405 (diff)
parente04f5f7e423018bcec84c11af2058cdce87816f3 (diff)
downloadlinux-f549953c15deab4c54708b39af86d4edecc6cddc.tar.xz
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (115 commits) EHCI: fix direction handling for interrupt data toggles USB: serial: add IDs for WinChipHead USB->RS232 adapter USB: OHCI: fix another regression for NVIDIA controllers usb: gadget: m66592-udc: add pullup function usb: gadget: m66592-udc: add function for external controller usb: gadget: r8a66597-udc: add pullup function usb: renesas_usbhs: support multi driver usb: renesas_usbhs: inaccessible pipe is not an error usb: renesas_usbhs: care buff alignment when dma handler USB: PL2303: correctly handle baudrates above 115200 usb: r8a66597-hcd: fixup USB_PORT_STAT_C_SUSPEND shift usb: renesas_usbhs: compile/config are rescued usb: renesas_usbhs: fixup comment-out usb: update email address in ohci-sh and r8a66597-hcd usb: r8a66597-hcd: add function for external controller EHCI: only power off port if over-current is active USB: mon: Allow to use usbmon without debugfs USB: EHCI: go back to using the system clock for QH unlinks ehci: add pci quirk for Ordissimo and RM Slate 100 too ehci: refactor pci quirk to use standard dmi_check_system method ... Fix up trivial conflicts in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/atm/ueagle-atm.c38
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--drivers/usb/class/usblp.c7
-rw-r--r--drivers/usb/core/hcd.c17
-rw-r--r--drivers/usb/gadget/Kconfig313
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/amd5536udc.c18
-rw-r--r--drivers/usb/gadget/at91_udc.c20
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c26
-rw-r--r--drivers/usb/gadget/audio.c1
-rw-r--r--drivers/usb/gadget/cdc2.c1
-rw-r--r--drivers/usb/gadget/ci13xxx_msm.c1
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c62
-rw-r--r--drivers/usb/gadget/composite.c363
-rw-r--r--drivers/usb/gadget/config.c25
-rw-r--r--drivers/usb/gadget/dbgp.c10
-rw-r--r--drivers/usb/gadget/dummy_hcd.c1162
-rw-r--r--drivers/usb/gadget/epautoconf.c132
-rw-r--r--drivers/usb/gadget/ether.c1
-rw-r--r--drivers/usb/gadget/f_acm.c50
-rw-r--r--drivers/usb/gadget/f_audio.c5
-rw-r--r--drivers/usb/gadget/f_ecm.c152
-rw-r--r--drivers/usb/gadget/f_eem.c90
-rw-r--r--drivers/usb/gadget/f_fs.c3
-rw-r--r--drivers/usb/gadget/f_hid.c22
-rw-r--r--drivers/usb/gadget/f_loopback.c72
-rw-r--r--drivers/usb/gadget/f_mass_storage.c33
-rw-r--r--drivers/usb/gadget/f_ncm.c58
-rw-r--r--drivers/usb/gadget/f_obex.c32
-rw-r--r--drivers/usb/gadget/f_phonet.c17
-rw-r--r--drivers/usb/gadget/f_rndis.c151
-rw-r--r--drivers/usb/gadget/f_serial.c32
-rw-r--r--drivers/usb/gadget/f_sourcesink.c71
-rw-r--r--drivers/usb/gadget/f_subset.c95
-rw-r--r--drivers/usb/gadget/f_uvc.c8
-rw-r--r--drivers/usb/gadget/file_storage.c15
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c20
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c20
-rw-r--r--drivers/usb/gadget/fusb300_udc.c111
-rw-r--r--drivers/usb/gadget/g_ffs.c1
-rw-r--r--drivers/usb/gadget/gadget_chips.h178
-rw-r--r--drivers/usb/gadget/gmidi.c9
-rw-r--r--drivers/usb/gadget/goku_udc.c19
-rw-r--r--drivers/usb/gadget/hid.c1
-rw-r--r--drivers/usb/gadget/imx_udc.c20
-rw-r--r--drivers/usb/gadget/inode.c11
-rw-r--r--drivers/usb/gadget/langwell_udc.c32
-rw-r--r--drivers/usb/gadget/m66592-udc.c62
-rw-r--r--drivers/usb/gadget/m66592-udc.h40
-rw-r--r--drivers/usb/gadget/mass_storage.c1
-rw-r--r--drivers/usb/gadget/multi.c1
-rw-r--r--drivers/usb/gadget/mv_udc_core.c21
-rw-r--r--drivers/usb/gadget/ncm.c1
-rw-r--r--drivers/usb/gadget/net2272.c2752
-rw-r--r--drivers/usb/gadget/net2272.h601
-rw-r--r--drivers/usb/gadget/net2280.c74
-rw-r--r--drivers/usb/gadget/nokia.c1
-rw-r--r--drivers/usb/gadget/omap_udc.c22
-rw-r--r--drivers/usb/gadget/pch_udc.c16
-rw-r--r--drivers/usb/gadget/printer.c40
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c19
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c23
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c71
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h2
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c19
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c17
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c60
-rw-r--r--drivers/usb/gadget/serial.c1
-rw-r--r--drivers/usb/gadget/storage_common.c2
-rw-r--r--drivers/usb/gadget/u_ether.c24
-rw-r--r--drivers/usb/gadget/u_ether.h4
-rw-r--r--drivers/usb/gadget/u_serial.c4
-rw-r--r--drivers/usb/gadget/u_serial.h2
-rw-r--r--drivers/usb/gadget/udc-core.c484
-rw-r--r--drivers/usb/gadget/webcam.c1
-rw-r--r--drivers/usb/gadget/zero.c1
-rw-r--r--drivers/usb/host/ehci-hcd.c21
-rw-r--r--drivers/usb/host/ehci-hub.c78
-rw-r--r--drivers/usb/host/ehci-msm.c20
-rw-r--r--drivers/usb/host/ehci-q.c85
-rw-r--r--drivers/usb/host/ehci-s5p.c95
-rw-r--r--drivers/usb/host/ehci-sched.c17
-rw-r--r--drivers/usb/host/ehci-sysfs.c190
-rw-r--r--drivers/usb/host/ehci.h6
-rw-r--r--drivers/usb/host/ohci-sh.c2
-rw-r--r--drivers/usb/host/pci-quirks.c56
-rw-r--r--drivers/usb/host/r8a66597-hcd.c6
-rw-r--r--drivers/usb/host/r8a66597.h38
-rw-r--r--drivers/usb/host/xhci-dbg.c22
-rw-r--r--drivers/usb/host/xhci-mem.c26
-rw-r--r--drivers/usb/host/xhci-ring.c42
-rw-r--r--drivers/usb/host/xhci.c10
-rw-r--r--drivers/usb/host/xhci.h7
-rw-r--r--drivers/usb/mon/mon_text.c9
-rw-r--r--drivers/usb/musb/Kconfig75
-rw-r--r--drivers/usb/musb/Makefile4
-rw-r--r--drivers/usb/musb/am35x.c4
-rw-r--r--drivers/usb/musb/blackfin.h2
-rw-r--r--drivers/usb/musb/da8xx.c12
-rw-r--r--drivers/usb/musb/davinci.c5
-rw-r--r--drivers/usb/musb/musb_core.c133
-rw-r--r--drivers/usb/musb/musb_core.h70
-rw-r--r--drivers/usb/musb/musb_gadget.c102
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c4
-rw-r--r--drivers/usb/musb/musb_host.h4
-rw-r--r--drivers/usb/musb/musb_virthub.c6
-rw-r--r--drivers/usb/musb/omap2430.c15
-rw-r--r--drivers/usb/musb/tusb6010.c26
-rw-r--r--drivers/usb/otg/twl6030-usb.c30
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig13
-rw-r--r--drivers/usb/renesas_usbhs/Makefile2
-rw-r--r--drivers/usb/renesas_usbhs/common.c34
-rw-r--r--drivers/usb/renesas_usbhs/common.h44
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c1015
-rw-r--r--drivers/usb/renesas_usbhs/fifo.h104
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c840
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c298
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h47
-rw-r--r--drivers/usb/serial/pl2303.c27
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/storage/Kconfig4
-rw-r--r--drivers/usb/storage/realtek_cr.c490
-rw-r--r--drivers/usb/wusbcore/cbaf.c4
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c7
124 files changed, 8928 insertions, 3124 deletions
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index e71521ce3010..428f36801e06 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -116,14 +116,14 @@ struct uea_cmvs_v1 {
u32 address;
u16 offset;
u32 data;
-} __attribute__ ((packed));
+} __packed;
struct uea_cmvs_v2 {
u32 group;
u32 address;
u32 offset;
u32 data;
-} __attribute__ ((packed));
+} __packed;
/* information about currently processed cmv */
struct cmv_dsc_e1 {
@@ -352,7 +352,7 @@ struct block_index {
__le32 PageAddress;
__le16 dummy1;
__le16 PageNumber;
-} __attribute__ ((packed));
+} __packed;
#define E4_IS_BOOT_PAGE(PageSize) ((le32_to_cpu(PageSize)) & 0x80000000)
#define E4_PAGE_BYTES(PageSize) ((le32_to_cpu(PageSize) & 0x7fffffff) * 4)
@@ -367,7 +367,7 @@ struct l1_code {
u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
u8 code[0];
-} __attribute__ ((packed));
+} __packed;
/* structures describing a block within a DSP page */
struct block_info_e1 {
@@ -377,7 +377,7 @@ struct block_info_e1 {
__le16 wOvlOffset;
__le16 wOvl; /* overlay */
__le16 wLast;
-} __attribute__ ((packed));
+} __packed;
#define E1_BLOCK_INFO_SIZE 12
struct block_info_e4 {
@@ -387,7 +387,7 @@ struct block_info_e4 {
__be32 dwSize;
__be32 dwAddress;
__be16 wReserved;
-} __attribute__ ((packed));
+} __packed;
#define E4_BLOCK_INFO_SIZE 14
#define UEA_BIHDR 0xabcd
@@ -467,7 +467,7 @@ struct cmv_e1 {
__le32 dwSymbolicAddress;
__le16 wOffsetAddress;
__le32 dwData;
-} __attribute__ ((packed));
+} __packed;
struct cmv_e4 {
__be16 wGroup;
@@ -475,17 +475,17 @@ struct cmv_e4 {
__be16 wOffset;
__be16 wAddress;
__be32 dwData[6];
-} __attribute__ ((packed));
+} __packed;
/* structures representing swap information */
struct swap_info_e1 {
__u8 bSwapPageNo;
__u8 bOvl; /* overlay */
-} __attribute__ ((packed));
+} __packed;
struct swap_info_e4 {
__u8 bSwapPageNo;
-} __attribute__ ((packed));
+} __packed;
/* structures representing interrupt data */
#define e1_bSwapPageNo u.e1.s1.swapinfo.bSwapPageNo
@@ -499,23 +499,23 @@ union intr_data_e1 {
struct {
struct swap_info_e1 swapinfo;
__le16 wDataSize;
- } __attribute__ ((packed)) s1;
+ } __packed s1;
struct {
struct cmv_e1 cmv;
__le16 wDataSize;
- } __attribute__ ((packed)) s2;
-} __attribute__ ((packed));
+ } __packed s2;
+} __packed;
union intr_data_e4 {
struct {
struct swap_info_e4 swapinfo;
__le16 wDataSize;
- } __attribute__ ((packed)) s1;
+ } __packed s1;
struct {
struct cmv_e4 cmv;
__le16 wDataSize;
- } __attribute__ ((packed)) s2;
-} __attribute__ ((packed));
+ } __packed s2;
+} __packed;
struct intr_pkt {
__u8 bType;
@@ -528,15 +528,15 @@ struct intr_pkt {
union intr_data_e1 e1;
union intr_data_e4 e4;
} u;
-} __attribute__ ((packed));
+} __packed;
#define E1_INTR_PKT_SIZE 28
#define E4_INTR_PKT_SIZE 64
static struct usb_driver uea_driver;
static DEFINE_MUTEX(uea_mutex);
-static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III",
- "Eagle IV"};
+static const char * const chip_name[] = {
+ "ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"};
static int modem_index;
static unsigned int debug;
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 989e16e4ab5c..d3448ca110ce 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -81,6 +81,7 @@
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/kthread.h>
+#include <linux/ratelimit.h>
#ifdef VERBOSE_DEBUG
static int usbatm_print_packet(const unsigned char *data, int len);
@@ -668,8 +669,7 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
/* racy disconnection check - fine */
if (!instance || instance->disconnected) {
#ifdef DEBUG
- if (printk_ratelimit())
- printk(KERN_DEBUG "%s: %s!\n", __func__, instance ? "disconnected" : "NULL instance");
+ printk_ratelimited(KERN_DEBUG "%s: %s!\n", __func__, instance ? "disconnected" : "NULL instance");
#endif
err = -ENODEV;
goto fail;
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 9eca4053312e..cb3a93243a05 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -58,6 +58,7 @@
#include <linux/mutex.h>
#undef DEBUG
#include <linux/usb.h>
+#include <linux/ratelimit.h>
/*
* Version Information
@@ -348,8 +349,7 @@ static int usblp_check_status(struct usblp *usblp, int err)
mutex_lock(&usblp->mut);
if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
mutex_unlock(&usblp->mut);
- if (printk_ratelimit())
- printk(KERN_ERR
+ printk_ratelimited(KERN_ERR
"usblp%d: error %d reading printer status\n",
usblp->minor, error);
return 0;
@@ -653,8 +653,7 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case LPGETSTATUS:
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
- if (printk_ratelimit())
- printk(KERN_ERR "usblp%d:"
+ printk_ratelimited(KERN_ERR "usblp%d:"
"failed reading printer status (%d)\n",
usblp->minor, retval);
retval = -EIO;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index ace9f8442e5d..8669ba3fe794 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -337,6 +337,17 @@ static const u8 ss_rh_config_descriptor[] = {
0x02, 0x00 /* __le16 ss_wBytesPerInterval; 15 bits for max 15 ports */
};
+/* authorized_default behaviour:
+ * -1 is authorized for all devices except wireless (old behaviour)
+ * 0 is unauthorized for all devices
+ * 1 is authorized for all devices
+ */
+static int authorized_default = -1;
+module_param(authorized_default, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(authorized_default,
+ "Default USB device authorization: 0 is not authorized, 1 is "
+ "authorized, -1 is authorized except for wireless USB (default, "
+ "old behaviour");
/*-------------------------------------------------------------------------*/
/**
@@ -2371,7 +2382,11 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
- hcd->authorized_default = hcd->wireless? 0 : 1;
+ /* Keep old behaviour if authorized_default is not in [0, 1]. */
+ if (authorized_default < 0 || authorized_default > 1)
+ hcd->authorized_default = hcd->wireless? 0 : 1;
+ else
+ hcd->authorized_default = authorized_default;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* HC is in reset state, but accessible. Now do the one-time init,
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 029e288805b6..44b6b40aafb4 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -96,9 +96,6 @@ config USB_GADGET_VBUS_DRAW
This value will be used except for system-specific gadget
drivers that have more specific information.
-config USB_GADGET_SELECTED
- boolean
-
#
# USB Peripheral Controller Support
#
@@ -122,10 +119,9 @@ choice
# Integrated controllers
#
-config USB_GADGET_AT91
- boolean "Atmel AT91 USB Device Port"
+config USB_AT91
+ tristate "Atmel AT91 USB Device Port"
depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
- select USB_GADGET_SELECTED
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
@@ -135,27 +131,16 @@ config USB_GADGET_AT91
dynamically linked module called "at91_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_AT91
- tristate
- depends on USB_GADGET_AT91
- default USB_GADGET
-
-config USB_GADGET_ATMEL_USBA
- boolean "Atmel USBA"
+config USB_ATMEL_USBA
+ tristate "Atmel USBA"
select USB_GADGET_DUALSPEED
depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
-config USB_ATMEL_USBA
- tristate
- depends on USB_GADGET_ATMEL_USBA
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_FSL_USB2
- boolean "Freescale Highspeed USB DR Peripheral Controller"
+config USB_FSL_USB2
+ tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
select USB_GADGET_DUALSPEED
select USB_FSL_MPH_DR_OF if OF
@@ -170,26 +155,15 @@ config USB_GADGET_FSL_USB2
dynamically linked module called "fsl_usb2_udc" and force
all gadget drivers to also be dynamically linked.
-config USB_FSL_USB2
- tristate
- depends on USB_GADGET_FSL_USB2
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_FUSB300
- boolean "Faraday FUSB300 USB Peripheral Controller"
+config USB_FUSB300
+ tristate "Faraday FUSB300 USB Peripheral Controller"
+ depends on !PHYS_ADDR_T_64BIT
select USB_GADGET_DUALSPEED
help
Faraday usb device controller FUSB300 driver
-config USB_FUSB300
- tristate
- depends on USB_GADGET_FUSB300
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_OMAP
- boolean "OMAP USB Device Controller"
+config USB_OMAP
+ tristate "OMAP USB Device Controller"
depends on ARCH_OMAP
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG
select USB_OTG_UTILS if ARCH_OMAP
@@ -204,14 +178,8 @@ config USB_GADGET_OMAP
dynamically linked module called "omap_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_OMAP
- tristate
- depends on USB_GADGET_OMAP
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_PXA25X
- boolean "PXA 25x or IXP 4xx"
+config USB_PXA25X
+ tristate "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
select USB_OTG_UTILS
help
@@ -226,24 +194,18 @@ config USB_GADGET_PXA25X
dynamically linked module called "pxa25x_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_PXA25X
- tristate
- depends on USB_GADGET_PXA25X
- default USB_GADGET
- select USB_GADGET_SELECTED
-
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA25X_SMALL
- depends on USB_GADGET_PXA25X
+ depends on USB_PXA25X
bool
default n if USB_ETH_RNDIS
default y if USB_ZERO
default y if USB_ETH
default y if USB_G_SERIAL
-config USB_GADGET_R8A66597
- boolean "Renesas R8A66597 USB Peripheral Controller"
+config USB_R8A66597
+ tristate "Renesas R8A66597 USB Peripheral Controller"
select USB_GADGET_DUALSPEED
help
R8A66597 is a discrete USB host and peripheral controller chip that
@@ -254,32 +216,22 @@ config USB_GADGET_R8A66597
dynamically linked module called "r8a66597_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_R8A66597
- tristate
- depends on USB_GADGET_R8A66597
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_RENESAS_USBHS
- boolean "Renesas USBHS"
+config USB_RENESAS_USBHS_UDC
+ tristate 'Renesas USBHS controller'
+ depends on SUPERH || ARCH_SHMOBILE
depends on USB_RENESAS_USBHS
select USB_GADGET_DUALSPEED
help
- Renesas USBHS is a discrete USB host and peripheral controller
- chip that supports both full and high speed USB 2.0 data transfers.
- platform is able to configure endpoint (pipe) style
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
- Say "y" to enable the gadget specific portion of the USBHS driver.
-
-
-config USB_RENESAS_USBHS_UDC
- tristate
- depends on USB_GADGET_RENESAS_USBHS
- default USB_GADGET
- select USB_GADGET_SELECTED
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs" and force all
+ gadget drivers to also be dynamically linked.
-config USB_GADGET_PXA27X
- boolean "PXA 27x"
+config USB_PXA27X
+ tristate "PXA 27x"
depends on ARCH_PXA && (PXA27x || PXA3xx)
select USB_OTG_UTILS
help
@@ -293,14 +245,8 @@ config USB_GADGET_PXA27X
dynamically linked module called "pxa27x_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_PXA27X
- tristate
- depends on USB_GADGET_PXA27X
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_S3C_HSOTG
- boolean "S3C HS/OtG USB Device controller"
+config USB_S3C_HSOTG
+ tristate "S3C HS/OtG USB Device controller"
depends on S3C_DEV_USB_HSOTG
select USB_GADGET_S3C_HSOTG_PIO
select USB_GADGET_DUALSPEED
@@ -308,14 +254,8 @@ config USB_GADGET_S3C_HSOTG
The Samsung S3C64XX USB2.0 high-speed gadget controller
integrated into the S3C64XX series SoC.
-config USB_S3C_HSOTG
- tristate
- depends on USB_GADGET_S3C_HSOTG
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_IMX
- boolean "Freescale IMX USB Peripheral Controller"
+config USB_IMX
+ tristate "Freescale IMX USB Peripheral Controller"
depends on ARCH_MX1
help
Freescale's IMX series include an integrated full speed
@@ -329,14 +269,8 @@ config USB_GADGET_IMX
dynamically linked module called "imx_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_IMX
- tristate
- depends on USB_GADGET_IMX
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_S3C2410
- boolean "S3C2410 USB Device Controller"
+config USB_S3C2410
+ tristate "S3C2410 USB Device Controller"
depends on ARCH_S3C2410
help
Samsung's S3C2410 is an ARM-4 processor with an integrated
@@ -346,18 +280,12 @@ config USB_GADGET_S3C2410
This driver has been tested on the S3C2410, S3C2412, and
S3C2440 processors.
-config USB_S3C2410
- tristate
- depends on USB_GADGET_S3C2410
- default USB_GADGET
- select USB_GADGET_SELECTED
-
config USB_S3C2410_DEBUG
boolean "S3C2410 udc debug messages"
- depends on USB_GADGET_S3C2410
+ depends on USB_S3C2410
-config USB_GADGET_S3C_HSUDC
- boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+config USB_S3C_HSUDC
+ tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
depends on ARCH_S3C2410
select USB_GADGET_DUALSPEED
help
@@ -367,41 +295,29 @@ config USB_GADGET_S3C_HSUDC
This driver has been tested on S3C2416 and S3C2450 processors.
-config USB_S3C_HSUDC
- tristate
- depends on USB_GADGET_S3C_HSUDC
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_PXA_U2O
- boolean "PXA9xx Processor USB2.0 controller"
+config USB_PXA_U2O
+ tristate "PXA9xx Processor USB2.0 controller"
+ depends on ARCH_MMP
select USB_GADGET_DUALSPEED
help
PXA9xx Processor series include a high speed USB2.0 device
controller, which support high speed and full speed USB peripheral.
-config USB_PXA_U2O
- tristate
- depends on USB_GADGET_PXA_U2O
- default USB_GADGET
- select USB_GADGET_SELECTED
-
#
# Controllers available in both integrated and discrete versions
#
# musb builds in ../musb along with host support
config USB_GADGET_MUSB_HDRC
- boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)"
+ tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
select USB_GADGET_DUALSPEED
- select USB_GADGET_SELECTED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
-config USB_GADGET_M66592
- boolean "Renesas M66592 USB Peripheral Controller"
+config USB_M66592
+ tristate "Renesas M66592 USB Peripheral Controller"
select USB_GADGET_DUALSPEED
help
M66592 is a discrete USB peripheral controller chip that
@@ -412,18 +328,12 @@ config USB_GADGET_M66592
dynamically linked module called "m66592_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_M66592
- tristate
- depends on USB_GADGET_M66592
- default USB_GADGET
- select USB_GADGET_SELECTED
-
#
# Controllers available only in discrete form (and all PCI controllers)
#
-config USB_GADGET_AMD5536UDC
- boolean "AMD5536 UDC"
+config USB_AMD5536UDC
+ tristate "AMD5536 UDC"
depends on PCI
select USB_GADGET_DUALSPEED
help
@@ -437,14 +347,8 @@ config USB_GADGET_AMD5536UDC
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
-config USB_AMD5536UDC
- tristate
- depends on USB_GADGET_AMD5536UDC
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_FSL_QE
- boolean "Freescale QE/CPM USB Device Controller"
+config USB_FSL_QE
+ tristate "Freescale QE/CPM USB Device Controller"
depends on FSL_SOC && (QUICC_ENGINE || CPM)
help
Some of Freescale PowerPC processors have a Full Speed
@@ -456,14 +360,8 @@ config USB_GADGET_FSL_QE
Set CONFIG_USB_GADGET to "m" to build this driver as a
dynamically linked module called "fsl_qe_udc".
-config USB_FSL_QE
- tristate
- depends on USB_GADGET_FSL_QE
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_CI13XXX_PCI
- boolean "MIPS USB CI13xxx PCI UDC"
+config USB_CI13XXX_PCI
+ tristate "MIPS USB CI13xxx PCI UDC"
depends on PCI
select USB_GADGET_DUALSPEED
help
@@ -474,14 +372,31 @@ config USB_GADGET_CI13XXX_PCI
dynamically linked module called "ci13xxx_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_CI13XXX_PCI
- tristate
- depends on USB_GADGET_CI13XXX_PCI
- default USB_GADGET
- select USB_GADGET_SELECTED
+config USB_NET2272
+ tristate "PLX NET2272"
+ select USB_GADGET_DUALSPEED
+ help
+ PLX NET2272 is a USB peripheral controller which supports
+ both full and high speed USB 2.0 data transfers.
+
+ It has three configurable endpoints, as well as endpoint zero
+ (for control transfer).
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "net2272" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_NET2272_DMA
+ boolean "Support external DMA controller"
+ depends on USB_NET2272
+ help
+ The NET2272 part can optionally support an external DMA
+ controller, but your board has to have support in the
+ driver itself.
-config USB_GADGET_NET2280
- boolean "NetChip 228x"
+ If unsure, say "N" here. The driver works fine in PIO mode.
+
+config USB_NET2280
+ tristate "NetChip 228x"
depends on PCI
select USB_GADGET_DUALSPEED
help
@@ -496,14 +411,8 @@ config USB_GADGET_NET2280
dynamically linked module called "net2280" and force all
gadget drivers to also be dynamically linked.
-config USB_NET2280
- tristate
- depends on USB_GADGET_NET2280
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_GOKU
- boolean "Toshiba TC86C001 'Goku-S'"
+config USB_GOKU
+ tristate "Toshiba TC86C001 'Goku-S'"
depends on PCI
help
The Toshiba TC86C001 is a PCI device which includes controllers
@@ -516,15 +425,10 @@ config USB_GADGET_GOKU
dynamically linked module called "goku_udc" and to force all
gadget drivers to also be dynamically linked.
-config USB_GOKU
- tristate
- depends on USB_GADGET_GOKU
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_LANGWELL
- boolean "Intel Langwell USB Device Controller"
+config USB_LANGWELL
+ tristate "Intel Langwell USB Device Controller"
depends on PCI
+ depends on !PHYS_ADDR_T_64BIT
select USB_GADGET_DUALSPEED
help
Intel Langwell USB Device Controller is a High-Speed USB
@@ -537,14 +441,8 @@ config USB_GADGET_LANGWELL
dynamically linked module called "langwell_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_LANGWELL
- tristate
- depends on USB_GADGET_LANGWELL
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_EG20T
- boolean "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH UDC"
+config USB_EG20T
+ tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH UDC"
depends on PCI
select USB_GADGET_DUALSPEED
help
@@ -565,14 +463,8 @@ config USB_GADGET_EG20T
ML7213 is companion chip for Intel Atom E6xx series.
ML7213 is completely compatible for Intel EG20T PCH.
-config USB_EG20T
- tristate
- depends on USB_GADGET_EG20T
- default USB_GADGET
- select USB_GADGET_SELECTED
-
-config USB_GADGET_CI13XXX_MSM
- boolean "MIPS USB CI13xxx for MSM"
+config USB_CI13XXX_MSM
+ tristate "MIPS USB CI13xxx for MSM"
depends on ARCH_MSM
select USB_GADGET_DUALSPEED
select USB_MSM_OTG
@@ -588,31 +480,26 @@ config USB_GADGET_CI13XXX_MSM
dynamically linked module called "ci13xxx_msm" and force all
gadget drivers to also be dynamically linked.
-config USB_CI13XXX_MSM
- tristate
- depends on USB_GADGET_CI13XXX_MSM
- default USB_GADGET
- select USB_GADGET_SELECTED
-
#
# LAST -- dummy/emulated controller
#
-config USB_GADGET_DUMMY_HCD
- boolean "Dummy HCD (DEVELOPMENT)"
+config USB_DUMMY_HCD
+ tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
select USB_GADGET_DUALSPEED
+ select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
side is the master; the gadget side is the slave. Gadget drivers
can be high, full, or low speed; and they have access to endpoints
like those from NET2280, PXA2xx, or SA1100 hardware.
-
+
This may help in some stages of creating a driver to embed in a
Linux device, since it lets you debug several parts of the gadget
driver without its hardware or drivers being involved.
-
+
Since such a gadget side driver needs to interoperate with a host
side Linux-USB device driver, this may help to debug both sides
of a USB protocol stack.
@@ -621,12 +508,6 @@ config USB_GADGET_DUMMY_HCD
dynamically linked module called "dummy_hcd" and force all
gadget drivers to also be dynamically linked.
-config USB_DUMMY_HCD
- tristate
- depends on USB_GADGET_DUMMY_HCD
- default USB_GADGET
- select USB_GADGET_SELECTED
-
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
# first and will be selected by default.
@@ -637,12 +518,18 @@ config USB_GADGET_DUALSPEED
bool
depends on USB_GADGET
+# Selected by UDC drivers that support super-speed opperation
+config USB_GADGET_SUPERSPEED
+ bool
+ depends on USB_GADGET
+ depends on USB_GADGET_DUALSPEED
+
#
# USB Gadget Drivers
#
choice
tristate "USB Gadget Drivers"
- depends on USB_GADGET && USB_GADGET_SELECTED
+ depends on USB_GADGET
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
@@ -848,7 +735,7 @@ config USB_FUNCTIONFS_GENERIC
no Ethernet interface.
config USB_FILE_STORAGE
- tristate "File-backed Storage Gadget"
+ tristate "File-backed Storage Gadget (DEPRECATED)"
depends on BLOCK
help
The File-backed Storage Gadget acts as a USB Mass Storage
@@ -859,6 +746,9 @@ config USB_FILE_STORAGE
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_file_storage".
+ NOTE: This driver is deprecated. Its replacement is the
+ Mass Storage Gadget.
+
config USB_FILE_STORAGE_TEST
bool "File-backed Storage Gadget testing version"
depends on USB_FILE_STORAGE
@@ -878,14 +768,11 @@ config USB_MASS_STORAGE
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
- This is heavily based on File-backed Storage Gadget and in most
- cases you will want to use FSG instead. This gadget is mostly
- here to test the functionality of the Mass Storage Function
- which may be used with composite framework.
+ This driver is an updated replacement for the deprecated
+ File-backed Storage Gadget (g_file_storage).
Say "y" to link the driver statically, or "m" to build
- a dynamically linked module called "g_mass_storage". If unsure,
- consider File-backed Storage Gadget.
+ a dynamically linked module called "g_mass_storage".
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 4fe92b18a055..9ba725af4a08 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,7 +3,9 @@
#
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
+obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 95e8138cd48f..70f2b376c86d 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -1438,10 +1438,15 @@ static int udc_wakeup(struct usb_gadget *gadget)
return 0;
}
+static int amd5536_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int amd5536_stop(struct usb_gadget_driver *driver);
/* gadget operations */
static const struct usb_gadget_ops udc_ops = {
.wakeup = udc_wakeup,
.get_frame = udc_get_frame,
+ .start = amd5536_start,
+ .stop = amd5536_stop,
};
/* Setups endpoint parameters, adds endpoints to linked list */
@@ -1955,7 +1960,7 @@ static int setup_ep0(struct udc *dev)
}
/* Called by gadget driver to register itself */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int amd5536_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct udc *dev = udc;
@@ -2002,7 +2007,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
/* shutdown requests and disconnect from gadget */
static void
@@ -2027,7 +2031,7 @@ __acquires(dev->lock)
}
/* Called by gadget driver to unregister itself */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int amd5536_stop(struct usb_gadget_driver *driver)
{
struct udc *dev = udc;
unsigned long flags;
@@ -2057,8 +2061,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/* Clear pending NAK bits */
static void udc_process_cnak_queue(struct udc *dev)
@@ -3134,6 +3136,7 @@ static void udc_pci_remove(struct pci_dev *pdev)
dev = pci_get_drvdata(pdev);
+ usb_del_gadget_udc(&udc->gadget);
/* gadget driver must not be registered */
BUG_ON(dev->driver != NULL);
@@ -3382,8 +3385,13 @@ static int udc_probe(struct udc *dev)
"driver version: %s(for Geode5536 B1)\n", tmp);
udc = dev;
+ retval = usb_add_gadget_udc(&udc->pdev->dev, &dev->gadget);
+ if (retval)
+ goto finished;
+
retval = device_register(&dev->gadget.dev);
if (retval) {
+ usb_del_gadget_udc(&dev->gadget);
put_device(&dev->gadget.dev);
goto finished;
}
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index f4690ffcb489..98cbc06c30fd 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -985,12 +985,18 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int at91_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int at91_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops at91_udc_ops = {
.get_frame = at91_get_frame,
.wakeup = at91_wakeup,
.set_selfpowered = at91_set_selfpowered,
.vbus_session = at91_vbus_session,
.pullup = at91_pullup,
+ .start = at91_start,
+ .stop = at91_stop,
/*
* VBUS-powered devices may also also want to support bigger
@@ -1628,7 +1634,7 @@ static void at91_vbus_timer(unsigned long data)
schedule_work(&udc->vbus_timer_work);
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int at91_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct at91_udc *udc = &controller;
@@ -1672,9 +1678,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
DBG("bound to %s\n", driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+static int at91_stop(struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
unsigned long flags;
@@ -1696,7 +1701,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
DBG("unbound from %s\n", driver->driver.name);
return 0;
}
-EXPORT_SYMBOL (usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
@@ -1854,13 +1858,18 @@ static int __init at91udc_probe(struct platform_device *pdev)
DBG("no VBUS detection, assuming always-on\n");
udc->vbus = 1;
}
+ retval = usb_add_gadget_udc(dev, &udc->gadget);
+ if (retval)
+ goto fail4;
dev_set_drvdata(dev, udc);
device_init_wakeup(dev, 1);
create_debug_file(udc);
INFO("%s version %s\n", driver_name, DRIVER_VERSION);
return 0;
-
+fail4:
+ if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled)
+ free_irq(udc->board.vbus_pin, udc);
fail3:
if (udc->board.vbus_pin > 0)
gpio_free(udc->board.vbus_pin);
@@ -1887,6 +1896,7 @@ static int __exit at91udc_remove(struct platform_device *pdev)
DBG("remove\n");
+ usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index f045c8968a6e..5b1665eb1bef 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -1007,10 +1007,16 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
return 0;
}
+static int atmel_usba_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int atmel_usba_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops usba_udc_ops = {
.get_frame = usba_udc_get_frame,
.wakeup = usba_udc_wakeup,
.set_selfpowered = usba_udc_set_selfpowered,
+ .start = atmel_usba_start,
+ .stop = atmel_usba_stop,
};
static struct usb_endpoint_descriptor usba_ep0_desc = {
@@ -1789,7 +1795,7 @@ out:
return IRQ_HANDLED;
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int atmel_usba_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct usba_udc *udc = &the_udc;
@@ -1842,9 +1848,8 @@ err_driver_bind:
udc->gadget.dev.driver = NULL;
return ret;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int atmel_usba_stop(struct usb_gadget_driver *driver)
{
struct usba_udc *udc = &the_udc;
unsigned long flags;
@@ -1880,7 +1885,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
static int __init usba_udc_probe(struct platform_device *pdev)
{
@@ -2021,12 +2025,24 @@ static int __init usba_udc_probe(struct platform_device *pdev)
}
}
+ ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+ if (ret)
+ goto err_add_udc;
+
usba_init_debugfs(udc);
for (i = 1; i < pdata->num_ep; i++)
usba_ep_init_debugfs(udc, &usba_ep[i]);
return 0;
+err_add_udc:
+ if (gpio_is_valid(pdata->vbus_pin)) {
+ free_irq(gpio_to_irq(udc->vbus_pin), udc);
+ gpio_free(udc->vbus_pin);
+ }
+
+ device_unregister(&udc->gadget.dev);
+
err_device_add:
free_irq(irq, udc);
err_request_irq:
@@ -2053,6 +2069,8 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
udc = platform_get_drvdata(pdev);
+ usb_del_gadget_udc(&udc->gadget);
+
for (i = 1; i < pdata->num_ep; i++)
usba_ep_cleanup_debugfs(&usba_ep[i]);
usba_cleanup_debugfs(udc);
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index 93b999e49ef3..9d89ae4765a9 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -165,6 +165,7 @@ static struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc,
.strings = audio_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(audio_unbind),
};
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index 2720ab07ef1a..b1c1afbb8750 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -244,6 +244,7 @@ static struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(cdc_unbind),
};
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 139ac9419597..470981ad6f77 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -126,6 +126,7 @@ static struct platform_driver ci13xxx_msm_driver = {
.probe = ci13xxx_msm_probe,
.driver = { .name = "msm_hsusb", },
};
+MODULE_ALIAS("platform:msm_hsusb");
static int __init ci13xxx_msm_init(void)
{
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index baaf87ed7685..1265a8502ea0 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -857,7 +857,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra)
stamp = stamp * 1000000 + tval.tv_usec;
scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
- "%04X\t» %02X %-7.7s %4i «\t%s\n",
+ "%04X\t? %02X %-7.7s %4i ?\t%s\n",
stamp, addr, name, status, extra);
dbg_inc(&dbg_data.idx);
@@ -865,7 +865,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra)
write_unlock_irqrestore(&dbg_data.lck, flags);
if (dbg_data.tty != 0)
- pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n",
+ pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n",
stamp, addr, name, status, extra);
}
@@ -1025,15 +1025,15 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
isr_statistics.test);
- n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n",
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n",
isr_statistics.ui);
- n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n",
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n",
isr_statistics.uei);
- n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n",
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n",
isr_statistics.pci);
- n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n",
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n",
isr_statistics.uri);
- n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n",
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n",
isr_statistics.sli);
n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n",
isr_statistics.none);
@@ -1214,12 +1214,13 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
*
* Check "device.h" for details
*/
+#define DUMP_ENTRIES 512
static ssize_t show_registers(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
- u32 dump[512];
+ u32 *dump;
unsigned i, k, n = 0;
dbg_trace("[%s] %p\n", __func__, buf);
@@ -1228,8 +1229,14 @@ static ssize_t show_registers(struct device *dev,
return 0;
}
+ dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
+ if (!dump) {
+ dev_err(dev, "%s: out of memory\n", __func__);
+ return 0;
+ }
+
spin_lock_irqsave(udc->lock, flags);
- k = hw_register_read(dump, sizeof(dump)/sizeof(u32));
+ k = hw_register_read(dump, DUMP_ENTRIES);
spin_unlock_irqrestore(udc->lock, flags);
for (i = 0; i < k; i++) {
@@ -1237,6 +1244,7 @@ static ssize_t show_registers(struct device *dev,
"reg[0x%04X] = 0x%08X\n",
i * (unsigned)sizeof(u32), dump[i]);
}
+ kfree(dump);
return n;
}
@@ -2515,6 +2523,9 @@ static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -ENOTSUPP;
}
+static int ci13xxx_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int ci13xxx_stop(struct usb_gadget_driver *driver);
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
@@ -2524,17 +2535,19 @@ static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci13xxx_vbus_session,
.wakeup = ci13xxx_wakeup,
.vbus_draw = ci13xxx_vbus_draw,
+ .start = ci13xxx_start,
+ .stop = ci13xxx_stop,
};
/**
- * usb_gadget_probe_driver: register a gadget driver
+ * ci13xxx_start: register a gadget driver
* @driver: the driver being registered
* @bind: the driver's bind callback
*
- * Check usb_gadget_probe_driver() at <linux/usb/gadget.h> for details.
+ * Check ci13xxx_start() at <linux/usb/gadget.h> for details.
* Interrupts are enabled here.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int ci13xxx_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct ci13xxx *udc = _udc;
@@ -2615,10 +2628,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
if (retval)
goto done;
spin_unlock_irqrestore(udc->lock, flags);
- retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc);
+ udc->ep0out.ep.desc = &ctrl_endpt_out_desc;
+ retval = usb_ep_enable(&udc->ep0out.ep);
if (retval)
return retval;
- retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc);
+
+ udc->ep0in.ep.desc = &ctrl_endpt_in_desc;
+ retval = usb_ep_enable(&udc->ep0in.ep);
if (retval)
return retval;
spin_lock_irqsave(udc->lock, flags);
@@ -2657,14 +2673,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
spin_unlock_irqrestore(udc->lock, flags);
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
/**
- * usb_gadget_unregister_driver: unregister a gadget driver
+ * ci13xxx_stop: unregister a gadget driver
*
* Check usb_gadget_unregister_driver() at "usb_gadget.h" for details
*/
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int ci13xxx_stop(struct usb_gadget_driver *driver)
{
struct ci13xxx *udc = _udc;
unsigned long i, flags;
@@ -2726,7 +2741,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/******************************************************************************
* BUS block
@@ -2901,12 +2915,23 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
if (retval)
goto remove_dbg;
}
+
+ retval = usb_add_gadget_udc(dev, &udc->gadget);
+ if (retval)
+ goto remove_trans;
+
pm_runtime_no_callbacks(&udc->gadget.dev);
pm_runtime_enable(&udc->gadget.dev);
_udc = udc;
return retval;
+remove_trans:
+ if (udc->transceiver) {
+ otg_set_peripheral(udc->transceiver, &udc->gadget);
+ otg_put_transceiver(udc->transceiver);
+ }
+
err("error = %i", retval);
remove_dbg:
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
@@ -2936,6 +2961,7 @@ static void udc_remove(void)
err("EINVAL");
return;
}
+ usb_del_gadget_udc(&udc->gadget);
if (udc->transceiver) {
otg_set_peripheral(udc->transceiver, &udc->gadget);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 5cbb1a41c223..5ef87794fd32 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -27,7 +27,7 @@
#include <linux/utsname.h>
#include <linux/usb/composite.h>
-
+#include <asm/unaligned.h>
/*
* The code in this file is utility code, used to build a gadget driver
@@ -74,6 +74,130 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
static char composite_manufacturer[50];
/*-------------------------------------------------------------------------*/
+/**
+ * next_ep_desc() - advance to the next EP descriptor
+ * @t: currect pointer within descriptor array
+ *
+ * Return: next EP descriptor or NULL
+ *
+ * Iterate over @t until either EP descriptor found or
+ * NULL (that indicates end of list) encountered
+ */
+static struct usb_descriptor_header**
+next_ep_desc(struct usb_descriptor_header **t)
+{
+ for (; *t; t++) {
+ if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+ return t;
+ }
+ return NULL;
+}
+
+/*
+ * for_each_ep_desc()- iterate over endpoint descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @ep_desc: endpoint descriptor to use as the loop cursor
+ */
+#define for_each_ep_desc(start, ep_desc) \
+ for (ep_desc = next_ep_desc(start); \
+ ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep)
+{
+ struct usb_endpoint_descriptor *chosen_desc = NULL;
+ struct usb_descriptor_header **speed_desc = NULL;
+
+ struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
+ int want_comp_desc = 0;
+
+ struct usb_descriptor_header **d_spd; /* cursor for speed desc */
+
+ if (!g || !f || !_ep)
+ return -EIO;
+
+ /* select desired speed */
+ switch (g->speed) {
+ case USB_SPEED_SUPER:
+ if (gadget_is_superspeed(g)) {
+ speed_desc = f->ss_descriptors;
+ want_comp_desc = 1;
+ break;
+ }
+ /* else: Fall trough */
+ case USB_SPEED_HIGH:
+ if (gadget_is_dualspeed(g)) {
+ speed_desc = f->hs_descriptors;
+ break;
+ }
+ /* else: fall through */
+ default:
+ speed_desc = f->descriptors;
+ }
+ /* find descriptors */
+ for_each_ep_desc(speed_desc, d_spd) {
+ chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
+ if (chosen_desc->bEndpointAddress == _ep->address)
+ goto ep_found;
+ }
+ return -EIO;
+
+ep_found:
+ /* commit results */
+ _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize);
+ _ep->desc = chosen_desc;
+ _ep->comp_desc = NULL;
+ _ep->maxburst = 0;
+ _ep->mult = 0;
+ if (!want_comp_desc)
+ return 0;
+
+ /*
+ * Companion descriptor should follow EP descriptor
+ * USB 3.0 spec, #9.6.7
+ */
+ comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd);
+ if (!comp_desc ||
+ (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
+ return -EIO;
+ _ep->comp_desc = comp_desc;
+ if (g->speed == USB_SPEED_SUPER) {
+ switch (usb_endpoint_type(_ep->desc)) {
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ _ep->maxburst = comp_desc->bMaxBurst;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* mult: bits 1:0 of bmAttributes */
+ _ep->mult = comp_desc->bmAttributes & 0x3;
+ break;
+ default:
+ /* Do nothing for control endpoints */
+ break;
+ }
+ }
+ return 0;
+}
/**
* usb_add_function() - add a function to a configuration
@@ -123,6 +247,8 @@ int usb_add_function(struct usb_configuration *config,
config->fullspeed = true;
if (!config->highspeed && function->hs_descriptors)
config->highspeed = true;
+ if (!config->superspeed && function->ss_descriptors)
+ config->superspeed = true;
done:
if (value)
@@ -266,10 +392,17 @@ static int config_buf(struct usb_configuration *config,
list_for_each_entry(f, &config->functions, list) {
struct usb_descriptor_header **descriptors;
- if (speed == USB_SPEED_HIGH)
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
descriptors = f->hs_descriptors;
- else
+ break;
+ default:
descriptors = f->descriptors;
+ }
+
if (!descriptors)
continue;
status = usb_descriptor_fillbuf(next, len,
@@ -292,9 +425,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
u8 type = w_value >> 8;
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
- if (gadget_is_dualspeed(gadget)) {
- int hs = 0;
-
+ if (gadget->speed == USB_SPEED_SUPER)
+ speed = gadget->speed;
+ else if (gadget_is_dualspeed(gadget)) {
+ int hs = 0;
if (gadget->speed == USB_SPEED_HIGH)
hs = 1;
if (type == USB_DT_OTHER_SPEED_CONFIG)
@@ -308,13 +442,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
w_value &= 0xff;
list_for_each_entry(c, &cdev->configs, list) {
/* ignore configs that won't work at this speed */
- if (speed == USB_SPEED_HIGH) {
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ if (!c->superspeed)
+ continue;
+ break;
+ case USB_SPEED_HIGH:
if (!c->highspeed)
continue;
- } else {
+ break;
+ default:
if (!c->fullspeed)
continue;
}
+
if (w_value == 0)
return config_buf(c, speed, cdev->req->buf, type);
w_value--;
@@ -328,16 +469,22 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
struct usb_configuration *c;
unsigned count = 0;
int hs = 0;
+ int ss = 0;
if (gadget_is_dualspeed(gadget)) {
if (gadget->speed == USB_SPEED_HIGH)
hs = 1;
+ if (gadget->speed == USB_SPEED_SUPER)
+ ss = 1;
if (type == USB_DT_DEVICE_QUALIFIER)
hs = !hs;
}
list_for_each_entry(c, &cdev->configs, list) {
/* ignore configs that won't work at this speed */
- if (hs) {
+ if (ss) {
+ if (!c->superspeed)
+ continue;
+ } else if (hs) {
if (!c->highspeed)
continue;
} else {
@@ -349,6 +496,71 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
return count;
}
+/**
+ * bos_desc() - prepares the BOS descriptor.
+ * @cdev: pointer to usb_composite device to generate the bos
+ * descriptor for
+ *
+ * This function generates the BOS (Binary Device Object)
+ * descriptor and its device capabilities descriptors. The BOS
+ * descriptor should be supported by a SuperSpeed device.
+ */
+static int bos_desc(struct usb_composite_dev *cdev)
+{
+ struct usb_ext_cap_descriptor *usb_ext;
+ struct usb_ss_cap_descriptor *ss_cap;
+ struct usb_dcd_config_params dcd_config_params;
+ struct usb_bos_descriptor *bos = cdev->req->buf;
+
+ bos->bLength = USB_DT_BOS_SIZE;
+ bos->bDescriptorType = USB_DT_BOS;
+
+ bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
+ bos->bNumDeviceCaps = 0;
+
+ /*
+ * A SuperSpeed device shall include the USB2.0 extension descriptor
+ * and shall support LPM when operating in USB2.0 HS mode.
+ */
+ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
+ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
+ usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
+
+ /*
+ * The Superspeed USB Capability descriptor shall be implemented by all
+ * SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+
+ /* Get Controller configuration */
+ 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.bU2DevExitLat =
+ cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+
+ return le16_to_cpu(bos->wTotalLength);
+}
+
static void device_qual(struct usb_composite_dev *cdev)
{
struct usb_qualifier_descriptor *qual = cdev->req->buf;
@@ -361,7 +573,7 @@ static void device_qual(struct usb_composite_dev *cdev)
qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
/* ASSUME same EP0 fifo size at both speeds */
- qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0;
+ qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket;
qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
qual->bRESERVED = 0;
}
@@ -392,28 +604,46 @@ static int set_config(struct usb_composite_dev *cdev,
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
- if (cdev->config)
- reset_config(cdev);
-
if (number) {
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == number) {
+ /*
+ * We disable the FDs of the previous
+ * configuration only if the new configuration
+ * is a valid one
+ */
+ if (cdev->config)
+ reset_config(cdev);
result = 0;
break;
}
}
if (result < 0)
goto done;
- } else
+ } else { /* Zero configuration value - need to reset the config */
+ if (cdev->config)
+ reset_config(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;
- default: speed = "?"; break;
+ 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");
if (!c)
@@ -435,10 +665,16 @@ static int set_config(struct usb_composite_dev *cdev,
* function's setup callback instead of the current
* configuration's setup callback.
*/
- if (gadget->speed == USB_SPEED_HIGH)
+ switch (gadget->speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
descriptors = f->hs_descriptors;
- else
+ break;
+ default:
descriptors = f->descriptors;
+ }
for (; *descriptors; ++descriptors) {
struct usb_endpoint_descriptor *ep;
@@ -531,8 +767,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
} else {
unsigned i;
- DBG(cdev, "cfg %d/%p speeds:%s%s\n",
+ DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
config->bConfigurationValue, config,
+ config->superspeed ? " super" : "",
config->highspeed ? " high" : "",
config->fullspeed
? (gadget_is_dualspeed(cdev->gadget)
@@ -811,6 +1048,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
+ int status = 0;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
@@ -838,18 +1076,29 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_DEVICE:
cdev->desc.bNumConfigurations =
count_configs(cdev, USB_DT_DEVICE);
+ cdev->desc.bMaxPacketSize0 =
+ cdev->gadget->ep0->maxpacket;
+ if (gadget_is_superspeed(gadget)) {
+ if (gadget->speed >= USB_SPEED_SUPER)
+ cdev->desc.bcdUSB = cpu_to_le16(0x0300);
+ else
+ cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ }
+
value = min(w_length, (u16) sizeof cdev->desc);
memcpy(req->buf, &cdev->desc, value);
break;
case USB_DT_DEVICE_QUALIFIER:
- if (!gadget_is_dualspeed(gadget))
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
break;
device_qual(cdev);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
case USB_DT_OTHER_SPEED_CONFIG:
- if (!gadget_is_dualspeed(gadget))
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
@@ -863,6 +1112,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (value >= 0)
value = min(w_length, (u16) value);
break;
+ case USB_DT_BOS:
+ if (gadget_is_superspeed(gadget)) {
+ value = bos_desc(cdev);
+ value = min(w_length, (u16) value);
+ }
+ break;
}
break;
@@ -930,6 +1185,61 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*((u8 *)req->buf) = value;
value = min(w_length, (u16) 1);
break;
+
+ /*
+ * USB 3.0 additions:
+ * Function driver should handle get_status request. If such cb
+ * wasn't supplied we respond with default value = 0
+ * Note: function driver should supply such cb only for the first
+ * interface of the function
+ */
+ case USB_REQ_GET_STATUS:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
+ goto unknown;
+ value = 2; /* This is the length of the get_status reply */
+ put_unaligned_le16(0, req->buf);
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ status = f->get_status ? f->get_status(f) : 0;
+ if (status < 0)
+ break;
+ put_unaligned_le16(status & 0x0000ffff, req->buf);
+ break;
+ /*
+ * Function drivers should handle SetFeature/ClearFeature
+ * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
+ * only for the first interface of the function
+ */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
+ goto unknown;
+ switch (w_value) {
+ case USB_INTRF_FUNC_SUSPEND:
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ value = 0;
+ if (f->func_suspend)
+ value = f->func_suspend(f, w_index >> 8);
+ if (value < 0) {
+ ERROR(cdev,
+ "func_suspend() returned error %d\n",
+ value);
+ value = 0;
+ }
+ break;
+ }
+ break;
default:
unknown:
VDBG(cdev,
@@ -1140,7 +1450,6 @@ static int composite_bind(struct usb_gadget *gadget)
goto fail;
cdev->desc = *composite->dev;
- cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
if (idVendor)
@@ -1247,7 +1556,11 @@ composite_resume(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
+#ifdef CONFIG_USB_GADGET_SUPERSPEED
+ .speed = USB_SPEED_SUPER,
+#else
.speed = USB_SPEED_HIGH,
+#endif
.unbind = composite_unbind,
@@ -1293,6 +1606,8 @@ int usb_composite_probe(struct usb_composite_driver *driver,
driver->iProduct = driver->name;
composite_driver.function = (char *) driver->name;
composite_driver.driver.name = driver->name;
+ composite_driver.speed = min((u8)composite_driver.speed,
+ (u8)driver->max_speed);
composite = driver;
composite_gadget_bind = bind;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 09084fd646ab..b2c001334876 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -165,28 +165,3 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
return ret;
}
-/**
- * usb_find_endpoint - find a copy of an endpoint descriptor
- * @src: original vector of descriptors
- * @copy: copy of @src
- * @match: endpoint descriptor found in @src
- *
- * This returns the copy of the @match descriptor made for @copy. Its
- * intended use is to help remembering the endpoint descriptor to use
- * when enabling a given endpoint.
- */
-struct usb_endpoint_descriptor *
-usb_find_endpoint(
- struct usb_descriptor_header **src,
- struct usb_descriptor_header **copy,
- struct usb_endpoint_descriptor *match
-)
-{
- while (*src) {
- if (*src == (void *) match)
- return (void *)*copy;
- src++;
- copy++;
- }
- return NULL;
-}
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
index dbe92ee88477..8beefdd36787 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/dbgp.c
@@ -173,7 +173,9 @@ fail_1:
static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
{
- int err = usb_ep_enable(ep, desc);
+ int err;
+ ep->desc = desc;
+ err = usb_ep_enable(ep);
ep->driver_data = dbgp.gadget;
return err;
}
@@ -268,8 +270,8 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
dbgp.serial->in = dbgp.i_ep;
dbgp.serial->out = dbgp.o_ep;
- dbgp.serial->in_desc = &i_desc;
- dbgp.serial->out_desc = &o_desc;
+ dbgp.serial->in->desc = &i_desc;
+ dbgp.serial->out->desc = &o_desc;
if (gserial_setup(gadget, 1) < 0) {
stp = 3;
@@ -312,7 +314,6 @@ static int __init dbgp_bind(struct usb_gadget *gadget)
dbgp.req->length = DBGP_REQ_EP0_LEN;
gadget->ep0->driver_data = gadget;
- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
#ifdef CONFIG_USB_G_DBGP_SERIAL
dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
@@ -363,6 +364,7 @@ static int dbgp_setup(struct usb_gadget *gadget,
dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
len = sizeof device_desc;
data = &device_desc;
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
break;
case USB_DT_DEBUG:
dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index d3dcabc1a5fc..e755a9d267fc 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -70,6 +70,19 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
+struct dummy_hcd_module_parameters {
+ bool is_super_speed;
+ bool is_high_speed;
+};
+
+static struct dummy_hcd_module_parameters mod_data = {
+ .is_super_speed = false,
+ .is_high_speed = true,
+};
+module_param_named(is_super_speed, mod_data.is_super_speed, bool, S_IRUGO);
+MODULE_PARM_DESC(is_super_speed, "true to simulate SuperSpeed connection");
+module_param_named(is_high_speed, mod_data.is_high_speed, bool, S_IRUGO);
+MODULE_PARM_DESC(is_high_speed, "true to simulate HighSpeed connection");
/*-------------------------------------------------------------------------*/
/* gadget side driver data structres */
@@ -152,6 +165,22 @@ enum dummy_rh_state {
DUMMY_RH_RUNNING
};
+struct dummy_hcd {
+ struct dummy *dum;
+ enum dummy_rh_state rh_state;
+ struct timer_list timer;
+ u32 port_status;
+ u32 old_status;
+ unsigned long re_timeout;
+
+ struct usb_device *udev;
+ struct list_head urbp_list;
+
+ unsigned active:1;
+ unsigned old_active:1;
+ unsigned resuming:1;
+};
+
struct dummy {
spinlock_t lock;
@@ -167,36 +196,27 @@ struct dummy {
u16 devstatus;
unsigned udc_suspended:1;
unsigned pullup:1;
- unsigned active:1;
- unsigned old_active:1;
/*
* MASTER/HOST side support
*/
- enum dummy_rh_state rh_state;
- struct timer_list timer;
- u32 port_status;
- u32 old_status;
- unsigned resuming:1;
- unsigned long re_timeout;
-
- struct usb_device *udev;
- struct list_head urbp_list;
+ struct dummy_hcd *hs_hcd;
+ struct dummy_hcd *ss_hcd;
};
-static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd)
+static inline struct dummy_hcd *hcd_to_dummy_hcd(struct usb_hcd *hcd)
{
- return (struct dummy *) (hcd->hcd_priv);
+ return (struct dummy_hcd *) (hcd->hcd_priv);
}
-static inline struct usb_hcd *dummy_to_hcd (struct dummy *dum)
+static inline struct usb_hcd *dummy_hcd_to_hcd(struct dummy_hcd *dum)
{
return container_of((void *) dum, struct usb_hcd, hcd_priv);
}
-static inline struct device *dummy_dev (struct dummy *dum)
+static inline struct device *dummy_dev(struct dummy_hcd *dum)
{
- return dummy_to_hcd(dum)->self.controller;
+ return dummy_hcd_to_hcd(dum)->self.controller;
}
static inline struct device *udc_dev (struct dummy *dum)
@@ -209,9 +229,13 @@ static inline struct dummy *ep_to_dummy (struct dummy_ep *ep)
return container_of (ep->gadget, struct dummy, gadget);
}
-static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget)
+static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget)
{
- return container_of (gadget, struct dummy, gadget);
+ struct dummy *dum = container_of(gadget, struct dummy, gadget);
+ if (dum->gadget.speed == USB_SPEED_SUPER)
+ return dum->ss_hcd;
+ else
+ return dum->hs_hcd;
}
static inline struct dummy *gadget_dev_to_dummy (struct device *dev)
@@ -219,7 +243,7 @@ static inline struct dummy *gadget_dev_to_dummy (struct device *dev)
return container_of (dev, struct dummy, gadget.dev);
}
-static struct dummy *the_controller;
+static struct dummy the_controller;
/*-------------------------------------------------------------------------*/
@@ -259,61 +283,122 @@ stop_activity (struct dummy *dum)
/* driver now does any non-usb quiescing necessary */
}
-/* caller must hold lock */
-static void
-set_link_state (struct dummy *dum)
-{
- dum->active = 0;
- if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
- dum->port_status = 0;
-
- /* UDC suspend must cause a disconnect */
- else if (!dum->pullup || dum->udc_suspended) {
- dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
- USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_LOW_SPEED |
- USB_PORT_STAT_HIGH_SPEED |
- USB_PORT_STAT_SUSPEND);
- if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
- dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+/**
+ * set_link_state_by_speed() - Sets the current state of the link according to
+ * the hcd speed
+ * @dum_hcd: pointer to the dummy_hcd structure to update the link state for
+ *
+ * This function updates the port_status according to the link state and the
+ * speed of the hcd.
+ */
+static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
+{
+ struct dummy *dum = dum_hcd->dum;
+
+ if (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3) {
+ if ((dum_hcd->port_status & USB_SS_PORT_STAT_POWER) == 0) {
+ dum_hcd->port_status = 0;
+ } else if (!dum->pullup || dum->udc_suspended) {
+ /* UDC suspend must cause a disconnect */
+ dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_ENABLE);
+ if ((dum_hcd->old_status &
+ USB_PORT_STAT_CONNECTION) != 0)
+ dum_hcd->port_status |=
+ (USB_PORT_STAT_C_CONNECTION << 16);
+ } else {
+ /* device is connected and not suspended */
+ dum_hcd->port_status |= (USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_SPEED_5GBPS) ;
+ if ((dum_hcd->old_status &
+ USB_PORT_STAT_CONNECTION) == 0)
+ dum_hcd->port_status |=
+ (USB_PORT_STAT_C_CONNECTION << 16);
+ if ((dum_hcd->port_status &
+ USB_PORT_STAT_ENABLE) == 1 &&
+ (dum_hcd->port_status &
+ USB_SS_PORT_LS_U0) == 1 &&
+ dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
+ dum_hcd->active = 1;
+ }
} else {
- dum->port_status |= USB_PORT_STAT_CONNECTION;
- if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
- dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
- if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0)
- dum->port_status &= ~USB_PORT_STAT_SUSPEND;
- else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
- dum->rh_state != DUMMY_RH_SUSPENDED)
- dum->active = 1;
+ if ((dum_hcd->port_status & USB_PORT_STAT_POWER) == 0) {
+ dum_hcd->port_status = 0;
+ } else if (!dum->pullup || dum->udc_suspended) {
+ /* UDC suspend must cause a disconnect */
+ dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_HIGH_SPEED |
+ USB_PORT_STAT_SUSPEND);
+ if ((dum_hcd->old_status &
+ USB_PORT_STAT_CONNECTION) != 0)
+ dum_hcd->port_status |=
+ (USB_PORT_STAT_C_CONNECTION << 16);
+ } else {
+ dum_hcd->port_status |= USB_PORT_STAT_CONNECTION;
+ if ((dum_hcd->old_status &
+ USB_PORT_STAT_CONNECTION) == 0)
+ dum_hcd->port_status |=
+ (USB_PORT_STAT_C_CONNECTION << 16);
+ if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0)
+ dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
+ else if ((dum_hcd->port_status &
+ USB_PORT_STAT_SUSPEND) == 0 &&
+ dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
+ dum_hcd->active = 1;
+ }
}
+}
+
+/* caller must hold lock */
+static void set_link_state(struct dummy_hcd *dum_hcd)
+{
+ struct dummy *dum = dum_hcd->dum;
- if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active)
- dum->resuming = 0;
+ dum_hcd->active = 0;
+ if (dum->pullup)
+ if ((dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 &&
+ dum->gadget.speed != USB_SPEED_SUPER) ||
+ (dummy_hcd_to_hcd(dum_hcd)->speed != HCD_USB3 &&
+ dum->gadget.speed == USB_SPEED_SUPER))
+ return;
- if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
- (dum->port_status & USB_PORT_STAT_RESET) != 0) {
- if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
- (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
- dum->driver) {
- stop_activity (dum);
- spin_unlock (&dum->lock);
- dum->driver->disconnect (&dum->gadget);
- spin_lock (&dum->lock);
+ set_link_state_by_speed(dum_hcd);
+
+ if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
+ dum_hcd->active)
+ dum_hcd->resuming = 0;
+
+ /* if !connected or reset */
+ if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+ (dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
+ /*
+ * We're connected and not reset (reset occurred now),
+ * and driver attached - disconnect!
+ */
+ if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
+ (dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 &&
+ dum->driver) {
+ stop_activity(dum);
+ spin_unlock(&dum->lock);
+ dum->driver->disconnect(&dum->gadget);
+ spin_lock(&dum->lock);
}
- } else if (dum->active != dum->old_active) {
- if (dum->old_active && dum->driver->suspend) {
- spin_unlock (&dum->lock);
- dum->driver->suspend (&dum->gadget);
- spin_lock (&dum->lock);
- } else if (!dum->old_active && dum->driver->resume) {
- spin_unlock (&dum->lock);
- dum->driver->resume (&dum->gadget);
- spin_lock (&dum->lock);
+ } else if (dum_hcd->active != dum_hcd->old_active) {
+ if (dum_hcd->old_active && dum->driver->suspend) {
+ spin_unlock(&dum->lock);
+ dum->driver->suspend(&dum->gadget);
+ spin_lock(&dum->lock);
+ } else if (!dum_hcd->old_active && dum->driver->resume) {
+ spin_unlock(&dum->lock);
+ dum->driver->resume(&dum->gadget);
+ spin_lock(&dum->lock);
}
}
- dum->old_status = dum->port_status;
- dum->old_active = dum->active;
+ dum_hcd->old_status = dum_hcd->port_status;
+ dum_hcd->old_active = dum_hcd->active;
}
/*-------------------------------------------------------------------------*/
@@ -332,6 +417,7 @@ static int
dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
struct dummy_ep *ep;
unsigned max;
int retval;
@@ -341,9 +427,19 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|| desc->bDescriptorType != USB_DT_ENDPOINT)
return -EINVAL;
dum = ep_to_dummy (ep);
- if (!dum->driver || !is_enabled (dum))
+ if (!dum->driver)
+ return -ESHUTDOWN;
+
+ dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
+ if (!is_enabled(dum_hcd))
return -ESHUTDOWN;
- max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
+
+ /*
+ * For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the
+ * maximum packet size.
+ * For SS devices the wMaxPacketSize is limited by 1024.
+ */
+ max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
@@ -361,6 +457,10 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
}
switch (dum->gadget.speed) {
+ case USB_SPEED_SUPER:
+ if (max == 1024)
+ break;
+ goto done;
case USB_SPEED_HIGH:
if (max == 512)
break;
@@ -379,6 +479,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
/* real hardware might not handle all packet sizes */
switch (dum->gadget.speed) {
+ case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
if (max <= 1024)
break;
@@ -399,6 +500,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto done;
/* real hardware might not handle all packet sizes */
switch (dum->gadget.speed) {
+ case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
if (max <= 1024)
break;
@@ -425,10 +527,18 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
({ char *val;
switch (desc->bmAttributes & 0x03) {
- case USB_ENDPOINT_XFER_BULK: val = "bulk"; break;
- case USB_ENDPOINT_XFER_ISOC: val = "iso"; break;
- case USB_ENDPOINT_XFER_INT: val = "intr"; break;
- default: val = "ctrl"; break;
+ case USB_ENDPOINT_XFER_BULK:
+ val = "bulk";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ val = "iso";
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ val = "intr";
+ break;
+ default:
+ val = "ctrl";
+ break;
}; val; }),
max);
@@ -507,6 +617,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
struct dummy_ep *ep;
struct dummy_request *req;
struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
unsigned long flags;
req = usb_request_to_dummy_request (_req);
@@ -518,7 +629,8 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
return -EINVAL;
dum = ep_to_dummy (ep);
- if (!dum->driver || !is_enabled (dum))
+ dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
+ if (!dum->driver || !is_enabled(dum_hcd))
return -ESHUTDOWN;
#if 0
@@ -662,24 +774,24 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget)
static int dummy_wakeup (struct usb_gadget *_gadget)
{
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
- dum = gadget_to_dummy (_gadget);
- if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE)
+ dum_hcd = gadget_to_dummy_hcd(_gadget);
+ if (!(dum_hcd->dum->devstatus & ((1 << USB_DEVICE_B_HNP_ENABLE)
| (1 << USB_DEVICE_REMOTE_WAKEUP))))
return -EINVAL;
- if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0)
+ if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0)
return -ENOLINK;
- if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
- dum->rh_state != DUMMY_RH_SUSPENDED)
+ if ((dum_hcd->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+ dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
return -EIO;
/* FIXME: What if the root hub is suspended but the port isn't? */
/* hub notices our request, issues downstream resume, etc */
- dum->resuming = 1;
- dum->re_timeout = jiffies + msecs_to_jiffies(20);
- mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout);
+ dum_hcd->resuming = 1;
+ dum_hcd->re_timeout = jiffies + msecs_to_jiffies(20);
+ mod_timer(&dummy_hcd_to_hcd(dum_hcd)->rh_timer, dum_hcd->re_timeout);
return 0;
}
@@ -687,7 +799,7 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
{
struct dummy *dum;
- dum = gadget_to_dummy (_gadget);
+ dum = (gadget_to_dummy_hcd(_gadget))->dum;
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
@@ -695,26 +807,68 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
return 0;
}
+static void dummy_udc_udpate_ep0(struct dummy *dum)
+{
+ u32 i;
+
+ if (dum->gadget.speed == USB_SPEED_SUPER) {
+ for (i = 0; i < DUMMY_ENDPOINTS; i++)
+ dum->ep[i].ep.max_streams = 0x10;
+ dum->ep[0].ep.maxpacket = 9;
+ } else {
+ for (i = 0; i < DUMMY_ENDPOINTS; i++)
+ dum->ep[i].ep.max_streams = 0;
+ dum->ep[0].ep.maxpacket = 64;
+ }
+}
+
static int dummy_pullup (struct usb_gadget *_gadget, int value)
{
+ struct dummy_hcd *dum_hcd;
struct dummy *dum;
unsigned long flags;
- dum = gadget_to_dummy (_gadget);
+ dum = gadget_dev_to_dummy(&_gadget->dev);
+
+ if (value && dum->driver) {
+ if (mod_data.is_super_speed)
+ dum->gadget.speed = dum->driver->speed;
+ else if (mod_data.is_high_speed)
+ dum->gadget.speed = min_t(u8, USB_SPEED_HIGH,
+ dum->driver->speed);
+ else
+ dum->gadget.speed = USB_SPEED_FULL;
+ dummy_udc_udpate_ep0(dum);
+
+ if (dum->gadget.speed < dum->driver->speed)
+ dev_dbg(udc_dev(dum), "This device can perform faster"
+ " if you connect it to a %s port...\n",
+ (dum->driver->speed == USB_SPEED_SUPER ?
+ "SuperSpeed" : "HighSpeed"));
+ }
+ dum_hcd = gadget_to_dummy_hcd(_gadget);
+
spin_lock_irqsave (&dum->lock, flags);
dum->pullup = (value != 0);
- set_link_state (dum);
+ set_link_state(dum_hcd);
spin_unlock_irqrestore (&dum->lock, flags);
- usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
return 0;
}
+static int dummy_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int dummy_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops dummy_ops = {
.get_frame = dummy_g_get_frame,
.wakeup = dummy_wakeup,
.set_selfpowered = dummy_set_selfpowered,
.pullup = dummy_pullup,
+ .udc_start = dummy_udc_start,
+ .udc_stop = dummy_udc_stop,
};
/*-------------------------------------------------------------------------*/
@@ -747,18 +901,13 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
* for each driver that registers: just add to a big root hub.
*/
-int
-usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int dummy_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct dummy *dum = the_controller;
- int retval, i;
+ struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
+ struct dummy *dum = dum_hcd->dum;
- if (!dum)
- return -EINVAL;
- if (dum->driver)
- return -EBUSY;
- if (!bind || !driver->setup || driver->speed == USB_SPEED_UNKNOWN)
+ if (driver->speed == USB_SPEED_UNKNOWN)
return -EINVAL;
/*
@@ -768,121 +917,77 @@ usb_gadget_probe_driver(struct usb_gadget_driver *driver,
dum->devstatus = 0;
- INIT_LIST_HEAD (&dum->gadget.ep_list);
- for (i = 0; i < DUMMY_ENDPOINTS; i++) {
- struct dummy_ep *ep = &dum->ep [i];
-
- if (!ep_name [i])
- break;
- ep->ep.name = ep_name [i];
- ep->ep.ops = &dummy_ep_ops;
- list_add_tail (&ep->ep.ep_list, &dum->gadget.ep_list);
- ep->halted = ep->wedged = ep->already_seen =
- ep->setup_stage = 0;
- ep->ep.maxpacket = ~0;
- ep->last_io = jiffies;
- ep->gadget = &dum->gadget;
- ep->desc = NULL;
- INIT_LIST_HEAD (&ep->queue);
- }
-
- dum->gadget.ep0 = &dum->ep [0].ep;
- dum->ep [0].ep.maxpacket = 64;
- list_del_init (&dum->ep [0].ep.ep_list);
- INIT_LIST_HEAD(&dum->fifo_req.queue);
-
- driver->driver.bus = NULL;
dum->driver = driver;
- dum->gadget.dev.driver = &driver->driver;
dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
- retval = bind(&dum->gadget);
- if (retval) {
- dum->driver = NULL;
- dum->gadget.dev.driver = NULL;
- return retval;
- }
-
- /* khubd will enumerate this in a while */
- spin_lock_irq (&dum->lock);
- dum->pullup = 1;
- set_link_state (dum);
- spin_unlock_irq (&dum->lock);
-
- usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int
-usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+static int dummy_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct dummy *dum = the_controller;
- unsigned long flags;
-
- if (!dum)
- return -ENODEV;
- if (!driver || driver != dum->driver || !driver->unbind)
- return -EINVAL;
+ struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
+ struct dummy *dum = dum_hcd->dum;
dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
driver->driver.name);
- spin_lock_irqsave (&dum->lock, flags);
- dum->pullup = 0;
- set_link_state (dum);
- spin_unlock_irqrestore (&dum->lock, flags);
-
- driver->unbind (&dum->gadget);
- dum->gadget.dev.driver = NULL;
dum->driver = NULL;
- spin_lock_irqsave (&dum->lock, flags);
- dum->pullup = 0;
- set_link_state (dum);
- spin_unlock_irqrestore (&dum->lock, flags);
-
- usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ dummy_pullup(&dum->gadget, 0);
return 0;
}
-EXPORT_SYMBOL (usb_gadget_unregister_driver);
#undef is_enabled
-/* just declare this in any driver that really need it */
-extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
-
-int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
-{
- return -ENOSYS;
-}
-EXPORT_SYMBOL (net2280_set_fifo_mode);
-
-
/* The gadget structure is stored inside the hcd structure and will be
* released along with it. */
static void
dummy_gadget_release (struct device *dev)
{
- struct dummy *dum = gadget_dev_to_dummy (dev);
+ return;
+}
+
+static void init_dummy_udc_hw(struct dummy *dum)
+{
+ int i;
+
+ INIT_LIST_HEAD(&dum->gadget.ep_list);
+ for (i = 0; i < DUMMY_ENDPOINTS; i++) {
+ struct dummy_ep *ep = &dum->ep[i];
+
+ if (!ep_name[i])
+ break;
+ ep->ep.name = ep_name[i];
+ ep->ep.ops = &dummy_ep_ops;
+ list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list);
+ ep->halted = ep->wedged = ep->already_seen =
+ ep->setup_stage = 0;
+ ep->ep.maxpacket = ~0;
+ ep->last_io = jiffies;
+ ep->gadget = &dum->gadget;
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+ }
+
+ dum->gadget.ep0 = &dum->ep[0].ep;
+ list_del_init(&dum->ep[0].ep.ep_list);
+ INIT_LIST_HEAD(&dum->fifo_req.queue);
- usb_put_hcd (dummy_to_hcd (dum));
+#ifdef CONFIG_USB_OTG
+ dum->gadget.is_otg = 1;
+#endif
}
static int dummy_udc_probe (struct platform_device *pdev)
{
- struct dummy *dum = the_controller;
+ struct dummy *dum = &the_controller;
int rc;
- usb_get_hcd(dummy_to_hcd(dum));
-
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
dum->gadget.is_dualspeed = 1;
- /* maybe claim OTG support, though we won't complete HNP */
- dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
-
dev_set_name(&dum->gadget.dev, "gadget");
dum->gadget.dev.parent = &pdev->dev;
dum->gadget.dev.release = dummy_gadget_release;
@@ -892,11 +997,22 @@ static int dummy_udc_probe (struct platform_device *pdev)
return rc;
}
+ init_dummy_udc_hw(dum);
+
+ rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget);
+ if (rc < 0)
+ goto err_udc;
+
rc = device_create_file (&dum->gadget.dev, &dev_attr_function);
if (rc < 0)
- device_unregister (&dum->gadget.dev);
- else
- platform_set_drvdata(pdev, dum);
+ goto err_dev;
+ platform_set_drvdata(pdev, dum);
+ return rc;
+
+err_dev:
+ usb_del_gadget_udc(&dum->gadget);
+err_udc:
+ device_unregister(&dum->gadget.dev);
return rc;
}
@@ -904,37 +1020,41 @@ static int dummy_udc_remove (struct platform_device *pdev)
{
struct dummy *dum = platform_get_drvdata (pdev);
+ usb_del_gadget_udc(&dum->gadget);
platform_set_drvdata (pdev, NULL);
device_remove_file (&dum->gadget.dev, &dev_attr_function);
device_unregister (&dum->gadget.dev);
return 0;
}
-static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state)
+static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd,
+ int suspend)
{
- struct dummy *dum = platform_get_drvdata(pdev);
+ spin_lock_irq(&dum->lock);
+ dum->udc_suspended = suspend;
+ set_link_state(dum_hcd);
+ spin_unlock_irq(&dum->lock);
+}
- dev_dbg (&pdev->dev, "%s\n", __func__);
- spin_lock_irq (&dum->lock);
- dum->udc_suspended = 1;
- set_link_state (dum);
- spin_unlock_irq (&dum->lock);
+static int dummy_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct dummy *dum = platform_get_drvdata(pdev);
+ struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
- usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+ dummy_udc_pm(dum, dum_hcd, 1);
+ usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
return 0;
}
-static int dummy_udc_resume (struct platform_device *pdev)
+static int dummy_udc_resume(struct platform_device *pdev)
{
- struct dummy *dum = platform_get_drvdata(pdev);
+ struct dummy *dum = platform_get_drvdata(pdev);
+ struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
- dev_dbg (&pdev->dev, "%s\n", __func__);
- spin_lock_irq (&dum->lock);
- dum->udc_suspended = 0;
- set_link_state (dum);
- spin_unlock_irq (&dum->lock);
-
- usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+ dummy_udc_pm(dum, dum_hcd, 0);
+ usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
return 0;
}
@@ -968,7 +1088,7 @@ static int dummy_urb_enqueue (
struct urb *urb,
gfp_t mem_flags
) {
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
struct urbp *urbp;
unsigned long flags;
int rc;
@@ -981,51 +1101,51 @@ static int dummy_urb_enqueue (
return -ENOMEM;
urbp->urb = urb;
- dum = hcd_to_dummy (hcd);
- spin_lock_irqsave (&dum->lock, flags);
+ dum_hcd = hcd_to_dummy_hcd(hcd);
+ spin_lock_irqsave(&dum_hcd->dum->lock, flags);
rc = usb_hcd_link_urb_to_ep(hcd, urb);
if (rc) {
kfree(urbp);
goto done;
}
- if (!dum->udev) {
- dum->udev = urb->dev;
- usb_get_dev (dum->udev);
- } else if (unlikely (dum->udev != urb->dev))
- dev_err (dummy_dev(dum), "usb_device address has changed!\n");
+ if (!dum_hcd->udev) {
+ dum_hcd->udev = urb->dev;
+ usb_get_dev(dum_hcd->udev);
+ } else if (unlikely(dum_hcd->udev != urb->dev))
+ dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n");
- list_add_tail (&urbp->urbp_list, &dum->urbp_list);
+ list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
urb->hcpriv = urbp;
if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */
/* kick the scheduler, it'll do the rest */
- if (!timer_pending (&dum->timer))
- mod_timer (&dum->timer, jiffies + 1);
+ if (!timer_pending(&dum_hcd->timer))
+ mod_timer(&dum_hcd->timer, jiffies + 1);
done:
- spin_unlock_irqrestore(&dum->lock, flags);
+ spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return rc;
}
static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
unsigned long flags;
int rc;
/* giveback happens automatically in timer callback,
* so make sure the callback happens */
- dum = hcd_to_dummy (hcd);
- spin_lock_irqsave (&dum->lock, flags);
+ dum_hcd = hcd_to_dummy_hcd(hcd);
+ spin_lock_irqsave(&dum_hcd->dum->lock, flags);
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (!rc && dum->rh_state != DUMMY_RH_RUNNING &&
- !list_empty(&dum->urbp_list))
- mod_timer (&dum->timer, jiffies);
+ if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
+ !list_empty(&dum_hcd->urbp_list))
+ mod_timer(&dum_hcd->timer, jiffies);
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return rc;
}
@@ -1162,10 +1282,25 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
}
+ if (dum->gadget.speed == USB_SPEED_SUPER) {
+ switch (ep->desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Sec. 4.4.8.2 USB3.0 Spec */
+ limit = 3 * 16 * 1024 * 8;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ /* Sec. 4.4.7.2 USB3.0 Spec */
+ limit = 3 * 1024 * 8;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ default:
+ break;
+ }
+ }
return limit;
}
-#define is_active(dum) ((dum->port_status & \
+#define is_active(dum_hcd) ((dum_hcd->port_status & \
(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
USB_PORT_STAT_SUSPEND)) \
== (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
@@ -1174,7 +1309,8 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
{
int i;
- if (!is_active (dum))
+ if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
+ dum->ss_hcd : dum->hs_hcd)))
return NULL;
if ((address & ~USB_DIR_IN) == 0)
return &dum->ep [0];
@@ -1211,11 +1347,12 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
* 1 - if the request wasn't handles
* error code on error
*/
-static int handle_control_request(struct dummy *dum, struct urb *urb,
+static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
struct usb_ctrlrequest *setup,
int *status)
{
struct dummy_ep *ep2;
+ struct dummy *dum = dum_hcd->dum;
int ret_val = 1;
unsigned w_index;
unsigned w_value;
@@ -1247,6 +1384,27 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
case USB_DEVICE_A_ALT_HNP_SUPPORT:
dum->gadget.a_alt_hnp_support = 1;
break;
+ case USB_DEVICE_U1_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_U1_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_U2_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_LTM_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
default:
ret_val = -EOPNOTSUPP;
}
@@ -1273,6 +1431,27 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
case USB_DEVICE_REMOTE_WAKEUP:
w_value = USB_DEVICE_REMOTE_WAKEUP;
break;
+ case USB_DEVICE_U1_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_U1_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_U2_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ if (dummy_hcd_to_hcd(dum_hcd)->speed ==
+ HCD_USB3)
+ w_value = USB_DEV_STAT_LTM_ENABLED;
+ else
+ ret_val = -EOPNOTSUPP;
+ break;
default:
ret_val = -EOPNOTSUPP;
break;
@@ -1334,9 +1513,10 @@ static int handle_control_request(struct dummy *dum, struct urb *urb,
/* drive both sides of the transfers; looks like irq handlers to
* both drivers except the callbacks aren't in_irq().
*/
-static void dummy_timer (unsigned long _dum)
+static void dummy_timer(unsigned long _dum_hcd)
{
- struct dummy *dum = (struct dummy *) _dum;
+ struct dummy_hcd *dum_hcd = (struct dummy_hcd *) _dum_hcd;
+ struct dummy *dum = dum_hcd->dum;
struct urbp *urbp, *tmp;
unsigned long flags;
int limit, total;
@@ -1353,8 +1533,12 @@ static void dummy_timer (unsigned long _dum)
case USB_SPEED_HIGH:
total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
break;
+ case USB_SPEED_SUPER:
+ /* Bus speed is 500000 bytes/ms, so use a little less */
+ total = 490000;
+ break;
default:
- dev_err (dummy_dev(dum), "bogus device speed\n");
+ dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
return;
}
@@ -1363,8 +1547,8 @@ static void dummy_timer (unsigned long _dum)
/* look at each urb queued by the host side driver */
spin_lock_irqsave (&dum->lock, flags);
- if (!dum->udev) {
- dev_err (dummy_dev(dum),
+ if (!dum_hcd->udev) {
+ dev_err(dummy_dev(dum_hcd),
"timer fired with no URBs pending?\n");
spin_unlock_irqrestore (&dum->lock, flags);
return;
@@ -1377,7 +1561,7 @@ static void dummy_timer (unsigned long _dum)
}
restart:
- list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) {
+ list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) {
struct urb *urb;
struct dummy_request *req;
u8 address;
@@ -1388,7 +1572,7 @@ restart:
urb = urbp->urb;
if (urb->unlinked)
goto return_urb;
- else if (dum->rh_state != DUMMY_RH_RUNNING)
+ else if (dum_hcd->rh_state != DUMMY_RH_RUNNING)
continue;
type = usb_pipetype (urb->pipe);
@@ -1406,7 +1590,7 @@ restart:
ep = find_endpoint(dum, address);
if (!ep) {
/* set_configuration() disagreement */
- dev_dbg (dummy_dev(dum),
+ dev_dbg(dummy_dev(dum_hcd),
"no ep configured for urb %p\n",
urb);
status = -EPROTO;
@@ -1422,7 +1606,7 @@ restart:
}
if (ep->halted && !ep->setup_stage) {
/* NOTE: must not be iso! */
- dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n",
+ dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n",
ep->ep.name, urb);
status = -EPIPE;
goto return_urb;
@@ -1457,7 +1641,7 @@ restart:
ep->setup_stage = 0;
ep->halted = 0;
- value = handle_control_request(dum, urb, &setup,
+ value = handle_control_request(dum_hcd, urb, &setup,
&status);
/* gadget driver handles all other requests. block
@@ -1527,20 +1711,20 @@ return_urb:
if (ep)
ep->already_seen = ep->setup_stage = 0;
- usb_hcd_unlink_urb_from_ep(dummy_to_hcd(dum), urb);
+ usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb);
spin_unlock (&dum->lock);
- usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status);
+ usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status);
spin_lock (&dum->lock);
goto restart;
}
- if (list_empty (&dum->urbp_list)) {
- usb_put_dev (dum->udev);
- dum->udev = NULL;
- } else if (dum->rh_state == DUMMY_RH_RUNNING) {
+ if (list_empty(&dum_hcd->urbp_list)) {
+ usb_put_dev(dum_hcd->udev);
+ dum_hcd->udev = NULL;
+ } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
/* want a 1 msec delay here */
- mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
+ mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1));
}
spin_unlock_irqrestore (&dum->lock, flags);
@@ -1557,36 +1741,48 @@ return_urb:
static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
{
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
unsigned long flags;
int retval = 0;
- dum = hcd_to_dummy (hcd);
+ dum_hcd = hcd_to_dummy_hcd(hcd);
- spin_lock_irqsave (&dum->lock, flags);
+ spin_lock_irqsave(&dum_hcd->dum->lock, flags);
if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
- if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
- dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
- dum->port_status &= ~USB_PORT_STAT_SUSPEND;
- set_link_state (dum);
+ if (dum_hcd->resuming && time_after_eq(jiffies, dum_hcd->re_timeout)) {
+ dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+ dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
+ set_link_state(dum_hcd);
}
- if ((dum->port_status & PORT_C_MASK) != 0) {
+ if ((dum_hcd->port_status & PORT_C_MASK) != 0) {
*buf = (1 << 1);
- dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
- dum->port_status);
+ dev_dbg(dummy_dev(dum_hcd), "port status 0x%08x has changes\n",
+ dum_hcd->port_status);
retval = 1;
- if (dum->rh_state == DUMMY_RH_SUSPENDED)
+ if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED)
usb_hcd_resume_root_hub (hcd);
}
done:
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return retval;
}
static inline void
+ss_hub_descriptor(struct usb_hub_descriptor *desc)
+{
+ memset(desc, 0, sizeof *desc);
+ desc->bDescriptorType = 0x2a;
+ desc->bDescLength = 12;
+ desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->bNbrPorts = 1;
+ desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
+ desc->u.ss.DeviceRemovable = 0xffff;
+}
+
+static inline void
hub_descriptor (struct usb_hub_descriptor *desc)
{
memset (desc, 0, sizeof *desc);
@@ -1606,39 +1802,64 @@ static int dummy_hub_control (
char *buf,
u16 wLength
) {
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
int retval = 0;
unsigned long flags;
if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
- dum = hcd_to_dummy (hcd);
- spin_lock_irqsave (&dum->lock, flags);
+ dum_hcd = hcd_to_dummy_hcd(hcd);
+
+ spin_lock_irqsave(&dum_hcd->dum->lock, flags);
switch (typeReq) {
case ClearHubFeature:
break;
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- if (dum->port_status & USB_PORT_STAT_SUSPEND) {
+ if (hcd->speed == HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "USB_PORT_FEAT_SUSPEND req not "
+ "supported for USB 3.0 roothub\n");
+ goto error;
+ }
+ if (dum_hcd->port_status & USB_PORT_STAT_SUSPEND) {
/* 20msec resume signaling */
- dum->resuming = 1;
- dum->re_timeout = jiffies +
+ dum_hcd->resuming = 1;
+ dum_hcd->re_timeout = jiffies +
msecs_to_jiffies(20);
}
break;
case USB_PORT_FEAT_POWER:
- if (dum->port_status & USB_PORT_STAT_POWER)
- dev_dbg (dummy_dev(dum), "power-off\n");
+ if (hcd->speed == HCD_USB3) {
+ if (dum_hcd->port_status & USB_PORT_STAT_POWER)
+ dev_dbg(dummy_dev(dum_hcd),
+ "power-off\n");
+ } else
+ if (dum_hcd->port_status &
+ USB_SS_PORT_STAT_POWER)
+ dev_dbg(dummy_dev(dum_hcd),
+ "power-off\n");
/* FALLS THROUGH */
default:
- dum->port_status &= ~(1 << wValue);
- set_link_state (dum);
+ dum_hcd->port_status &= ~(1 << wValue);
+ set_link_state(dum_hcd);
}
break;
case GetHubDescriptor:
- hub_descriptor ((struct usb_hub_descriptor *) buf);
+ if (hcd->speed == HCD_USB3 &&
+ (wLength < USB_DT_SS_HUB_SIZE ||
+ wValue != (USB_DT_SS_HUB << 8))) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "Wrong hub descriptor type for "
+ "USB 3.0 roothub.\n");
+ goto error;
+ }
+ if (hcd->speed == HCD_USB3)
+ ss_hub_descriptor((struct usb_hub_descriptor *) buf);
+ else
+ hub_descriptor((struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
*(__le32 *) buf = cpu_to_le32 (0);
@@ -1650,127 +1871,210 @@ static int dummy_hub_control (
/* whoever resets or resumes must GetPortStatus to
* complete it!!
*/
- if (dum->resuming &&
- time_after_eq (jiffies, dum->re_timeout)) {
- dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
- dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+ if (dum_hcd->resuming &&
+ time_after_eq(jiffies, dum_hcd->re_timeout)) {
+ dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+ dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
}
- if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
- time_after_eq (jiffies, dum->re_timeout)) {
- dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
- dum->port_status &= ~USB_PORT_STAT_RESET;
- if (dum->pullup) {
- dum->port_status |= USB_PORT_STAT_ENABLE;
- /* give it the best speed we agree on */
- dum->gadget.speed = dum->driver->speed;
- dum->gadget.ep0->maxpacket = 64;
- switch (dum->gadget.speed) {
- case USB_SPEED_HIGH:
- dum->port_status |=
- USB_PORT_STAT_HIGH_SPEED;
- break;
- case USB_SPEED_LOW:
- dum->gadget.ep0->maxpacket = 8;
- dum->port_status |=
- USB_PORT_STAT_LOW_SPEED;
- break;
- default:
- dum->gadget.speed = USB_SPEED_FULL;
- break;
+ if ((dum_hcd->port_status & USB_PORT_STAT_RESET) != 0 &&
+ time_after_eq(jiffies, dum_hcd->re_timeout)) {
+ dum_hcd->port_status |= (USB_PORT_STAT_C_RESET << 16);
+ dum_hcd->port_status &= ~USB_PORT_STAT_RESET;
+ if (dum_hcd->dum->pullup) {
+ dum_hcd->port_status |= USB_PORT_STAT_ENABLE;
+
+ if (hcd->speed < HCD_USB3) {
+ switch (dum_hcd->dum->gadget.speed) {
+ case USB_SPEED_HIGH:
+ dum_hcd->port_status |=
+ USB_PORT_STAT_HIGH_SPEED;
+ break;
+ case USB_SPEED_LOW:
+ dum_hcd->dum->gadget.ep0->
+ maxpacket = 8;
+ dum_hcd->port_status |=
+ USB_PORT_STAT_LOW_SPEED;
+ break;
+ default:
+ dum_hcd->dum->gadget.speed =
+ USB_SPEED_FULL;
+ break;
+ }
}
}
}
- set_link_state (dum);
- ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
- ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
+ set_link_state(dum_hcd);
+ ((__le16 *) buf)[0] = cpu_to_le16 (dum_hcd->port_status);
+ ((__le16 *) buf)[1] = cpu_to_le16 (dum_hcd->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
break;
case SetPortFeature:
switch (wValue) {
+ case USB_PORT_FEAT_LINK_STATE:
+ if (hcd->speed != HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "USB_PORT_FEAT_LINK_STATE req not "
+ "supported for USB 2.0 roothub\n");
+ goto error;
+ }
+ /*
+ * Since this is dummy we don't have an actual link so
+ * there is nothing to do for the SET_LINK_STATE cmd
+ */
+ break;
+ case USB_PORT_FEAT_U1_TIMEOUT:
+ case USB_PORT_FEAT_U2_TIMEOUT:
+ /* TODO: add suspend/resume support! */
+ if (hcd->speed != HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "USB_PORT_FEAT_U1/2_TIMEOUT req not "
+ "supported for USB 2.0 roothub\n");
+ goto error;
+ }
+ break;
case USB_PORT_FEAT_SUSPEND:
- if (dum->active) {
- dum->port_status |= USB_PORT_STAT_SUSPEND;
+ /* Applicable only for USB2.0 hub */
+ if (hcd->speed == HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "USB_PORT_FEAT_SUSPEND req not "
+ "supported for USB 3.0 roothub\n");
+ goto error;
+ }
+ if (dum_hcd->active) {
+ dum_hcd->port_status |= USB_PORT_STAT_SUSPEND;
/* HNP would happen here; for now we
* assume b_bus_req is always true.
*/
- set_link_state (dum);
+ set_link_state(dum_hcd);
if (((1 << USB_DEVICE_B_HNP_ENABLE)
- & dum->devstatus) != 0)
- dev_dbg (dummy_dev(dum),
+ & dum_hcd->dum->devstatus) != 0)
+ dev_dbg(dummy_dev(dum_hcd),
"no HNP yet!\n");
}
break;
case USB_PORT_FEAT_POWER:
- dum->port_status |= USB_PORT_STAT_POWER;
- set_link_state (dum);
+ if (hcd->speed == HCD_USB3)
+ dum_hcd->port_status |= USB_SS_PORT_STAT_POWER;
+ else
+ dum_hcd->port_status |= USB_PORT_STAT_POWER;
+ set_link_state(dum_hcd);
break;
+ case USB_PORT_FEAT_BH_PORT_RESET:
+ /* Applicable only for USB3.0 hub */
+ if (hcd->speed != HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "USB_PORT_FEAT_BH_PORT_RESET req not "
+ "supported for USB 2.0 roothub\n");
+ goto error;
+ }
+ /* FALLS THROUGH */
case USB_PORT_FEAT_RESET:
/* if it's already enabled, disable */
- dum->port_status &= ~(USB_PORT_STAT_ENABLE
+ if (hcd->speed == HCD_USB3) {
+ dum_hcd->port_status = 0;
+ dum_hcd->port_status =
+ (USB_SS_PORT_STAT_POWER |
+ USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_RESET);
+ } else
+ dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
- dum->devstatus = 0;
- /* 50msec reset signaling */
- dum->re_timeout = jiffies + msecs_to_jiffies(50);
+ /*
+ * We want to reset device status. All but the
+ * Self powered feature
+ */
+ dum_hcd->dum->devstatus &=
+ (1 << USB_DEVICE_SELF_POWERED);
+ /*
+ * FIXME USB3.0: what is the correct reset signaling
+ * interval? Is it still 50msec as for HS?
+ */
+ dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
/* FALLS THROUGH */
default:
- if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
- dum->port_status |= (1 << wValue);
- set_link_state (dum);
- }
+ if (hcd->speed == HCD_USB3) {
+ if ((dum_hcd->port_status &
+ USB_SS_PORT_STAT_POWER) != 0) {
+ dum_hcd->port_status |= (1 << wValue);
+ set_link_state(dum_hcd);
+ }
+ } else
+ if ((dum_hcd->port_status &
+ USB_PORT_STAT_POWER) != 0) {
+ dum_hcd->port_status |= (1 << wValue);
+ set_link_state(dum_hcd);
+ }
+ }
+ break;
+ case GetPortErrorCount:
+ if (hcd->speed != HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "GetPortErrorCount req not "
+ "supported for USB 2.0 roothub\n");
+ goto error;
+ }
+ /* We'll always return 0 since this is a dummy hub */
+ *(__le32 *) buf = cpu_to_le32(0);
+ break;
+ case SetHubDepth:
+ if (hcd->speed != HCD_USB3) {
+ dev_dbg(dummy_dev(dum_hcd),
+ "SetHubDepth req not supported for "
+ "USB 2.0 roothub\n");
+ goto error;
}
break;
-
default:
- dev_dbg (dummy_dev(dum),
+ dev_dbg(dummy_dev(dum_hcd),
"hub control req%04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
-
+error:
/* "protocol stall" on error */
retval = -EPIPE;
}
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
- if ((dum->port_status & PORT_C_MASK) != 0)
+ if ((dum_hcd->port_status & PORT_C_MASK) != 0)
usb_hcd_poll_rh_status (hcd);
return retval;
}
static int dummy_bus_suspend (struct usb_hcd *hcd)
{
- struct dummy *dum = hcd_to_dummy (hcd);
+ struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
- spin_lock_irq (&dum->lock);
- dum->rh_state = DUMMY_RH_SUSPENDED;
- set_link_state (dum);
+ spin_lock_irq(&dum_hcd->dum->lock);
+ dum_hcd->rh_state = DUMMY_RH_SUSPENDED;
+ set_link_state(dum_hcd);
hcd->state = HC_STATE_SUSPENDED;
- spin_unlock_irq (&dum->lock);
+ spin_unlock_irq(&dum_hcd->dum->lock);
return 0;
}
static int dummy_bus_resume (struct usb_hcd *hcd)
{
- struct dummy *dum = hcd_to_dummy (hcd);
+ struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
int rc = 0;
dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
- spin_lock_irq (&dum->lock);
+ spin_lock_irq(&dum_hcd->dum->lock);
if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
} else {
- dum->rh_state = DUMMY_RH_RUNNING;
- set_link_state (dum);
- if (!list_empty(&dum->urbp_list))
- mod_timer (&dum->timer, jiffies);
+ dum_hcd->rh_state = DUMMY_RH_RUNNING;
+ set_link_state(dum_hcd);
+ if (!list_empty(&dum_hcd->urbp_list))
+ mod_timer(&dum_hcd->timer, jiffies);
hcd->state = HC_STATE_RUNNING;
}
- spin_unlock_irq (&dum->lock);
+ spin_unlock_irq(&dum_hcd->dum->lock);
return rc;
}
@@ -1786,18 +2090,37 @@ show_urb (char *buf, size_t size, struct urb *urb)
urb,
({ char *s;
switch (urb->dev->speed) {
- case USB_SPEED_LOW: s = "ls"; break;
- case USB_SPEED_FULL: s = "fs"; break;
- case USB_SPEED_HIGH: s = "hs"; break;
- default: s = "?"; break;
+ case USB_SPEED_LOW:
+ s = "ls";
+ break;
+ case USB_SPEED_FULL:
+ s = "fs";
+ break;
+ case USB_SPEED_HIGH:
+ s = "hs";
+ break;
+ case USB_SPEED_SUPER:
+ s = "ss";
+ break;
+ default:
+ s = "?";
+ break;
}; s; }),
ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "",
({ char *s; \
switch (usb_pipetype (urb->pipe)) { \
- case PIPE_CONTROL: s = ""; break; \
- case PIPE_BULK: s = "-bulk"; break; \
- case PIPE_INTERRUPT: s = "-int"; break; \
- default: s = "-iso"; break; \
+ case PIPE_CONTROL: \
+ s = ""; \
+ break; \
+ case PIPE_BULK: \
+ s = "-bulk"; \
+ break; \
+ case PIPE_INTERRUPT: \
+ s = "-int"; \
+ break; \
+ default: \
+ s = "-iso"; \
+ break; \
}; s;}),
urb->actual_length, urb->transfer_buffer_length);
}
@@ -1806,43 +2129,63 @@ static ssize_t
show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_hcd *hcd = dev_get_drvdata (dev);
- struct dummy *dum = hcd_to_dummy (hcd);
+ struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
struct urbp *urbp;
size_t size = 0;
unsigned long flags;
- spin_lock_irqsave (&dum->lock, flags);
- list_for_each_entry (urbp, &dum->urbp_list, urbp_list) {
+ spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+ list_for_each_entry(urbp, &dum_hcd->urbp_list, urbp_list) {
size_t temp;
temp = show_urb (buf, PAGE_SIZE - size, urbp->urb);
buf += temp;
size += temp;
}
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return size;
}
static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
-static int dummy_start (struct usb_hcd *hcd)
+static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
- struct dummy *dum;
+ init_timer(&dum_hcd->timer);
+ dum_hcd->timer.function = dummy_timer;
+ dum_hcd->timer.data = (unsigned long)dum_hcd;
+ dum_hcd->rh_state = DUMMY_RH_RUNNING;
+ INIT_LIST_HEAD(&dum_hcd->urbp_list);
+ dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET;
+ dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING;
+ dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1;
+#ifdef CONFIG_USB_OTG
+ dummy_hcd_to_hcd(dum_hcd)->self.otg_port = 1;
+#endif
+ return 0;
+
+ /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
+ return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs);
+}
- dum = hcd_to_dummy (hcd);
+static int dummy_start(struct usb_hcd *hcd)
+{
+ struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
/*
* MASTER side init ... we emulate a root hub that'll only ever
* talk to one device (the slave side). Also appears in sysfs,
* just like more familiar pci-based HCDs.
*/
- spin_lock_init (&dum->lock);
- init_timer (&dum->timer);
- dum->timer.function = dummy_timer;
- dum->timer.data = (unsigned long) dum;
- dum->rh_state = DUMMY_RH_RUNNING;
+ if (!usb_hcd_is_primary_hcd(hcd))
+ return dummy_start_ss(dum_hcd);
- INIT_LIST_HEAD (&dum->urbp_list);
+ spin_lock_init(&dum_hcd->dum->lock);
+ init_timer(&dum_hcd->timer);
+ dum_hcd->timer.function = dummy_timer;
+ dum_hcd->timer.data = (unsigned long)dum_hcd;
+ dum_hcd->rh_state = DUMMY_RH_RUNNING;
+
+ INIT_LIST_HEAD(&dum_hcd->urbp_list);
hcd->power_budget = POWER_BUDGET;
hcd->state = HC_STATE_RUNNING;
@@ -1853,18 +2196,17 @@ static int dummy_start (struct usb_hcd *hcd)
#endif
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
- return device_create_file (dummy_dev(dum), &dev_attr_urbs);
+ return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs);
}
static void dummy_stop (struct usb_hcd *hcd)
{
struct dummy *dum;
- dum = hcd_to_dummy (hcd);
-
- device_remove_file (dummy_dev(dum), &dev_attr_urbs);
- usb_gadget_unregister_driver (dum->driver);
- dev_info (dummy_dev(dum), "stopped\n");
+ dum = (hcd_to_dummy_hcd(hcd))->dum;
+ device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
+ usb_gadget_unregister_driver(dum->driver);
+ dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
}
/*-------------------------------------------------------------------------*/
@@ -1874,13 +2216,59 @@ static int dummy_h_get_frame (struct usb_hcd *hcd)
return dummy_g_get_frame (NULL);
}
-static const struct hc_driver dummy_hcd = {
+static int dummy_setup(struct usb_hcd *hcd)
+{
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ the_controller.hs_hcd = hcd_to_dummy_hcd(hcd);
+ the_controller.hs_hcd->dum = &the_controller;
+ /*
+ * Mark the first roothub as being USB 2.0.
+ * The USB 3.0 roothub will be registered later by
+ * dummy_hcd_probe()
+ */
+ hcd->speed = HCD_USB2;
+ hcd->self.root_hub->speed = USB_SPEED_HIGH;
+ } else {
+ the_controller.ss_hcd = hcd_to_dummy_hcd(hcd);
+ the_controller.ss_hcd->dum = &the_controller;
+ hcd->speed = HCD_USB3;
+ hcd->self.root_hub->speed = USB_SPEED_SUPER;
+ }
+ return 0;
+}
+
+/* Change a group of bulk endpoints to support multiple stream IDs */
+int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ unsigned int num_streams, gfp_t mem_flags)
+{
+ if (hcd->speed != HCD_USB3)
+ dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
+ "%s() - ERROR! Not supported for USB2.0 roothub\n",
+ __func__);
+ return 0;
+}
+
+/* Reverts a group of bulk endpoints back to not using stream IDs. */
+int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint **eps, unsigned int num_eps,
+ gfp_t mem_flags)
+{
+ if (hcd->speed != HCD_USB3)
+ dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
+ "%s() - ERROR! Not supported for USB2.0 roothub\n",
+ __func__);
+ return 0;
+}
+
+static struct hc_driver dummy_hcd = {
.description = (char *) driver_name,
.product_desc = "Dummy host controller",
- .hcd_priv_size = sizeof(struct dummy),
+ .hcd_priv_size = sizeof(struct dummy_hcd),
- .flags = HCD_USB2,
+ .flags = HCD_USB3 | HCD_SHARED,
+ .reset = dummy_setup,
.start = dummy_start,
.stop = dummy_stop,
@@ -1893,51 +2281,85 @@ static const struct hc_driver dummy_hcd = {
.hub_control = dummy_hub_control,
.bus_suspend = dummy_bus_suspend,
.bus_resume = dummy_bus_resume,
+
+ .alloc_streams = dummy_alloc_streams,
+ .free_streams = dummy_free_streams,
};
static int dummy_hcd_probe(struct platform_device *pdev)
{
- struct usb_hcd *hcd;
+ struct usb_hcd *hs_hcd;
+ struct usb_hcd *ss_hcd;
int retval;
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
- hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd)
+ if (!mod_data.is_super_speed)
+ dummy_hcd.flags = HCD_USB2;
+ hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
+ if (!hs_hcd)
return -ENOMEM;
- the_controller = hcd_to_dummy (hcd);
- hcd->has_tt = 1;
+ hs_hcd->has_tt = 1;
- retval = usb_add_hcd(hcd, 0, 0);
+ retval = usb_add_hcd(hs_hcd, 0, 0);
if (retval != 0) {
- usb_put_hcd (hcd);
- the_controller = NULL;
+ usb_put_hcd(hs_hcd);
+ return retval;
+ }
+
+ if (mod_data.is_super_speed) {
+ ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
+ dev_name(&pdev->dev), hs_hcd);
+ if (!ss_hcd) {
+ retval = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
+
+ retval = usb_add_hcd(ss_hcd, 0, 0);
+ if (retval)
+ goto put_usb3_hcd;
}
+ return 0;
+
+put_usb3_hcd:
+ usb_put_hcd(ss_hcd);
+dealloc_usb2_hcd:
+ usb_put_hcd(hs_hcd);
+ the_controller.hs_hcd = the_controller.ss_hcd = NULL;
return retval;
}
-static int dummy_hcd_remove (struct platform_device *pdev)
+static int dummy_hcd_remove(struct platform_device *pdev)
{
- struct usb_hcd *hcd;
+ struct dummy *dum;
+
+ dum = (hcd_to_dummy_hcd(platform_get_drvdata(pdev)))->dum;
+
+ if (dum->ss_hcd) {
+ usb_remove_hcd(dummy_hcd_to_hcd(dum->ss_hcd));
+ usb_put_hcd(dummy_hcd_to_hcd(dum->ss_hcd));
+ }
+
+ usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
+ usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
+
+ the_controller.hs_hcd = NULL;
+ the_controller.ss_hcd = NULL;
- hcd = platform_get_drvdata (pdev);
- usb_remove_hcd (hcd);
- usb_put_hcd (hcd);
- the_controller = NULL;
return 0;
}
static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state)
{
struct usb_hcd *hcd;
- struct dummy *dum;
+ struct dummy_hcd *dum_hcd;
int rc = 0;
dev_dbg (&pdev->dev, "%s\n", __func__);
hcd = platform_get_drvdata (pdev);
- dum = hcd_to_dummy (hcd);
- if (dum->rh_state == DUMMY_RH_RUNNING) {
+ dum_hcd = hcd_to_dummy_hcd(hcd);
+ if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
dev_warn(&pdev->dev, "Root hub isn't suspended!\n");
rc = -EBUSY;
} else
@@ -1980,6 +2402,9 @@ static int __init init (void)
if (usb_disabled ())
return -ENODEV;
+ if (!mod_data.is_high_speed && mod_data.is_super_speed)
+ return -EINVAL;
+
the_hcd_pdev = platform_device_alloc(driver_name, -1);
if (!the_hcd_pdev)
return retval;
@@ -1997,7 +2422,8 @@ static int __init init (void)
retval = platform_device_add(the_hcd_pdev);
if (retval < 0)
goto err_add_hcd;
- if (!the_controller) {
+ if (!the_controller.hs_hcd ||
+ (!the_controller.ss_hcd && mod_data.is_super_speed)) {
/*
* The hcd was added successfully but its probe function failed
* for some reason.
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 9b7360ff5aa7..7a7e6b7e1fd6 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -63,13 +63,16 @@ static int
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
- struct usb_endpoint_descriptor *desc
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
)
{
u8 type;
const char *tmp;
u16 max;
+ int num_req_streams = 0;
+
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
@@ -129,6 +132,22 @@ ep_matches (
}
/*
+ * Get the number of required streams from the EP companion
+ * descriptor and see if the EP matches it
+ */
+ if (usb_endpoint_xfer_bulk(desc)) {
+ if (ep_comp) {
+ num_req_streams = ep_comp->bmAttributes & 0x1f;
+ if (num_req_streams > ep->max_streams)
+ return 0;
+ /* Update the ep_comp descriptor if needed */
+ if (num_req_streams != ep->max_streams)
+ ep_comp->bmAttributes = ep->max_streams;
+ }
+
+ }
+
+ /*
* If the protocol driver hasn't yet decided on wMaxPacketSize
* and wants to know the maximum possible, provide the info.
*/
@@ -142,13 +161,13 @@ ep_matches (
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
switch (type) {
case USB_ENDPOINT_XFER_INT:
- /* INT: limit 64 bytes full speed, 1024 high speed */
+ /* INT: limit 64 bytes full speed, 1024 high/super speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
- /* ISO: limit 1023 bytes full speed, 1024 high speed */
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
@@ -183,7 +202,7 @@ ep_matches (
}
/* report (variable) full speed bulk maxpacket */
- if (USB_ENDPOINT_XFER_BULK == type) {
+ if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
@@ -191,6 +210,7 @@ ep_matches (
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
+ ep->address = desc->bEndpointAddress;
return 1;
}
@@ -207,38 +227,53 @@ find_ep (struct usb_gadget *gadget, const char *name)
}
/**
- * usb_ep_autoconfig - choose an endpoint matching the descriptor
+ * usb_ep_autoconfig_ss() - choose an endpoint matching the ep
+ * descriptor and ep companion descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
- * initialized. For periodic transfers, the maximum packet
- * size must also be initialized. This is modified on success.
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on
+ * success.
+ * @ep_comp: Endpoint companion descriptor, with the required
+ * number of streams. Will be modified when the chosen EP
+ * supports a different number of streams.
*
- * By choosing an endpoint to use with the specified descriptor, this
- * routine simplifies writing gadget drivers that work with multiple
- * USB device controllers. The endpoint would be passed later to
- * usb_ep_enable(), along with some descriptor.
+ * This routine replaces the usb_ep_autoconfig when needed
+ * superspeed enhancments. If such enhancemnets are required,
+ * the FD should call usb_ep_autoconfig_ss directly and provide
+ * the additional ep_comp parameter.
+ *
+ * By choosing an endpoint to use with the specified descriptor,
+ * this routine simplifies writing gadget drivers that work with
+ * multiple USB device controllers. The endpoint would be
+ * passed later to usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
- * Be sure to examine and test the results of autoconfiguration on your
- * hardware. This code may not make the best choices about how to use the
- * USB controller, and it can't know all the restrictions that may apply.
- * Some combinations of driver and hardware won't be able to autoconfigure.
+ * Be sure to examine and test the results of autoconfiguration
+ * on your hardware. This code may not make the best choices
+ * about how to use the USB controller, and it can't know all
+ * the restrictions that may apply. Some combinations of driver
+ * and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
- * is initialized as if the endpoint were used at full speed. To prevent
- * the endpoint from being returned by a later autoconfig call, claim it
- * by assigning ep->driver_data to some non-null value.
+ * is initialized as if the endpoint were used at full speed and
+ * the bmAttribute field in the ep companion descriptor is
+ * updated with the assigned number of streams if it is
+ * different from the original value. To prevent the endpoint
+ * from being returned by a later autoconfig call, claim it by
+ * assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
-struct usb_ep *usb_ep_autoconfig (
+struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
- struct usb_endpoint_descriptor *desc
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
)
{
struct usb_ep *ep;
@@ -252,23 +287,24 @@ struct usb_ep *usb_ep_autoconfig (
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
- if (ep && ep_matches (gadget, ep, desc))
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
ep = find_ep (gadget, "ep-f");
- if (ep && ep_matches (gadget, ep, desc))
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
- ep = find_ep (gadget, "ep3-bulk");
- if (ep && ep_matches (gadget, ep, desc))
+ ep = find_ep(gadget, "ep3-bulk");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
- ep = find_ep (gadget, "ep2-bulk");
- if (ep && ep_matches (gadget, ep, desc))
+ ep = find_ep(gadget, "ep2-bulk");
+ if (ep && ep_matches(gadget, ep, desc,
+ ep_comp))
return ep;
}
@@ -287,14 +323,14 @@ struct usb_ep *usb_ep_autoconfig (
ep = find_ep(gadget, "ep2out");
} else
ep = NULL;
- if (ep && ep_matches (gadget, ep, desc))
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
return ep;
#endif
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
- if (ep_matches (gadget, ep, desc))
+ if (ep_matches(gadget, ep, desc, ep_comp))
return ep;
}
@@ -303,6 +339,46 @@ struct usb_ep *usb_ep_autoconfig (
}
/**
+ * usb_ep_autoconfig() - choose an endpoint matching the
+ * descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on success.
+ *
+ * By choosing an endpoint to use with the specified descriptor, this
+ * routine simplifies writing gadget drivers that work with multiple
+ * USB device controllers. The endpoint would be passed later to
+ * usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration on your
+ * hardware. This code may not make the best choices about how to use the
+ * USB controller, and it can't know all the restrictions that may apply.
+ * Some combinations of driver and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed. To prevent
+ * the endpoint from being returned by a later autoconfig call, claim it
+ * by assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc
+)
+{
+ return usb_ep_autoconfig_ss(gadget, desc, NULL);
+}
+
+
+/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 1690c9d68256..aafc84f33e26 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -401,6 +401,7 @@ static struct usb_composite_driver eth_driver = {
.name = "g_ether",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
.unbind = __exit_p(eth_unbind),
};
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index bd6226cbae86..3f8849339ade 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -39,12 +39,6 @@
* descriptors (roughly equivalent to CDC Unions) may sometimes help.
*/
-struct acm_ep_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
- struct usb_endpoint_descriptor *notify;
-};
-
struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
@@ -58,11 +52,7 @@ struct f_acm {
*/
spinlock_t lock;
- struct acm_ep_descs fs;
- struct acm_ep_descs hs;
-
struct usb_ep *notify;
- struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -405,23 +395,27 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(acm->notify);
} else {
VDBG(cdev, "init acm ctrl interface %d\n", intf);
- acm->notify_desc = ep_choose(cdev->gadget,
- acm->hs.notify,
- acm->fs.notify);
+ if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+ return -EINVAL;
}
- usb_ep_enable(acm->notify, acm->notify_desc);
+ usb_ep_enable(acm->notify);
acm->notify->driver_data = acm;
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
gserial_disconnect(&acm->port);
- } else {
+ }
+ if (!acm->port.in->desc || !acm->port.out->desc) {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
- acm->port.in_desc = ep_choose(cdev->gadget,
- acm->hs.in, acm->fs.in);
- acm->port.out_desc = ep_choose(cdev->gadget,
- acm->hs.out, acm->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ acm->port.in) ||
+ config_ep_by_speed(cdev->gadget, f,
+ acm->port.out)) {
+ acm->port.in->desc = NULL;
+ acm->port.out->desc = NULL;
+ return -EINVAL;
+ }
}
gserial_connect(&acm->port, acm->port_num);
@@ -629,18 +623,11 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm->notify_req->complete = acm_cdc_notify_complete;
acm->notify_req->context = acm;
- /* copy descriptors, and track endpoint copies */
+ /* copy descriptors */
f->descriptors = usb_copy_descriptors(acm_fs_function);
if (!f->descriptors)
goto fail;
- acm->fs.in = usb_find_endpoint(acm_fs_function,
- f->descriptors, &acm_fs_in_desc);
- acm->fs.out = usb_find_endpoint(acm_fs_function,
- f->descriptors, &acm_fs_out_desc);
- acm->fs.notify = usb_find_endpoint(acm_fs_function,
- f->descriptors, &acm_fs_notify_desc);
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -653,15 +640,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm_hs_notify_desc.bEndpointAddress =
acm_fs_notify_desc.bEndpointAddress;
- /* copy descriptors, and track endpoint copies */
+ /* copy descriptors */
f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
-
- acm->hs.in = usb_find_endpoint(acm_hs_function,
- f->hs_descriptors, &acm_hs_in_desc);
- acm->hs.out = usb_find_endpoint(acm_hs_function,
- f->hs_descriptors, &acm_hs_out_desc);
- acm->hs.notify = usb_find_endpoint(acm_hs_function,
- f->hs_descriptors, &acm_hs_notify_desc);
}
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index 8ee330a2ab58..02a02700b51d 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -279,7 +279,6 @@ struct f_audio {
/* endpoints handle full and/or high speeds */
struct usb_ep *out_ep;
- struct usb_endpoint_descriptor *out_desc;
spinlock_t lock;
struct f_audio_buf *copy_buf;
@@ -575,7 +574,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (intf == 1) {
if (alt == 1) {
- usb_ep_enable(out_ep, audio->out_desc);
+ usb_ep_enable(out_ep);
out_ep->driver_data = audio;
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
if (IS_ERR(audio->copy_buf))
@@ -677,6 +676,7 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (!ep)
goto fail;
audio->out_ep = ep;
+ audio->out_ep->desc = &as_out_ep_desc;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
@@ -776,7 +776,6 @@ int __init audio_bind_config(struct usb_configuration *c)
audio->card.func.set_alt = f_audio_set_alt;
audio->card.func.setup = f_audio_setup;
audio->card.func.disable = f_audio_disable;
- audio->out_desc = &as_out_ep_desc;
control_selector_init(audio);
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 544257a89ed2..3691a0cb9465 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -46,11 +46,6 @@
* and also means that a get_alt() method is required.
*/
-struct ecm_ep_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
- struct usb_endpoint_descriptor *notify;
-};
enum ecm_notify_state {
ECM_NOTIFY_NONE, /* don't notify */
@@ -64,11 +59,7 @@ struct f_ecm {
char ethaddr[14];
- struct ecm_ep_descs fs;
- struct ecm_ep_descs hs;
-
struct usb_ep *notify;
- struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
u8 notify_state;
bool is_open;
@@ -86,10 +77,12 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ecm_bitrate(struct usb_gadget *g)
{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
- return 19 * 64 * 1 * 1000 * 8;
+ return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
@@ -219,8 +212,10 @@ static struct usb_descriptor_header *ecm_fs_function[] = {
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
+
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &fs_ecm_notify_desc,
+
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
@@ -240,6 +235,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
+
static struct usb_endpoint_descriptor hs_ecm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -264,8 +260,10 @@ static struct usb_descriptor_header *ecm_hs_function[] = {
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
+
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &hs_ecm_notify_desc,
+
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
@@ -274,6 +272,76 @@ static struct usb_descriptor_header *ecm_hs_function[] = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
+ .bLength = sizeof ss_ecm_intr_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ecm_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
+ .bLength = sizeof ss_ecm_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *ecm_ss_function[] = {
+ /* CDC ECM control descriptors */
+ (struct usb_descriptor_header *) &ecm_control_intf,
+ (struct usb_descriptor_header *) &ecm_header_desc,
+ (struct usb_descriptor_header *) &ecm_union_desc,
+ (struct usb_descriptor_header *) &ecm_desc,
+
+ /* NOTE: status endpoint might need to be removed */
+ (struct usb_descriptor_header *) &ss_ecm_notify_desc,
+ (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
+
+ /* data interface, altsettings 0 and 1 */
+ (struct usb_descriptor_header *) &ecm_data_nop_intf,
+ (struct usb_descriptor_header *) &ecm_data_intf,
+ (struct usb_descriptor_header *) &ss_ecm_in_desc,
+ (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_ecm_out_desc,
+ (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
static struct usb_string ecm_string_defs[] = {
@@ -464,13 +532,13 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (ecm->notify->driver_data) {
VDBG(cdev, "reset ecm control %d\n", intf);
usb_ep_disable(ecm->notify);
- } else {
+ }
+ if (!(ecm->notify->desc)) {
VDBG(cdev, "init ecm ctrl %d\n", intf);
- ecm->notify_desc = ep_choose(cdev->gadget,
- ecm->hs.notify,
- ecm->fs.notify);
+ if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
+ goto fail;
}
- usb_ep_enable(ecm->notify, ecm->notify_desc);
+ usb_ep_enable(ecm->notify);
ecm->notify->driver_data = ecm;
/* Data interface has two altsettings, 0 and 1 */
@@ -483,12 +551,17 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gether_disconnect(&ecm->port);
}
- if (!ecm->port.in) {
+ if (!ecm->port.in_ep->desc ||
+ !ecm->port.out_ep->desc) {
DBG(cdev, "init ecm\n");
- ecm->port.in = ep_choose(cdev->gadget,
- ecm->hs.in, ecm->fs.in);
- ecm->port.out = ep_choose(cdev->gadget,
- ecm->hs.out, ecm->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ ecm->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ ecm->port.out_ep)) {
+ ecm->port.in_ep->desc = NULL;
+ ecm->port.out_ep->desc = NULL;
+ goto fail;
+ }
}
/* CDC Ethernet only sends data in non-default altsettings.
@@ -549,7 +622,7 @@ static void ecm_disable(struct usb_function *f)
if (ecm->notify->driver_data) {
usb_ep_disable(ecm->notify);
ecm->notify->driver_data = NULL;
- ecm->notify_desc = NULL;
+ ecm->notify->desc = NULL;
}
}
@@ -665,13 +738,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->descriptors)
goto fail;
- ecm->fs.in = usb_find_endpoint(ecm_fs_function,
- f->descriptors, &fs_ecm_in_desc);
- ecm->fs.out = usb_find_endpoint(ecm_fs_function,
- f->descriptors, &fs_ecm_out_desc);
- ecm->fs.notify = usb_find_endpoint(ecm_fs_function,
- f->descriptors, &fs_ecm_notify_desc);
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -688,13 +754,20 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
f->hs_descriptors = usb_copy_descriptors(ecm_hs_function);
if (!f->hs_descriptors)
goto fail;
+ }
- ecm->hs.in = usb_find_endpoint(ecm_hs_function,
- f->hs_descriptors, &hs_ecm_in_desc);
- ecm->hs.out = usb_find_endpoint(ecm_hs_function,
- f->hs_descriptors, &hs_ecm_out_desc);
- ecm->hs.notify = usb_find_endpoint(ecm_hs_function,
- f->hs_descriptors, &hs_ecm_notify_desc);
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_ecm_in_desc.bEndpointAddress =
+ fs_ecm_in_desc.bEndpointAddress;
+ ss_ecm_out_desc.bEndpointAddress =
+ fs_ecm_out_desc.bEndpointAddress;
+ ss_ecm_notify_desc.bEndpointAddress =
+ fs_ecm_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(ecm_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
}
/* NOTE: all that is done without knowing or caring about
@@ -706,6 +779,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm->port.close = ecm_close;
DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
@@ -714,6 +788,8 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
fail:
if (f->descriptors)
usb_free_descriptors(f->descriptors);
+ if (f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
if (ecm->notify_req) {
kfree(ecm->notify_req->buf);
@@ -723,9 +799,9 @@ fail:
/* we might as well release our claims on endpoints */
if (ecm->notify)
ecm->notify->driver_data = NULL;
- if (ecm->port.out)
+ if (ecm->port.out_ep->desc)
ecm->port.out_ep->driver_data = NULL;
- if (ecm->port.in)
+ if (ecm->port.in_ep->desc)
ecm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -740,6 +816,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
DBG(c->cdev, "ecm unbind\n");
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
index b3c304290150..046c6d0e6960 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -35,17 +35,9 @@
* Ethernet link.
*/
-struct eem_ep_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
-};
-
struct f_eem {
struct gether port;
u8 ctrl_id;
-
- struct eem_ep_descs fs;
- struct eem_ep_descs hs;
};
static inline struct f_eem *func_to_eem(struct usb_function *f)
@@ -123,6 +115,45 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
+ .bLength = sizeof eem_ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *eem_ss_function[] __initdata = {
+ /* CDC EEM control descriptors */
+ (struct usb_descriptor_header *) &eem_intf,
+ (struct usb_descriptor_header *) &eem_ss_in_desc,
+ (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &eem_ss_out_desc,
+ (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
static struct usb_string eem_string_defs[] = {
@@ -176,12 +207,16 @@ static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gether_disconnect(&eem->port);
}
- if (!eem->port.in) {
+ if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
DBG(cdev, "init eem\n");
- eem->port.in = ep_choose(cdev->gadget,
- eem->hs.in, eem->fs.in);
- eem->port.out = ep_choose(cdev->gadget,
- eem->hs.out, eem->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ eem->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ eem->port.out_ep)) {
+ eem->port.in_ep->desc = NULL;
+ eem->port.out_ep->desc = NULL;
+ goto fail;
+ }
}
/* zlps should not occur because zero-length EEM packets
@@ -253,11 +288,6 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->descriptors)
goto fail;
- eem->fs.in = usb_find_endpoint(eem_fs_function,
- f->descriptors, &eem_fs_in_desc);
- eem->fs.out = usb_find_endpoint(eem_fs_function,
- f->descriptors, &eem_fs_out_desc);
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -272,14 +302,22 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
f->hs_descriptors = usb_copy_descriptors(eem_hs_function);
if (!f->hs_descriptors)
goto fail;
+ }
+
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ eem_ss_in_desc.bEndpointAddress =
+ eem_fs_in_desc.bEndpointAddress;
+ eem_ss_out_desc.bEndpointAddress =
+ eem_fs_out_desc.bEndpointAddress;
- eem->hs.in = usb_find_endpoint(eem_hs_function,
- f->hs_descriptors, &eem_hs_in_desc);
- eem->hs.out = usb_find_endpoint(eem_hs_function,
- f->hs_descriptors, &eem_hs_out_desc);
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(eem_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
}
DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
eem->port.in_ep->name, eem->port.out_ep->name);
return 0;
@@ -287,11 +325,13 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
fail:
if (f->descriptors)
usb_free_descriptors(f->descriptors);
+ if (f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
/* we might as well release our claims on endpoints */
- if (eem->port.out)
+ if (eem->port.out_ep->desc)
eem->port.out_ep->driver_data = NULL;
- if (eem->port.in)
+ if (eem->port.in_ep->desc)
eem->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -306,6 +346,8 @@ eem_unbind(struct usb_configuration *c, struct usb_function *f)
DBG(c->cdev, "eem unbind\n");
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 19fffccc370d..c161a9aaeb7e 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -1544,7 +1544,8 @@ static int ffs_func_eps_enable(struct ffs_function *func)
ds = ep->descs[ep->descs[1] ? 1 : 0];
ep->ep->driver_data = ep;
- ret = usb_ep_enable(ep->ep, ds);
+ ep->ep->desc = ds;
+ ret = usb_ep_enable(ep->ep);
if (likely(!ret)) {
epfile->ep = ep;
epfile->in = usb_endpoint_dir_in(ds);
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 598e7e2ab80c..403a48bcf560 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -59,8 +59,6 @@ struct f_hidg {
struct cdev cdev;
struct usb_function func;
struct usb_ep *in_ep;
- struct usb_endpoint_descriptor *fs_in_ep_desc;
- struct usb_endpoint_descriptor *hs_in_ep_desc;
};
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
@@ -416,7 +414,6 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_hidg *hidg = func_to_hidg(f);
- const struct usb_endpoint_descriptor *ep_desc;
int status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
@@ -426,9 +423,13 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (hidg->in_ep->driver_data != NULL)
usb_ep_disable(hidg->in_ep);
- ep_desc = ep_choose(f->config->cdev->gadget,
- hidg->hs_in_ep_desc, hidg->fs_in_ep_desc);
- status = usb_ep_enable(hidg->in_ep, ep_desc);
+ status = config_ep_by_speed(f->config->cdev->gadget, f,
+ hidg->in_ep);
+ if (status) {
+ ERROR(cdev, "config_ep_by_speed FAILED!\n");
+ goto fail;
+ }
+ status = usb_ep_enable(hidg->in_ep);
if (status < 0) {
ERROR(cdev, "Enable endpoint FAILED!\n");
goto fail;
@@ -498,21 +499,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->descriptors)
goto fail;
- hidg->fs_in_ep_desc = usb_find_endpoint(hidg_fs_descriptors,
- f->descriptors,
- &hidg_fs_in_ep_desc);
-
if (gadget_is_dualspeed(c->cdev->gadget)) {
hidg_hs_in_ep_desc.bEndpointAddress =
hidg_fs_in_ep_desc.bEndpointAddress;
f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
if (!f->hs_descriptors)
goto fail;
- hidg->hs_in_ep_desc = usb_find_endpoint(hidg_hs_descriptors,
- f->hs_descriptors,
- &hidg_hs_in_ep_desc);
- } else {
- hidg->hs_in_ep_desc = NULL;
}
mutex_init(&hidg->lock);
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index b37960f9e753..ca660d40b11a 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -118,6 +118,49 @@ static struct usb_descriptor_header *hs_loopback_descs[] = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_loop_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_endpoint_descriptor ss_loop_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_descriptor_header *ss_loopback_descs[] = {
+ (struct usb_descriptor_header *) &loopback_intf,
+ (struct usb_descriptor_header *) &ss_loop_source_desc,
+ (struct usb_descriptor_header *) &ss_loop_source_comp_desc,
+ (struct usb_descriptor_header *) &ss_loop_sink_desc,
+ (struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
+ NULL,
+};
+
/* function-specific strings: */
static struct usb_string strings_loopback[] = {
@@ -175,8 +218,18 @@ autoconf_fail:
f->hs_descriptors = hs_loopback_descs;
}
+ /* support super speed hardware */
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_loop_source_desc.bEndpointAddress =
+ fs_loop_source_desc.bEndpointAddress;
+ ss_loop_sink_desc.bEndpointAddress =
+ fs_loop_sink_desc.bEndpointAddress;
+ f->ss_descriptors = ss_loopback_descs;
+ }
+
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ (gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
@@ -250,26 +303,27 @@ static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
int result = 0;
- const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
struct usb_request *req;
unsigned i;
- src = ep_choose(cdev->gadget,
- &hs_loop_source_desc, &fs_loop_source_desc);
- sink = ep_choose(cdev->gadget,
- &hs_loop_sink_desc, &fs_loop_sink_desc);
-
/* one endpoint writes data back IN to the host */
ep = loop->in_ep;
- result = usb_ep_enable(ep, src);
+ result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
+ if (result)
+ return result;
+ result = usb_ep_enable(ep);
if (result < 0)
return result;
ep->driver_data = loop;
/* one endpoint just reads OUT packets */
ep = loop->out_ep;
- result = usb_ep_enable(ep, sink);
+ result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
+ if (result)
+ goto fail0;
+
+ result = usb_ep_enable(ep);
if (result < 0) {
fail0:
ep = loop->in_ep;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index efb58f9f5aa9..5b9339582007 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2324,18 +2324,6 @@ static int get_next_command(struct fsg_common *common)
/*-------------------------------------------------------------------------*/
-static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep,
- const struct usb_endpoint_descriptor *d)
-{
- int rc;
-
- ep->driver_data = common;
- rc = usb_ep_enable(ep, d);
- if (rc)
- ERROR(common, "can't enable %s, result %d\n", ep->name, rc);
- return rc;
-}
-
static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
struct usb_request **preq)
{
@@ -2349,7 +2337,6 @@ static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
/* Reset interface setting and re-init endpoint state (toggle etc). */
static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
{
- const struct usb_endpoint_descriptor *d;
struct fsg_dev *fsg;
int i, rc = 0;
@@ -2396,20 +2383,26 @@ reset:
fsg = common->fsg;
/* Enable the endpoints */
- d = fsg_ep_desc(common->gadget,
- &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
- rc = enable_endpoint(common, fsg->bulk_in, d);
+ rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_in);
if (rc)
goto reset;
+ fsg->bulk_in->driver_data = common;
fsg->bulk_in_enabled = 1;
- d = fsg_ep_desc(common->gadget,
- &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
- rc = enable_endpoint(common, fsg->bulk_out, d);
+ rc = config_ep_by_speed(common->gadget, &(fsg->function),
+ fsg->bulk_out);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_out);
if (rc)
goto reset;
+ fsg->bulk_out->driver_data = common;
fsg->bulk_out_enabled = 1;
- common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
+ common->bulk_out_maxpacket =
+ le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index 86902a60bcdb..ae69ed7e6b99 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -48,12 +48,6 @@
#define NCM_NDP_HDR_CRC 0x01000000
#define NCM_NDP_HDR_NOCRC 0x00000000
-struct ncm_ep_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
- struct usb_endpoint_descriptor *notify;
-};
-
enum ncm_notify_state {
NCM_NOTIFY_NONE, /* don't notify */
NCM_NOTIFY_CONNECT, /* issue CONNECT next */
@@ -66,11 +60,7 @@ struct f_ncm {
char ethaddr[14];
- struct ncm_ep_descs fs;
- struct ncm_ep_descs hs;
-
struct usb_ep *notify;
- struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
u8 notify_state;
bool is_open;
@@ -802,13 +792,14 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (ncm->notify->driver_data) {
DBG(cdev, "reset ncm control %d\n", intf);
usb_ep_disable(ncm->notify);
- } else {
+ }
+
+ if (!(ncm->notify->desc)) {
DBG(cdev, "init ncm ctrl %d\n", intf);
- ncm->notify_desc = ep_choose(cdev->gadget,
- ncm->hs.notify,
- ncm->fs.notify);
+ if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
+ goto fail;
}
- usb_ep_enable(ncm->notify, ncm->notify_desc);
+ usb_ep_enable(ncm->notify);
ncm->notify->driver_data = ncm;
/* Data interface has two altsettings, 0 and 1 */
@@ -829,14 +820,17 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt == 1) {
struct net_device *net;
- if (!ncm->port.in) {
+ if (!ncm->port.in_ep->desc ||
+ !ncm->port.out_ep->desc) {
DBG(cdev, "init ncm\n");
- ncm->port.in = ep_choose(cdev->gadget,
- ncm->hs.in,
- ncm->fs.in);
- ncm->port.out = ep_choose(cdev->gadget,
- ncm->hs.out,
- ncm->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ ncm->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ ncm->port.out_ep)) {
+ ncm->port.in_ep->desc = NULL;
+ ncm->port.out_ep->desc = NULL;
+ goto fail;
+ }
}
/* TODO */
@@ -1111,7 +1105,7 @@ static void ncm_disable(struct usb_function *f)
if (ncm->notify->driver_data) {
usb_ep_disable(ncm->notify);
ncm->notify->driver_data = NULL;
- ncm->notify_desc = NULL;
+ ncm->notify->desc = NULL;
}
}
@@ -1228,13 +1222,6 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->descriptors)
goto fail;
- ncm->fs.in = usb_find_endpoint(ncm_fs_function,
- f->descriptors, &fs_ncm_in_desc);
- ncm->fs.out = usb_find_endpoint(ncm_fs_function,
- f->descriptors, &fs_ncm_out_desc);
- ncm->fs.notify = usb_find_endpoint(ncm_fs_function,
- f->descriptors, &fs_ncm_notify_desc);
-
/*
* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -1252,13 +1239,6 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->hs_descriptors = usb_copy_descriptors(ncm_hs_function);
if (!f->hs_descriptors)
goto fail;
-
- ncm->hs.in = usb_find_endpoint(ncm_hs_function,
- f->hs_descriptors, &hs_ncm_in_desc);
- ncm->hs.out = usb_find_endpoint(ncm_hs_function,
- f->hs_descriptors, &hs_ncm_out_desc);
- ncm->hs.notify = usb_find_endpoint(ncm_hs_function,
- f->hs_descriptors, &hs_ncm_notify_desc);
}
/*
@@ -1288,9 +1268,9 @@ fail:
/* we might as well release our claims on endpoints */
if (ncm->notify)
ncm->notify->driver_data = NULL;
- if (ncm->port.out)
+ if (ncm->port.out_ep->desc)
ncm->port.out_ep->driver_data = NULL;
- if (ncm->port.in)
+ if (ncm->port.in_ep->desc)
ncm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
index 8f8c64371475..394502abeb96 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/f_obex.c
@@ -39,20 +39,12 @@
* ready to handle the commands.
*/
-struct obex_ep_descs {
- struct usb_endpoint_descriptor *obex_in;
- struct usb_endpoint_descriptor *obex_out;
-};
-
struct f_obex {
struct gserial port;
u8 ctrl_id;
u8 data_id;
u8 port_num;
u8 can_activate;
-
- struct obex_ep_descs fs;
- struct obex_ep_descs hs;
};
static inline struct f_obex *func_to_obex(struct usb_function *f)
@@ -227,12 +219,16 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gserial_disconnect(&obex->port);
}
- if (!obex->port.in_desc) {
+ if (!obex->port.in->desc || !obex->port.out->desc) {
DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
- obex->port.in_desc = ep_choose(cdev->gadget,
- obex->hs.obex_in, obex->fs.obex_in);
- obex->port.out_desc = ep_choose(cdev->gadget,
- obex->hs.obex_out, obex->fs.obex_out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ obex->port.in) ||
+ config_ep_by_speed(cdev->gadget, f,
+ obex->port.out)) {
+ obex->port.out->desc = NULL;
+ obex->port.in->desc = NULL;
+ goto fail;
+ }
}
if (alt == 1) {
@@ -346,11 +342,6 @@ obex_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(fs_function);
- obex->fs.obex_in = usb_find_endpoint(fs_function,
- f->descriptors, &obex_fs_ep_in_desc);
- obex->fs.obex_out = usb_find_endpoint(fs_function,
- f->descriptors, &obex_fs_ep_out_desc);
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -364,11 +355,6 @@ obex_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(hs_function);
-
- obex->hs.obex_in = usb_find_endpoint(hs_function,
- f->hs_descriptors, &obex_hs_ep_in_desc);
- obex->hs.obex_out = usb_find_endpoint(hs_function,
- f->hs_descriptors, &obex_hs_ep_out_desc);
}
/* Avoid letting this gadget enumerate until the userspace
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index f22fc685ddfd..8f8d3f6cd89e 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -428,17 +428,16 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
spin_lock(&port->lock);
__pn_reset(f);
if (alt == 1) {
- struct usb_endpoint_descriptor *out, *in;
int i;
- out = ep_choose(gadget,
- &pn_hs_sink_desc,
- &pn_fs_sink_desc);
- in = ep_choose(gadget,
- &pn_hs_source_desc,
- &pn_fs_source_desc);
- usb_ep_enable(fp->out_ep, out);
- usb_ep_enable(fp->in_ep, in);
+ if (config_ep_by_speed(gadget, f, fp->in_ep) ||
+ config_ep_by_speed(gadget, f, fp->out_ep)) {
+ fp->in_ep->desc = NULL;
+ fp->out_ep->desc = NULL;
+ return -EINVAL;
+ }
+ usb_ep_enable(fp->out_ep);
+ usb_ep_enable(fp->in_ep);
port->usb = fp;
fp->out_ep->driver_data = fp;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index fa12ec8364ef..8f3eae90919f 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -76,23 +76,13 @@
* - MS-Windows drivers sometimes emit undocumented requests.
*/
-struct rndis_ep_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
- struct usb_endpoint_descriptor *notify;
-};
-
struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
int config;
- struct rndis_ep_descs fs;
- struct rndis_ep_descs hs;
-
struct usb_ep *notify;
- struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
atomic_t notify_count;
};
@@ -105,10 +95,12 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */
static unsigned int bitrate(struct usb_gadget *g)
{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
- return 19 * 64 * 1 * 1000 * 8;
+ return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
@@ -226,6 +218,7 @@ static struct usb_endpoint_descriptor fs_out_desc = {
static struct usb_descriptor_header *eth_fs_function[] = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
+
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
@@ -233,6 +226,7 @@ static struct usb_descriptor_header *eth_fs_function[] = {
(struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &fs_notify_desc,
+
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &fs_in_desc,
@@ -251,6 +245,7 @@ static struct usb_endpoint_descriptor hs_notify_desc = {
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
+
static struct usb_endpoint_descriptor hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -271,6 +266,7 @@ static struct usb_endpoint_descriptor hs_out_desc = {
static struct usb_descriptor_header *eth_hs_function[] = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
+
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
@@ -278,6 +274,7 @@ static struct usb_descriptor_header *eth_hs_function[] = {
(struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &hs_notify_desc,
+
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &hs_in_desc,
@@ -285,6 +282,76 @@ static struct usb_descriptor_header *eth_hs_function[] = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+ .bLength = sizeof ss_intr_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *eth_ss_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &ss_notify_desc,
+ (struct usb_descriptor_header *) &ss_intr_comp_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &ss_in_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_out_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
static struct usb_string rndis_string_defs[] = {
@@ -484,13 +551,13 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (rndis->notify->driver_data) {
VDBG(cdev, "reset rndis control %d\n", intf);
usb_ep_disable(rndis->notify);
- } else {
+ }
+ if (!rndis->notify->desc) {
VDBG(cdev, "init rndis ctrl %d\n", intf);
- rndis->notify_desc = ep_choose(cdev->gadget,
- rndis->hs.notify,
- rndis->fs.notify);
+ if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
+ goto fail;
}
- usb_ep_enable(rndis->notify, rndis->notify_desc);
+ usb_ep_enable(rndis->notify);
rndis->notify->driver_data = rndis;
} else if (intf == rndis->data_id) {
@@ -501,12 +568,16 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gether_disconnect(&rndis->port);
}
- if (!rndis->port.in) {
+ if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
DBG(cdev, "init rndis\n");
- rndis->port.in = ep_choose(cdev->gadget,
- rndis->hs.in, rndis->fs.in);
- rndis->port.out = ep_choose(cdev->gadget,
- rndis->hs.out, rndis->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f,
+ rndis->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ rndis->port.out_ep)) {
+ rndis->port.in_ep->desc = NULL;
+ rndis->port.out_ep->desc = NULL;
+ goto fail;
+ }
}
/* Avoid ZLPs; they can be troublesome. */
@@ -662,13 +733,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->descriptors)
goto fail;
- rndis->fs.in = usb_find_endpoint(eth_fs_function,
- f->descriptors, &fs_in_desc);
- rndis->fs.out = usb_find_endpoint(eth_fs_function,
- f->descriptors, &fs_out_desc);
- rndis->fs.notify = usb_find_endpoint(eth_fs_function,
- f->descriptors, &fs_notify_desc);
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -683,16 +747,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
-
if (!f->hs_descriptors)
goto fail;
+ }
- rndis->hs.in = usb_find_endpoint(eth_hs_function,
- f->hs_descriptors, &hs_in_desc);
- rndis->hs.out = usb_find_endpoint(eth_hs_function,
- f->hs_descriptors, &hs_out_desc);
- rndis->hs.notify = usb_find_endpoint(eth_hs_function,
- f->hs_descriptors, &hs_notify_desc);
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ ss_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+ ss_notify_desc.bEndpointAddress =
+ fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
}
rndis->port.open = rndis_open;
@@ -719,12 +789,15 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
*/
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
fail:
+ if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
+ usb_free_descriptors(f->ss_descriptors);
if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
usb_free_descriptors(f->hs_descriptors);
if (f->descriptors)
@@ -738,9 +811,9 @@ fail:
/* we might as well release our claims on endpoints */
if (rndis->notify)
rndis->notify->driver_data = NULL;
- if (rndis->port.out)
+ if (rndis->port.out_ep->desc)
rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in)
+ if (rndis->port.in_ep->desc)
rndis->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -756,6 +829,8 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f)
rndis_deregister(rndis->config);
rndis_exit();
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 490b00b01a7d..91fdf790ed20 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -27,18 +27,10 @@
* if you can arrange appropriate host side drivers.
*/
-struct gser_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
-};
-
struct f_gser {
struct gserial port;
u8 data_id;
u8 port_num;
-
- struct gser_descs fs;
- struct gser_descs hs;
};
static inline struct f_gser *func_to_gser(struct usb_function *f)
@@ -136,12 +128,15 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (gser->port.in->driver_data) {
DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
gserial_disconnect(&gser->port);
- } else {
+ }
+ if (!gser->port.in->desc || !gser->port.out->desc) {
DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
- gser->port.in_desc = ep_choose(cdev->gadget,
- gser->hs.in, gser->fs.in);
- gser->port.out_desc = ep_choose(cdev->gadget,
- gser->hs.out, gser->fs.out);
+ if (!config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
+ !config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
+ gser->port.in->desc = NULL;
+ gser->port.out->desc = NULL;
+ return -EINVAL;
+ }
}
gserial_connect(&gser->port, gser->port_num);
return 0;
@@ -193,12 +188,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(gser_fs_function);
- gser->fs.in = usb_find_endpoint(gser_fs_function,
- f->descriptors, &gser_fs_in_desc);
- gser->fs.out = usb_find_endpoint(gser_fs_function,
- f->descriptors, &gser_fs_out_desc);
-
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -211,11 +200,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
-
- gser->hs.in = usb_find_endpoint(gser_hs_function,
- f->hs_descriptors, &gser_hs_in_desc);
- gser->hs.out = usb_find_endpoint(gser_hs_function,
- f->hs_descriptors, &gser_hs_out_desc);
}
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index e403a534dd55..e18b4f520951 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -131,6 +131,49 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_endpoint_descriptor ss_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
+ .bLength = USB_DT_SS_EP_COMP_SIZE,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_descriptor_header *ss_source_sink_descs[] = {
+ (struct usb_descriptor_header *) &source_sink_intf,
+ (struct usb_descriptor_header *) &ss_source_desc,
+ (struct usb_descriptor_header *) &ss_source_comp_desc,
+ (struct usb_descriptor_header *) &ss_sink_desc,
+ (struct usb_descriptor_header *) &ss_sink_comp_desc,
+ NULL,
+};
+
/* function-specific strings: */
static struct usb_string strings_sourcesink[] = {
@@ -187,8 +230,18 @@ autoconf_fail:
f->hs_descriptors = hs_source_sink_descs;
}
+ /* support super speed hardware */
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_source_desc.bEndpointAddress =
+ fs_source_desc.bEndpointAddress;
+ ss_sink_desc.bEndpointAddress =
+ fs_sink_desc.bEndpointAddress;
+ f->ss_descriptors = ss_source_sink_descs;
+ }
+
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ (gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, ss->in_ep->name, ss->out_ep->name);
return 0;
}
@@ -343,15 +396,14 @@ static int
enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
{
int result = 0;
- const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
- src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
- sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
-
/* one endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
- result = usb_ep_enable(ep, src);
+ result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+ if (result)
+ return result;
+ result = usb_ep_enable(ep);
if (result < 0)
return result;
ep->driver_data = ss;
@@ -367,7 +419,10 @@ fail:
/* one endpoint reads (sinks) anything OUT (from the host) */
ep = ss->out_ep;
- result = usb_ep_enable(ep, sink);
+ result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+ if (result)
+ goto fail;
+ result = usb_ep_enable(ep);
if (result < 0)
goto fail;
ep->driver_data = ss;
@@ -435,6 +490,8 @@ static int sourcesink_setup(struct usb_configuration *c,
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ req->length = USB_BUFSIZ;
+
/* composite driver infrastructure handles everything except
* the two control test requests.
*/
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
index 8675ca415329..3dc53754ab60 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -57,18 +57,10 @@
* caring about specific product and vendor IDs.
*/
-struct geth_descs {
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
-};
-
struct f_gether {
struct gether port;
char ethaddr[14];
-
- struct geth_descs fs;
- struct geth_descs hs;
};
static inline struct f_gether *func_to_geth(struct usb_function *f)
@@ -209,6 +201,46 @@ static struct usb_descriptor_header *hs_eth_function[] __initdata = {
NULL,
};
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_subset_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_subset_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc __initdata = {
+ .bLength = sizeof ss_subset_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *ss_eth_function[] __initdata = {
+ (struct usb_descriptor_header *) &subset_data_intf,
+ (struct usb_descriptor_header *) &mdlm_header_desc,
+ (struct usb_descriptor_header *) &mdlm_desc,
+ (struct usb_descriptor_header *) &mdlm_detail_desc,
+ (struct usb_descriptor_header *) &ether_desc,
+ (struct usb_descriptor_header *) &ss_subset_in_desc,
+ (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_subset_out_desc,
+ (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
static struct usb_string geth_string_defs[] = {
@@ -243,10 +275,12 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
DBG(cdev, "init + activate cdc subset\n");
- geth->port.in = ep_choose(cdev->gadget,
- geth->hs.in, geth->fs.in);
- geth->port.out = ep_choose(cdev->gadget,
- geth->hs.out, geth->fs.out);
+ if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
+ geth->port.in_ep->desc = NULL;
+ geth->port.out_ep->desc = NULL;
+ return -EINVAL;
+ }
net = gether_connect(&geth->port);
return IS_ERR(net) ? PTR_ERR(net) : 0;
@@ -296,12 +330,8 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(fs_eth_function);
-
- geth->fs.in = usb_find_endpoint(fs_eth_function,
- f->descriptors, &fs_subset_in_desc);
- geth->fs.out = usb_find_endpoint(fs_eth_function,
- f->descriptors, &fs_subset_out_desc);
-
+ if (!f->descriptors)
+ goto fail;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -315,11 +345,20 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(hs_eth_function);
+ if (!f->hs_descriptors)
+ goto fail;
+ }
- geth->hs.in = usb_find_endpoint(hs_eth_function,
- f->hs_descriptors, &hs_subset_in_desc);
- geth->hs.out = usb_find_endpoint(hs_eth_function,
- f->hs_descriptors, &hs_subset_out_desc);
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_subset_in_desc.bEndpointAddress =
+ fs_subset_in_desc.bEndpointAddress;
+ ss_subset_out_desc.bEndpointAddress =
+ fs_subset_out_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(ss_eth_function);
+ if (!f->ss_descriptors)
+ goto fail;
}
/* NOTE: all that is done without knowing or caring about
@@ -328,15 +367,21 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
*/
DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
fail:
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+ if (f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
+
/* we might as well release our claims on endpoints */
- if (geth->port.out)
+ if (geth->port.out_ep->desc)
geth->port.out_ep->driver_data = NULL;
- if (geth->port.in)
+ if (geth->port.in_ep->desc)
geth->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -347,6 +392,8 @@ fail:
static void
geth_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index be446b7e7eaa..7a8b9aa4aea5 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -262,8 +262,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_CONNECTED)
return 0;
- if (uvc->video.ep)
- usb_ep_enable(uvc->video.ep, &uvc_streaming_ep);
+ if (uvc->video.ep) {
+ uvc->video.ep->desc = &uvc_streaming_ep;
+ usb_ep_enable(uvc->video.ep);
+ }
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
@@ -649,7 +651,7 @@ uvc_bind_config(struct usb_configuration *c,
if (ret)
kfree(uvc);
- return 0;
+ return ret;
error:
kfree(uvc);
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 0360f56221ea..639e14a2fd15 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -929,6 +929,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
case USB_DT_DEVICE:
VDBG(fsg, "get device descriptor\n");
+ device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
value = sizeof device_desc;
memcpy(req->buf, &device_desc, value);
break;
@@ -936,6 +937,11 @@ static int standard_setup_req(struct fsg_dev *fsg,
VDBG(fsg, "get device qualifier\n");
if (!gadget_is_dualspeed(fsg->gadget))
break;
+ /*
+ * Assume ep0 uses the same maxpacket value for both
+ * speeds
+ */
+ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
value = sizeof dev_qualifier;
memcpy(req->buf, &dev_qualifier, value);
break;
@@ -2713,7 +2719,8 @@ static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep,
int rc;
ep->driver_data = fsg;
- rc = usb_ep_enable(ep, d);
+ ep->desc = d;
+ rc = usb_ep_enable(ep);
if (rc)
ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc);
return rc;
@@ -3416,7 +3423,6 @@ static int __init fsg_bind(struct usb_gadget *gadget)
}
/* Fix up the descriptors */
- device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
device_desc.idVendor = cpu_to_le16(mod_data.vendor);
device_desc.idProduct = cpu_to_le16(mod_data.product);
device_desc.bcdDevice = cpu_to_le16(mod_data.release);
@@ -3430,9 +3436,6 @@ static int __init fsg_bind(struct usb_gadget *gadget)
if (gadget_is_dualspeed(gadget)) {
fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
- /* Assume ep0 uses the same maxpacket value for both speeds */
- dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
-
/* Assume endpoint addresses are the same for both speeds */
fsg_hs_bulk_in_desc.bEndpointAddress =
fsg_fs_bulk_in_desc.bEndpointAddress;
@@ -3486,6 +3489,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
}
INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+ INFO(fsg, "NOTE: This driver is deprecated. "
+ "Consider using g_mass_storage instead.\n");
INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 3a68e09309f7..3bf872e1ad39 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -1927,6 +1927,10 @@ static int qe_pullup(struct usb_gadget *gadget, int is_on)
return -ENOTSUPP;
}
+static int fsl_qe_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int fsl_qe_stop(struct usb_gadget_driver *driver);
+
/* defined in usb_gadget.h */
static struct usb_gadget_ops qe_gadget_ops = {
.get_frame = qe_get_frame,
@@ -1935,6 +1939,8 @@ static struct usb_gadget_ops qe_gadget_ops = {
.vbus_session = qe_vbus_session,
.vbus_draw = qe_vbus_draw,
.pullup = qe_pullup,
+ .start = fsl_qe_start,
+ .stop = fsl_qe_stop,
};
/*-------------------------------------------------------------------------
@@ -2320,7 +2326,7 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc)
/*-------------------------------------------------------------------------
Gadget driver probe and unregister.
--------------------------------------------------------------------------*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int fsl_qe_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
int retval;
@@ -2369,9 +2375,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
udc_controller->gadget.name, driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int fsl_qe_stop(struct usb_gadget_driver *driver)
{
struct qe_ep *loop_ep;
unsigned long flags;
@@ -2411,7 +2416,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/* udc structure's alloc and setup, include ep-param alloc */
static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev)
@@ -2662,11 +2666,17 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev)
if (ret)
goto err6;
+ ret = usb_add_gadget_udc(&ofdev->dev, &udc_controller->gadget);
+ if (ret)
+ goto err7;
+
dev_info(udc_controller->dev,
"%s USB controller initialized as device\n",
(udc_controller->soc_type == PORT_QE) ? "QE" : "CPM");
return 0;
+err7:
+ device_unregister(&udc_controller->gadget.dev);
err6:
free_irq(udc_controller->usb_irq, udc_controller);
err5:
@@ -2721,6 +2731,8 @@ static int __devexit qe_udc_remove(struct platform_device *ofdev)
if (!udc_controller)
return -ENODEV;
+ usb_del_gadget_udc(&udc_controller->gadget);
+
udc_controller->done = &done;
tasklet_disable(&udc_controller->rx_tasklet);
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 3e59035e6de8..de24a4233c25 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -1244,6 +1244,9 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int fsl_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int fsl_stop(struct usb_gadget_driver *driver);
/* defined in gadget.h */
static struct usb_gadget_ops fsl_gadget_ops = {
.get_frame = fsl_get_frame,
@@ -1252,6 +1255,8 @@ static struct usb_gadget_ops fsl_gadget_ops = {
.vbus_session = fsl_vbus_session,
.vbus_draw = fsl_vbus_draw,
.pullup = fsl_pullup,
+ .start = fsl_start,
+ .stop = fsl_stop,
};
/* Set protocol stall on ep0, protocol stall will automatically be cleared
@@ -1927,7 +1932,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
* Hook to gadget drivers
* Called by initialization code of gadget drivers
*----------------------------------------------------------------*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int fsl_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
int retval = -ENODEV;
@@ -1995,10 +2000,9 @@ out:
retval);
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
/* Disconnect from gadget driver */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int fsl_stop(struct usb_gadget_driver *driver)
{
struct fsl_ep *loop_ep;
unsigned long flags;
@@ -2041,7 +2045,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------
PROC File System Support
@@ -2590,9 +2593,16 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_unregister;
}
+
+ ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget);
+ if (ret)
+ goto err_del_udc;
+
create_proc_file();
return 0;
+err_del_udc:
+ dma_pool_destroy(udc_controller->td_pool);
err_unregister:
device_unregister(&udc_controller->gadget.dev);
err_free_irq:
@@ -2624,6 +2634,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
if (!udc_controller)
return -ENODEV;
+
+ usb_del_gadget_udc(&udc_controller->gadget);
udc_controller->done = &done;
fsl_udc_clk_release();
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index 763d462454b9..24a924330c81 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -767,56 +767,6 @@ static void fusb300_rdfifo(struct fusb300_ep *ep,
} while (!reg);
}
-/* write data to fifo */
-static void fusb300_wrfifo(struct fusb300_ep *ep,
- struct fusb300_request *req)
-{
- int i = 0;
- u8 *tmp;
- u32 data, reg;
- struct fusb300 *fusb300 = ep->fusb300;
-
- tmp = req->req.buf;
- req->req.actual = req->req.length;
-
- for (i = (req->req.length >> 2); i > 0; i--) {
- data = *tmp | *(tmp + 1) << 8 |
- *(tmp + 2) << 16 | *(tmp + 3) << 24;
-
- iowrite32(data, fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- tmp += 4;
- }
-
- switch (req->req.length % 4) {
- case 1:
- data = *tmp;
- iowrite32(data, fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- break;
- case 2:
- data = *tmp | *(tmp + 1) << 8;
- iowrite32(data, fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- break;
- case 3:
- data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
- iowrite32(data, fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- break;
- default:
- break;
- }
-
- do {
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
- reg &= FUSB300_IGR1_SYNF0_EMPTY_INT;
- if (i)
- printk(KERN_INFO"sync fifo is not empty!\n");
- i++;
- } while (!reg);
-}
-
static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep)
{
u8 value;
@@ -980,11 +930,6 @@ static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
} \
} while (0)
-static void fusb300_ep0_complete(struct usb_ep *ep,
- struct usb_request *req)
-{
-}
-
static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
{
u8 *p = (u8 *)ctrl;
@@ -1029,17 +974,6 @@ static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
return ret;
}
-static void fusb300_set_ep_bycnt(struct fusb300_ep *ep, u32 bycnt)
-{
- struct fusb300 *fusb300 = ep->fusb300;
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
-
- reg &= ~FUSB300_FFR_BYCNT;
- reg |= bycnt & FUSB300_FFR_BYCNT;
-
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
-}
-
static void done(struct fusb300_ep *ep, struct fusb300_request *req,
int status)
{
@@ -1063,8 +997,8 @@ static void done(struct fusb300_ep *ep, struct fusb300_request *req,
fusb300_set_cxdone(ep->fusb300);
}
-void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep,
- struct fusb300_request *req)
+static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d,
+ u32 len)
{
u32 value;
u32 reg;
@@ -1076,10 +1010,9 @@ void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep,
reg &= FUSB300_EPPRD0_H;
} while (reg);
- iowrite32((u32) req->req.buf, ep->fusb300->reg +
- FUSB300_OFFSET_EPPRD_W1(ep->epnum));
+ iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum));
- value = FUSB300_EPPRD0_BTC(req->req.length) | FUSB300_EPPRD0_H |
+ value = FUSB300_EPPRD0_BTC(len) | FUSB300_EPPRD0_H |
FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I;
iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum));
@@ -1116,13 +1049,12 @@ static void fusb300_set_idma(struct fusb300_ep *ep,
struct fusb300_request *req)
{
dma_addr_t d;
- u8 *tmp = NULL;
d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE);
if (dma_mapping_error(NULL, d)) {
- kfree(req->req.buf);
printk(KERN_DEBUG "dma_mapping_error\n");
+ return;
}
dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE);
@@ -1130,17 +1062,11 @@ static void fusb300_set_idma(struct fusb300_ep *ep,
fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0,
FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
- tmp = req->req.buf;
- req->req.buf = (u8 *)d;
-
- fusb300_fill_idma_prdtbl(ep, req);
+ fusb300_fill_idma_prdtbl(ep, d, req->req.length);
/* check idma is done */
fusb300_wait_idma_finished(ep);
- req->req.buf = tmp;
-
- if (d)
- dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE);
+ dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE);
}
static void in_ep_fifo_handler(struct fusb300_ep *ep)
@@ -1148,14 +1074,8 @@ static void in_ep_fifo_handler(struct fusb300_ep *ep)
struct fusb300_request *req = list_entry(ep->queue.next,
struct fusb300_request, queue);
- if (req->req.length) {
-#if 0
- fusb300_set_ep_bycnt(ep, req->req.length);
- fusb300_wrfifo(ep, req);
-#else
+ if (req->req.length)
fusb300_set_idma(ep, req);
-#endif
- }
done(ep, req, 0);
}
@@ -1500,7 +1420,7 @@ static void init_controller(struct fusb300 *fusb300)
/*------------------------------------------------------------------------*/
static struct fusb300 *the_controller;
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int fusb300_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct fusb300 *fusb300 = the_controller;
@@ -1544,9 +1464,8 @@ error:
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int fusb300_udc_stop(struct usb_gadget_driver *driver)
{
struct fusb300 *fusb300 = the_controller;
@@ -1562,7 +1481,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*--------------------------------------------------------------------------*/
static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
@@ -1572,12 +1490,15 @@ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
static struct usb_gadget_ops fusb300_gadget_ops = {
.pullup = fusb300_udc_pullup,
+ .start = fusb300_udc_start,
+ .stop = fusb300_udc_stop,
};
static int __exit fusb300_remove(struct platform_device *pdev)
{
struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev);
+ usb_del_gadget_udc(&fusb300->gadget);
iounmap(fusb300->reg);
free_irq(platform_get_irq(pdev, 0), fusb300);
@@ -1702,9 +1623,15 @@ static int __init fusb300_probe(struct platform_device *pdev)
goto clean_up3;
init_controller(fusb300);
+ ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget);
+ if (ret)
+ goto err_add_udc;
+
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
return 0;
+err_add_udc:
+ fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
clean_up3:
free_irq(ires->start, fusb300);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index ebf6970a10bf..704c2800ac00 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -162,6 +162,7 @@ static struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
.strings = gfs_dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = gfs_unbind,
.iProduct = DRIVER_DESC,
};
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index bcdac7c73e89..f3a83cd0ef50 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,150 +15,40 @@
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
-#ifdef CONFIG_USB_GADGET_NET2280
-#define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
-#else
-#define gadget_is_net2280(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_AMD5536UDC
-#define gadget_is_amd5536udc(g) !strcmp("amd5536udc", (g)->name)
-#else
-#define gadget_is_amd5536udc(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_DUMMY_HCD
-#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name)
-#else
-#define gadget_is_dummy(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_PXA25X
-#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name)
-#else
-#define gadget_is_pxa(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_GOKU
-#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name)
-#else
-#define gadget_is_goku(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_OMAP
-#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name)
-#else
-#define gadget_is_omap(g) 0
-#endif
-
-/* various unstable versions available */
-#ifdef CONFIG_USB_GADGET_PXA27X
-#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name)
-#else
-#define gadget_is_pxa27x(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_ATMEL_USBA
-#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name)
-#else
-#define gadget_is_atmel_usba(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_S3C2410
-#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name)
-#else
-#define gadget_is_s3c2410(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_AT91
-#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name)
-#else
-#define gadget_is_at91(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_IMX
-#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name)
-#else
-#define gadget_is_imx(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_FSL_USB2
-#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name)
-#else
-#define gadget_is_fsl_usb2(g) 0
-#endif
-
-/* Mentor high speed "dual role" controller, in peripheral role */
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name)
-#else
-#define gadget_is_musbhdrc(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_LANGWELL
-#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
-#else
-#define gadget_is_langwell(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_M66592
-#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name)
-#else
-#define gadget_is_m66592(g) 0
-#endif
-
-/* Freescale CPM/QE UDC SUPPORT */
-#ifdef CONFIG_USB_GADGET_FSL_QE
-#define gadget_is_fsl_qe(g) !strcmp("fsl_qe_udc", (g)->name)
-#else
-#define gadget_is_fsl_qe(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_CI13XXX_PCI
-#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
-#else
-#define gadget_is_ci13xxx_pci(g) 0
-#endif
-
-// CONFIG_USB_GADGET_SX2
-// CONFIG_USB_GADGET_AU1X00
-// ...
-
-#ifdef CONFIG_USB_GADGET_R8A66597
-#define gadget_is_r8a66597(g) !strcmp("r8a66597_udc", (g)->name)
-#else
-#define gadget_is_r8a66597(g) 0
-#endif
-
-#ifdef CONFIG_USB_S3C_HSOTG
-#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
-#else
-#define gadget_is_s3c_hsotg(g) 0
-#endif
-
-#ifdef CONFIG_USB_S3C_HSUDC
-#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
-#else
-#define gadget_is_s3c_hsudc(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_EG20T
-#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
-#else
-#define gadget_is_pch(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_CI13XXX_MSM
+/*
+ * NOTICE: the entries below are alphabetical and should be kept
+ * that way.
+ *
+ * Always be sure to add new entries to the correct position or
+ * accept the bashing later.
+ *
+ * If you have forgotten the alphabetical order let VIM/EMACS
+ * do that for you.
+ */
+#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
+#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
+#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
-#else
-#define gadget_is_ci13xxx_msm(g) 0
-#endif
-
-#ifdef CONFIG_USB_GADGET_RENESAS_USBHS
-#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
-#else
-#define gadget_is_renesas_usbhs(g) 0
-#endif
+#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
+#define gadget_is_dummy(g) (!strcmp("dummy_udc", (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))
+#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
+#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
+#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
+#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
+#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
+#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
+#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
+#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
+#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
+#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
+#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
+#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
+#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
+#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
/**
* usb_gadget_controller_number - support bcdDevice id convention
@@ -223,6 +113,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x29;
else if (gadget_is_s3c_hsudc(gadget))
return 0x30;
+ else if (gadget_is_net2272(gadget))
+ return 0x31;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 47b86b99d449..8b9220e128a7 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -537,14 +537,16 @@ static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
struct usb_ep *ep;
unsigned i;
- err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
+ dev->in_ep->desc = &bulk_in_desc;
+ err = usb_ep_enable(dev->in_ep);
if (err) {
ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
goto fail;
}
dev->in_ep->driver_data = dev;
- err = usb_ep_enable(dev->out_ep, &bulk_out_desc);
+ dev->out_ep->desc = &bulk_out_desc;
+ err = usb_ep_enable(dev->out_ep);
if (err) {
ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
goto fail;
@@ -693,6 +695,7 @@ static int gmidi_setup(struct usb_gadget *gadget,
switch (w_value >> 8) {
case USB_DT_DEVICE:
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
value = min(w_length, (u16) sizeof(device_desc));
memcpy(req->buf, &device_desc, value);
break;
@@ -1247,8 +1250,6 @@ autoconf_fail:
dev->req->complete = gmidi_setup_complete;
- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
gadget->ep0->driver_data = dev;
INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index bf6e11c758d5..7f87805cddc4 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -996,8 +996,14 @@ static int goku_get_frame(struct usb_gadget *_gadget)
return -EOPNOTSUPP;
}
+static int goku_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int goku_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops goku_ops = {
.get_frame = goku_get_frame,
+ .start = goku_start,
+ .stop = goku_stop,
// no remote wakeup
// not selfpowered
};
@@ -1344,7 +1350,7 @@ static struct goku_udc *the_controller;
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int goku_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct goku_udc *dev = the_controller;
@@ -1382,7 +1388,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
DBG(dev, "registered gadget driver '%s'\n", driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
static void
stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
@@ -1408,7 +1413,7 @@ stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
udc_enable(dev);
}
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int goku_stop(struct usb_gadget_driver *driver)
{
struct goku_udc *dev = the_controller;
unsigned long flags;
@@ -1429,8 +1434,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
DBG(dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/*-------------------------------------------------------------------------*/
@@ -1730,6 +1733,8 @@ static void goku_remove(struct pci_dev *pdev)
DBG(dev, "%s\n", __func__);
+ usb_del_gadget_udc(&dev->gadget);
+
BUG_ON(dev->driver);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
@@ -1854,6 +1859,10 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err;
}
dev->registered = 1;
+ retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ if (retval)
+ goto err;
+
return 0;
err:
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index 2523e54097bd..9fb575034a0e 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -255,6 +255,7 @@ static struct usb_composite_driver hidg_driver = {
.name = "g_hid",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(hid_unbind),
};
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index ade40066decf..692fd9b2248b 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -1237,9 +1237,14 @@ irq_handler_t intr_handler(int i)
*******************************************************************************
*/
+static int imx_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int imx_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops imx_udc_ops = {
.get_frame = imx_udc_get_frame,
.wakeup = imx_udc_wakeup,
+ .start = imx_udc_start,
+ .stop = imx_udc_stop,
};
static struct imx_udc_struct controller = {
@@ -1324,7 +1329,7 @@ static struct imx_udc_struct controller = {
* USB gadget driver functions
*******************************************************************************
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int imx_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct imx_udc_struct *imx_usb = &controller;
@@ -1368,9 +1373,8 @@ fail:
imx_usb->gadget.dev.driver = NULL;
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int imx_udc_stop(struct usb_gadget_driver *driver)
{
struct imx_udc_struct *imx_usb = &controller;
@@ -1394,7 +1398,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*******************************************************************************
* Module functions
@@ -1504,8 +1507,14 @@ static int __init imx_udc_probe(struct platform_device *pdev)
imx_usb->timer.function = handle_config;
imx_usb->timer.data = (unsigned long)imx_usb;
- return 0;
+ ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget);
+ if (ret)
+ goto fail4;
+ return 0;
+fail4:
+ for (i = 0; i < IMX_USB_NB_EP + 1; i++)
+ free_irq(imx_usb->usbd_int[i], imx_usb);
fail3:
clk_put(clk);
clk_disable(clk);
@@ -1525,6 +1534,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)
struct imxusb_platform_data *pdata = pdev->dev.platform_data;
int i;
+ usb_del_gadget_udc(&imx_usb->gadget);
imx_udc_disable(imx_usb);
del_timer(&imx_usb->timer);
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index a56876aaf76c..1b240990448f 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -832,14 +832,16 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
switch (data->dev->gadget->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
- value = usb_ep_enable (ep, &data->desc);
+ ep->desc = &data->desc;
+ value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
- value = usb_ep_enable (ep, &data->hs_desc);
+ ep->desc = &data->hs_desc;
+ value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
@@ -1345,7 +1347,7 @@ static void make_qualifier (struct dev_data *dev)
qual.bDeviceProtocol = desc->bDeviceProtocol;
/* assumes ep0 uses the same value for both speeds ... */
- qual.bMaxPacketSize0 = desc->bMaxPacketSize0;
+ qual.bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
qual.bNumConfigurations = 1;
qual.bRESERVED = 0;
@@ -1402,7 +1404,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
}
dev->state = STATE_DEV_CONNECTED;
- dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket;
INFO (dev, "connected\n");
event = next_event (dev, GADGETFS_CONNECT);
@@ -1430,6 +1431,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_DEVICE:
value = min (w_length, (u16) sizeof *dev->dev);
+ dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
req->buf = dev->dev;
break;
#ifdef CONFIG_USB_GADGET_DUALSPEED
@@ -1710,7 +1712,6 @@ gadgetfs_bind (struct usb_gadget *gadget)
set_gadget_data (gadget, dev);
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
- dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket;
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index 9cee88a43a73..a06e2c27b435 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -593,8 +593,8 @@ static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req)
/* ep0 */
dev_vdbg(&dev->pdev->dev, "%s-%s\n", ep->name, DIR_STRING(ep));
- dev_vdbg(&dev->pdev->dev, "ep_dqh[%d] addr: 0x%08x\n",
- i, (u32)&(dev->ep_dqh[i]));
+ dev_vdbg(&dev->pdev->dev, "ep_dqh[%d] addr: 0x%p\n",
+ i, &(dev->ep_dqh[i]));
bit_mask = is_in(ep) ?
(1 << (ep->ep_num + 16)) : (1 << (ep->ep_num));
@@ -1321,7 +1321,9 @@ 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);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops langwell_ops = {
@@ -1342,6 +1344,9 @@ 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,
};
@@ -1852,7 +1857,7 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup);
* the driver might get unbound.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int langwell_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct langwell_udc *dev = the_controller;
@@ -1914,11 +1919,9 @@ err_unbind:
dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-
/* unregister gadget driver */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int langwell_stop(struct usb_gadget_driver *driver)
{
struct langwell_udc *dev = the_controller;
unsigned long flags;
@@ -1965,8 +1968,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/*-------------------------------------------------------------------------*/
@@ -3270,7 +3271,7 @@ static int langwell_udc_probe(struct pci_dev *pdev,
/* allocate device dQH memory */
size = dev->ep_max * sizeof(struct langwell_dqh);
- dev_vdbg(&dev->pdev->dev, "orig size = %d\n", size);
+ dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size);
if (size < DQH_ALIGNMENT)
size = DQH_ALIGNMENT;
else if ((size % DQH_ALIGNMENT) != 0) {
@@ -3285,7 +3286,7 @@ static int langwell_udc_probe(struct pci_dev *pdev,
goto error;
}
dev->ep_dqh_size = size;
- dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %d\n", dev->ep_dqh_size);
+ dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %zd\n", dev->ep_dqh_size);
/* initialize ep0 status request structure */
dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL);
@@ -3373,6 +3374,10 @@ static int langwell_udc_probe(struct pci_dev *pdev,
if (retval)
goto error;
+ retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ if (retval)
+ goto error;
+
retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc);
if (retval)
goto error;
@@ -3403,6 +3408,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
+ usb_del_gadget_udc(&dev->gadget);
/* disable interrupt and set controller to stop state */
langwell_udc_stop(dev);
@@ -3464,7 +3470,7 @@ static int langwell_udc_resume(struct pci_dev *pdev)
/* allocate device dQH memory */
size = dev->ep_max * sizeof(struct langwell_dqh);
- dev_vdbg(&dev->pdev->dev, "orig size = %d\n", size);
+ dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size);
if (size < DQH_ALIGNMENT)
size = DQH_ALIGNMENT;
else if ((size % DQH_ALIGNMENT) != 0) {
@@ -3478,7 +3484,7 @@ static int langwell_udc_resume(struct pci_dev *pdev)
return -ENOMEM;
}
dev->ep_dqh_size = size;
- dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %d\n", dev->ep_dqh_size);
+ dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %zd\n", dev->ep_dqh_size);
/* create dTD dma_pool resource */
dev->dtd_pool = dma_pool_create("langwell_dtd",
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 084aa080a2d5..491f825ed5c9 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006-2007 Renesas Solutions Corp.
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
@@ -691,6 +691,7 @@ static void init_controller(struct m66592 *m66592)
static void disable_controller(struct m66592 *m66592)
{
+ m66592_bclr(m66592, M66592_UTST, M66592_TESTMODE);
if (!m66592->pdata->on_chip) {
m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
udelay(1);
@@ -780,7 +781,7 @@ static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
/* write fifo */
if (req->req.buf) {
if (size > 0)
- m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+ m66592_write_fifo(m66592, ep, buf, size);
if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
}
@@ -826,7 +827,7 @@ static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
/* write fifo */
if (req->req.buf) {
- m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+ m66592_write_fifo(m66592, ep, buf, size);
if ((size == 0)
|| ((size % ep->ep.maxpacket) != 0)
|| ((bufsize != ep->ep.maxpacket)
@@ -1048,10 +1049,30 @@ static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
{
+ u16 tmp;
+ int timeout = 3000;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- control_end(m66592, 1);
+ switch (le16_to_cpu(ctrl->wValue)) {
+ case USB_DEVICE_TEST_MODE:
+ control_end(m66592, 1);
+ /* Wait for the completion of status stage */
+ do {
+ tmp = m66592_read(m66592, M66592_INTSTS0) &
+ M66592_CTSQ;
+ udelay(1);
+ } while (tmp != M66592_CS_IDST || timeout-- > 0);
+
+ if (tmp == M66592_CS_IDST)
+ m66592_bset(m66592,
+ le16_to_cpu(ctrl->wIndex >> 8),
+ M66592_TESTMODE);
+ break;
+ default:
+ pipe_stall(m66592, 0);
+ break;
+ }
break;
case USB_RECIP_INTERFACE:
control_end(m66592, 1);
@@ -1454,7 +1475,7 @@ static struct usb_ep_ops m66592_ep_ops = {
/*-------------------------------------------------------------------------*/
static struct m66592 *the_controller;
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int m66592_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct m66592 *m66592 = the_controller;
@@ -1506,9 +1527,8 @@ error:
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int m66592_stop(struct usb_gadget_driver *driver)
{
struct m66592 *m66592 = the_controller;
unsigned long flags;
@@ -1533,7 +1553,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
m66592->driver = NULL;
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
static int m66592_get_frame(struct usb_gadget *_gadget)
@@ -1542,14 +1561,34 @@ static int m66592_get_frame(struct usb_gadget *_gadget)
return m66592_read(m66592, M66592_FRMNUM) & 0x03FF;
}
+static int m66592_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct m66592 *m66592 = gadget_to_m66592(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&m66592->lock, flags);
+ if (is_on)
+ m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
+ else
+ m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+ spin_unlock_irqrestore(&m66592->lock, flags);
+
+ return 0;
+}
+
static struct usb_gadget_ops m66592_gadget_ops = {
.get_frame = m66592_get_frame,
+ .start = m66592_start,
+ .stop = m66592_stop,
+ .pullup = m66592_pullup,
};
static int __exit m66592_remove(struct platform_device *pdev)
{
struct m66592 *m66592 = dev_get_drvdata(&pdev->dev);
+ usb_del_gadget_udc(&m66592->gadget);
+
del_timer_sync(&m66592->timer);
iounmap(m66592->reg);
free_irq(platform_get_irq(pdev, 0), m66592);
@@ -1691,9 +1730,16 @@ static int __init m66592_probe(struct platform_device *pdev)
init_controller(m66592);
+ ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget);
+ if (ret)
+ goto err_add_udc;
+
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
return 0;
+err_add_udc:
+ m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
+
clean_up3:
#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
index c3caf1ac73ce..7b93d579af37 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006-2007 Renesas Solutions Corp.
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
@@ -561,11 +561,26 @@ static inline void m66592_write(struct m66592 *m66592, u16 val,
iowrite16(val, m66592->reg + offset);
}
+static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
+ unsigned long offset)
+{
+ u16 tmp;
+ tmp = m66592_read(m66592, offset);
+ tmp = tmp & (~pat);
+ tmp = tmp | val;
+ m66592_write(m66592, tmp, offset);
+}
+
+#define m66592_bclr(m66592, val, offset) \
+ m66592_mdfy(m66592, 0, val, offset)
+#define m66592_bset(m66592, val, offset) \
+ m66592_mdfy(m66592, val, 0, offset)
+
static inline void m66592_write_fifo(struct m66592 *m66592,
- unsigned long offset,
+ struct m66592_ep *ep,
void *buf, unsigned long len)
{
- void __iomem *fifoaddr = m66592->reg + offset;
+ void __iomem *fifoaddr = m66592->reg + ep->fifoaddr;
if (m66592->pdata->on_chip) {
unsigned long count;
@@ -591,26 +606,15 @@ static inline void m66592_write_fifo(struct m66592 *m66592,
iowrite16_rep(fifoaddr, buf, len);
if (odd) {
unsigned char *p = buf + len*2;
+ if (m66592->pdata->wr0_shorted_to_wr1)
+ m66592_bclr(m66592, M66592_MBW_16, ep->fifosel);
iowrite8(*p, fifoaddr);
+ if (m66592->pdata->wr0_shorted_to_wr1)
+ m66592_bset(m66592, M66592_MBW_16, ep->fifosel);
}
}
}
-static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
- unsigned long offset)
-{
- u16 tmp;
- tmp = m66592_read(m66592, offset);
- tmp = tmp & (~pat);
- tmp = tmp | val;
- m66592_write(m66592, tmp, offset);
-}
-
-#define m66592_bclr(m66592, val, offset) \
- m66592_mdfy(m66592, 0, val, offset)
-#define m66592_bset(m66592, val, offset) \
- m66592_mdfy(m66592, val, 0, offset)
-
#endif /* ifndef __M66592_UDC_H__ */
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 01822422c3e8..d3eb27427c58 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -169,6 +169,7 @@ static struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
.iProduct = DRIVER_DESC,
+ .max_speed = USB_SPEED_HIGH,
.needs_serial = 1,
};
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index d9feced348e3..8c7b74717d85 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -351,6 +351,7 @@ static struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(multi_unbind),
.iProduct = DRIVER_DESC,
.needs_serial = 1,
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index b1a8146b9d50..ce1ac2bcb314 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -1128,6 +1128,9 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int mv_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int mv_udc_stop(struct usb_gadget_driver *driver);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops mv_ops = {
@@ -1139,6 +1142,8 @@ static const struct usb_gadget_ops mv_ops = {
/* 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)
@@ -1230,7 +1235,7 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
}
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int mv_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct mv_udc *udc = the_controller;
@@ -1270,9 +1275,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int mv_udc_stop(struct usb_gadget_driver *driver)
{
struct mv_udc *udc = the_controller;
unsigned long flags;
@@ -1296,7 +1300,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
static int
udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
@@ -1880,9 +1883,10 @@ static void gadget_release(struct device *_dev)
static int mv_udc_remove(struct platform_device *dev)
{
struct mv_udc *udc = the_controller;
-
DECLARE_COMPLETION(done);
+ usb_del_gadget_udc(&udc->gadget);
+
udc->done = &done;
/* free memory allocated in probe */
@@ -2074,11 +2078,12 @@ int mv_udc_probe(struct platform_device *dev)
the_controller = udc;
- goto out;
+ retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
+ if (!retval)
+ return retval;
error:
if (udc)
mv_udc_remove(udc->dev);
-out:
return retval;
}
@@ -2126,7 +2131,7 @@ static struct platform_driver udc_driver = {
#endif
},
};
-
+MODULE_ALIAS("platform:pxa-u2o");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
index 99c179ad729d..62ee5087dcac 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/ncm.c
@@ -228,6 +228,7 @@ static struct usb_composite_driver ncm_driver = {
.name = "g_ncm",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(gncm_unbind),
};
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
new file mode 100644
index 000000000000..7c7b0e120d88
--- /dev/null
+++ b/drivers/usb/gadget/net2272.c
@@ -0,0 +1,2752 @@
+/*
+ * Driver for PLX NET2272 USB device controller
+ *
+ * Copyright (C) 2005-2006 PLX Technology, Inc.
+ * Copyright (C) 2006-2011 Analog Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "net2272.h"
+
+#define DRIVER_DESC "PLX NET2272 USB Peripheral Controller"
+
+static const char driver_name[] = "net2272";
+static const char driver_vers[] = "2006 October 17/mainline";
+static const char driver_desc[] = DRIVER_DESC;
+
+static const char ep0name[] = "ep0";
+static const char * const ep_name[] = {
+ ep0name,
+ "ep-a", "ep-b", "ep-c",
+};
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+#ifdef CONFIG_USB_GADGET_NET2272_DMA
+/*
+ * use_dma: the NET2272 can use an external DMA controller.
+ * Note that since there is no generic DMA api, some functions,
+ * notably request_dma, start_dma, and cancel_dma will need to be
+ * modified for your platform's particular dma controller.
+ *
+ * If use_dma is disabled, pio will be used instead.
+ */
+static int use_dma = 0;
+module_param(use_dma, bool, 0644);
+
+/*
+ * dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b)
+ * The NET2272 can only use dma for a single endpoint at a time.
+ * At some point this could be modified to allow either endpoint
+ * to take control of dma as it becomes available.
+ *
+ * Note that DMA should not be used on OUT endpoints unless it can
+ * be guaranteed that no short packets will arrive on an IN endpoint
+ * while the DMA operation is pending. Otherwise the OUT DMA will
+ * terminate prematurely (See NET2272 Errata 630-0213-0101)
+ */
+static ushort dma_ep = 1;
+module_param(dma_ep, ushort, 0644);
+
+/*
+ * dma_mode: net2272 dma mode setting (see LOCCTL1 definiton):
+ * mode 0 == Slow DREQ mode
+ * mode 1 == Fast DREQ mode
+ * mode 2 == Burst mode
+ */
+static ushort dma_mode = 2;
+module_param(dma_mode, ushort, 0644);
+#else
+#define use_dma 0
+#define dma_ep 1
+#define dma_mode 2
+#endif
+
+/*
+ * fifo_mode: net2272 buffer configuration:
+ * mode 0 == ep-{a,b,c} 512db each
+ * mode 1 == ep-a 1k, ep-{b,c} 512db
+ * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db
+ * mode 3 == ep-a 1k, ep-b disabled, ep-c 512db
+ */
+static ushort fifo_mode = 0;
+module_param(fifo_mode, ushort, 0644);
+
+/*
+ * enable_suspend: When enabled, the driver will respond to
+ * USB suspend requests by powering down the NET2272. Otherwise,
+ * USB suspend requests will be ignored. This is acceptible for
+ * self-powered devices. For bus powered devices set this to 1.
+ */
+static ushort enable_suspend = 0;
+module_param(enable_suspend, ushort, 0644);
+
+static void assert_out_naking(struct net2272_ep *ep, const char *where)
+{
+ u8 tmp;
+
+#ifndef DEBUG
+ return;
+#endif
+
+ tmp = net2272_ep_read(ep, EP_STAT0);
+ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
+ dev_dbg(ep->dev->dev, "%s %s %02x !NAK\n",
+ ep->ep.name, where, tmp);
+ net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+ }
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
+
+static void stop_out_naking(struct net2272_ep *ep)
+{
+ u8 tmp = net2272_ep_read(ep, EP_STAT0);
+
+ if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
+ net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+}
+
+#define PIPEDIR(bAddress) (usb_pipein(bAddress) ? "in" : "out")
+
+static char *type_string(u8 bmAttributes)
+{
+ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK: return "bulk";
+ case USB_ENDPOINT_XFER_ISOC: return "iso";
+ case USB_ENDPOINT_XFER_INT: return "intr";
+ default: return "control";
+ }
+}
+
+static char *buf_state_string(unsigned state)
+{
+ switch (state) {
+ case BUFF_FREE: return "free";
+ case BUFF_VALID: return "valid";
+ case BUFF_LCL: return "local";
+ case BUFF_USB: return "usb";
+ default: return "unknown";
+ }
+}
+
+static char *dma_mode_string(void)
+{
+ if (!use_dma)
+ return "PIO";
+ switch (dma_mode) {
+ case 0: return "SLOW DREQ";
+ case 1: return "FAST DREQ";
+ case 2: return "BURST";
+ default: return "invalid";
+ }
+}
+
+static void net2272_dequeue_all(struct net2272_ep *);
+static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *);
+static int net2272_fifo_status(struct usb_ep *);
+
+static struct usb_ep_ops net2272_ep_ops;
+
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct net2272 *dev;
+ struct net2272_ep *ep;
+ u32 max;
+ u8 tmp;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ _ep->maxpacket = max & 0x7fff;
+ ep->desc = desc;
+
+ /* net2272_ep_reset() has already been called */
+ ep->stopped = 0;
+ ep->wedged = 0;
+
+ /* set speed-dependent max packet */
+ net2272_ep_write(ep, EP_MAXPKT0, max & 0xff);
+ net2272_ep_write(ep, EP_MAXPKT1, (max & 0xff00) >> 8);
+
+ /* set type, direction, address; reset fifo counters */
+ net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
+ tmp = usb_endpoint_type(desc);
+ if (usb_endpoint_xfer_bulk(desc)) {
+ /* catch some particularly blatant driver bugs */
+ if ((dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+ (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ERANGE;
+ }
+ }
+ ep->is_iso = usb_endpoint_xfer_isoc(desc) ? 1 : 0;
+ tmp <<= ENDPOINT_TYPE;
+ tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER);
+ tmp |= usb_endpoint_dir_in(desc) << ENDPOINT_DIRECTION;
+ tmp |= (1 << ENDPOINT_ENABLE);
+
+ /* for OUT transfers, block the rx fifo until a read is posted */
+ ep->is_in = usb_endpoint_dir_in(desc);
+ if (!ep->is_in)
+ net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+
+ net2272_ep_write(ep, EP_CFG, tmp);
+
+ /* enable irqs */
+ tmp = (1 << ep->num) | net2272_read(dev, IRQENB0);
+ net2272_write(dev, IRQENB0, tmp);
+
+ tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+ | net2272_ep_read(ep, EP_IRQENB);
+ net2272_ep_write(ep, EP_IRQENB, tmp);
+
+ tmp = desc->bEndpointAddress;
+ dev_dbg(dev->dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n",
+ _ep->name, tmp & 0x0f, PIPEDIR(tmp),
+ type_string(desc->bmAttributes), max,
+ net2272_ep_read(ep, EP_CFG));
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+static void net2272_ep_reset(struct net2272_ep *ep)
+{
+ u8 tmp;
+
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->ep.maxpacket = ~0;
+ ep->ep.ops = &net2272_ep_ops;
+
+ /* disable irqs, endpoint */
+ net2272_ep_write(ep, EP_IRQENB, 0);
+
+ /* init to our chosen defaults, notably so that we NAK OUT
+ * packets until the driver queues a read.
+ */
+ tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS);
+ net2272_ep_write(ep, EP_RSPSET, tmp);
+
+ tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE);
+ if (ep->num != 0)
+ tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT);
+
+ net2272_ep_write(ep, EP_RSPCLR, tmp);
+
+ /* scrub most status bits, and flush any fifo state */
+ net2272_ep_write(ep, EP_STAT0,
+ (1 << DATA_IN_TOKEN_INTERRUPT)
+ | (1 << DATA_OUT_TOKEN_INTERRUPT)
+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+ | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
+
+ net2272_ep_write(ep, EP_STAT1,
+ (1 << TIMEOUT)
+ | (1 << USB_OUT_ACK_SENT)
+ | (1 << USB_OUT_NAK_SENT)
+ | (1 << USB_IN_ACK_RCVD)
+ | (1 << USB_IN_NAK_SENT)
+ | (1 << USB_STALL_SENT)
+ | (1 << LOCAL_OUT_ZLP)
+ | (1 << BUFFER_FLUSH));
+
+ /* fifo size is handled seperately */
+}
+
+static int net2272_disable(struct usb_ep *_ep)
+{
+ struct net2272_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || !ep->desc || _ep->name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ net2272_dequeue_all(ep);
+ net2272_ep_reset(ep);
+
+ dev_vdbg(ep->dev->dev, "disabled %s\n", _ep->name);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static struct usb_request *
+net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct net2272_ep *ep;
+ struct net2272_request *req;
+
+ if (!_ep)
+ return NULL;
+ ep = container_of(_ep, struct net2272_ep, ep);
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void
+net2272_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2272_ep *ep;
+ struct net2272_request *req;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || !_req)
+ return;
+
+ req = container_of(_req, struct net2272_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+static void
+net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status)
+{
+ struct net2272 *dev;
+ unsigned stopped = ep->stopped;
+
+ if (ep->num == 0) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ }
+ allow_status(ep);
+ }
+
+ list_del_init(&req->queue);
+
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ dev = ep->dev;
+ if (use_dma && req->mapped) {
+ dma_unmap_single(dev->dev, req->req.dma, req->req.length,
+ ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ }
+
+ if (status && status != -ESHUTDOWN)
+ dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length, req->req.buf);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&dev->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->stopped = stopped;
+}
+
+static int
+net2272_write_packet(struct net2272_ep *ep, u8 *buf,
+ struct net2272_request *req, unsigned max)
+{
+ u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA);
+ u16 *bufp;
+ unsigned length, count;
+ u8 tmp;
+
+ length = min(req->req.length - req->req.actual, max);
+ req->req.actual += length;
+
+ dev_vdbg(ep->dev->dev, "write packet %s req %p max %u len %u avail %u\n",
+ ep->ep.name, req, max, length,
+ (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0));
+
+ count = length;
+ bufp = (u16 *)buf;
+
+ while (likely(count >= 2)) {
+ /* no byte-swap required; chip endian set during init */
+ writew(*bufp++, ep_data);
+ count -= 2;
+ }
+ buf = (u8 *)bufp;
+
+ /* write final byte by placing the NET2272 into 8-bit mode */
+ if (unlikely(count)) {
+ tmp = net2272_read(ep->dev, LOCCTL);
+ net2272_write(ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH));
+ writeb(*buf, ep_data);
+ net2272_write(ep->dev, LOCCTL, tmp);
+ }
+ return length;
+}
+
+/* returns: 0: still running, 1: completed, negative: errno */
+static int
+net2272_write_fifo(struct net2272_ep *ep, struct net2272_request *req)
+{
+ u8 *buf;
+ unsigned count, max;
+ int status;
+
+ dev_vdbg(ep->dev->dev, "write_fifo %s actual %d len %d\n",
+ ep->ep.name, req->req.actual, req->req.length);
+
+ /*
+ * Keep loading the endpoint until the final packet is loaded,
+ * or the endpoint buffer is full.
+ */
+ top:
+ /*
+ * Clear interrupt status
+ * - Packet Transmitted interrupt will become set again when the
+ * host successfully takes another packet
+ */
+ net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT));
+ while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_FULL))) {
+ buf = req->req.buf + req->req.actual;
+ prefetch(buf);
+
+ /* force pagesel */
+ net2272_ep_read(ep, EP_STAT0);
+
+ max = (net2272_ep_read(ep, EP_AVAIL1) << 8) |
+ (net2272_ep_read(ep, EP_AVAIL0));
+
+ if (max < ep->ep.maxpacket)
+ max = (net2272_ep_read(ep, EP_AVAIL1) << 8)
+ | (net2272_ep_read(ep, EP_AVAIL0));
+
+ count = net2272_write_packet(ep, buf, req, max);
+ /* see if we are done */
+ if (req->req.length == req->req.actual) {
+ /* validate short or zlp packet */
+ if (count < ep->ep.maxpacket)
+ set_fifo_bytecount(ep, 0);
+ net2272_done(ep, req, 0);
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2272_request,
+ queue);
+ status = net2272_kick_dma(ep, req);
+
+ if (status < 0)
+ if ((net2272_ep_read(ep, EP_STAT0)
+ & (1 << BUFFER_EMPTY)))
+ goto top;
+ }
+ return 1;
+ }
+ net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT));
+ }
+ return 0;
+}
+
+static void
+net2272_out_flush(struct net2272_ep *ep)
+{
+ ASSERT_OUT_NAKING(ep);
+
+ net2272_ep_write(ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT)
+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT));
+ net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
+}
+
+static int
+net2272_read_packet(struct net2272_ep *ep, u8 *buf,
+ struct net2272_request *req, unsigned avail)
+{
+ u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA);
+ unsigned is_short;
+ u16 *bufp;
+
+ req->req.actual += avail;
+
+ dev_vdbg(ep->dev->dev, "read packet %s req %p len %u avail %u\n",
+ ep->ep.name, req, avail,
+ (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0));
+
+ is_short = (avail < ep->ep.maxpacket);
+
+ if (unlikely(avail == 0)) {
+ /* remove any zlp from the buffer */
+ (void)readw(ep_data);
+ return is_short;
+ }
+
+ /* Ensure we get the final byte */
+ if (unlikely(avail % 2))
+ avail++;
+ bufp = (u16 *)buf;
+
+ do {
+ *bufp++ = readw(ep_data);
+ avail -= 2;
+ } while (avail);
+
+ /*
+ * To avoid false endpoint available race condition must read
+ * ep stat0 twice in the case of a short transfer
+ */
+ if (net2272_ep_read(ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))
+ net2272_ep_read(ep, EP_STAT0);
+
+ return is_short;
+}
+
+static int
+net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req)
+{
+ u8 *buf;
+ unsigned is_short;
+ int count;
+ int tmp;
+ int cleanup = 0;
+ int status = -1;
+
+ dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n",
+ ep->ep.name, req->req.actual, req->req.length);
+
+ top:
+ do {
+ buf = req->req.buf + req->req.actual;
+ prefetchw(buf);
+
+ count = (net2272_ep_read(ep, EP_AVAIL1) << 8)
+ | net2272_ep_read(ep, EP_AVAIL0);
+
+ net2272_ep_write(ep, EP_STAT0,
+ (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ (1 << DATA_PACKET_RECEIVED_INTERRUPT));
+
+ tmp = req->req.length - req->req.actual;
+
+ if (count > tmp) {
+ if ((tmp % ep->ep.maxpacket) != 0) {
+ dev_err(ep->dev->dev,
+ "%s out fifo %d bytes, expected %d\n",
+ ep->ep.name, count, tmp);
+ cleanup = 1;
+ }
+ count = (tmp > 0) ? tmp : 0;
+ }
+
+ is_short = net2272_read_packet(ep, buf, req, count);
+
+ /* completion */
+ if (unlikely(cleanup || is_short ||
+ ((req->req.actual == req->req.length)
+ && !req->req.zero))) {
+
+ if (cleanup) {
+ net2272_out_flush(ep);
+ net2272_done(ep, req, -EOVERFLOW);
+ } else
+ net2272_done(ep, req, 0);
+
+ /* re-initialize endpoint transfer registers
+ * otherwise they may result in erroneous pre-validation
+ * for subsequent control reads
+ */
+ if (unlikely(ep->num == 0)) {
+ net2272_ep_write(ep, EP_TRANSFER2, 0);
+ net2272_ep_write(ep, EP_TRANSFER1, 0);
+ net2272_ep_write(ep, EP_TRANSFER0, 0);
+ }
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2272_request, queue);
+ status = net2272_kick_dma(ep, req);
+ if ((status < 0) &&
+ !(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY)))
+ goto top;
+ }
+ return 1;
+ }
+ } while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY)));
+
+ return 0;
+}
+
+static void
+net2272_pio_advance(struct net2272_ep *ep)
+{
+ struct net2272_request *req;
+
+ if (unlikely(list_empty(&ep->queue)))
+ return;
+
+ req = list_entry(ep->queue.next, struct net2272_request, queue);
+ (ep->is_in ? net2272_write_fifo : net2272_read_fifo)(ep, req);
+}
+
+/* returns 0 on success, else negative errno */
+static int
+net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf,
+ unsigned len, unsigned dir)
+{
+ dev_vdbg(dev->dev, "request_dma ep %d buf %08x len %d dir %d\n",
+ ep, buf, len, dir);
+
+ /* The NET2272 only supports a single dma channel */
+ if (dev->dma_busy)
+ return -EBUSY;
+ /*
+ * EP_TRANSFER (used to determine the number of bytes received
+ * in an OUT transfer) is 24 bits wide; don't ask for more than that.
+ */
+ if ((dir == 1) && (len > 0x1000000))
+ return -EINVAL;
+
+ dev->dma_busy = 1;
+
+ /* initialize platform's dma */
+#ifdef CONFIG_PCI
+ /* NET2272 addr, buffer addr, length, etc. */
+ switch (dev->dev_id) {
+ case PCI_DEVICE_ID_RDK1:
+ /* Setup PLX 9054 DMA mode */
+ writel((1 << LOCAL_BUS_WIDTH) |
+ (1 << TA_READY_INPUT_ENABLE) |
+ (0 << LOCAL_BURST_ENABLE) |
+ (1 << DONE_INTERRUPT_ENABLE) |
+ (1 << LOCAL_ADDRESSING_MODE) |
+ (1 << DEMAND_MODE) |
+ (1 << DMA_EOT_ENABLE) |
+ (1 << FAST_SLOW_TERMINATE_MODE_SELECT) |
+ (1 << DMA_CHANNEL_INTERRUPT_SELECT),
+ dev->rdk1.plx9054_base_addr + DMAMODE0);
+
+ writel(0x100000, dev->rdk1.plx9054_base_addr + DMALADR0);
+ writel(buf, dev->rdk1.plx9054_base_addr + DMAPADR0);
+ writel(len, dev->rdk1.plx9054_base_addr + DMASIZ0);
+ writel((dir << DIRECTION_OF_TRANSFER) |
+ (1 << INTERRUPT_AFTER_TERMINAL_COUNT),
+ dev->rdk1.plx9054_base_addr + DMADPR0);
+ writel((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) |
+ readl(dev->rdk1.plx9054_base_addr + INTCSR),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+
+ break;
+ }
+#endif
+
+ net2272_write(dev, DMAREQ,
+ (0 << DMA_BUFFER_VALID) |
+ (1 << DMA_REQUEST_ENABLE) |
+ (1 << DMA_CONTROL_DACK) |
+ (dev->dma_eot_polarity << EOT_POLARITY) |
+ (dev->dma_dack_polarity << DACK_POLARITY) |
+ (dev->dma_dreq_polarity << DREQ_POLARITY) |
+ ((ep >> 1) << DMA_ENDPOINT_SELECT));
+
+ (void) net2272_read(dev, SCRATCH);
+
+ return 0;
+}
+
+static void
+net2272_start_dma(struct net2272 *dev)
+{
+ /* start platform's dma controller */
+#ifdef CONFIG_PCI
+ switch (dev->dev_id) {
+ case PCI_DEVICE_ID_RDK1:
+ writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START),
+ dev->rdk1.plx9054_base_addr + DMACSR0);
+ break;
+ }
+#endif
+}
+
+/* returns 0 on success, else negative errno */
+static int
+net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req)
+{
+ unsigned size;
+ u8 tmp;
+
+ if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma)
+ return -EINVAL;
+
+ /* don't use dma for odd-length transfers
+ * otherwise, we'd need to deal with the last byte with pio
+ */
+ if (req->req.length & 1)
+ return -EINVAL;
+
+ dev_vdbg(ep->dev->dev, "kick_dma %s req %p dma %08llx\n",
+ ep->ep.name, req, (unsigned long long) req->req.dma);
+
+ net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
+
+ /* The NET2272 can only use DMA on one endpoint at a time */
+ if (ep->dev->dma_busy)
+ return -EBUSY;
+
+ /* Make sure we only DMA an even number of bytes (we'll use
+ * pio to complete the transfer)
+ */
+ size = req->req.length;
+ size &= ~1;
+
+ /* device-to-host transfer */
+ if (ep->is_in) {
+ /* initialize platform's dma controller */
+ if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 0))
+ /* unable to obtain DMA channel; return error and use pio mode */
+ return -EBUSY;
+ req->req.actual += size;
+
+ /* host-to-device transfer */
+ } else {
+ tmp = net2272_ep_read(ep, EP_STAT0);
+
+ /* initialize platform's dma controller */
+ if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 1))
+ /* unable to obtain DMA channel; return error and use pio mode */
+ return -EBUSY;
+
+ if (!(tmp & (1 << BUFFER_EMPTY)))
+ ep->not_empty = 1;
+ else
+ ep->not_empty = 0;
+
+
+ /* allow the endpoint's buffer to fill */
+ net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+
+ /* this transfer completed and data's already in the fifo
+ * return error so pio gets used.
+ */
+ if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+
+ /* deassert dreq */
+ net2272_write(ep->dev, DMAREQ,
+ (0 << DMA_BUFFER_VALID) |
+ (0 << DMA_REQUEST_ENABLE) |
+ (1 << DMA_CONTROL_DACK) |
+ (ep->dev->dma_eot_polarity << EOT_POLARITY) |
+ (ep->dev->dma_dack_polarity << DACK_POLARITY) |
+ (ep->dev->dma_dreq_polarity << DREQ_POLARITY) |
+ ((ep->num >> 1) << DMA_ENDPOINT_SELECT));
+
+ return -EBUSY;
+ }
+ }
+
+ /* Don't use per-packet interrupts: use dma interrupts only */
+ net2272_ep_write(ep, EP_IRQENB, 0);
+
+ net2272_start_dma(ep->dev);
+
+ return 0;
+}
+
+static void net2272_cancel_dma(struct net2272 *dev)
+{
+#ifdef CONFIG_PCI
+ switch (dev->dev_id) {
+ case PCI_DEVICE_ID_RDK1:
+ writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0);
+ writeb(1 << CHANNEL_ABORT, dev->rdk1.plx9054_base_addr + DMACSR0);
+ while (!(readb(dev->rdk1.plx9054_base_addr + DMACSR0) &
+ (1 << CHANNEL_DONE)))
+ continue; /* wait for dma to stabalize */
+
+ /* dma abort generates an interrupt */
+ writeb(1 << CHANNEL_CLEAR_INTERRUPT,
+ dev->rdk1.plx9054_base_addr + DMACSR0);
+ break;
+ }
+#endif
+
+ dev->dma_busy = 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct net2272_request *req;
+ struct net2272_ep *ep;
+ struct net2272 *dev;
+ unsigned long flags;
+ int status = -1;
+ u8 s;
+
+ req = container_of(_req, struct net2272_request, req);
+ if (!_req || !_req->complete || !_req->buf
+ || !list_empty(&req->queue))
+ return -EINVAL;
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* set up dma mapping in case the caller didn't */
+ if (use_dma && ep->dma && _req->dma == DMA_ADDR_INVALID) {
+ _req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
+ ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ }
+
+ dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n",
+ _ep->name, _req, _req->length, _req->buf,
+ (unsigned long long) _req->dma, _req->zero ? "zero" : "!zero");
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ /* maybe there's no control data, just status ack */
+ if (ep->num == 0 && _req->length == 0) {
+ net2272_done(ep, req, 0);
+ dev_vdbg(dev->dev, "%s status ack\n", ep->ep.name);
+ goto done;
+ }
+
+ /* Return zlp, don't let it block subsequent packets */
+ s = net2272_ep_read(ep, EP_STAT0);
+ if (s & (1 << BUFFER_EMPTY)) {
+ /* Buffer is empty check for a blocking zlp, handle it */
+ if ((s & (1 << NAK_OUT_PACKETS)) &&
+ net2272_ep_read(ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP)) {
+ dev_dbg(dev->dev, "WARNING: returning ZLP short packet termination!\n");
+ /*
+ * Request is going to terminate with a short packet ...
+ * hope the client is ready for it!
+ */
+ status = net2272_read_fifo(ep, req);
+ /* clear short packet naking */
+ net2272_ep_write(ep, EP_STAT0, (1 << NAK_OUT_PACKETS));
+ goto done;
+ }
+ }
+
+ /* try dma first */
+ status = net2272_kick_dma(ep, req);
+
+ if (status < 0) {
+ /* dma failed (most likely in use by another endpoint)
+ * fallback to pio
+ */
+ status = 0;
+
+ if (ep->is_in)
+ status = net2272_write_fifo(ep, req);
+ else {
+ s = net2272_ep_read(ep, EP_STAT0);
+ if ((s & (1 << BUFFER_EMPTY)) == 0)
+ status = net2272_read_fifo(ep, req);
+ }
+
+ if (unlikely(status != 0)) {
+ if (status > 0)
+ status = 0;
+ req = NULL;
+ }
+ }
+ }
+ if (likely(req != 0))
+ list_add_tail(&req->queue, &ep->queue);
+
+ if (likely(!list_empty(&ep->queue)))
+ net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
+ done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/* dequeue ALL requests */
+static void
+net2272_dequeue_all(struct net2272_ep *ep)
+{
+ struct net2272_request *req;
+
+ /* called with spinlock held */
+ ep->stopped = 1;
+
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2272_request,
+ queue);
+ net2272_done(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* dequeue JUST ONE request */
+static int
+net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2272_ep *ep;
+ struct net2272_request *req;
+ unsigned long flags;
+ int stopped;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ stopped = ep->stopped;
+ ep->stopped = 1;
+
+ /* make sure it's still queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ /* queue head may be partially complete */
+ if (ep->queue.next == &req->queue) {
+ dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name);
+ net2272_done(ep, req, -ECONNRESET);
+ }
+ req = NULL;
+ ep->stopped = stopped;
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+ struct net2272_ep *ep;
+ unsigned long flags;
+ int ret = 0;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+ if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc))
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ if (!list_empty(&ep->queue))
+ ret = -EAGAIN;
+ else if (ep->is_in && value && net2272_fifo_status(_ep) != 0)
+ ret = -EAGAIN;
+ else {
+ dev_vdbg(ep->dev->dev, "%s %s %s\n", _ep->name,
+ value ? "set" : "clear",
+ wedged ? "wedge" : "halt");
+ /* set/clear */
+ if (value) {
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else
+ set_halt(ep);
+ if (wedged)
+ ep->wedged = 1;
+ } else {
+ clear_halt(ep);
+ ep->wedged = 0;
+ }
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ return ret;
+}
+
+static int
+net2272_set_halt(struct usb_ep *_ep, int value)
+{
+ return net2272_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int
+net2272_set_wedge(struct usb_ep *_ep)
+{
+ if (!_ep || _ep->name == ep0name)
+ return -EINVAL;
+ return net2272_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static int
+net2272_fifo_status(struct usb_ep *_ep)
+{
+ struct net2272_ep *ep;
+ u16 avail;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -ENODEV;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ avail = net2272_ep_read(ep, EP_AVAIL1) << 8;
+ avail |= net2272_ep_read(ep, EP_AVAIL0);
+ if (avail > ep->fifo_size)
+ return -EOVERFLOW;
+ if (ep->is_in)
+ avail = ep->fifo_size - avail;
+ return avail;
+}
+
+static void
+net2272_fifo_flush(struct usb_ep *_ep)
+{
+ struct net2272_ep *ep;
+
+ ep = container_of(_ep, struct net2272_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return;
+
+ net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
+}
+
+static struct usb_ep_ops net2272_ep_ops = {
+ .enable = net2272_enable,
+ .disable = net2272_disable,
+
+ .alloc_request = net2272_alloc_request,
+ .free_request = net2272_free_request,
+
+ .queue = net2272_queue,
+ .dequeue = net2272_dequeue,
+
+ .set_halt = net2272_set_halt,
+ .set_wedge = net2272_set_wedge,
+ .fifo_status = net2272_fifo_status,
+ .fifo_flush = net2272_fifo_flush,
+};
+
+/*---------------------------------------------------------------------------*/
+
+static int
+net2272_get_frame(struct usb_gadget *_gadget)
+{
+ struct net2272 *dev;
+ unsigned long flags;
+ u16 ret;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2272, gadget);
+ spin_lock_irqsave(&dev->lock, flags);
+
+ ret = net2272_read(dev, FRAME1) << 8;
+ ret |= net2272_read(dev, FRAME0);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int
+net2272_wakeup(struct usb_gadget *_gadget)
+{
+ struct net2272 *dev;
+ u8 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return 0;
+ dev = container_of(_gadget, struct net2272, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = net2272_read(dev, USBCTL0);
+ if (tmp & (1 << IO_WAKEUP_ENABLE))
+ net2272_write(dev, USBCTL1, (1 << GENERATE_RESUME));
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int
+net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+ struct net2272 *dev;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2272, gadget);
+
+ dev->is_selfpowered = value;
+
+ return 0;
+}
+
+static int
+net2272_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct net2272 *dev;
+ u8 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2272, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = net2272_read(dev, USBCTL0);
+ dev->softconnect = (is_on != 0);
+ if (is_on)
+ tmp |= (1 << USB_DETECT_ENABLE);
+ else
+ tmp &= ~(1 << USB_DETECT_ENABLE);
+ net2272_write(dev, USBCTL0, tmp);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ 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 const struct usb_gadget_ops net2272_ops = {
+ .get_frame = net2272_get_frame,
+ .wakeup = net2272_wakeup,
+ .set_selfpowered = net2272_set_selfpowered,
+ .pullup = net2272_pullup,
+ .start = net2272_start,
+ .stop = net2272_stop,
+};
+
+/*---------------------------------------------------------------------------*/
+
+static ssize_t
+net2272_show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+ struct net2272 *dev;
+ char *next;
+ unsigned size, t;
+ unsigned long flags;
+ u8 t1, t2;
+ int i;
+ const char *s;
+
+ dev = dev_get_drvdata(_dev);
+ next = buf;
+ size = PAGE_SIZE;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->driver)
+ s = dev->driver->driver.name;
+ else
+ s = "(none)";
+
+ /* Main Control Registers */
+ t = scnprintf(next, size, "%s version %s,"
+ "chiprev %02x, locctl %02x\n"
+ "irqenb0 %02x irqenb1 %02x "
+ "irqstat0 %02x irqstat1 %02x\n",
+ driver_name, driver_vers, dev->chiprev,
+ net2272_read(dev, LOCCTL),
+ net2272_read(dev, IRQENB0),
+ net2272_read(dev, IRQENB1),
+ net2272_read(dev, IRQSTAT0),
+ net2272_read(dev, IRQSTAT1));
+ size -= t;
+ next += t;
+
+ /* DMA */
+ t1 = net2272_read(dev, DMAREQ);
+ t = scnprintf(next, size, "\ndmareq %02x: %s %s%s%s%s\n",
+ t1, ep_name[(t1 & 0x01) + 1],
+ t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "",
+ t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "",
+ t1 & (1 << DMA_REQUEST) ? "req " : "",
+ t1 & (1 << DMA_BUFFER_VALID) ? "valid " : "");
+ size -= t;
+ next += t;
+
+ /* USB Control Registers */
+ t1 = net2272_read(dev, USBCTL1);
+ if (t1 & (1 << VBUS_PIN)) {
+ if (t1 & (1 << USB_HIGH_SPEED))
+ s = "high speed";
+ else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ s = "powered";
+ else
+ s = "full speed";
+ } else
+ s = "not attached";
+ t = scnprintf(next, size,
+ "usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n",
+ net2272_read(dev, USBCTL0), t1,
+ net2272_read(dev, OURADDR), s);
+ size -= t;
+ next += t;
+
+ /* Endpoint Registers */
+ for (i = 0; i < 4; ++i) {
+ struct net2272_ep *ep;
+
+ ep = &dev->ep[i];
+ if (i && !ep->desc)
+ continue;
+
+ t1 = net2272_ep_read(ep, EP_CFG);
+ t2 = net2272_ep_read(ep, EP_RSPSET);
+ t = scnprintf(next, size,
+ "\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s"
+ "irqenb %02x\n",
+ ep->ep.name, t1, t2,
+ (t2 & (1 << ALT_NAK_OUT_PACKETS)) ? "NAK " : "",
+ (t2 & (1 << HIDE_STATUS_PHASE)) ? "hide " : "",
+ (t2 & (1 << AUTOVALIDATE)) ? "auto " : "",
+ (t2 & (1 << INTERRUPT_MODE)) ? "interrupt " : "",
+ (t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "",
+ (t2 & (1 << NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "",
+ (t2 & (1 << ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ",
+ (t2 & (1 << ENDPOINT_HALT)) ? "HALT " : "",
+ net2272_ep_read(ep, EP_IRQENB));
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size,
+ "\tstat0 %02x stat1 %02x avail %04x "
+ "(ep%d%s-%s)%s\n",
+ net2272_ep_read(ep, EP_STAT0),
+ net2272_ep_read(ep, EP_STAT1),
+ (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0),
+ t1 & 0x0f,
+ ep->is_in ? "in" : "out",
+ type_string(t1 >> 5),
+ ep->stopped ? "*" : "");
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size,
+ "\tep_transfer %06x\n",
+ ((net2272_ep_read(ep, EP_TRANSFER2) & 0xff) << 16) |
+ ((net2272_ep_read(ep, EP_TRANSFER1) & 0xff) << 8) |
+ ((net2272_ep_read(ep, EP_TRANSFER0) & 0xff)));
+ size -= t;
+ next += t;
+
+ t1 = net2272_ep_read(ep, EP_BUFF_STATES) & 0x03;
+ t2 = (net2272_ep_read(ep, EP_BUFF_STATES) >> 2) & 0x03;
+ t = scnprintf(next, size,
+ "\tbuf-a %s buf-b %s\n",
+ buf_state_string(t1),
+ buf_state_string(t2));
+ size -= t;
+ next += t;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(registers, S_IRUGO, net2272_show_registers, NULL);
+
+/*---------------------------------------------------------------------------*/
+
+static void
+net2272_set_fifo_mode(struct net2272 *dev, int mode)
+{
+ u8 tmp;
+
+ tmp = net2272_read(dev, LOCCTL) & 0x3f;
+ tmp |= (mode << 6);
+ net2272_write(dev, LOCCTL, tmp);
+
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+ /* always ep-a, ep-c ... maybe not ep-b */
+ list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list);
+
+ switch (mode) {
+ case 0:
+ list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512;
+ break;
+ case 1:
+ list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = 1024;
+ dev->ep[2].fifo_size = 512;
+ break;
+ case 2:
+ list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024;
+ break;
+ case 3:
+ dev->ep[1].fifo_size = 1024;
+ break;
+ }
+
+ /* ep-c is always 2 512 byte buffers */
+ list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[3].fifo_size = 512;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static struct net2272 *the_controller;
+
+static void
+net2272_usb_reset(struct net2272 *dev)
+{
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ net2272_cancel_dma(dev);
+
+ net2272_write(dev, IRQENB0, 0);
+ net2272_write(dev, IRQENB1, 0);
+
+ /* clear irq state */
+ net2272_write(dev, IRQSTAT0, 0xff);
+ net2272_write(dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT));
+
+ net2272_write(dev, DMAREQ,
+ (0 << DMA_BUFFER_VALID) |
+ (0 << DMA_REQUEST_ENABLE) |
+ (1 << DMA_CONTROL_DACK) |
+ (dev->dma_eot_polarity << EOT_POLARITY) |
+ (dev->dma_dack_polarity << DACK_POLARITY) |
+ (dev->dma_dreq_polarity << DREQ_POLARITY) |
+ ((dma_ep >> 1) << DMA_ENDPOINT_SELECT));
+
+ net2272_cancel_dma(dev);
+ net2272_set_fifo_mode(dev, (fifo_mode <= 3) ? fifo_mode : 0);
+
+ /* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping
+ * note that the higher level gadget drivers are expected to convert data to little endian.
+ * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here
+ */
+ net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) | (1 << DATA_WIDTH));
+ net2272_write(dev, LOCCTL1, (dma_mode << DMA_MODE));
+}
+
+static void
+net2272_usb_reinit(struct net2272 *dev)
+{
+ int i;
+
+ /* basic endpoint init */
+ for (i = 0; i < 4; ++i) {
+ struct net2272_ep *ep = &dev->ep[i];
+
+ ep->ep.name = ep_name[i];
+ ep->dev = dev;
+ ep->num = i;
+ ep->not_empty = 0;
+
+ if (use_dma && ep->num == dma_ep)
+ ep->dma = 1;
+
+ if (i > 0 && i <= 3)
+ ep->fifo_size = 512;
+ else
+ ep->fifo_size = 64;
+ net2272_ep_reset(ep);
+ }
+ dev->ep[0].ep.maxpacket = 64;
+
+ dev->gadget.ep0 = &dev->ep[0].ep;
+ dev->ep[0].stopped = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+}
+
+static void
+net2272_ep0_start(struct net2272 *dev)
+{
+ struct net2272_ep *ep0 = &dev->ep[0];
+
+ net2272_ep_write(ep0, EP_RSPSET,
+ (1 << NAK_OUT_PACKETS_MODE) |
+ (1 << ALT_NAK_OUT_PACKETS));
+ net2272_ep_write(ep0, EP_RSPCLR,
+ (1 << HIDE_STATUS_PHASE) |
+ (1 << CONTROL_STATUS_PHASE_HANDSHAKE));
+ net2272_write(dev, USBCTL0,
+ (dev->softconnect << USB_DETECT_ENABLE) |
+ (1 << USB_ROOT_PORT_WAKEUP_ENABLE) |
+ (1 << IO_WAKEUP_ENABLE));
+ net2272_write(dev, IRQENB0,
+ (1 << SETUP_PACKET_INTERRUPT_ENABLE) |
+ (1 << ENDPOINT_0_INTERRUPT_ENABLE) |
+ (1 << DMA_DONE_INTERRUPT_ENABLE));
+ net2272_write(dev, IRQENB1,
+ (1 << VBUS_INTERRUPT_ENABLE) |
+ (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+ (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE));
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests. then usb traffic follows until a
+ * 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 *))
+{
+ struct net2272 *dev = the_controller;
+ int ret;
+ unsigned i;
+
+ if (!driver || !bind || !driver->unbind || !driver->setup ||
+ driver->speed != USB_SPEED_HIGH)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ for (i = 0; i < 4; ++i)
+ dev->ep[i].irqs = 0;
+ /* hook up the driver ... */
+ dev->softconnect = 1;
+ 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.
+ */
+ net2272_ep0_start(dev);
+
+ dev_dbg(dev->dev, "%s ready\n", driver->driver.name);
+
+ return 0;
+}
+
+static void
+stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect if it's not connected */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+
+ /* stop hardware; prevent new request submissions;
+ * and kill any outstanding requests.
+ */
+ net2272_usb_reset(dev);
+ 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)
+{
+ struct net2272 *dev = the_controller;
+ unsigned long flags;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ 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;
+
+ dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+/* handle ep-a/ep-b dma completions */
+static void
+net2272_handle_dma(struct net2272_ep *ep)
+{
+ struct net2272_request *req;
+ unsigned len;
+ int status;
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct net2272_request, queue);
+ else
+ req = NULL;
+
+ dev_vdbg(ep->dev->dev, "handle_dma %s req %p\n", ep->ep.name, req);
+
+ /* Ensure DREQ is de-asserted */
+ net2272_write(ep->dev, DMAREQ,
+ (0 << DMA_BUFFER_VALID)
+ | (0 << DMA_REQUEST_ENABLE)
+ | (1 << DMA_CONTROL_DACK)
+ | (ep->dev->dma_eot_polarity << EOT_POLARITY)
+ | (ep->dev->dma_dack_polarity << DACK_POLARITY)
+ | (ep->dev->dma_dreq_polarity << DREQ_POLARITY)
+ | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT));
+
+ ep->dev->dma_busy = 0;
+
+ net2272_ep_write(ep, EP_IRQENB,
+ (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+ | net2272_ep_read(ep, EP_IRQENB));
+
+ /* device-to-host transfer completed */
+ if (ep->is_in) {
+ /* validate a short packet or zlp if necessary */
+ if ((req->req.length % ep->ep.maxpacket != 0) ||
+ req->req.zero)
+ set_fifo_bytecount(ep, 0);
+
+ net2272_done(ep, req, 0);
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2272_request, queue);
+ status = net2272_kick_dma(ep, req);
+ if (status < 0)
+ net2272_pio_advance(ep);
+ }
+
+ /* host-to-device transfer completed */
+ } else {
+ /* terminated with a short packet? */
+ if (net2272_read(ep->dev, IRQSTAT0) &
+ (1 << DMA_DONE_INTERRUPT)) {
+ /* abort system dma */
+ net2272_cancel_dma(ep->dev);
+ }
+
+ /* EP_TRANSFER will contain the number of bytes
+ * actually received.
+ * NOTE: There is no overflow detection on EP_TRANSFER:
+ * We can't deal with transfers larger than 2^24 bytes!
+ */
+ len = (net2272_ep_read(ep, EP_TRANSFER2) << 16)
+ | (net2272_ep_read(ep, EP_TRANSFER1) << 8)
+ | (net2272_ep_read(ep, EP_TRANSFER0));
+
+ if (ep->not_empty)
+ len += 4;
+
+ req->req.actual += len;
+
+ /* get any remaining data */
+ net2272_pio_advance(ep);
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void
+net2272_handle_ep(struct net2272_ep *ep)
+{
+ struct net2272_request *req;
+ u8 stat0, stat1;
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct net2272_request, queue);
+ else
+ req = NULL;
+
+ /* ack all, and handle what we care about */
+ stat0 = net2272_ep_read(ep, EP_STAT0);
+ stat1 = net2272_ep_read(ep, EP_STAT1);
+ ep->irqs++;
+
+ dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n",
+ ep->ep.name, stat0, stat1, req ? &req->req : 0);
+
+ net2272_ep_write(ep, EP_STAT0, stat0 &
+ ~((1 << NAK_OUT_PACKETS)
+ | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)));
+ net2272_ep_write(ep, EP_STAT1, stat1);
+
+ /* data packet(s) received (in the fifo, OUT)
+ * direction must be validated, otherwise control read status phase
+ * could be interpreted as a valid packet
+ */
+ if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT)))
+ net2272_pio_advance(ep);
+ /* data packet(s) transmitted (IN) */
+ else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
+ net2272_pio_advance(ep);
+}
+
+static struct net2272_ep *
+net2272_get_ep_by_addr(struct net2272 *dev, u16 wIndex)
+{
+ struct net2272_ep *ep;
+
+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+ return &dev->ep[0];
+
+ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+ if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+ return ep;
+ }
+ return NULL;
+}
+
+/*
+ * USB Test Packet:
+ * JKJKJKJK * 9
+ * JJKKJJKK * 8
+ * JJJJKKKK * 8
+ * JJJJJJJKKKKKKK * 8
+ * JJJJJJJK * 8
+ * {JKKKKKKK * 10}, JK
+ */
+static const u8 net2272_test_packet[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
+ 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E
+};
+
+static void
+net2272_set_test_mode(struct net2272 *dev, int mode)
+{
+ int i;
+
+ /* Disable all net2272 interrupts:
+ * Nothing but a power cycle should stop the test.
+ */
+ net2272_write(dev, IRQENB0, 0x00);
+ net2272_write(dev, IRQENB1, 0x00);
+
+ /* Force tranceiver to high-speed */
+ net2272_write(dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED);
+
+ net2272_write(dev, PAGESEL, 0);
+ net2272_write(dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT);
+ net2272_write(dev, EP_RSPCLR,
+ (1 << CONTROL_STATUS_PHASE_HANDSHAKE)
+ | (1 << HIDE_STATUS_PHASE));
+ net2272_write(dev, EP_CFG, 1 << ENDPOINT_DIRECTION);
+ net2272_write(dev, EP_STAT1, 1 << BUFFER_FLUSH);
+
+ /* wait for status phase to complete */
+ while (!(net2272_read(dev, EP_STAT0) &
+ (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)))
+ ;
+
+ /* Enable test mode */
+ net2272_write(dev, USBTEST, mode);
+
+ /* load test packet */
+ if (mode == TEST_PACKET) {
+ /* switch to 8 bit mode */
+ net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) &
+ ~(1 << DATA_WIDTH));
+
+ for (i = 0; i < sizeof(net2272_test_packet); ++i)
+ net2272_write(dev, EP_DATA, net2272_test_packet[i]);
+
+ /* Validate test packet */
+ net2272_write(dev, EP_TRANSFER0, 0);
+ }
+}
+
+static void
+net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
+{
+ struct net2272_ep *ep;
+ u8 num, scratch;
+
+ /* starting a control request? */
+ if (unlikely(stat & (1 << SETUP_PACKET_INTERRUPT))) {
+ union {
+ u8 raw[8];
+ struct usb_ctrlrequest r;
+ } u;
+ int tmp = 0;
+ struct net2272_request *req;
+
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ if (net2272_read(dev, USBCTL1) & (1 << USB_HIGH_SPEED))
+ 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");
+ }
+
+ ep = &dev->ep[0];
+ ep->irqs++;
+
+ /* make sure any leftover interrupt state is cleared */
+ stat &= ~(1 << ENDPOINT_0_INTERRUPT);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2272_request, queue);
+ net2272_done(ep, req,
+ (req->req.actual == req->req.length) ? 0 : -EPROTO);
+ }
+ ep->stopped = 0;
+ dev->protocol_stall = 0;
+ net2272_ep_write(ep, EP_STAT0,
+ (1 << DATA_IN_TOKEN_INTERRUPT)
+ | (1 << DATA_OUT_TOKEN_INTERRUPT)
+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
+ | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
+ net2272_ep_write(ep, EP_STAT1,
+ (1 << TIMEOUT)
+ | (1 << USB_OUT_ACK_SENT)
+ | (1 << USB_OUT_NAK_SENT)
+ | (1 << USB_IN_ACK_RCVD)
+ | (1 << USB_IN_NAK_SENT)
+ | (1 << USB_STALL_SENT)
+ | (1 << LOCAL_OUT_ZLP));
+
+ /*
+ * Ensure Control Read pre-validation setting is beyond maximum size
+ * - Control Writes can leave non-zero values in EP_TRANSFER. If
+ * an EP0 transfer following the Control Write is a Control Read,
+ * the NET2272 sees the non-zero EP_TRANSFER as an unexpected
+ * pre-validation count.
+ * - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures
+ * the pre-validation count cannot cause an unexpected validatation
+ */
+ net2272_write(dev, PAGESEL, 0);
+ net2272_write(dev, EP_TRANSFER2, 0xff);
+ net2272_write(dev, EP_TRANSFER1, 0xff);
+ net2272_write(dev, EP_TRANSFER0, 0xff);
+
+ u.raw[0] = net2272_read(dev, SETUP0);
+ u.raw[1] = net2272_read(dev, SETUP1);
+ u.raw[2] = net2272_read(dev, SETUP2);
+ u.raw[3] = net2272_read(dev, SETUP3);
+ u.raw[4] = net2272_read(dev, SETUP4);
+ u.raw[5] = net2272_read(dev, SETUP5);
+ u.raw[6] = net2272_read(dev, SETUP6);
+ u.raw[7] = net2272_read(dev, SETUP7);
+ /*
+ * If you have a big endian cpu make sure le16_to_cpus
+ * performs the proper byte swapping here...
+ */
+ le16_to_cpus(&u.r.wValue);
+ le16_to_cpus(&u.r.wIndex);
+ le16_to_cpus(&u.r.wLength);
+
+ /* ack the irq */
+ net2272_write(dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT);
+ stat ^= (1 << SETUP_PACKET_INTERRUPT);
+
+ /* watch control traffic at the token level, and force
+ * synchronization before letting the status phase happen.
+ */
+ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+ if (ep->is_in) {
+ scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
+ | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
+ | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
+ stop_out_naking(ep);
+ } else
+ scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
+ | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
+ | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
+ net2272_ep_write(ep, EP_IRQENB, scratch);
+
+ if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+ goto delegate;
+ switch (u.r.bRequest) {
+ case USB_REQ_GET_STATUS: {
+ struct net2272_ep *e;
+ u16 status = 0;
+
+ switch (u.r.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_ENDPOINT:
+ e = net2272_get_ep_by_addr(dev, u.r.wIndex);
+ if (!e || u.r.wLength > 2)
+ goto do_stall;
+ if (net2272_ep_read(e, EP_RSPSET) & (1 << ENDPOINT_HALT))
+ status = __constant_cpu_to_le16(1);
+ else
+ status = __constant_cpu_to_le16(0);
+
+ /* don't bother with a request object! */
+ net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
+ writew(status, net2272_reg_addr(dev, EP_DATA));
+ set_fifo_bytecount(&dev->ep[0], 0);
+ allow_status(ep);
+ dev_vdbg(dev->dev, "%s stat %02x\n",
+ ep->ep.name, status);
+ goto next_endpoints;
+ case USB_RECIP_DEVICE:
+ if (u.r.wLength > 2)
+ goto do_stall;
+ if (dev->is_selfpowered)
+ status = (1 << USB_DEVICE_SELF_POWERED);
+
+ /* don't bother with a request object! */
+ net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
+ writew(status, net2272_reg_addr(dev, EP_DATA));
+ set_fifo_bytecount(&dev->ep[0], 0);
+ allow_status(ep);
+ dev_vdbg(dev->dev, "device stat %02x\n", status);
+ goto next_endpoints;
+ case USB_RECIP_INTERFACE:
+ if (u.r.wLength > 2)
+ goto do_stall;
+
+ /* don't bother with a request object! */
+ net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
+ writew(status, net2272_reg_addr(dev, EP_DATA));
+ set_fifo_bytecount(&dev->ep[0], 0);
+ allow_status(ep);
+ dev_vdbg(dev->dev, "interface status %02x\n", status);
+ goto next_endpoints;
+ }
+
+ break;
+ }
+ case USB_REQ_CLEAR_FEATURE: {
+ struct net2272_ep *e;
+
+ if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (u.r.wValue != USB_ENDPOINT_HALT ||
+ u.r.wLength != 0)
+ goto do_stall;
+ e = net2272_get_ep_by_addr(dev, u.r.wIndex);
+ if (!e)
+ goto do_stall;
+ if (e->wedged) {
+ dev_vdbg(dev->dev, "%s wedged, halt not cleared\n",
+ ep->ep.name);
+ } else {
+ dev_vdbg(dev->dev, "%s clear halt\n", ep->ep.name);
+ clear_halt(e);
+ }
+ allow_status(ep);
+ goto next_endpoints;
+ }
+ case USB_REQ_SET_FEATURE: {
+ struct net2272_ep *e;
+
+ if (u.r.bRequestType == USB_RECIP_DEVICE) {
+ if (u.r.wIndex != NORMAL_OPERATION)
+ net2272_set_test_mode(dev, (u.r.wIndex >> 8));
+ allow_status(ep);
+ dev_vdbg(dev->dev, "test mode: %d\n", u.r.wIndex);
+ goto next_endpoints;
+ } else if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (u.r.wValue != USB_ENDPOINT_HALT ||
+ u.r.wLength != 0)
+ goto do_stall;
+ e = net2272_get_ep_by_addr(dev, u.r.wIndex);
+ if (!e)
+ goto do_stall;
+ set_halt(e);
+ allow_status(ep);
+ dev_vdbg(dev->dev, "%s set halt\n", ep->ep.name);
+ goto next_endpoints;
+ }
+ case USB_REQ_SET_ADDRESS: {
+ net2272_write(dev, OURADDR, u.r.wValue & 0xff);
+ allow_status(ep);
+ break;
+ }
+ default:
+ delegate:
+ dev_vdbg(dev->dev, "setup %02x.%02x v%04x i%04x "
+ "ep_cfg %08x\n",
+ u.r.bRequestType, u.r.bRequest,
+ u.r.wValue, u.r.wIndex,
+ net2272_ep_read(ep, EP_CFG));
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
+
+ /* stall ep0 on error */
+ if (tmp < 0) {
+ do_stall:
+ dev_vdbg(dev->dev, "req %02x.%02x protocol STALL; stat %d\n",
+ u.r.bRequestType, u.r.bRequest, tmp);
+ dev->protocol_stall = 1;
+ }
+ /* endpoint dma irq? */
+ } else if (stat & (1 << DMA_DONE_INTERRUPT)) {
+ net2272_cancel_dma(dev);
+ net2272_write(dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT);
+ stat &= ~(1 << DMA_DONE_INTERRUPT);
+ num = (net2272_read(dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT))
+ ? 2 : 1;
+
+ ep = &dev->ep[num];
+ net2272_handle_dma(ep);
+ }
+
+ next_endpoints:
+ /* endpoint data irq? */
+ scratch = stat & 0x0f;
+ stat &= ~0x0f;
+ for (num = 0; scratch; num++) {
+ u8 t;
+
+ /* does this endpoint's FIFO and queue need tending? */
+ t = 1 << num;
+ if ((scratch & t) == 0)
+ continue;
+ scratch ^= t;
+
+ ep = &dev->ep[num];
+ net2272_handle_ep(ep);
+ }
+
+ /* some interrupts we can just ignore */
+ stat &= ~(1 << SOF_INTERRUPT);
+
+ if (stat)
+ dev_dbg(dev->dev, "unhandled irqstat0 %02x\n", stat);
+}
+
+static void
+net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
+{
+ u8 tmp, mask;
+
+ /* after disconnect there's nothing else to do! */
+ tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
+ mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED);
+
+ if (stat & tmp) {
+ net2272_write(dev, IRQSTAT1, tmp);
+ if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
+ ((net2272_read(dev, USBCTL1) & mask) == 0))
+ || ((net2272_read(dev, USBCTL1) & (1 << VBUS_PIN))
+ == 0))
+ && (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
+ dev_dbg(dev->dev, "disconnect %s\n",
+ dev->driver->driver.name);
+ stop_activity(dev, dev->driver);
+ net2272_ep0_start(dev);
+ return;
+ }
+ stat &= ~tmp;
+
+ if (!stat)
+ return;
+ }
+
+ tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
+ if (stat & tmp) {
+ net2272_write(dev, IRQSTAT1, tmp);
+ if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
+ if (dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+ if (!enable_suspend) {
+ stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
+ dev_dbg(dev->dev, "Suspend disabled, ignoring\n");
+ }
+ } else {
+ if (dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ }
+ stat &= ~tmp;
+ }
+
+ /* clear any other status/irqs */
+ if (stat)
+ net2272_write(dev, IRQSTAT1, stat);
+
+ /* some status we can just ignore */
+ stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
+ | (1 << SUSPEND_REQUEST_INTERRUPT)
+ | (1 << RESUME_INTERRUPT));
+ if (!stat)
+ return;
+ else
+ dev_dbg(dev->dev, "unhandled irqstat1 %02x\n", stat);
+}
+
+static irqreturn_t net2272_irq(int irq, void *_dev)
+{
+ struct net2272 *dev = _dev;
+#if defined(PLX_PCI_RDK) || defined(PLX_PCI_RDK2)
+ u32 intcsr;
+#endif
+#if defined(PLX_PCI_RDK)
+ u8 dmareq;
+#endif
+ spin_lock(&dev->lock);
+#if defined(PLX_PCI_RDK)
+ intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR);
+
+ if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) {
+ writel(intcsr & ~(1 << PCI_INTERRUPT_ENABLE),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+ net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1));
+ net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0));
+ intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR);
+ writel(intcsr | (1 << PCI_INTERRUPT_ENABLE),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+ }
+ if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) {
+ writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
+ dev->rdk1.plx9054_base_addr + DMACSR0);
+
+ dmareq = net2272_read(dev, DMAREQ);
+ if (dmareq & 0x01)
+ net2272_handle_dma(&dev->ep[2]);
+ else
+ net2272_handle_dma(&dev->ep[1]);
+ }
+#endif
+#if defined(PLX_PCI_RDK2)
+ /* see if PCI int for us by checking irqstat */
+ intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT);
+ if (!intcsr & (1 << NET2272_PCI_IRQ))
+ return IRQ_NONE;
+ /* check dma interrupts */
+#endif
+ /* Platform/devcice interrupt handler */
+#if !defined(PLX_PCI_RDK)
+ net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1));
+ net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0));
+#endif
+ spin_unlock(&dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int net2272_present(struct net2272 *dev)
+{
+ /*
+ * Quick test to see if CPU can communicate properly with the NET2272.
+ * Verifies connection using writes and reads to write/read and
+ * read-only registers.
+ *
+ * This routine is strongly recommended especially during early bring-up
+ * of new hardware, however for designs that do not apply Power On System
+ * Tests (POST) it may discarded (or perhaps minimized).
+ */
+ unsigned int ii;
+ u8 val, refval;
+
+ /* Verify NET2272 write/read SCRATCH register can write and read */
+ refval = net2272_read(dev, SCRATCH);
+ for (ii = 0; ii < 0x100; ii += 7) {
+ net2272_write(dev, SCRATCH, ii);
+ val = net2272_read(dev, SCRATCH);
+ if (val != ii) {
+ dev_dbg(dev->dev,
+ "%s: write/read SCRATCH register test failed: "
+ "wrote:0x%2.2x, read:0x%2.2x\n",
+ __func__, ii, val);
+ return -EINVAL;
+ }
+ }
+ /* To be nice, we write the original SCRATCH value back: */
+ net2272_write(dev, SCRATCH, refval);
+
+ /* Verify NET2272 CHIPREV register is read-only: */
+ refval = net2272_read(dev, CHIPREV_2272);
+ for (ii = 0; ii < 0x100; ii += 7) {
+ net2272_write(dev, CHIPREV_2272, ii);
+ val = net2272_read(dev, CHIPREV_2272);
+ if (val != refval) {
+ dev_dbg(dev->dev,
+ "%s: write/read CHIPREV register test failed: "
+ "wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n",
+ __func__, ii, val, refval);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Verify NET2272's "NET2270 legacy revision" register
+ * - NET2272 has two revision registers. The NET2270 legacy revision
+ * register should read the same value, regardless of the NET2272
+ * silicon revision. The legacy register applies to NET2270
+ * firmware being applied to the NET2272.
+ */
+ val = net2272_read(dev, CHIPREV_LEGACY);
+ if (val != NET2270_LEGACY_REV) {
+ /*
+ * Unexpected legacy revision value
+ * - Perhaps the chip is a NET2270?
+ */
+ dev_dbg(dev->dev,
+ "%s: WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n"
+ " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n",
+ __func__, NET2270_LEGACY_REV, val);
+ return -EINVAL;
+ }
+
+ /*
+ * Verify NET2272 silicon revision
+ * - This revision register is appropriate for the silicon version
+ * of the NET2272
+ */
+ val = net2272_read(dev, CHIPREV_2272);
+ switch (val) {
+ case CHIPREV_NET2272_R1:
+ /*
+ * NET2272 Rev 1 has DMA related errata:
+ * - Newer silicon (Rev 1A or better) required
+ */
+ dev_dbg(dev->dev,
+ "%s: Rev 1 detected: newer silicon recommended for DMA support\n",
+ __func__);
+ break;
+ case CHIPREV_NET2272_R1A:
+ break;
+ default:
+ /* NET2272 silicon version *may* not work with this firmware */
+ dev_dbg(dev->dev,
+ "%s: unexpected silicon revision register value: "
+ " CHIPREV_2272: 0x%2.2x\n",
+ __func__, val);
+ /*
+ * Return Success, even though the chip rev is not an expected value
+ * - Older, pre-built firmware can attempt to operate on newer silicon
+ * - Often, new silicon is perfectly compatible
+ */
+ }
+
+ /* Success: NET2272 checks out OK */
+ return 0;
+}
+
+static void
+net2272_gadget_release(struct device *_dev)
+{
+ struct net2272 *dev = dev_get_drvdata(_dev);
+ kfree(dev);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void __devexit
+net2272_remove(struct net2272 *dev)
+{
+ usb_del_gadget_udc(&dev->gadget);
+
+ /* start with the driver above us */
+ if (dev->driver) {
+ /* should have been done already by driver model core */
+ dev_warn(dev->dev, "pci remove, driver '%s' is still registered\n",
+ dev->driver->driver.name);
+ usb_gadget_unregister_driver(dev->driver);
+ }
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->base_addr);
+
+ device_unregister(&dev->gadget.dev);
+ device_remove_file(dev->dev, &dev_attr_registers);
+
+ dev_info(dev->dev, "unbind\n");
+ the_controller = NULL;
+}
+
+static struct net2272 * __devinit
+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);
+ }
+
+ /* alloc, and start init */
+ ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&ret->lock);
+ ret->irq = irq;
+ ret->dev = dev;
+ ret->gadget.ops = &net2272_ops;
+ ret->gadget.is_dualspeed = 1;
+
+ /* the "gadget" abstracts/virtualizes the controller */
+ dev_set_name(&ret->gadget.dev, "gadget");
+ ret->gadget.dev.parent = dev;
+ ret->gadget.dev.dma_mask = dev->dma_mask;
+ ret->gadget.dev.release = net2272_gadget_release;
+ ret->gadget.name = driver_name;
+
+ return ret;
+}
+
+static int __devinit
+net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
+{
+ int ret;
+
+ /* See if there... */
+ if (net2272_present(dev)) {
+ dev_warn(dev->dev, "2272 not found!\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ net2272_usb_reset(dev);
+ net2272_usb_reinit(dev);
+
+ ret = request_irq(dev->irq, net2272_irq, irqflags, driver_name, dev);
+ if (ret) {
+ dev_err(dev->dev, "request interrupt %i failed\n", dev->irq);
+ goto err;
+ }
+
+ dev->chiprev = net2272_read(dev, CHIPREV_2272);
+
+ /* done */
+ dev_info(dev->dev, "%s\n", driver_desc);
+ dev_info(dev->dev, "irq %i, mem %p, chip rev %04x, dma %s\n",
+ dev->irq, dev->base_addr, dev->chiprev,
+ 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;
+ ret = device_create_file(dev->dev, &dev_attr_registers);
+ if (ret)
+ goto err_dev_reg;
+
+ ret = usb_add_gadget_udc(dev->dev, &dev->gadget);
+ if (ret)
+ goto err_add_udc;
+
+ return 0;
+
+err_add_udc:
+ device_remove_file(dev->dev, &dev_attr_registers);
+ err_dev_reg:
+ device_unregister(&dev->gadget.dev);
+ err_irq:
+ free_irq(dev->irq, dev);
+ err:
+ return ret;
+}
+
+#ifdef CONFIG_PCI
+
+/*
+ * wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us
+ */
+
+static int __devinit
+net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev)
+{
+ unsigned long resource, len, tmp;
+ void __iomem *mem_mapped_addr[4];
+ int ret, i;
+
+ /*
+ * BAR 0 holds PLX 9054 config registers
+ * BAR 1 is i/o memory; unused here
+ * BAR 2 holds EPLD config registers
+ * BAR 3 holds NET2272 registers
+ */
+
+ /* Find and map all address spaces */
+ for (i = 0; i < 4; ++i) {
+ if (i == 1)
+ continue; /* BAR1 unused */
+
+ resource = pci_resource_start(pdev, i);
+ len = pci_resource_len(pdev, i);
+
+ if (!request_mem_region(resource, len, driver_name)) {
+ dev_dbg(dev->dev, "controller already in use\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ mem_mapped_addr[i] = ioremap_nocache(resource, len);
+ if (mem_mapped_addr[i] == NULL) {
+ release_mem_region(resource, len);
+ dev_dbg(dev->dev, "can't map memory\n");
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+
+ dev->rdk1.plx9054_base_addr = mem_mapped_addr[0];
+ dev->rdk1.epld_base_addr = mem_mapped_addr[2];
+ dev->base_addr = mem_mapped_addr[3];
+
+ /* Set PLX 9054 bus width (16 bits) */
+ tmp = readl(dev->rdk1.plx9054_base_addr + LBRD1);
+ writel((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT,
+ dev->rdk1.plx9054_base_addr + LBRD1);
+
+ /* Enable PLX 9054 Interrupts */
+ writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) |
+ (1 << PCI_INTERRUPT_ENABLE) |
+ (1 << LOCAL_INTERRUPT_INPUT_ENABLE),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+
+ writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
+ dev->rdk1.plx9054_base_addr + DMACSR0);
+
+ /* reset */
+ writeb((1 << EPLD_DMA_ENABLE) |
+ (1 << DMA_CTL_DACK) |
+ (1 << DMA_TIMEOUT_ENABLE) |
+ (1 << USER) |
+ (0 << MPX_MODE) |
+ (1 << BUSWIDTH) |
+ (1 << NET2272_RESET),
+ dev->base_addr + EPLD_IO_CONTROL_REGISTER);
+
+ mb();
+ writeb(readb(dev->base_addr + EPLD_IO_CONTROL_REGISTER) &
+ ~(1 << NET2272_RESET),
+ dev->base_addr + EPLD_IO_CONTROL_REGISTER);
+ udelay(200);
+
+ return 0;
+
+ err:
+ while (--i >= 0) {
+ iounmap(mem_mapped_addr[i]);
+ release_mem_region(pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+ }
+
+ return ret;
+}
+
+static int __devinit
+net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev)
+{
+ unsigned long resource, len;
+ void __iomem *mem_mapped_addr[2];
+ int ret, i;
+
+ /*
+ * BAR 0 holds FGPA config registers
+ * BAR 1 holds NET2272 registers
+ */
+
+ /* Find and map all address spaces, bar2-3 unused in rdk 2 */
+ for (i = 0; i < 2; ++i) {
+ resource = pci_resource_start(pdev, i);
+ len = pci_resource_len(pdev, i);
+
+ if (!request_mem_region(resource, len, driver_name)) {
+ dev_dbg(dev->dev, "controller already in use\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ mem_mapped_addr[i] = ioremap_nocache(resource, len);
+ if (mem_mapped_addr[i] == NULL) {
+ release_mem_region(resource, len);
+ dev_dbg(dev->dev, "can't map memory\n");
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+
+ dev->rdk2.fpga_base_addr = mem_mapped_addr[0];
+ dev->base_addr = mem_mapped_addr[1];
+
+ mb();
+ /* Set 2272 bus width (16 bits) and reset */
+ writel((1 << CHIP_RESET), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK);
+ udelay(200);
+ writel((1 << BUS_WIDTH), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK);
+ /* Print fpga version number */
+ dev_info(dev->dev, "RDK2 FPGA version %08x\n",
+ readl(dev->rdk2.fpga_base_addr + RDK2_FPGAREV));
+ /* Enable FPGA Interrupts */
+ writel((1 << NET2272_PCI_IRQ), dev->rdk2.fpga_base_addr + RDK2_IRQENB);
+
+ return 0;
+
+ err:
+ while (--i >= 0) {
+ iounmap(mem_mapped_addr[i]);
+ release_mem_region(pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+ }
+
+ return ret;
+}
+
+static int __devinit
+net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net2272 *dev;
+ int ret;
+
+ dev = net2272_probe_init(&pdev->dev, pdev->irq);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+ dev->dev_id = pdev->device;
+
+ if (pci_enable_device(pdev) < 0) {
+ ret = -ENODEV;
+ goto err_free;
+ }
+
+ pci_set_master(pdev);
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_RDK1: ret = net2272_rdk1_probe(pdev, dev); break;
+ case PCI_DEVICE_ID_RDK2: ret = net2272_rdk2_probe(pdev, dev); break;
+ default: BUG();
+ }
+ if (ret)
+ goto err_pci;
+
+ ret = net2272_probe_fin(dev, 0);
+ if (ret)
+ goto err_pci;
+
+ pci_set_drvdata(pdev, dev);
+
+ return 0;
+
+ err_pci:
+ pci_disable_device(pdev);
+ err_free:
+ kfree(dev);
+
+ return ret;
+}
+
+static void __devexit
+net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev)
+{
+ int i;
+
+ /* disable PLX 9054 interrupts */
+ writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) &
+ ~(1 << PCI_INTERRUPT_ENABLE),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+
+ /* clean up resources allocated during probe() */
+ iounmap(dev->rdk1.plx9054_base_addr);
+ iounmap(dev->rdk1.epld_base_addr);
+
+ for (i = 0; i < 4; ++i) {
+ if (i == 1)
+ continue; /* BAR1 unused */
+ release_mem_region(pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+ }
+}
+
+static void __devexit
+net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev)
+{
+ int i;
+
+ /* disable fpga interrupts
+ writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) &
+ ~(1 << PCI_INTERRUPT_ENABLE),
+ dev->rdk1.plx9054_base_addr + INTCSR);
+ */
+
+ /* clean up resources allocated during probe() */
+ iounmap(dev->rdk2.fpga_base_addr);
+
+ for (i = 0; i < 2; ++i)
+ release_mem_region(pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+}
+
+static void __devexit
+net2272_pci_remove(struct pci_dev *pdev)
+{
+ struct net2272 *dev = pci_get_drvdata(pdev);
+
+ net2272_remove(dev);
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_RDK1: net2272_rdk1_remove(pdev, dev); break;
+ case PCI_DEVICE_ID_RDK2: net2272_rdk2_remove(pdev, dev); break;
+ default: BUG();
+ }
+
+ pci_disable_device(pdev);
+
+ kfree(dev);
+}
+
+/* Table of matching PCI IDs */
+static struct pci_device_id __devinitdata pci_ids[] = {
+ { /* RDK 1 card */
+ .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
+ .class_mask = 0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = PCI_DEVICE_ID_RDK1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { /* RDK 2 card */
+ .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
+ .class_mask = 0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = PCI_DEVICE_ID_RDK2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver net2272_pci_driver = {
+ .name = driver_name,
+ .id_table = pci_ids,
+
+ .probe = net2272_pci_probe,
+ .remove = __devexit_p(net2272_pci_remove),
+};
+
+static int net2272_pci_register(void)
+{
+ return pci_register_driver(&net2272_pci_driver);
+}
+
+static void net2272_pci_unregister(void)
+{
+ pci_unregister_driver(&net2272_pci_driver);
+}
+
+#else
+static inline int net2272_pci_register(void) { return 0; }
+static inline void net2272_pci_unregister(void) { }
+#endif
+
+/*---------------------------------------------------------------------------*/
+
+static int __devinit
+net2272_plat_probe(struct platform_device *pdev)
+{
+ struct net2272 *dev;
+ int ret;
+ unsigned int irqflags;
+ resource_size_t base, len;
+ struct resource *iomem, *iomem_bus, *irq_res;
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iomem_bus = platform_get_resource(pdev, IORESOURCE_BUS, 0);
+ if (!irq_res || !iomem) {
+ dev_err(&pdev->dev, "must provide irq/base addr");
+ return -EINVAL;
+ }
+
+ dev = net2272_probe_init(&pdev->dev, irq_res->start);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ irqflags = 0;
+ if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE)
+ irqflags |= IRQF_TRIGGER_RISING;
+ if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE)
+ irqflags |= IRQF_TRIGGER_FALLING;
+ if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL)
+ irqflags |= IRQF_TRIGGER_HIGH;
+ if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
+ irqflags |= IRQF_TRIGGER_LOW;
+
+ base = iomem->start;
+ len = resource_size(iomem);
+ if (iomem_bus)
+ dev->base_shift = iomem_bus->start;
+
+ if (!request_mem_region(base, len, driver_name)) {
+ dev_dbg(dev->dev, "get request memory region!\n");
+ ret = -EBUSY;
+ goto err;
+ }
+ dev->base_addr = ioremap_nocache(base, len);
+ if (!dev->base_addr) {
+ dev_dbg(dev->dev, "can't map memory\n");
+ ret = -EFAULT;
+ goto err_req;
+ }
+
+ ret = net2272_probe_fin(dev, IRQF_TRIGGER_LOW);
+ if (ret)
+ goto err_io;
+
+ platform_set_drvdata(pdev, dev);
+ 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:
+ iounmap(dev->base_addr);
+ err_req:
+ release_mem_region(base, len);
+ err:
+ return ret;
+}
+
+static int __devexit
+net2272_plat_remove(struct platform_device *pdev)
+{
+ struct net2272 *dev = platform_get_drvdata(pdev);
+
+ net2272_remove(dev);
+
+ release_mem_region(pdev->resource[0].start,
+ resource_size(&pdev->resource[0]));
+
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver net2272_plat_driver = {
+ .probe = net2272_plat_probe,
+ .remove = __devexit_p(net2272_plat_remove),
+ .driver = {
+ .name = driver_name,
+ .owner = THIS_MODULE,
+ },
+ /* FIXME .suspend, .resume */
+};
+MODULE_ALIAS("platform:net2272");
+
+static int __init net2272_init(void)
+{
+ int ret;
+
+ ret = net2272_pci_register();
+ if (ret)
+ return ret;
+ ret = platform_driver_register(&net2272_plat_driver);
+ if (ret)
+ goto err_pci;
+ return ret;
+
+err_pci:
+ net2272_pci_unregister();
+ return ret;
+}
+module_init(net2272_init);
+
+static void __exit net2272_cleanup(void)
+{
+ net2272_pci_unregister();
+ platform_driver_unregister(&net2272_plat_driver);
+}
+module_exit(net2272_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("PLX Technology, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/net2272.h
new file mode 100644
index 000000000000..e59505789359
--- /dev/null
+++ b/drivers/usb/gadget/net2272.h
@@ -0,0 +1,601 @@
+/*
+ * PLX NET2272 high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2006 PLX Technology, Inc.
+ * Copyright (C) 2006-2011 Analog Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 __NET2272_H__
+#define __NET2272_H__
+
+/* Main Registers */
+#define REGADDRPTR 0x00
+#define REGDATA 0x01
+#define IRQSTAT0 0x02
+#define ENDPOINT_0_INTERRUPT 0
+#define ENDPOINT_A_INTERRUPT 1
+#define ENDPOINT_B_INTERRUPT 2
+#define ENDPOINT_C_INTERRUPT 3
+#define VIRTUALIZED_ENDPOINT_INTERRUPT 4
+#define SETUP_PACKET_INTERRUPT 5
+#define DMA_DONE_INTERRUPT 6
+#define SOF_INTERRUPT 7
+#define IRQSTAT1 0x03
+#define CONTROL_STATUS_INTERRUPT 1
+#define VBUS_INTERRUPT 2
+#define SUSPEND_REQUEST_INTERRUPT 3
+#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4
+#define RESUME_INTERRUPT 5
+#define ROOT_PORT_RESET_INTERRUPT 6
+#define RESET_STATUS 7
+#define PAGESEL 0x04
+#define DMAREQ 0x1c
+#define DMA_ENDPOINT_SELECT 0
+#define DREQ_POLARITY 1
+#define DACK_POLARITY 2
+#define EOT_POLARITY 3
+#define DMA_CONTROL_DACK 4
+#define DMA_REQUEST_ENABLE 5
+#define DMA_REQUEST 6
+#define DMA_BUFFER_VALID 7
+#define SCRATCH 0x1d
+#define IRQENB0 0x20
+#define ENDPOINT_0_INTERRUPT_ENABLE 0
+#define ENDPOINT_A_INTERRUPT_ENABLE 1
+#define ENDPOINT_B_INTERRUPT_ENABLE 2
+#define ENDPOINT_C_INTERRUPT_ENABLE 3
+#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4
+#define SETUP_PACKET_INTERRUPT_ENABLE 5
+#define DMA_DONE_INTERRUPT_ENABLE 6
+#define SOF_INTERRUPT_ENABLE 7
+#define IRQENB1 0x21
+#define VBUS_INTERRUPT_ENABLE 2
+#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
+#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4
+#define RESUME_INTERRUPT_ENABLE 5
+#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6
+#define LOCCTL 0x22
+#define DATA_WIDTH 0
+#define LOCAL_CLOCK_OUTPUT 1
+#define LOCAL_CLOCK_OUTPUT_OFF 0
+#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1
+#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2
+#define LOCAL_CLOCK_OUTPUT_15MHZ 3
+#define LOCAL_CLOCK_OUTPUT_30MHZ 4
+#define LOCAL_CLOCK_OUTPUT_60MHZ 5
+#define DMA_SPLIT_BUS_MODE 4
+#define BYTE_SWAP 5
+#define BUFFER_CONFIGURATION 6
+#define BUFFER_CONFIGURATION_EPA512_EPB512 0
+#define BUFFER_CONFIGURATION_EPA1024_EPB512 1
+#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2
+#define BUFFER_CONFIGURATION_EPA1024DB 3
+#define CHIPREV_LEGACY 0x23
+#define NET2270_LEGACY_REV 0x40
+#define LOCCTL1 0x24
+#define DMA_MODE 0
+#define SLOW_DREQ 0
+#define FAST_DREQ 1
+#define BURST_MODE 2
+#define DMA_DACK_ENABLE 2
+#define CHIPREV_2272 0x25
+#define CHIPREV_NET2272_R1 0x10
+#define CHIPREV_NET2272_R1A 0x11
+/* USB Registers */
+#define USBCTL0 0x18
+#define IO_WAKEUP_ENABLE 1
+#define USB_DETECT_ENABLE 3
+#define USB_ROOT_PORT_WAKEUP_ENABLE 5
+#define USBCTL1 0x19
+#define VBUS_PIN 0
+#define USB_FULL_SPEED 1
+#define USB_HIGH_SPEED 2
+#define GENERATE_RESUME 3
+#define VIRTUAL_ENDPOINT_ENABLE 4
+#define FRAME0 0x1a
+#define FRAME1 0x1b
+#define OURADDR 0x30
+#define FORCE_IMMEDIATE 7
+#define USBDIAG 0x31
+#define FORCE_TRANSMIT_CRC_ERROR 0
+#define PREVENT_TRANSMIT_BIT_STUFF 1
+#define FORCE_RECEIVE_ERROR 2
+#define FAST_TIMES 4
+#define USBTEST 0x32
+#define TEST_MODE_SELECT 0
+#define NORMAL_OPERATION 0
+#define TEST_J 1
+#define TEST_K 2
+#define TEST_SE0_NAK 3
+#define TEST_PACKET 4
+#define TEST_FORCE_ENABLE 5
+#define XCVRDIAG 0x33
+#define FORCE_FULL_SPEED 2
+#define FORCE_HIGH_SPEED 3
+#define OPMODE 4
+#define NORMAL_OPERATION 0
+#define NON_DRIVING 1
+#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2
+#define LINESTATE 6
+#define SE0_STATE 0
+#define J_STATE 1
+#define K_STATE 2
+#define SE1_STATE 3
+#define VIRTOUT0 0x34
+#define VIRTOUT1 0x35
+#define VIRTIN0 0x36
+#define VIRTIN1 0x37
+#define SETUP0 0x40
+#define SETUP1 0x41
+#define SETUP2 0x42
+#define SETUP3 0x43
+#define SETUP4 0x44
+#define SETUP5 0x45
+#define SETUP6 0x46
+#define SETUP7 0x47
+/* Endpoint Registers (Paged via PAGESEL) */
+#define EP_DATA 0x05
+#define EP_STAT0 0x06
+#define DATA_IN_TOKEN_INTERRUPT 0
+#define DATA_OUT_TOKEN_INTERRUPT 1
+#define DATA_PACKET_TRANSMITTED_INTERRUPT 2
+#define DATA_PACKET_RECEIVED_INTERRUPT 3
+#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4
+#define NAK_OUT_PACKETS 5
+#define BUFFER_EMPTY 6
+#define BUFFER_FULL 7
+#define EP_STAT1 0x07
+#define TIMEOUT 0
+#define USB_OUT_ACK_SENT 1
+#define USB_OUT_NAK_SENT 2
+#define USB_IN_ACK_RCVD 3
+#define USB_IN_NAK_SENT 4
+#define USB_STALL_SENT 5
+#define LOCAL_OUT_ZLP 6
+#define BUFFER_FLUSH 7
+#define EP_TRANSFER0 0x08
+#define EP_TRANSFER1 0x09
+#define EP_TRANSFER2 0x0a
+#define EP_IRQENB 0x0b
+#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0
+#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1
+#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2
+#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3
+#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4
+#define EP_AVAIL0 0x0c
+#define EP_AVAIL1 0x0d
+#define EP_RSPCLR 0x0e
+#define EP_RSPSET 0x0f
+#define ENDPOINT_HALT 0
+#define ENDPOINT_TOGGLE 1
+#define NAK_OUT_PACKETS_MODE 2
+#define CONTROL_STATUS_PHASE_HANDSHAKE 3
+#define INTERRUPT_MODE 4
+#define AUTOVALIDATE 5
+#define HIDE_STATUS_PHASE 6
+#define ALT_NAK_OUT_PACKETS 7
+#define EP_MAXPKT0 0x28
+#define EP_MAXPKT1 0x29
+#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3
+#define NONE_ADDITIONAL_TRANSACTION 0
+#define ONE_ADDITIONAL_TRANSACTION 1
+#define TWO_ADDITIONAL_TRANSACTION 2
+#define EP_CFG 0x2a
+#define ENDPOINT_NUMBER 0
+#define ENDPOINT_DIRECTION 4
+#define ENDPOINT_TYPE 5
+#define ENDPOINT_ENABLE 7
+#define EP_HBW 0x2b
+#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0
+#define DATA0_PID 0
+#define DATA1_PID 1
+#define DATA2_PID 2
+#define MDATA_PID 3
+#define EP_BUFF_STATES 0x2c
+#define BUFFER_A_STATE 0
+#define BUFFER_B_STATE 2
+#define BUFF_FREE 0
+#define BUFF_VALID 1
+#define BUFF_LCL 2
+#define BUFF_USB 3
+
+/*---------------------------------------------------------------------------*/
+
+#define PCI_DEVICE_ID_RDK1 0x9054
+
+/* PCI-RDK EPLD Registers */
+#define RDK_EPLD_IO_REGISTER1 0x00000000
+#define RDK_EPLD_USB_RESET 0
+#define RDK_EPLD_USB_POWERDOWN 1
+#define RDK_EPLD_USB_WAKEUP 2
+#define RDK_EPLD_USB_EOT 3
+#define RDK_EPLD_DPPULL 4
+#define RDK_EPLD_IO_REGISTER2 0x00000004
+#define RDK_EPLD_BUSWIDTH 0
+#define RDK_EPLD_USER 2
+#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
+#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
+#define RDK_EPLD_STATUS_REGISTER 0x00000008
+#define RDK_EPLD_USB_LRESET 0
+#define RDK_EPLD_REVISION_REGISTER 0x0000000c
+
+/* PCI-RDK PLX 9054 Registers */
+#define INTCSR 0x68
+#define PCI_INTERRUPT_ENABLE 8
+#define LOCAL_INTERRUPT_INPUT_ENABLE 11
+#define LOCAL_INPUT_INTERRUPT_ACTIVE 15
+#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18
+#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19
+#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21
+#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22
+#define CNTRL 0x6C
+#define RELOAD_CONFIGURATION_REGISTERS 29
+#define PCI_ADAPTER_SOFTWARE_RESET 30
+#define DMAMODE0 0x80
+#define LOCAL_BUS_WIDTH 0
+#define INTERNAL_WAIT_STATES 2
+#define TA_READY_INPUT_ENABLE 6
+#define LOCAL_BURST_ENABLE 8
+#define SCATTER_GATHER_MODE 9
+#define DONE_INTERRUPT_ENABLE 10
+#define LOCAL_ADDRESSING_MODE 11
+#define DEMAND_MODE 12
+#define DMA_EOT_ENABLE 14
+#define FAST_SLOW_TERMINATE_MODE_SELECT 15
+#define DMA_CHANNEL_INTERRUPT_SELECT 17
+#define DMAPADR0 0x84
+#define DMALADR0 0x88
+#define DMASIZ0 0x8c
+#define DMADPR0 0x90
+#define DESCRIPTOR_LOCATION 0
+#define END_OF_CHAIN 1
+#define INTERRUPT_AFTER_TERMINAL_COUNT 2
+#define DIRECTION_OF_TRANSFER 3
+#define DMACSR0 0xa8
+#define CHANNEL_ENABLE 0
+#define CHANNEL_START 1
+#define CHANNEL_ABORT 2
+#define CHANNEL_CLEAR_INTERRUPT 3
+#define CHANNEL_DONE 4
+#define DMATHR 0xb0
+#define LBRD1 0xf8
+#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0
+#define W8_BIT 0
+#define W16_BIT 1
+
+/* Special OR'ing of INTCSR bits */
+#define LOCAL_INTERRUPT_TEST \
+ ((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_INTERRUPT_INPUT_ENABLE))
+
+#define DMA_CHANNEL_0_TEST \
+ ((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE))
+
+#define DMA_CHANNEL_1_TEST \
+ ((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \
+ (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE))
+
+/* EPLD Registers */
+#define RDK_EPLD_IO_REGISTER1 0x00000000
+#define RDK_EPLD_USB_RESET 0
+#define RDK_EPLD_USB_POWERDOWN 1
+#define RDK_EPLD_USB_WAKEUP 2
+#define RDK_EPLD_USB_EOT 3
+#define RDK_EPLD_DPPULL 4
+#define RDK_EPLD_IO_REGISTER2 0x00000004
+#define RDK_EPLD_BUSWIDTH 0
+#define RDK_EPLD_USER 2
+#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
+#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
+#define RDK_EPLD_STATUS_REGISTER 0x00000008
+#define RDK_EPLD_USB_LRESET 0
+#define RDK_EPLD_REVISION_REGISTER 0x0000000c
+
+#define EPLD_IO_CONTROL_REGISTER 0x400
+#define NET2272_RESET 0
+#define BUSWIDTH 1
+#define MPX_MODE 3
+#define USER 4
+#define DMA_TIMEOUT_ENABLE 5
+#define DMA_CTL_DACK 6
+#define EPLD_DMA_ENABLE 7
+#define EPLD_DMA_CONTROL_REGISTER 0x800
+#define SPLIT_DMA_MODE 0
+#define SPLIT_DMA_DIRECTION 1
+#define SPLIT_DMA_ENABLE 2
+#define SPLIT_DMA_INTERRUPT_ENABLE 3
+#define SPLIT_DMA_INTERRUPT 4
+#define EPLD_DMA_MODE 5
+#define EPLD_DMA_CONTROLLER_ENABLE 7
+#define SPLIT_DMA_ADDRESS_LOW 0xc00
+#define SPLIT_DMA_ADDRESS_HIGH 0x1000
+#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400
+#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800
+#define EPLD_REVISION_REGISTER 0x1c00
+#define SPLIT_DMA_RAM 0x4000
+#define DMA_RAM_SIZE 0x1000
+
+/*---------------------------------------------------------------------------*/
+
+#define PCI_DEVICE_ID_RDK2 0x3272
+
+/* PCI-RDK version 2 registers */
+
+/* Main Control Registers */
+
+#define RDK2_IRQENB 0x00
+#define RDK2_IRQSTAT 0x04
+#define PB7 23
+#define PB6 22
+#define PB5 21
+#define PB4 20
+#define PB3 19
+#define PB2 18
+#define PB1 17
+#define PB0 16
+#define GP3 23
+#define GP2 23
+#define GP1 23
+#define GP0 23
+#define DMA_RETRY_ABORT 6
+#define DMA_PAUSE_DONE 5
+#define DMA_ABORT_DONE 4
+#define DMA_OUT_FIFO_TRANSFER_DONE 3
+#define DMA_LOCAL_DONE 2
+#define DMA_PCI_DONE 1
+#define NET2272_PCI_IRQ 0
+
+#define RDK2_LOCCTLRDK 0x08
+#define CHIP_RESET 3
+#define SPLIT_DMA 2
+#define MULTIPLEX_MODE 1
+#define BUS_WIDTH 0
+
+#define RDK2_GPIOCTL 0x10
+#define GP3_OUT_ENABLE 7
+#define GP2_OUT_ENABLE 6
+#define GP1_OUT_ENABLE 5
+#define GP0_OUT_ENABLE 4
+#define GP3_DATA 3
+#define GP2_DATA 2
+#define GP1_DATA 1
+#define GP0_DATA 0
+
+#define RDK2_LEDSW 0x14
+#define LED3 27
+#define LED2 26
+#define LED1 25
+#define LED0 24
+#define PBUTTON 16
+#define DIPSW 0
+
+#define RDK2_DIAG 0x18
+#define RDK2_FAST_TIMES 2
+#define FORCE_PCI_SERR 1
+#define FORCE_PCI_INT 0
+#define RDK2_FPGAREV 0x1C
+
+/* Dma Control registers */
+#define RDK2_DMACTL 0x80
+#define ADDR_HOLD 24
+#define RETRY_COUNT 16 /* 23:16 */
+#define FIFO_THRESHOLD 11 /* 15:11 */
+#define MEM_WRITE_INVALIDATE 10
+#define READ_MULTIPLE 9
+#define READ_LINE 8
+#define RDK2_DMA_MODE 6 /* 7:6 */
+#define CONTROL_DACK 5
+#define EOT_ENABLE 4
+#define EOT_POLARITY 3
+#define DACK_POLARITY 2
+#define DREQ_POLARITY 1
+#define DMA_ENABLE 0
+
+#define RDK2_DMASTAT 0x84
+#define GATHER_COUNT 12 /* 14:12 */
+#define FIFO_COUNT 6 /* 11:6 */
+#define FIFO_FLUSH 5
+#define FIFO_TRANSFER 4
+#define PAUSE_DONE 3
+#define ABORT_DONE 2
+#define DMA_ABORT 1
+#define DMA_START 0
+
+#define RDK2_DMAPCICOUNT 0x88
+#define DMA_DIRECTION 31
+#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */
+
+#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */
+
+#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */
+
+/*---------------------------------------------------------------------------*/
+
+#define REG_INDEXED_THRESHOLD (1 << 5)
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+struct net2272_ep {
+ struct usb_ep ep;
+ struct net2272 *dev;
+ unsigned long irqs;
+
+ /* analogous to a host-side qh */
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ unsigned num:8,
+ fifo_size:12,
+ stopped:1,
+ wedged:1,
+ is_in:1,
+ is_iso:1,
+ dma:1,
+ not_empty:1;
+};
+
+struct net2272 {
+ /* each device provides one gadget, several endpoints */
+ struct usb_gadget gadget;
+ struct device *dev;
+ unsigned short dev_id;
+
+ spinlock_t lock;
+ struct net2272_ep ep[4];
+ struct usb_gadget_driver *driver;
+ unsigned protocol_stall:1,
+ softconnect:1,
+ is_selfpowered:1,
+ wakeup:1,
+ dma_eot_polarity:1,
+ dma_dack_polarity:1,
+ dma_dreq_polarity:1,
+ dma_busy:1;
+ u16 chiprev;
+ u8 pagesel;
+
+ unsigned int irq;
+ unsigned short fifo_mode;
+
+ unsigned int base_shift;
+ u16 __iomem *base_addr;
+ union {
+#ifdef CONFIG_PCI
+ struct {
+ void __iomem *plx9054_base_addr;
+ void __iomem *epld_base_addr;
+ } rdk1;
+ struct {
+ /* Bar0, Bar1 is base_addr both mem-mapped */
+ void __iomem *fpga_base_addr;
+ } rdk2;
+#endif
+ };
+};
+
+static void __iomem *
+net2272_reg_addr(struct net2272 *dev, unsigned int reg)
+{
+ return dev->base_addr + (reg << dev->base_shift);
+}
+
+static void
+net2272_write(struct net2272 *dev, unsigned int reg, u8 value)
+{
+ if (reg >= REG_INDEXED_THRESHOLD) {
+ /*
+ * Indexed register; use REGADDRPTR/REGDATA
+ * - Save and restore REGADDRPTR. This prevents REGADDRPTR from
+ * changes between other code sections, but it is time consuming.
+ * - Performance tips: either do not save and restore REGADDRPTR (if it
+ * is safe) or do save/restore operations only in critical sections.
+ u8 tmp = readb(dev->base_addr + REGADDRPTR);
+ */
+ writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
+ writeb(value, net2272_reg_addr(dev, REGDATA));
+ /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
+ } else
+ writeb(value, net2272_reg_addr(dev, reg));
+}
+
+static u8
+net2272_read(struct net2272 *dev, unsigned int reg)
+{
+ u8 ret;
+
+ if (reg >= REG_INDEXED_THRESHOLD) {
+ /*
+ * Indexed register; use REGADDRPTR/REGDATA
+ * - Save and restore REGADDRPTR. This prevents REGADDRPTR from
+ * changes between other code sections, but it is time consuming.
+ * - Performance tips: either do not save and restore REGADDRPTR (if it
+ * is safe) or do save/restore operations only in critical sections.
+ u8 tmp = readb(dev->base_addr + REGADDRPTR);
+ */
+ writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
+ ret = readb(net2272_reg_addr(dev, REGDATA));
+ /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
+ } else
+ ret = readb(net2272_reg_addr(dev, reg));
+
+ return ret;
+}
+
+static void
+net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value)
+{
+ struct net2272 *dev = ep->dev;
+
+ if (dev->pagesel != ep->num) {
+ net2272_write(dev, PAGESEL, ep->num);
+ dev->pagesel = ep->num;
+ }
+ net2272_write(dev, reg, value);
+}
+
+static u8
+net2272_ep_read(struct net2272_ep *ep, unsigned int reg)
+{
+ struct net2272 *dev = ep->dev;
+
+ if (dev->pagesel != ep->num) {
+ net2272_write(dev, PAGESEL, ep->num);
+ dev->pagesel = ep->num;
+ }
+ return net2272_read(dev, reg);
+}
+
+static void allow_status(struct net2272_ep *ep)
+{
+ /* ep0 only */
+ net2272_ep_write(ep, EP_RSPCLR,
+ (1 << CONTROL_STATUS_PHASE_HANDSHAKE) |
+ (1 << ALT_NAK_OUT_PACKETS) |
+ (1 << NAK_OUT_PACKETS_MODE));
+ ep->stopped = 1;
+}
+
+static void set_halt(struct net2272_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE);
+ net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT);
+}
+
+static void clear_halt(struct net2272_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ net2272_ep_write(ep, EP_RSPCLR,
+ (1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE));
+}
+
+/* count (<= 4) bytes in the next fifo write will be valid */
+static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count)
+{
+ /* net2272_ep_write will truncate to u8 for us */
+ net2272_ep_write(ep, EP_TRANSFER2, count >> 16);
+ net2272_ep_write(ep, EP_TRANSFER1, count >> 8);
+ net2272_ep_write(ep, EP_TRANSFER0, count);
+}
+
+struct net2272_request {
+ struct usb_request req;
+ struct list_head queue;
+ unsigned mapped:1,
+ valid:1;
+};
+
+#endif
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 476d88e1ae97..3dd40b4e675c 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -1410,11 +1410,17 @@ 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 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,
};
/*-------------------------------------------------------------------------*/
@@ -1738,62 +1744,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode)
list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
}
-/* just declare this in any driver that really need it */
-extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
-
-/**
- * net2280_set_fifo_mode - change allocation of fifo buffers
- * @gadget: access to the net2280 device that will be updated
- * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- * 1 for two 2kB buffers (ep-a and ep-b only);
- * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
- *
- * returns zero on success, else negative errno. when this succeeds,
- * the contents of gadget->ep_list may have changed.
- *
- * you may only call this function when endpoints a-d are all disabled.
- * use it whenever extra hardware buffering can help performance, such
- * as before enabling "high bandwidth" interrupt endpoints that use
- * maxpacket bigger than 512 (when double buffering would otherwise
- * be unavailable).
- */
-int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
-{
- int i;
- struct net2280 *dev;
- int status = 0;
- unsigned long flags;
-
- if (!gadget)
- return -ENODEV;
- dev = container_of (gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
-
- for (i = 1; i <= 4; i++)
- if (dev->ep [i].desc) {
- status = -EINVAL;
- break;
- }
- if (mode < 0 || mode > 2)
- status = -EINVAL;
- if (status == 0)
- set_fifo_mode (dev, mode);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- if (status == 0) {
- if (mode == 1)
- DEBUG (dev, "fifo: ep-a 2K, ep-b 2K\n");
- else if (mode == 2)
- DEBUG (dev, "fifo: ep-a 2K, ep-b 1K, ep-c 1K\n");
- /* else all are 1K */
- }
- return status;
-}
-EXPORT_SYMBOL (net2280_set_fifo_mode);
-
-/*-------------------------------------------------------------------------*/
-
/* keeping it simple:
* - one bus driver, initted first;
* - one function driver, initted second
@@ -1930,7 +1880,7 @@ static void ep0_start (struct net2280 *dev)
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int net2280_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct net2280 *dev = the_controller;
@@ -1994,7 +1944,6 @@ err_unbind:
dev->driver = NULL;
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
static void
stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
@@ -2022,7 +1971,7 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
usb_reinit (dev);
}
-int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+static int net2280_stop(struct usb_gadget_driver *driver)
{
struct net2280 *dev = the_controller;
unsigned long flags;
@@ -2049,8 +1998,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
-EXPORT_SYMBOL (usb_gadget_unregister_driver);
-
/*-------------------------------------------------------------------------*/
@@ -2732,6 +2679,8 @@ static void net2280_remove (struct pci_dev *pdev)
{
struct net2280 *dev = pci_get_drvdata (pdev);
+ usb_del_gadget_udc(&dev->gadget);
+
BUG_ON(dev->driver);
/* then clean up the resources we allocated during probe() */
@@ -2916,6 +2865,9 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
retval = device_create_file (&pdev->dev, &dev_attr_registers);
if (retval) goto done;
+ retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ if (retval)
+ goto done;
return 0;
done:
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c
index 55ca63ad3506..c7fb7723c014 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/nokia.c
@@ -241,6 +241,7 @@ static struct usb_composite_driver nokia_driver = {
.name = "g_nokia",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = __exit_p(nokia_unbind),
};
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 82fd24935332..740c7daed279 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1375,6 +1375,10 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int omap_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int omap_udc_stop(struct usb_gadget_driver *driver);
+
static struct usb_gadget_ops omap_gadget_ops = {
.get_frame = omap_get_frame,
.wakeup = omap_wakeup,
@@ -1382,6 +1386,8 @@ static struct usb_gadget_ops omap_gadget_ops = {
.vbus_session = omap_vbus_session,
.vbus_draw = omap_vbus_draw,
.pullup = omap_pullup,
+ .start = omap_udc_start,
+ .stop = omap_udc_stop,
};
/*-------------------------------------------------------------------------*/
@@ -2102,7 +2108,7 @@ static inline int machine_without_vbus_sense(void)
);
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int omap_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
int status = -ENODEV;
@@ -2186,9 +2192,8 @@ done:
omap_udc_enable_clock(0);
return status;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+static int omap_udc_stop(struct usb_gadget_driver *driver)
{
unsigned long flags;
int status = -ENODEV;
@@ -2222,8 +2227,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
DBG("unregistered driver '%s'\n", driver->driver.name);
return status;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/*-------------------------------------------------------------------------*/
@@ -2991,9 +2994,16 @@ known:
create_proc_file();
status = device_add(&udc->gadget.dev);
+ if (status)
+ goto cleanup4;
+
+ status = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (!status)
return status;
/* If fail, fall through */
+cleanup4:
+ remove_proc_file();
+
#ifdef USE_ISO
cleanup3:
free_irq(pdev->resource[2].start, udc);
@@ -3029,6 +3039,8 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
if (!udc)
return -ENODEV;
+
+ usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 68dbcc3e4cc2..f96615ab6b77 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -1176,6 +1176,9 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
return -EOPNOTSUPP;
}
+static int pch_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int pch_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pch_udc_ops = {
.get_frame = pch_udc_pcd_get_frame,
.wakeup = pch_udc_pcd_wakeup,
@@ -1183,6 +1186,8 @@ static const struct usb_gadget_ops pch_udc_ops = {
.pullup = pch_udc_pcd_pullup,
.vbus_session = pch_udc_pcd_vbus_session,
.vbus_draw = pch_udc_pcd_vbus_draw,
+ .start = pch_udc_start,
+ .stop = pch_udc_stop,
};
/**
@@ -2690,7 +2695,7 @@ static int init_dma_pools(struct pch_udc_dev *dev)
return 0;
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int pch_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct pch_udc_dev *dev = pch_udc;
@@ -2733,9 +2738,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
dev->connected = 1;
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int pch_udc_stop(struct usb_gadget_driver *driver)
{
struct pch_udc_dev *dev = pch_udc;
@@ -2761,7 +2765,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
pch_udc_set_disconnect(dev);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
static void pch_udc_shutdown(struct pci_dev *pdev)
{
@@ -2778,6 +2781,8 @@ static void pch_udc_remove(struct pci_dev *pdev)
{
struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+ usb_del_gadget_udc(&dev->gadget);
+
/* gadget driver must not be registered */
if (dev->driver)
dev_err(&pdev->dev,
@@ -2953,6 +2958,9 @@ static int pch_udc_probe(struct pci_dev *pdev,
/* Put the device in disconnected state till a driver is bound */
pch_udc_set_disconnect(dev);
+ retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ if (retval)
+ goto finished;
return 0;
finished:
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index 978e6a101bf2..a341dde6f9c3 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -89,8 +89,7 @@ struct printer_dev {
u8 config;
s8 interface;
struct usb_ep *in_ep, *out_ep;
- const struct usb_endpoint_descriptor
- *in, *out;
+
struct list_head rx_reqs; /* List of free RX structs */
struct list_head rx_reqs_active; /* List of Active RX xfers */
struct list_head rx_buffers; /* List of completed xfers */
@@ -898,19 +897,20 @@ set_printer_interface(struct printer_dev *dev)
{
int result = 0;
- dev->in = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc);
+ dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc);
dev->in_ep->driver_data = dev;
- dev->out = ep_desc(dev->gadget, &hs_ep_out_desc, &fs_ep_out_desc);
+ dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc,
+ &fs_ep_out_desc);
dev->out_ep->driver_data = dev;
- result = usb_ep_enable(dev->in_ep, dev->in);
+ result = usb_ep_enable(dev->in_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
}
- result = usb_ep_enable(dev->out_ep, dev->out);
+ result = usb_ep_enable(dev->out_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
@@ -921,8 +921,8 @@ done:
if (result != 0) {
(void) usb_ep_disable(dev->in_ep);
(void) usb_ep_disable(dev->out_ep);
- dev->in = NULL;
- dev->out = NULL;
+ dev->in_ep->desc = NULL;
+ dev->out_ep->desc = NULL;
}
/* caller is responsible for cleanup on error */
@@ -936,12 +936,14 @@ static void printer_reset_interface(struct printer_dev *dev)
DBG(dev, "%s\n", __func__);
- if (dev->in)
+ if (dev->in_ep->desc)
usb_ep_disable(dev->in_ep);
- if (dev->out)
+ if (dev->out_ep->desc)
usb_ep_disable(dev->out_ep);
+ dev->in_ep->desc = NULL;
+ dev->out_ep->desc = NULL;
dev->interface = -1;
}
@@ -1107,9 +1109,9 @@ static void printer_soft_reset(struct printer_dev *dev)
list_add(&req->list, &dev->tx_reqs);
}
- if (usb_ep_enable(dev->in_ep, dev->in))
+ if (usb_ep_enable(dev->in_ep))
DBG(dev, "Failed to enable USB in_ep\n");
- if (usb_ep_enable(dev->out_ep, dev->out))
+ if (usb_ep_enable(dev->out_ep))
DBG(dev, "Failed to enable USB out_ep\n");
wake_up_interruptible(&dev->rx_wait);
@@ -1149,6 +1151,8 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
switch (wValue >> 8) {
case USB_DT_DEVICE:
+ device_desc.bMaxPacketSize0 =
+ gadget->ep0->maxpacket;
value = min(wLength, (u16) sizeof device_desc);
memcpy(req->buf, &device_desc, value);
break;
@@ -1156,6 +1160,12 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_DEVICE_QUALIFIER:
if (!gadget->is_dualspeed)
break;
+ /*
+ * assumes ep0 uses the same value for both
+ * speeds
+ */
+ dev_qualifier.bMaxPacketSize0 =
+ gadget->ep0->maxpacket;
value = min(wLength,
(u16) sizeof dev_qualifier);
memcpy(req->buf, &dev_qualifier, value);
@@ -1451,15 +1461,11 @@ autoconf_fail:
out_ep->driver_data = out_ep; /* claim */
#ifdef CONFIG_USB_GADGET_DUALSPEED
- /* assumes ep0 uses the same value for both speeds ... */
- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
-
- /* and that all endpoints are dual-speed */
+ /* assumes that all endpoints are dual-speed */
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
#endif /* DUALSPEED */
- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
usb_gadget_set_selfpowered(gadget);
if (gadget->is_otg) {
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 774545494cf2..e4e59b4de25d 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -1011,12 +1011,18 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -EOPNOTSUPP;
}
+static int pxa25x_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int pxa25x_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops pxa25x_udc_ops = {
.get_frame = pxa25x_udc_get_frame,
.wakeup = pxa25x_udc_wakeup,
.vbus_session = pxa25x_udc_vbus_session,
.pullup = pxa25x_udc_pullup,
.vbus_draw = pxa25x_udc_vbus_draw,
+ .start = pxa25x_start,
+ .stop = pxa25x_stop,
};
/*-------------------------------------------------------------------------*/
@@ -1263,7 +1269,7 @@ static void udc_enable (struct pxa25x_udc *dev)
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int pxa25x_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct pxa25x_udc *dev = the_controller;
@@ -1322,7 +1328,6 @@ fail:
bind_fail:
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
static void
stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
@@ -1351,7 +1356,7 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
udc_reinit(dev);
}
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int pxa25x_stop(struct usb_gadget_driver *driver)
{
struct pxa25x_udc *dev = the_controller;
@@ -1379,8 +1384,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
dump_state(dev);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/*-------------------------------------------------------------------------*/
@@ -2231,8 +2234,11 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
#endif
create_debug_files(dev);
- return 0;
+ retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ if (!retval)
+ return retval;
+ remove_debug_files(dev);
#ifdef CONFIG_ARCH_LUBBOCK
lubbock_fail0:
free_irq(LUBBOCK_USB_DISC_IRQ, dev);
@@ -2261,6 +2267,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
{
struct pxa25x_udc *dev = platform_get_drvdata(pdev);
+ usb_del_gadget_udc(&dev->gadget);
if (dev->driver)
return -EBUSY;
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 57607696735c..85b68c75dc9d 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1680,12 +1680,18 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -EOPNOTSUPP;
}
+static int pxa27x_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int pxa27x_udc_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops pxa_udc_ops = {
.get_frame = pxa_udc_get_frame,
.wakeup = pxa_udc_wakeup,
.pullup = pxa_udc_pullup,
.vbus_session = pxa_udc_vbus_session,
.vbus_draw = pxa_udc_vbus_draw,
+ .start = pxa27x_udc_start,
+ .stop = pxa27x_udc_stop,
};
/**
@@ -1791,7 +1797,7 @@ static void udc_enable(struct pxa_udc *udc)
}
/**
- * usb_gadget_probe_driver - Register gadget driver
+ * pxa27x_start - Register gadget driver
* @driver: gadget driver
* @bind: bind function
*
@@ -1805,7 +1811,7 @@ static void udc_enable(struct pxa_udc *udc)
*
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int pxa27x_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct pxa_udc *udc = the_controller;
@@ -1860,8 +1866,6 @@ add_fail:
udc->gadget.dev.driver = NULL;
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-
/**
* stop_activity - Stops udc endpoints
@@ -1888,12 +1892,12 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
}
/**
- * usb_gadget_unregister_driver - Unregister the gadget driver
+ * pxa27x_udc_stop - Unregister the gadget driver
* @driver: gadget driver
*
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
*/
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
{
struct pxa_udc *udc = the_controller;
@@ -1917,7 +1921,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return otg_set_peripheral(udc->transceiver, NULL);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/**
* handle_ep0_ctrl_req - handle control endpoint control request
@@ -2516,9 +2519,14 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
driver_name, IRQ_USB, retval);
goto err_irq;
}
+ retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+ if (retval)
+ goto err_add_udc;
pxa_init_debugfs(udc);
return 0;
+err_add_udc:
+ free_irq(udc->irq, udc);
err_irq:
iounmap(udc->regs);
err_map:
@@ -2537,6 +2545,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)
struct pxa_udc *udc = platform_get_drvdata(_dev);
int gpio = udc->mach->gpio_pullup;
+ usb_del_gadget_udc(&udc->gadget);
usb_gadget_unregister_driver(udc->driver);
free_irq(udc->irq, udc);
pxa_cleanup_debugfs(udc);
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index 6dcc1f68fa60..50991e5bd5e8 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006-2009 Renesas Solutions Corp.
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
@@ -576,7 +576,11 @@ static void init_controller(struct r8a66597 *r8a66597)
u16 endian = r8a66597->pdata->endian ? BIGEND : 0;
if (r8a66597->pdata->on_chip) {
- r8a66597_bset(r8a66597, 0x04, SYSCFG1);
+ if (r8a66597->pdata->buswait)
+ r8a66597_write(r8a66597, r8a66597->pdata->buswait,
+ SYSCFG1);
+ else
+ r8a66597_write(r8a66597, 0x0f, SYSCFG1);
r8a66597_bset(r8a66597, HSE, SYSCFG0);
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
@@ -618,6 +622,7 @@ static void disable_controller(struct r8a66597 *r8a66597)
{
if (r8a66597->pdata->on_chip) {
r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+ r8a66597_bclr(r8a66597, UTST, TESTMODE);
/* disable interrupts */
r8a66597_write(r8a66597, 0, INTENB0);
@@ -635,6 +640,7 @@ static void disable_controller(struct r8a66597 *r8a66597)
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
} else {
+ r8a66597_bclr(r8a66597, UTST, TESTMODE);
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
udelay(1);
r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
@@ -999,10 +1005,29 @@ static void clear_feature(struct r8a66597 *r8a66597,
static void set_feature(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
{
+ u16 tmp;
+ int timeout = 3000;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- control_end(r8a66597, 1);
+ switch (le16_to_cpu(ctrl->wValue)) {
+ case USB_DEVICE_TEST_MODE:
+ control_end(r8a66597, 1);
+ /* Wait for the completion of status stage */
+ do {
+ tmp = r8a66597_read(r8a66597, INTSTS0) & CTSQ;
+ udelay(1);
+ } while (tmp != CS_IDST || timeout-- > 0);
+
+ if (tmp == CS_IDST)
+ r8a66597_bset(r8a66597,
+ le16_to_cpu(ctrl->wIndex >> 8),
+ TESTMODE);
+ break;
+ default:
+ pipe_stall(r8a66597, 0);
+ break;
+ }
break;
case USB_RECIP_INTERFACE:
control_end(r8a66597, 1);
@@ -1410,7 +1435,7 @@ static struct usb_ep_ops r8a66597_ep_ops = {
/*-------------------------------------------------------------------------*/
static struct r8a66597 *the_controller;
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int r8a66597_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct r8a66597 *r8a66597 = the_controller;
@@ -1444,6 +1469,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
goto error;
}
+ init_controller(r8a66597);
r8a66597_bset(r8a66597, VBSE, INTENB0);
if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) {
r8a66597_start_xclock(r8a66597);
@@ -1462,9 +1488,8 @@ error:
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int r8a66597_stop(struct usb_gadget_driver *driver)
{
struct r8a66597 *r8a66597 = the_controller;
unsigned long flags;
@@ -1475,20 +1500,16 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
spin_lock_irqsave(&r8a66597->lock, flags);
if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN)
r8a66597_usb_disconnect(r8a66597);
- spin_unlock_irqrestore(&r8a66597->lock, flags);
-
r8a66597_bclr(r8a66597, VBSE, INTENB0);
+ disable_controller(r8a66597);
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
driver->unbind(&r8a66597->gadget);
- init_controller(r8a66597);
- disable_controller(r8a66597);
-
device_del(&r8a66597->gadget.dev);
r8a66597->driver = NULL;
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
static int r8a66597_get_frame(struct usb_gadget *_gadget)
@@ -1497,14 +1518,33 @@ static int r8a66597_get_frame(struct usb_gadget *_gadget)
return r8a66597_read(r8a66597, FRMNUM) & 0x03FF;
}
+static int r8a66597_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ if (is_on)
+ r8a66597_bset(r8a66597, DPRPU, SYSCFG0);
+ else
+ r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+ return 0;
+}
+
static struct usb_gadget_ops r8a66597_gadget_ops = {
.get_frame = r8a66597_get_frame,
+ .start = r8a66597_start,
+ .stop = r8a66597_stop,
+ .pullup = r8a66597_pullup,
};
static int __exit r8a66597_remove(struct platform_device *pdev)
{
struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
+ usb_del_gadget_udc(&r8a66597->gadget);
del_timer_sync(&r8a66597->timer);
iounmap(r8a66597->reg);
free_irq(platform_get_irq(pdev, 0), r8a66597);
@@ -1645,11 +1685,15 @@ static int __init r8a66597_probe(struct platform_device *pdev)
goto clean_up3;
r8a66597->ep0_req->complete = nop_completion;
- init_controller(r8a66597);
+ ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget);
+ if (ret)
+ goto err_add_udc;
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
return 0;
+err_add_udc:
+ r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
clean_up3:
free_irq(irq, r8a66597);
clean_up2:
@@ -1679,6 +1723,7 @@ static struct platform_driver r8a66597_driver = {
.name = (char *) udc_name,
},
};
+MODULE_ALIAS("platform:r8a66597_udc");
static int __init r8a66597_udc_init(void)
{
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h
index 5fc22e09a0f1..503f766c23a7 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/r8a66597-udc.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2007-2009 Renesas Solutions Corp.
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 0dfee282878a..8bdee67ce09a 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2574,7 +2574,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
return 0;
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int s3c_hsotg_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct s3c_hsotg *hsotg = our_hsotg;
@@ -2745,9 +2745,8 @@ err:
hsotg->gadget.dev.driver = NULL;
return ret;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int s3c_hsotg_stop(struct usb_gadget_driver *driver)
{
struct s3c_hsotg *hsotg = our_hsotg;
int ep;
@@ -2775,7 +2774,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
{
@@ -2784,6 +2782,8 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
.get_frame = s3c_hsotg_gadget_getframe,
+ .start = s3c_hsotg_start,
+ .stop = s3c_hsotg_stop,
};
/**
@@ -3403,6 +3403,10 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
for (epnum = 0; epnum < S3C_HSOTG_EPS; epnum++)
s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
+ ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
+ if (ret)
+ goto err_add_udc;
+
s3c_hsotg_create_debug(hsotg);
s3c_hsotg_dump(hsotg);
@@ -3410,6 +3414,11 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
our_hsotg = hsotg;
return 0;
+err_add_udc:
+ s3c_hsotg_gate(pdev, false);
+ clk_disable(hsotg->clk);
+ clk_put(hsotg->clk);
+
err_regs:
iounmap(hsotg->regs);
@@ -3427,6 +3436,8 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
{
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
+ usb_del_gadget_udc(&hsotg->gadget);
+
s3c_hsotg_delete_debug(hsotg);
usb_gadget_unregister_driver(hsotg->driver);
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index d5e3e1e58626..3fa717c5f4bc 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -1133,7 +1133,7 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
return IRQ_HANDLED;
}
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int s3c_hsudc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct s3c_hsudc *hsudc = the_controller;
@@ -1181,9 +1181,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
return 0;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int s3c_hsudc_stop(struct usb_gadget_driver *driver)
{
struct s3c_hsudc *hsudc = the_controller;
unsigned long flags;
@@ -1210,7 +1209,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
driver->driver.name);
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
{
@@ -1224,6 +1222,8 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
.get_frame = s3c_hsudc_gadget_getframe,
+ .start = s3c_hsudc_start,
+ .stop = s3c_hsudc_stop,
};
static int s3c_hsudc_probe(struct platform_device *pdev)
@@ -1311,7 +1311,15 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
disable_irq(hsudc->irq);
local_irq_enable();
+
+ ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
+ if (ret)
+ goto err_add_udc;
+
return 0;
+err_add_udc:
+ clk_disable(hsudc->uclk);
+ clk_put(hsudc->uclk);
err_clk:
free_irq(hsudc->irq, hsudc);
err_irq:
@@ -1333,6 +1341,7 @@ static struct platform_driver s3c_hsudc_driver = {
},
.probe = s3c_hsudc_probe,
};
+MODULE_ALIAS("platform:s3c-hsudc");
static int __init s3c_hsudc_modinit(void)
{
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 100f2635cf0a..85c1b0d66293 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -1552,6 +1552,10 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
return -ENOTSUPP;
}
+static int s3c2410_udc_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int s3c2410_udc_stop(struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops s3c2410_ops = {
.get_frame = s3c2410_udc_get_frame,
.wakeup = s3c2410_udc_wakeup,
@@ -1559,6 +1563,8 @@ static const struct usb_gadget_ops s3c2410_ops = {
.pullup = s3c2410_udc_pullup,
.vbus_session = s3c2410_udc_vbus_session,
.vbus_draw = s3c2410_vbus_draw,
+ .start = s3c2410_udc_start,
+ .stop = s3c2410_udc_stop,
};
static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
@@ -1567,7 +1573,7 @@ static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
return;
if (udc_info->udc_command) {
- udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+ udc_info->udc_command(cmd);
} else if (gpio_is_valid(udc_info->pullup_pin)) {
int value;
@@ -1672,10 +1678,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
}
-/*
- * usb_gadget_probe_driver
- */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int s3c2410_udc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct s3c2410_udc *udc = the_controller;
@@ -1730,12 +1733,8 @@ register_error:
udc->gadget.dev.driver = NULL;
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-/*
- * usb_gadget_unregister_driver
- */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int s3c2410_udc_stop(struct usb_gadget_driver *driver)
{
struct s3c2410_udc *udc = the_controller;
@@ -1955,6 +1954,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
goto err_vbus_irq;
}
+ retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+ if (retval)
+ goto err_add_udc;
+
if (s3c2410_udc_debugfs_root) {
udc->regs_info = debugfs_create_file("registers", S_IRUGO,
s3c2410_udc_debugfs_root,
@@ -1967,6 +1970,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return 0;
+err_add_udc:
+ if (udc_info && !udc_info->udc_command &&
+ gpio_is_valid(udc_info->pullup_pin))
+ gpio_free(udc_info->pullup_pin);
err_vbus_irq:
if (udc_info && udc_info->vbus_pin > 0)
free_irq(gpio_to_irq(udc_info->vbus_pin), udc);
@@ -1992,6 +1999,8 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
unsigned int irq;
dev_dbg(&pdev->dev, "%s()\n", __func__);
+
+ usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
@@ -2048,26 +2057,22 @@ static int s3c2410_udc_resume(struct platform_device *pdev)
#define s3c2410_udc_resume NULL
#endif
-static struct platform_driver udc_driver_2410 = {
- .driver = {
- .name = "s3c2410-usbgadget",
- .owner = THIS_MODULE,
- },
- .probe = s3c2410_udc_probe,
- .remove = s3c2410_udc_remove,
- .suspend = s3c2410_udc_suspend,
- .resume = s3c2410_udc_resume,
+static const struct platform_device_id s3c_udc_ids[] = {
+ { "s3c2410-usbgadget", },
+ { "s3c2440-usbgadget", },
};
+MODULE_DEVICE_TABLE(platform, s3c_udc_ids);
-static struct platform_driver udc_driver_2440 = {
+static struct platform_driver udc_driver_24x0 = {
.driver = {
- .name = "s3c2440-usbgadget",
+ .name = "s3c24x0-usbgadget",
.owner = THIS_MODULE,
},
.probe = s3c2410_udc_probe,
.remove = s3c2410_udc_remove,
.suspend = s3c2410_udc_suspend,
.resume = s3c2410_udc_resume,
+ .id_table = s3c_udc_ids,
};
static int __init udc_init(void)
@@ -2083,11 +2088,7 @@ static int __init udc_init(void)
s3c2410_udc_debugfs_root = NULL;
}
- retval = platform_driver_register(&udc_driver_2410);
- if (retval)
- goto err;
-
- retval = platform_driver_register(&udc_driver_2440);
+ retval = platform_driver_register(&udc_driver_24x0);
if (retval)
goto err;
@@ -2100,13 +2101,10 @@ err:
static void __exit udc_exit(void)
{
- platform_driver_unregister(&udc_driver_2410);
- platform_driver_unregister(&udc_driver_2440);
+ platform_driver_unregister(&udc_driver_24x0);
debugfs_remove(s3c2410_udc_debugfs_root);
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
module_init(udc_init);
module_exit(udc_exit);
@@ -2114,5 +2112,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c2410-usbgadget");
-MODULE_ALIAS("platform:s3c2440-usbgadget");
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 1ac57a973aa9..ed1b816e58d8 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -242,6 +242,7 @@ static struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_HIGH,
};
static int __init init(void)
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 1fa4f705b0b4..d3dd227a2bfc 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -494,7 +494,7 @@ static struct usb_descriptor_header *fsg_hs_function[] = {
};
/* Maxpacket and other transfer characteristics vary by speed. */
-static struct usb_endpoint_descriptor *
+static __maybe_unused struct usb_endpoint_descriptor *
fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
struct usb_endpoint_descriptor *hs)
{
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 2ac1d2147325..dfed4c1d96c0 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -97,16 +97,17 @@ struct eth_dev {
static unsigned qmult = 5;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(qmult, "queue length multiplier at high speed");
+MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
#else /* full speed (low speed doesn't do bulk) */
#define qmult 1
#endif
-/* for dual-speed hardware, use deeper queues at highspeed */
+/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
- if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH)
+ if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+ gadget->speed == USB_SPEED_SUPER))
return qmult * DEFAULT_QLEN;
else
return DEFAULT_QLEN;
@@ -598,9 +599,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->length = length;
- /* throttle highspeed IRQ rate back slightly */
+ /* throttle high/super speed IRQ rate back slightly */
if (gadget_is_dualspeed(dev->gadget))
- req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
+ dev->gadget->speed == USB_SPEED_SUPER)
? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
: 0;
@@ -693,8 +695,8 @@ static int eth_stop(struct net_device *net)
usb_ep_disable(link->out_ep);
if (netif_carrier_ok(net)) {
DBG(dev, "host still using in/out endpoints\n");
- usb_ep_enable(link->in_ep, link->in);
- usb_ep_enable(link->out_ep, link->out);
+ usb_ep_enable(link->in_ep);
+ usb_ep_enable(link->out_ep);
}
}
spin_unlock_irqrestore(&dev->lock, flags);
@@ -871,7 +873,7 @@ struct net_device *gether_connect(struct gether *link)
return ERR_PTR(-EINVAL);
link->in_ep->driver_data = dev;
- result = usb_ep_enable(link->in_ep, link->in);
+ result = usb_ep_enable(link->in_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->in_ep->name, result);
@@ -879,7 +881,7 @@ struct net_device *gether_connect(struct gether *link)
}
link->out_ep->driver_data = dev;
- result = usb_ep_enable(link->out_ep, link->out);
+ result = usb_ep_enable(link->out_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->out_ep->name, result);
@@ -969,7 +971,7 @@ void gether_disconnect(struct gether *link)
}
spin_unlock(&dev->req_lock);
link->in_ep->driver_data = NULL;
- link->in = NULL;
+ link->in_ep->desc = NULL;
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
@@ -984,7 +986,7 @@ void gether_disconnect(struct gether *link)
}
spin_unlock(&dev->req_lock);
link->out_ep->driver_data = NULL;
- link->out = NULL;
+ link->out_ep->desc = NULL;
/* finish forgetting about this USB link episode */
dev->header_len = 0;
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index b56e1e7d423c..c966440ddd70 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -52,10 +52,6 @@ struct gether {
struct usb_ep *in_ep;
struct usb_ep *out_ep;
- /* descriptors match device speed at gether_connect() time */
- struct usb_endpoint_descriptor *in;
- struct usb_endpoint_descriptor *out;
-
bool is_zlp_ok;
u16 cdc_filter;
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 40f7716b31fc..a8aa46962d81 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -1247,12 +1247,12 @@ int gserial_connect(struct gserial *gser, u8 port_num)
port = ports[port_num].port;
/* activate the endpoints */
- status = usb_ep_enable(gser->in, gser->in_desc);
+ status = usb_ep_enable(gser->in);
if (status < 0)
return status;
gser->in->driver_data = port;
- status = usb_ep_enable(gser->out, gser->out_desc);
+ status = usb_ep_enable(gser->out);
if (status < 0)
goto fail_out;
gser->out->driver_data = port;
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index 300f0ed9475d..9b0fe6450fbf 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -35,8 +35,6 @@ struct gserial {
struct usb_ep *in;
struct usb_ep *out;
- struct usb_endpoint_descriptor *in_desc;
- struct usb_endpoint_descriptor *out_desc;
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
new file mode 100644
index 000000000000..05ba47214361
--- /dev/null
+++ b/drivers/usb/gadget/udc-core.c
@@ -0,0 +1,484 @@
+/**
+ * udc.c - Core UDC Framework
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/**
+ * struct usb_udc - describes one usb device controller
+ * @driver - the gadget driver pointer. For use by the class code
+ * @dev - the child device to the actual controller
+ * @gadget - the gadget. For use by the class code
+ * @list - for use by the udc class driver
+ *
+ * This represents the internal data structure which is used by the UDC-class
+ * to hold information about udc driver and gadget together.
+ */
+struct usb_udc {
+ struct usb_gadget_driver *driver;
+ struct usb_gadget *gadget;
+ struct device dev;
+ struct list_head list;
+};
+
+static struct class *udc_class;
+static LIST_HEAD(udc_list);
+static DEFINE_MUTEX(udc_lock);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_start - tells usb device controller to start up
+ * @gadget: The gadget we want to get started
+ * @driver: The driver we want to bind to @gadget
+ * @bind: The bind function for @driver
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int usb_gadget_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ return gadget->ops->start(driver, bind);
+}
+
+/**
+ * usb_gadget_udc_start - tells usb device controller to start up
+ * @gadget: The gadget we want to get started
+ * @driver: The driver we want to bind to @gadget
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ return gadget->ops->udc_start(gadget, driver);
+}
+
+/**
+ * usb_gadget_stop - tells usb device controller we don't need it anymore
+ * @gadget: The device we want to stop activity
+ * @driver: The driver to unbind from @gadget
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ */
+static inline void usb_gadget_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ gadget->ops->stop(driver);
+}
+
+/**
+ * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
+ * @gadget: The device we want to stop activity
+ * @driver: The driver to unbind from @gadget
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ */
+static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ gadget->ops->udc_stop(gadget, driver);
+}
+
+/**
+ * usb_udc_release - release the usb_udc struct
+ * @dev: the dev member within usb_udc
+ *
+ * This is called by driver's core in order to free memory once the last
+ * reference is released.
+ */
+static void usb_udc_release(struct device *dev)
+{
+ struct usb_udc *udc;
+
+ udc = container_of(dev, struct usb_udc, dev);
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+ kfree(udc);
+}
+
+static const struct attribute_group *usb_udc_attr_groups[];
+/**
+ * usb_add_gadget_udc - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller
+ * driver's device.
+ * @gadget: the gadget to be added to the list
+ *
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
+{
+ struct usb_udc *udc;
+ int ret = -ENOMEM;
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ goto err1;
+
+ device_initialize(&udc->dev);
+ udc->dev.release = usb_udc_release;
+ udc->dev.class = udc_class;
+ udc->dev.groups = usb_udc_attr_groups;
+ udc->dev.parent = parent;
+ ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
+ if (ret)
+ goto err2;
+
+ udc->gadget = gadget;
+
+ mutex_lock(&udc_lock);
+ list_add_tail(&udc->list, &udc_list);
+
+ ret = device_add(&udc->dev);
+ if (ret)
+ goto err3;
+
+ mutex_unlock(&udc_lock);
+
+ return 0;
+err3:
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+err2:
+ put_device(&udc->dev);
+
+err1:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+
+static int udc_is_newstyle(struct usb_udc *udc)
+{
+ if (udc->gadget->ops->udc_start && udc->gadget->ops->udc_stop)
+ return 1;
+ return 0;
+}
+
+
+static void usb_gadget_remove_driver(struct usb_udc *udc)
+{
+ dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
+ udc->gadget->name);
+
+ kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+
+ if (udc_is_newstyle(udc)) {
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->unbind(udc->gadget);
+ usb_gadget_udc_stop(udc->gadget, udc->driver);
+
+ } else {
+ usb_gadget_stop(udc->gadget, udc->driver);
+ }
+
+ udc->driver = NULL;
+ udc->dev.driver = NULL;
+}
+
+/**
+ * usb_del_gadget_udc - deletes @udc from udc_list
+ * @gadget: the gadget to be removed.
+ *
+ * This, will call usb_gadget_unregister_driver() if
+ * the @udc is still busy.
+ */
+void usb_del_gadget_udc(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc = NULL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+
+ dev_err(gadget->dev.parent, "gadget not registered.\n");
+ mutex_unlock(&udc_lock);
+
+ return;
+
+found:
+ dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+
+ list_del(&udc->list);
+ mutex_unlock(&udc_lock);
+
+ if (udc->driver)
+ usb_gadget_remove_driver(udc);
+
+ kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+ device_unregister(&udc->dev);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct usb_udc *udc = NULL;
+ int ret;
+
+ if (!driver || !bind || !driver->setup)
+ return -EINVAL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list) {
+ /* For now we take the first one */
+ if (!udc->driver)
+ goto found;
+ }
+
+ pr_debug("couldn't find an available UDC\n");
+ mutex_unlock(&udc_lock);
+ return -ENODEV;
+
+found:
+ dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
+ driver->function);
+
+ udc->driver = driver;
+ udc->dev.driver = &driver->driver;
+
+ if (udc_is_newstyle(udc)) {
+ ret = bind(udc->gadget);
+ if (ret)
+ goto err1;
+ ret = usb_gadget_udc_start(udc->gadget, driver);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_gadget_connect(udc->gadget);
+ } else {
+
+ ret = usb_gadget_start(udc->gadget, driver, bind);
+ if (ret)
+ goto err1;
+
+ }
+
+ kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+ mutex_unlock(&udc_lock);
+ return 0;
+
+err1:
+ dev_err(&udc->dev, "failed to start %s: %d\n",
+ udc->driver->function, ret);
+ udc->driver = NULL;
+ udc->dev.driver = NULL;
+ mutex_unlock(&udc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usb_udc *udc = NULL;
+ int ret = -ENODEV;
+
+ if (!driver || !driver->unbind)
+ return -EINVAL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->driver == driver) {
+ usb_gadget_remove_driver(udc);
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&udc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t usb_udc_srp_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct usb_udc *udc = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, "1"))
+ usb_gadget_wakeup(udc->gadget);
+
+ return n;
+}
+static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store);
+
+static ssize_t usb_udc_softconn_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct usb_udc *udc = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, "connect")) {
+ usb_gadget_connect(udc->gadget);
+ } else if (sysfs_streq(buf, "disconnect")) {
+ usb_gadget_disconnect(udc->gadget);
+ } else {
+ dev_err(dev, "unsupported command '%s'\n", buf);
+ return -EINVAL;
+ }
+
+ return n;
+}
+static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
+
+static ssize_t usb_udc_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ struct usb_gadget *gadget = udc->gadget;
+
+ switch (gadget->speed) {
+ case USB_SPEED_LOW:
+ return snprintf(buf, PAGE_SIZE, "low-speed\n");
+ case USB_SPEED_FULL:
+ return snprintf(buf, PAGE_SIZE, "full-speed\n");
+ case USB_SPEED_HIGH:
+ return snprintf(buf, PAGE_SIZE, "high-speed\n");
+ case USB_SPEED_WIRELESS:
+ return snprintf(buf, PAGE_SIZE, "wireless\n");
+ case USB_SPEED_SUPER:
+ return snprintf(buf, PAGE_SIZE, "super-speed\n");
+ case USB_SPEED_UNKNOWN: /* FALLTHROUGH */
+ default:
+ return snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
+ }
+}
+static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL);
+
+#define USB_UDC_ATTR(name) \
+ssize_t usb_udc_##name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
+ struct usb_gadget *gadget = udc->gadget; \
+ \
+ return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
+} \
+static DEVICE_ATTR(name, S_IRUSR, usb_udc_##name##_show, NULL)
+
+static USB_UDC_ATTR(is_dualspeed);
+static USB_UDC_ATTR(is_otg);
+static USB_UDC_ATTR(is_a_peripheral);
+static USB_UDC_ATTR(b_hnp_enable);
+static USB_UDC_ATTR(a_hnp_support);
+static USB_UDC_ATTR(a_alt_hnp_support);
+
+static struct attribute *usb_udc_attrs[] = {
+ &dev_attr_srp.attr,
+ &dev_attr_soft_connect.attr,
+ &dev_attr_speed.attr,
+
+ &dev_attr_is_dualspeed.attr,
+ &dev_attr_is_otg.attr,
+ &dev_attr_is_a_peripheral.attr,
+ &dev_attr_b_hnp_enable.attr,
+ &dev_attr_a_hnp_support.attr,
+ &dev_attr_a_alt_hnp_support.attr,
+ NULL,
+};
+
+static const struct attribute_group usb_udc_attr_group = {
+ .attrs = usb_udc_attrs,
+};
+
+static const struct attribute_group *usb_udc_attr_groups[] = {
+ &usb_udc_attr_group,
+ NULL,
+};
+
+static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ int ret;
+
+ ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name);
+ if (ret) {
+ dev_err(dev, "failed to add uevent USB_UDC_NAME\n");
+ return ret;
+ }
+
+ if (udc->driver) {
+ ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
+ udc->driver->function);
+ if (ret) {
+ dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int __init usb_udc_init(void)
+{
+ udc_class = class_create(THIS_MODULE, "udc");
+ if (IS_ERR(udc_class)) {
+ pr_err("failed to create udc class --> %ld\n",
+ PTR_ERR(udc_class));
+ return PTR_ERR(udc_class);
+ }
+
+ udc_class->dev_uevent = usb_udc_uevent;
+ return 0;
+}
+subsys_initcall(usb_udc_init);
+
+static void __exit usb_udc_exit(void)
+{
+ class_destroy(udc_class);
+}
+module_exit(usb_udc_exit);
+
+MODULE_DESCRIPTION("UDC Framework");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index a5a0fdb808c7..df6882de50bf 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -373,6 +373,7 @@ static struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
+ .max_speed = USB_SPEED_HIGH,
.unbind = webcam_unbind,
};
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 6d16db9d9d2d..00e2fd2d4791 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -340,6 +340,7 @@ static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
+ .max_speed = USB_SPEED_SUPER,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f8030ee928e8..f72ae0b6ee7f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -94,7 +94,8 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
-#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */
+#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
+ /* 200-ms async qh unlink delay */
/* Initial IRQ latency: faster than hw default */
static int log2_irq_thresh = 0; // 0 to 6
@@ -114,7 +115,7 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
/* for link power management(LPM) feature */
static unsigned int hird;
module_param(hird, int, S_IRUGO);
-MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
@@ -152,10 +153,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
break;
/* case TIMER_ASYNC_SHRINK: */
default:
- /* add a jiffie since we synch against the
- * 8 KHz uframe counter.
- */
- t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1;
+ t = EHCI_SHRINK_JIFFIES;
break;
}
mod_timer(&ehci->watchdog, t + jiffies);
@@ -340,6 +338,7 @@ static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-mem.c"
#include "ehci-q.c"
#include "ehci-sched.c"
+#include "ehci-sysfs.c"
/*-------------------------------------------------------------------------*/
@@ -524,7 +523,7 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_reset (ehci);
spin_unlock_irq(&ehci->lock);
- remove_companion_file(ehci);
+ remove_sysfs_files(ehci);
remove_debug_files (ehci);
/* root hub is shut down separately (first, when possible) */
@@ -575,6 +574,12 @@ static int ehci_init(struct usb_hcd *hcd)
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
/*
+ * by default set standard 80% (== 100 usec/uframe) max periodic
+ * bandwidth as required by USB 2.0
+ */
+ ehci->uframe_periodic_max = 100;
+
+ /*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
*/
@@ -758,7 +763,7 @@ static int ehci_run (struct usb_hcd *hcd)
* since the class device isn't created that early.
*/
create_debug_files(ehci);
- create_companion_file(ehci);
+ create_sysfs_files(ehci);
return 0;
}
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index ea6184bf48d0..bf2c8f65e1ae 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -471,29 +471,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-/* Display the ports dedicated to the companion controller */
-static ssize_t show_companion(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct ehci_hcd *ehci;
- int nports, index, n;
- int count = PAGE_SIZE;
- char *ptr = buf;
-
- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
- nports = HCS_N_PORTS(ehci->hcs_params);
-
- for (index = 0; index < nports; ++index) {
- if (test_bit(index, &ehci->companion_ports)) {
- n = scnprintf(ptr, count, "%d\n", index + 1);
- ptr += n;
- count -= n;
- }
- }
- return ptr - buf;
-}
-
/*
* Sets the owner of a port
*/
@@ -528,58 +505,6 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
}
}
-/*
- * Dedicate or undedicate a port to the companion controller.
- * Syntax is "[-]portnum", where a leading '-' sign means
- * return control of the port to the EHCI controller.
- */
-static ssize_t store_companion(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ehci_hcd *ehci;
- int portnum, new_owner;
-
- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
- new_owner = PORT_OWNER; /* Owned by companion */
- if (sscanf(buf, "%d", &portnum) != 1)
- return -EINVAL;
- if (portnum < 0) {
- portnum = - portnum;
- new_owner = 0; /* Owned by EHCI */
- }
- if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
- return -ENOENT;
- portnum--;
- if (new_owner)
- set_bit(portnum, &ehci->companion_ports);
- else
- clear_bit(portnum, &ehci->companion_ports);
- set_owner(ehci, portnum, new_owner);
- return count;
-}
-static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
-
-static inline int create_companion_file(struct ehci_hcd *ehci)
-{
- int i = 0;
-
- /* with integrated TT there is no companion! */
- if (!ehci_is_TDI(ehci))
- i = device_create_file(ehci_to_hcd(ehci)->self.controller,
- &dev_attr_companion);
- return i;
-}
-
-static inline void remove_companion_file(struct ehci_hcd *ehci)
-{
- /* with integrated TT there is no companion! */
- if (!ehci_is_TDI(ehci))
- device_remove_file(ehci_to_hcd(ehci)->self.controller,
- &dev_attr_companion);
-}
-
-
/*-------------------------------------------------------------------------*/
static int check_reset_complete (
@@ -891,10 +816,11 @@ static int ehci_hub_control (
* power switching; they're allowed to just limit the
* current. khubd will turn the power back on.
*/
- if (HCS_PPC (ehci->hcs_params)){
+ if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) {
ehci_writel(ehci,
temp & ~(PORT_RWC_BITS | PORT_POWER),
status_reg);
+ temp = ehci_readl(ehci, status_reg);
}
}
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index b5a0bf649c95..592d5f76803e 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -40,27 +40,9 @@ static int ehci_msm_reset(struct usb_hcd *hcd)
int retval;
ehci->caps = USB_CAPLENGTH;
- ehci->regs = USB_CAPLENGTH +
- HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
- dbg_hcs_params(ehci, "reset");
- dbg_hcc_params(ehci, "reset");
-
- /* cache the data to minimize the chip reads*/
- ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
-
hcd->has_tt = 1;
- ehci->sbrn = HCD_USB2;
-
- retval = ehci_halt(ehci);
- if (retval)
- return retval;
-
- /* data structure init */
- retval = ehci_init(hcd);
- if (retval)
- return retval;
- retval = ehci_reset(ehci);
+ retval = ehci_setup(hcd);
if (retval)
return retval;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 5d6bc624c961..0917e3a32465 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -103,7 +103,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;
- is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
+ is_out = qh->is_out;
epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
@@ -946,6 +946,7 @@ done:
hw = qh->hw;
hw->hw_info1 = cpu_to_hc32(ehci, info1);
hw->hw_info2 = cpu_to_hc32(ehci, info2);
+ qh->is_out = !is_input;
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
@@ -1231,6 +1232,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
prev->hw->hw_next = qh->hw->hw_next;
prev->qh_next = qh->qh_next;
+ if (ehci->qh_scan_next == qh)
+ ehci->qh_scan_next = qh->qh_next.qh;
wmb ();
/* If the controller isn't running, we don't have to wait for it */
@@ -1256,53 +1259,49 @@ static void scan_async (struct ehci_hcd *ehci)
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
- ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
-rescan:
stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
- qh = ehci->async->qh_next.qh;
- if (likely (qh != NULL)) {
- do {
- /* clean any finished work for this qh */
- if (!list_empty(&qh->qtd_list) && (stopped ||
- qh->stamp != ehci->stamp)) {
- int temp;
-
- /* unlinks could happen here; completion
- * reporting drops the lock. rescan using
- * the latest schedule, but don't rescan
- * qhs we already finished (no looping)
- * unless the controller is stopped.
- */
- qh = qh_get (qh);
- qh->stamp = ehci->stamp;
- temp = qh_completions (ehci, qh);
- if (qh->needs_rescan)
- unlink_async(ehci, qh);
- qh_put (qh);
- if (temp != 0) {
- goto rescan;
- }
- }
- /* unlink idle entries, reducing DMA usage as well
- * as HCD schedule-scanning costs. delay for any qh
- * we just scanned, there's a not-unusual case that it
- * doesn't stay idle for long.
- * (plus, avoids some kind of re-activation race.)
+ ehci->qh_scan_next = ehci->async->qh_next.qh;
+ while (ehci->qh_scan_next) {
+ qh = ehci->qh_scan_next;
+ ehci->qh_scan_next = qh->qh_next.qh;
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why ehci->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then ehci->qh_scan_next is adjusted
+ * in start_unlink_async().
*/
- if (list_empty(&qh->qtd_list)
- && qh->qh_state == QH_STATE_LINKED) {
- if (!ehci->reclaim && (stopped ||
- ((ehci->stamp - qh->stamp) & 0x1fff)
- >= EHCI_SHRINK_FRAMES * 8))
- start_unlink_async(ehci, qh);
- else
- action = TIMER_ASYNC_SHRINK;
- }
+ qh = qh_get(qh);
+ temp = qh_completions(ehci, qh);
+ if (qh->needs_rescan)
+ unlink_async(ehci, qh);
+ qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
+ qh_put(qh);
+ if (temp != 0)
+ goto rescan;
+ }
- qh = qh->qh_next.qh;
- } while (qh);
+ /* unlink idle entries, reducing DMA usage as well
+ * as HCD schedule-scanning costs. delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ * (plus, avoids some kind of re-activation race.)
+ */
+ if (list_empty(&qh->qtd_list)
+ && qh->qh_state == QH_STATE_LINKED) {
+ if (!ehci->reclaim && (stopped ||
+ time_after_eq(jiffies, qh->unlink_time)))
+ start_unlink_async(ehci, qh);
+ else
+ action = TIMER_ASYNC_SHRINK;
+ }
}
if (action == TIMER_ASYNC_SHRINK)
timer_action (ehci, TIMER_ASYNC_SHRINK);
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index e3374c8f7b3f..b3958b3d3163 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -189,6 +189,100 @@ static void s5p_ehci_shutdown(struct platform_device *pdev)
hcd->driver->shutdown(hcd);
}
+#ifdef CONFIG_PM
+static int s5p_ehci_suspend(struct device *dev)
+{
+ struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = s5p_ehci->hcd;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
+ unsigned long flags;
+ int rc = 0;
+
+ if (time_before(jiffies, ehci->next_statechange))
+ msleep(20);
+
+ /*
+ * Root hub was already suspended. Disable irq emission and
+ * mark HW unaccessible. The PM and USB cores make sure that
+ * the root hub is either suspended or stopped.
+ */
+ ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
+ spin_lock_irqsave(&ehci->lock, flags);
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ (void)ehci_readl(ehci, &ehci->regs->intr_enable);
+
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+
+ if (pdata && pdata->phy_exit)
+ pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+
+ return rc;
+}
+
+static int s5p_ehci_resume(struct device *dev)
+{
+ struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = s5p_ehci->hcd;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
+
+ if (pdata && pdata->phy_init)
+ pdata->phy_init(pdev, S5P_USB_PHY_HOST);
+
+ if (time_before(jiffies, ehci->next_statechange))
+ msleep(100);
+
+ /* Mark hardware accessible again as we are out of D3 state by now */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
+ int mask = INTR_MASK;
+
+ ehci_prepare_ports_for_controller_resume(ehci);
+ if (!hcd->self.root_hub->do_remote_wakeup)
+ mask &= ~STS_PCD;
+ ehci_writel(ehci, mask, &ehci->regs->intr_enable);
+ ehci_readl(ehci, &ehci->regs->intr_enable);
+ return 0;
+ }
+
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
+ (void) ehci_halt(ehci);
+ (void) ehci_reset(ehci);
+
+ /* emptying the schedule aborts any urbs */
+ spin_lock_irq(&ehci->lock);
+ if (ehci->reclaim)
+ end_unlink_async(ehci);
+ ehci_work(ehci);
+ spin_unlock_irq(&ehci->lock);
+
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+
+ /* here we "know" root ports should always stay powered */
+ ehci_port_power(ehci, 1);
+
+ hcd->state = HC_STATE_SUSPENDED;
+
+ return 0;
+}
+#else
+#define s5p_ehci_suspend NULL
+#define s5p_ehci_resume NULL
+#endif
+
+static const struct dev_pm_ops s5p_ehci_pm_ops = {
+ .suspend = s5p_ehci_suspend,
+ .resume = s5p_ehci_resume,
+};
+
static struct platform_driver s5p_ehci_driver = {
.probe = s5p_ehci_probe,
.remove = __devexit_p(s5p_ehci_remove),
@@ -196,6 +290,7 @@ static struct platform_driver s5p_ehci_driver = {
.driver = {
.name = "s5p-ehci",
.owner = THIS_MODULE,
+ .pm = &s5p_ehci_pm_ops,
}
};
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 6c9fbe352f73..2abf8543f083 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -172,7 +172,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
}
}
#ifdef DEBUG
- if (usecs > 100)
+ if (usecs > ehci->uframe_periodic_max)
ehci_err (ehci, "uframe %d sched overrun: %d usecs\n",
frame * 8 + uframe, usecs);
#endif
@@ -709,11 +709,8 @@ static int check_period (
if (uframe >= 8)
return 0;
- /*
- * 80% periodic == 100 usec/uframe available
- * convert "usecs we need" to "max already claimed"
- */
- usecs = 100 - usecs;
+ /* convert "usecs we need" to "max already claimed" */
+ usecs = ehci->uframe_periodic_max - usecs;
/* we "know" 2 and 4 uframe intervals were rejected; so
* for period 0, check _every_ microframe in the schedule.
@@ -1286,9 +1283,9 @@ itd_slot_ok (
{
uframe %= period;
do {
- /* can't commit more than 80% periodic == 100 usec */
+ /* can't commit more than uframe_periodic_max usec */
if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
- > (100 - usecs))
+ > (ehci->uframe_periodic_max - usecs))
return 0;
/* we know urb->interval is 2^N uframes */
@@ -1345,7 +1342,7 @@ sitd_slot_ok (
#endif
/* check starts (OUT uses more than one) */
- max_used = 100 - stream->usecs;
+ max_used = ehci->uframe_periodic_max - stream->usecs;
for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
if (periodic_usecs (ehci, frame, uf) > max_used)
return 0;
@@ -1354,7 +1351,7 @@ sitd_slot_ok (
/* for IN, check CSPLIT */
if (stream->c_usecs) {
uf = uframe & 7;
- max_used = 100 - stream->c_usecs;
+ max_used = ehci->uframe_periodic_max - stream->c_usecs;
do {
tmp = 1 << uf;
tmp <<= 8;
diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c
new file mode 100644
index 000000000000..14ced00ba220
--- /dev/null
+++ b/drivers/usb/host/ehci-sysfs.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2007 by Alan Stern
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+
+/* Display the ports dedicated to the companion controller */
+static ssize_t show_companion(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ehci_hcd *ehci;
+ int nports, index, n;
+ int count = PAGE_SIZE;
+ char *ptr = buf;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ nports = HCS_N_PORTS(ehci->hcs_params);
+
+ for (index = 0; index < nports; ++index) {
+ if (test_bit(index, &ehci->companion_ports)) {
+ n = scnprintf(ptr, count, "%d\n", index + 1);
+ ptr += n;
+ count -= n;
+ }
+ }
+ return ptr - buf;
+}
+
+/*
+ * Dedicate or undedicate a port to the companion controller.
+ * Syntax is "[-]portnum", where a leading '-' sign means
+ * return control of the port to the EHCI controller.
+ */
+static ssize_t store_companion(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ehci_hcd *ehci;
+ int portnum, new_owner;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ new_owner = PORT_OWNER; /* Owned by companion */
+ if (sscanf(buf, "%d", &portnum) != 1)
+ return -EINVAL;
+ if (portnum < 0) {
+ portnum = - portnum;
+ new_owner = 0; /* Owned by EHCI */
+ }
+ if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
+ return -ENOENT;
+ portnum--;
+ if (new_owner)
+ set_bit(portnum, &ehci->companion_ports);
+ else
+ clear_bit(portnum, &ehci->companion_ports);
+ set_owner(ehci, portnum, new_owner);
+ return count;
+}
+static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
+
+
+/*
+ * Display / Set uframe_periodic_max
+ */
+static ssize_t show_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ehci_hcd *ehci;
+ int n;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
+ return n;
+}
+
+
+static ssize_t store_uframe_periodic_max(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ehci_hcd *ehci;
+ unsigned uframe_periodic_max;
+ unsigned frame, uframe;
+ unsigned short allocated_max;
+ unsigned long flags;
+ ssize_t ret;
+
+ ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+ if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
+ return -EINVAL;
+
+ if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
+ ehci_info(ehci, "rejecting invalid request for "
+ "uframe_periodic_max=%u\n", uframe_periodic_max);
+ return -EINVAL;
+ }
+
+ ret = -EINVAL;
+
+ /*
+ * lock, so that our checking does not race with possible periodic
+ * bandwidth allocation through submitting new urbs.
+ */
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /*
+ * for request to decrease max periodic bandwidth, we have to check
+ * every microframe in the schedule to see whether the decrease is
+ * possible.
+ */
+ if (uframe_periodic_max < ehci->uframe_periodic_max) {
+ allocated_max = 0;
+
+ for (frame = 0; frame < ehci->periodic_size; ++frame)
+ for (uframe = 0; uframe < 7; ++uframe)
+ allocated_max = max(allocated_max,
+ periodic_usecs (ehci, frame, uframe));
+
+ if (allocated_max > uframe_periodic_max) {
+ ehci_info(ehci,
+ "cannot decrease uframe_periodic_max becase "
+ "periodic bandwidth is already allocated "
+ "(%u > %u)\n",
+ allocated_max, uframe_periodic_max);
+ goto out_unlock;
+ }
+ }
+
+ /* increasing is always ok */
+
+ ehci_info(ehci, "setting max periodic bandwidth to %u%% "
+ "(== %u usec/uframe)\n",
+ 100*uframe_periodic_max/125, uframe_periodic_max);
+
+ if (uframe_periodic_max != 100)
+ ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
+
+ ehci->uframe_periodic_max = uframe_periodic_max;
+ ret = count;
+
+out_unlock:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return ret;
+}
+static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max);
+
+
+static inline int create_sysfs_files(struct ehci_hcd *ehci)
+{
+ struct device *controller = ehci_to_hcd(ehci)->self.controller;
+ int i = 0;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ i = device_create_file(controller, &dev_attr_companion);
+ if (i)
+ goto out;
+
+ i = device_create_file(controller, &dev_attr_uframe_periodic_max);
+out:
+ return i;
+}
+
+static inline void remove_sysfs_files(struct ehci_hcd *ehci)
+{
+ struct device *controller = ehci_to_hcd(ehci)->self.controller;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ device_remove_file(controller, &dev_attr_companion);
+
+ device_remove_file(controller, &dev_attr_uframe_periodic_max);
+}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index bd6ff489baf9..cc7d337ec355 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -75,6 +75,7 @@ struct ehci_hcd { /* one per controller */
struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */
struct ehci_qh *reclaim;
+ struct ehci_qh *qh_scan_next;
unsigned scanning : 1;
/* periodic schedule support */
@@ -87,6 +88,8 @@ struct ehci_hcd { /* one per controller */
union ehci_shadow *pshadow; /* mirror hw periodic table */
int next_uframe; /* scan periodic, start here */
unsigned periodic_sched; /* periodic activity count */
+ unsigned uframe_periodic_max; /* max periodic time per uframe */
+
/* list of itds & sitds completed while clock_frame was still active */
struct list_head cached_itd_list;
@@ -117,7 +120,6 @@ struct ehci_hcd { /* one per controller */
struct timer_list iaa_watchdog;
struct timer_list watchdog;
unsigned long actions;
- unsigned stamp;
unsigned periodic_stamp;
unsigned random_frame;
unsigned long next_statechange;
@@ -343,6 +345,7 @@ struct ehci_qh {
struct ehci_qh *reclaim; /* next to reclaim */
struct ehci_hcd *ehci;
+ unsigned long unlink_time;
/*
* Do NOT use atomic operations for QH refcounting. On some CPUs
@@ -374,6 +377,7 @@ struct ehci_qh {
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
+ unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
};
diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c
index f47867ff78c7..14cecb52a9fe 100644
--- a/drivers/usb/host/ohci-sh.c
+++ b/drivers/usb/host/ohci-sh.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Renesas Solutions Corp.
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index fd930618c28f..a9d315906e3d 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -35,6 +35,8 @@
#define OHCI_INTRSTATUS 0x0c
#define OHCI_INTRENABLE 0x10
#define OHCI_INTRDISABLE 0x14
+#define OHCI_FMINTERVAL 0x34
+#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
@@ -497,6 +499,32 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
/* reset controller, preserving RWC (and possibly IR) */
writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
+ readl(base + OHCI_CONTROL);
+
+ /* Some NVIDIA controllers stop working if kept in RESET for too long */
+ if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
+ u32 fminterval;
+ int cnt;
+
+ /* drive reset for at least 50 ms (7.1.7.5) */
+ msleep(50);
+
+ /* software reset of the controller, preserving HcFmInterval */
+ fminterval = readl(base + OHCI_FMINTERVAL);
+ writel(OHCI_HCR, base + OHCI_CMDSTATUS);
+
+ /* reset requires max 10 us delay */
+ for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */
+ if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0)
+ break;
+ udelay(1);
+ }
+ writel(fminterval, base + OHCI_FMINTERVAL);
+
+ /* Now we're in the SUSPEND state with all devices reset
+ * and wakeups and interrupts disabled
+ */
+ }
/*
* disable interrupts
@@ -507,20 +535,34 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
iounmap(base);
}
+static const struct dmi_system_id __initconst ehci_dmi_nohandoff_table[] = {
+ {
+ /* Pegatron Lucid (ExoPC) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "EXOPG06411"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-CE-133"),
+ },
+ },
+ {
+ /* Pegatron Lucid (Ordissimo AIRIS) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "M11JB"),
+ DMI_MATCH(DMI_BIOS_VERSION, "Lucid-GE-133"),
+ },
+ },
+ { }
+};
+
static void __devinit ehci_bios_handoff(struct pci_dev *pdev,
void __iomem *op_reg_base,
u32 cap, u8 offset)
{
int try_handoff = 1, tried_handoff = 0;
- /* The Pegatron Lucid (ExoPC) tablet sporadically waits for 90
- * seconds trying the handoff on its unused controller. Skip
- * it. */
+ /* The Pegatron Lucid tablet sporadically waits for 98 seconds trying
+ * the handoff on its unused controller. Skip it. */
if (pdev->vendor == 0x8086 && pdev->device == 0x283a) {
- const char *dmi_bn = dmi_get_system_info(DMI_BOARD_NAME);
- const char *dmi_bv = dmi_get_system_info(DMI_BIOS_VERSION);
- if (dmi_bn && !strcmp(dmi_bn, "EXOPG06411") &&
- dmi_bv && !strcmp(dmi_bv, "Lucid-CE-133"))
+ if (dmi_check_system(ehci_dmi_nohandoff_table))
try_handoff = 0;
}
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 4586369dda00..40a0d8b03ad7 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -6,7 +6,7 @@
* Portions Copyright (C) 2004-2005 David Brownell
* Portions Copyright (C) 1999 Roman Weissgaerber
*
- * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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
@@ -1438,7 +1438,7 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
if (pipenum > 0)
r8a66597_write(r8a66597, ~(1 << pipenum), BEMPSTS);
if (urb->transfer_buffer) {
- r8a66597_write_fifo(r8a66597, td->pipe->fifoaddr, buf, size);
+ r8a66597_write_fifo(r8a66597, td->pipe, buf, size);
if (!usb_pipebulk(urb->pipe) || td->maxpacket != size)
r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr);
}
@@ -2306,7 +2306,7 @@ static int r8a66597_bus_resume(struct usb_hcd *hcd)
dbg("resume port = %d", port);
rh->port &= ~USB_PORT_STAT_SUSPEND;
- rh->port |= USB_PORT_STAT_C_SUSPEND < 16;
+ rh->port |= USB_PORT_STAT_C_SUSPEND << 16;
r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg);
msleep(50);
r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg);
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index 25563e9a90bc..f28782d20eef 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -201,11 +201,26 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
iowrite16(val, r8a66597->reg + offset);
}
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+ u16 val, u16 pat, unsigned long offset)
+{
+ u16 tmp;
+ tmp = r8a66597_read(r8a66597, offset);
+ tmp = tmp & (~pat);
+ tmp = tmp | val;
+ r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, val, 0, offset)
+
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
- unsigned long offset, u16 *buf,
+ struct r8a66597_pipe *pipe, u16 *buf,
int len)
{
- void __iomem *fifoaddr = r8a66597->reg + offset;
+ void __iomem *fifoaddr = r8a66597->reg + pipe->fifoaddr;
unsigned long count;
unsigned char *pb;
int i;
@@ -230,26 +245,15 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
iowrite16_rep(fifoaddr, buf, len);
if (unlikely(odd)) {
buf = &buf[len];
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bclr(r8a66597, MBW_16, pipe->fifosel);
iowrite8((unsigned char)*buf, fifoaddr);
+ if (r8a66597->pdata->wr0_shorted_to_wr1)
+ r8a66597_bset(r8a66597, MBW_16, pipe->fifosel);
}
}
}
-static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
- u16 val, u16 pat, unsigned long offset)
-{
- u16 tmp;
- tmp = r8a66597_read(r8a66597, offset);
- tmp = tmp & (~pat);
- tmp = tmp | val;
- r8a66597_write(r8a66597, tmp, offset);
-}
-
-#define r8a66597_bclr(r8a66597, val, offset) \
- r8a66597_mdfy(r8a66597, 0, val, offset)
-#define r8a66597_bset(r8a66597, val, offset) \
- r8a66597_mdfy(r8a66597, val, 0, offset)
-
static inline unsigned long get_syscfg_reg(int port)
{
return port == 0 ? SYSCFG0 : SYSCFG1;
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 1f50b4468e87..e9b0f043455d 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -266,11 +266,11 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
xhci_dbg(xhci, "Interrupter target = 0x%x\n",
GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target)));
xhci_dbg(xhci, "Cycle bit = %u\n",
- (unsigned int) (le32_to_cpu(trb->link.control) & TRB_CYCLE));
+ le32_to_cpu(trb->link.control) & TRB_CYCLE);
xhci_dbg(xhci, "Toggle cycle bit = %u\n",
- (unsigned int) (le32_to_cpu(trb->link.control) & LINK_TOGGLE));
+ le32_to_cpu(trb->link.control) & LINK_TOGGLE);
xhci_dbg(xhci, "No Snoop bit = %u\n",
- (unsigned int) (le32_to_cpu(trb->link.control) & TRB_NO_SNOOP));
+ le32_to_cpu(trb->link.control) & TRB_NO_SNOOP);
break;
case TRB_TYPE(TRB_TRANSFER):
address = le64_to_cpu(trb->trans_event.buffer);
@@ -284,9 +284,9 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
address = le64_to_cpu(trb->event_cmd.cmd_trb);
xhci_dbg(xhci, "Command TRB pointer = %llu\n", address);
xhci_dbg(xhci, "Completion status = %u\n",
- (unsigned int) GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status)));
+ GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status)));
xhci_dbg(xhci, "Flags = 0x%x\n",
- (unsigned int) le32_to_cpu(trb->event_cmd.flags));
+ le32_to_cpu(trb->event_cmd.flags));
break;
default:
xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n",
@@ -318,10 +318,10 @@ void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg)
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &seg->trbs[i];
xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr,
- (u32)lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
- (u32)upper_32_bits(le64_to_cpu(trb->link.segment_ptr)),
- (unsigned int) le32_to_cpu(trb->link.intr_target),
- (unsigned int) le32_to_cpu(trb->link.control));
+ lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ upper_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ le32_to_cpu(trb->link.intr_target),
+ le32_to_cpu(trb->link.control));
addr += sizeof(*trb);
}
}
@@ -402,8 +402,8 @@ void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
addr,
lower_32_bits(le64_to_cpu(entry->seg_addr)),
upper_32_bits(le64_to_cpu(entry->seg_addr)),
- (unsigned int) le32_to_cpu(entry->seg_size),
- (unsigned int) le32_to_cpu(entry->rsvd));
+ le32_to_cpu(entry->seg_size),
+ le32_to_cpu(entry->rsvd));
addr += sizeof(*entry);
}
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index fcb7f7efc86d..d446886b22b0 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -89,8 +89,8 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
return;
prev->next = next;
if (link_trbs) {
- prev->trbs[TRBS_PER_SEGMENT-1].link.
- segment_ptr = cpu_to_le64(next->dma);
+ prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr =
+ cpu_to_le64(next->dma);
/* Set the last TRB in the segment to have a TRB type ID of Link TRB */
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
@@ -187,8 +187,8 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (link_trbs) {
/* See section 4.9.2.1 and 6.4.4.1 */
- prev->trbs[TRBS_PER_SEGMENT-1].link.
- control |= cpu_to_le32(LINK_TOGGLE);
+ prev->trbs[TRBS_PER_SEGMENT-1].link.control |=
+ cpu_to_le32(LINK_TOGGLE);
xhci_dbg(xhci, "Wrote link toggle flag to"
" segment %p (virtual), 0x%llx (DMA)\n",
prev, (unsigned long long)prev->dma);
@@ -549,8 +549,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
addr = cur_ring->first_seg->dma |
SCT_FOR_CTX(SCT_PRI_TR) |
cur_ring->cycle_state;
- stream_info->stream_ctx_array[cur_stream].
- stream_ring = cpu_to_le64(addr);
+ stream_info->stream_ctx_array[cur_stream].stream_ring =
+ cpu_to_le64(addr);
xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
cur_stream, (unsigned long long) addr);
@@ -786,7 +786,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
slot_id,
&xhci->dcbaa->dev_context_ptrs[slot_id],
- (unsigned long long) le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
+ le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
return 1;
fail:
@@ -890,19 +890,19 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
/* 3) Only the control endpoint is valid - one endpoint context */
- slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | (u32) udev->route);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | udev->route);
switch (udev->speed) {
case USB_SPEED_SUPER:
- slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_SS);
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
break;
case USB_SPEED_HIGH:
- slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_HS);
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
break;
case USB_SPEED_FULL:
- slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_FS);
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
break;
case USB_SPEED_LOW:
- slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_LS);
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
break;
case USB_SPEED_WIRELESS:
xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
@@ -916,7 +916,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
port_num = xhci_find_real_port_number(xhci, udev);
if (!port_num)
return -EINVAL;
- slot_ctx->dev_info2 |= cpu_to_le32((u32) ROOT_HUB_PORT(port_num));
+ slot_ctx->dev_info2 |= cpu_to_le32(ROOT_HUB_PORT(port_num));
/* Set the port number in the virtual_device to the faked port number */
for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 70cacbbe7fb9..7113d16e2d3a 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -113,15 +113,13 @@ static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (ring == xhci->event_ring)
return trb == &seg->trbs[TRBS_PER_SEGMENT];
else
- return (le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_LINK);
+ return TRB_TYPE_LINK_LE32(trb->link.control);
}
static int enqueue_is_link_trb(struct xhci_ring *ring)
{
struct xhci_link_trb *link = &ring->enqueue->link;
- return ((le32_to_cpu(link->control) & TRB_TYPE_BITMASK) ==
- TRB_TYPE(TRB_LINK));
+ return TRB_TYPE_LINK_LE32(link->control);
}
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
@@ -372,7 +370,7 @@ static struct xhci_segment *find_trb_seg(
while (cur_seg->trbs > trb ||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
- if (le32_to_cpu(generic_trb->field[3]) & LINK_TOGGLE)
+ if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE))
*cycle_state ^= 0x1;
cur_seg = cur_seg->next;
if (cur_seg == start_seg)
@@ -489,8 +487,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
}
trb = &state->new_deq_ptr->generic;
- if ((le32_to_cpu(trb->field[3]) & TRB_TYPE_BITMASK) ==
- TRB_TYPE(TRB_LINK) && (le32_to_cpu(trb->field[3]) & LINK_TOGGLE))
+ if (TRB_TYPE_LINK_LE32(trb->field[3]) &&
+ (trb->field[3] & cpu_to_le32(LINK_TOGGLE)))
state->new_cycle_state ^= 0x1;
next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
@@ -525,8 +523,7 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb;
true;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_LINK)) {
+ if (TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) {
/* Unchain any chained Link TRBs, but
* leave the pointers intact.
*/
@@ -1000,7 +997,7 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
* but we don't care.
*/
xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n",
- (unsigned int) GET_COMP_CODE(le32_to_cpu(event->status)));
+ GET_COMP_CODE(le32_to_cpu(event->status)));
/* HW with the reset endpoint quirk needs to have a configure endpoint
* command complete before the endpoint can be used. Queue that here
@@ -1458,7 +1455,8 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
* endpoint anyway. Check if a babble halted the
* endpoint.
*/
- if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED)
+ if ((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) ==
+ cpu_to_le32(EP_STATE_HALTED))
return 1;
return 0;
@@ -1753,10 +1751,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((le32_to_cpu(cur_trb->generic.field[3]) &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (le32_to_cpu(cur_trb->generic.field[3]) &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) &&
+ !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
}
len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
@@ -1885,10 +1881,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
cur_trb != event_trb;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((le32_to_cpu(cur_trb->generic.field[3]) &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (le32_to_cpu(cur_trb->generic.field[3]) &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) &&
+ !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
td->urb->actual_length +=
TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
}
@@ -2047,8 +2041,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (le32_to_cpu(event->flags)
- & TRB_TYPE_BITMASK)>>10);
+ (le32_to_cpu(event->flags) &
+ TRB_TYPE_BITMASK)>>10);
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
if (ep->skip) {
ep->skip = false;
@@ -2119,9 +2113,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* corresponding TD has been cancelled. Just ignore
* the TD.
*/
- if ((le32_to_cpu(event_trb->generic.field[3])
- & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_TR_NOOP)) {
+ if (TRB_TYPE_NOOP_LE32(event_trb->generic.field[3])) {
xhci_dbg(xhci,
"event_trb is a no-op TRB. Skip it\n");
goto cleanup;
@@ -2452,7 +2444,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
next->link.control |= cpu_to_le32(TRB_CHAIN);
wmb();
- next->link.control ^= cpu_to_le32((u32) TRB_CYCLE);
+ next->link.control ^= cpu_to_le32(TRB_CYCLE);
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index f5fe1ac301ab..763f484bc092 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1342,8 +1342,8 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* If the HC already knows the endpoint is disabled,
* or the HCD has noted it is disabled, ignore this request
*/
- if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
- EP_STATE_DISABLED ||
+ if (((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) ==
+ cpu_to_le32(EP_STATE_DISABLED)) ||
le32_to_cpu(ctrl_ctx->drop_flags) &
xhci_get_endpoint_flag(&ep->desc)) {
xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
@@ -1758,8 +1758,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
- if ((le32_to_cpu(command->command_trb->link.control)
- & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
+ if (TRB_TYPE_LINK_LE32(command->command_trb->link.control))
command->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
@@ -2559,8 +2558,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
- if ((le32_to_cpu(reset_device_cmd->command_trb->link.control)
- & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
+ if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control))
reset_device_cmd->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index d8bbf5ccb10d..cae8e23308bf 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1072,6 +1072,13 @@ union xhci_trb {
/* Get NEC firmware revision. */
#define TRB_NEC_GET_FW 49
+#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
+/* Above, but for __le32 types -- can avoid work by swapping constants: */
+#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_LINK)))
+#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)))
+
#define NEC_FW_MINOR(p) (((p) >> 0) & 0xff)
#define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff)
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index c302e1983c70..1c3afcc11bd9 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -670,6 +670,9 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
int busnum = ubus? ubus->busnum: 0;
int rc;
+ if (mon_dir == NULL)
+ return 0;
+
if (ubus != NULL) {
rc = snprintf(name, NAMESZ, "%dt", busnum);
if (rc <= 0 || rc >= NAMESZ)
@@ -740,12 +743,12 @@ int __init mon_text_init(void)
mondir = debugfs_create_dir("usbmon", usb_debug_root);
if (IS_ERR(mondir)) {
- printk(KERN_NOTICE TAG ": debugfs is not available\n");
- return -ENODEV;
+ /* debugfs not available, but we can use usbmon without it */
+ return 0;
}
if (mondir == NULL) {
printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
- return -ENODEV;
+ return -ENOMEM;
}
mon_dir = mondir;
return 0;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 13093481f918..6192b45959f4 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -8,7 +8,7 @@ comment "Enable Host or Gadget support to see Inventra options"
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
- depends on (USB || USB_GADGET)
+ depends on USB && USB_GADGET
depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523))
select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
select TWL4030_USB if MACH_OMAP_3430SDP
@@ -67,79 +67,6 @@ config USB_MUSB_UX500
endchoice
-choice
- prompt "Driver Mode"
- depends on USB_MUSB_HDRC
- help
- Dual-Role devices can support both host and peripheral roles,
- as well as a the special "OTG Device" role which can switch
- between both roles as needed.
-
-# use USB_MUSB_HDRC_HCD not USB_MUSB_HOST to #ifdef host side support;
-# OTG needs both roles, not just USB_MUSB_HOST.
-config USB_MUSB_HOST
- depends on USB
- bool "USB Host"
- help
- Say Y here if your system supports the USB host role.
- If it has a USB "A" (rectangular), "Mini-A" (uncommon),
- or "Mini-AB" connector, it supports the host role.
- (With a "Mini-AB" connector, you should enable USB OTG.)
-
-# use USB_GADGET_MUSB_HDRC not USB_MUSB_PERIPHERAL to #ifdef peripheral
-# side support ... OTG needs both roles
-config USB_MUSB_PERIPHERAL
- depends on USB_GADGET
- bool "USB Peripheral (gadget stack)"
- select USB_GADGET_MUSB_HDRC
- help
- Say Y here if your system supports the USB peripheral role.
- If it has a USB "B" (squarish), "Mini-B", or "Mini-AB"
- connector, it supports the peripheral role.
- (With a "Mini-AB" connector, you should enable USB OTG.)
-
-config USB_MUSB_OTG
- depends on USB && USB_GADGET && PM && EXPERIMENTAL
- bool "Both host and peripheral: USB OTG (On The Go) Device"
- select USB_GADGET_MUSB_HDRC
- select USB_OTG
- help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role choice can be changed
- later, when two dual-role devices talk to each other.
-
- At this writing, the OTG support in this driver is incomplete,
- omitting the mandatory HNP or SRP protocols. However, some
- of the cable based role switching works. (That is, grounding
- the ID pin switches the controller to host mode, while leaving
- it floating leaves it in peripheral mode.)
-
- Select this if your system has a Mini-AB connector, or
- to simplify certain kinds of configuration.
-
- To implement your OTG Targeted Peripherals List (TPL), enable
- USB_OTG_WHITELIST and update "drivers/usb/core/otg_whitelist.h"
- to match your requirements.
-
-endchoice
-
-# enable peripheral support (including with OTG)
-config USB_GADGET_MUSB_HDRC
- bool
- depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
-# default y
-# select USB_GADGET_DUALSPEED
-# select USB_GADGET_SELECTED
-
-# enables host support (including with OTG)
-config USB_MUSB_HDRC_HCD
- bool
- depends on USB_MUSB_HDRC && (USB_MUSB_HOST || USB_MUSB_OTG)
- select USB_OTG if USB_GADGET_MUSB_HDRC
- default y
-
-
config MUSB_PIO_ONLY
bool 'Disable DMA (always use PIO)'
depends on USB_MUSB_HDRC
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index c4d228b6ef8a..d8fd9d092dec 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -6,8 +6,8 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
musb_hdrc-y := musb_core.o
-musb_hdrc-$(CONFIG_USB_GADGET_MUSB_HDRC) += musb_gadget_ep0.o musb_gadget.o
-musb_hdrc-$(CONFIG_USB_MUSB_HDRC_HCD) += musb_virthub.o musb_host.o
+musb_hdrc-y += musb_gadget_ep0.o musb_gadget.o
+musb_hdrc-y += musb_virthub.o musb_host.o
musb_hdrc-$(CONFIG_DEBUG_FS) += musb_debugfs.o
# Hardware Glue Layer
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index 23ac28f98d91..08f1d0b662a3 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -124,11 +124,7 @@ static void am35x_musb_disable(struct musb *musb)
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
#define portstate(stmt) stmt
-#else
-#define portstate(stmt)
-#endif
static void am35x_musb_set_vbus(struct musb *musb, int is_on)
{
diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h
index bd9352a2ef2a..c84dae546dc6 100644
--- a/drivers/usb/musb/blackfin.h
+++ b/drivers/usb/musb/blackfin.h
@@ -47,7 +47,7 @@
* So, need to either use silicon v0.2+ or disable DMA mode in MUSB.
*/
#if ANOMALY_05000380 && defined(CONFIG_BF52x) && \
- defined(CONFIG_USB_MUSB_HDRC) && !defined(CONFIG_MUSB_PIO_ONLY)
+ !defined(CONFIG_MUSB_PIO_ONLY)
# error "Please use PIO mode in MUSB driver on bf52x chip v0.0 and v0.1"
#endif
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 662ed34980bd..4da7492ddbdb 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -172,11 +172,7 @@ static void da8xx_musb_disable(struct musb *musb)
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-#define portstate(stmt) stmt
-#else
-#define portstate(stmt)
-#endif
+#define portstate(stmt) stmt
static void da8xx_musb_set_vbus(struct musb *musb, int is_on)
{
@@ -397,21 +393,15 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
cfgchip2 &= ~CFGCHIP2_OTGMODE;
switch (musb_mode) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
cfgchip2 |= CFGCHIP2_FORCE_HOST;
break;
-#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
cfgchip2 |= CFGCHIP2_FORCE_DEVICE;
break;
-#endif
-#ifdef CONFIG_USB_MUSB_OTG
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
cfgchip2 |= CFGCHIP2_NO_OVERRIDE;
break;
-#endif
default:
dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode);
}
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 2a2adf6492cd..8bdf25a8b023 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -143,12 +143,7 @@ static void davinci_musb_disable(struct musb *musb)
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
#define portstate(stmt) stmt
-#else
-#define portstate(stmt)
-#endif
-
/*
* VBUS SWITCHING IS BOARD-SPECIFIC ... at least for the DM6446 EVM,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index bcbd1aba961a..20a28731c338 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -328,8 +328,6 @@ void musb_load_testpacket(struct musb *musb)
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_MUSB_OTG
-
/*
* Handles OTG hnp timeouts, such as b_ase0_brst
*/
@@ -401,8 +399,6 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
-#endif
-
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
@@ -432,7 +428,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state));
if (devctl & MUSB_DEVCTL_HM) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
void __iomem *mbase = musb->mregs;
switch (musb->xceiv->state) {
@@ -472,17 +467,13 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
"host",
otg_state_string(musb->xceiv->state));
}
-#endif
} else {
switch (musb->xceiv->state) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_SUSPEND:
/* possibly DISCONNECT is upcoming */
musb->xceiv->state = OTG_STATE_A_HOST;
usb_hcd_resume_root_hub(musb_to_hcd(musb));
break;
-#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_PERIPHERAL:
/* disconnect while suspended? we may
@@ -500,7 +491,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
case OTG_STATE_B_IDLE:
musb->int_usb &= ~MUSB_INTR_SUSPEND;
break;
-#endif
default:
WARNING("bogus %s RESUME (%s)\n",
"peripheral",
@@ -509,7 +499,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* see manual for the order of the tests */
if (int_usb & MUSB_INTR_SESSREQ) {
void __iomem *mbase = musb->mregs;
@@ -609,14 +598,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
handled = IRQ_HANDLED;
}
-#endif
if (int_usb & MUSB_INTR_SUSPEND) {
dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x power %02x\n",
otg_state_string(musb->xceiv->state), devctl, power);
handled = IRQ_HANDLED;
switch (musb->xceiv->state) {
-#ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_A_PERIPHERAL:
/* We also come here if the cable is removed, since
* this silicon doesn't report ID-no-longer-grounded.
@@ -633,7 +620,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
? : OTG_TIME_A_WAIT_BCON));
break;
-#endif
case OTG_STATE_B_IDLE:
if (!musb->is_active)
break;
@@ -642,13 +628,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->gadget->b_hnp_enable;
if (musb->is_active) {
-#ifdef CONFIG_USB_MUSB_OTG
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
OTG_TIME_B_ASE0_BRST));
-#endif
}
break;
case OTG_STATE_A_WAIT_BCON:
@@ -672,7 +656,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (int_usb & MUSB_INTR_CONNECT) {
struct usb_hcd *hcd = musb_to_hcd(musb);
@@ -682,7 +665,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->ep0_stage = MUSB_EP0_START;
-#ifdef CONFIG_USB_MUSB_OTG
/* flush endpoints when transitioning from Device Mode */
if (is_peripheral_active(musb)) {
/* REVISIT HNP; just force disconnect */
@@ -690,7 +672,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask);
musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe);
musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
-#endif
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|USB_PORT_STAT_HIGH_SPEED
|USB_PORT_STAT_ENABLE
@@ -739,7 +720,6 @@ b_host:
dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n",
otg_state_string(musb->xceiv->state), devctl);
}
-#endif /* CONFIG_USB_MUSB_HDRC_HCD */
if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n",
@@ -748,7 +728,6 @@ b_host:
handled = IRQ_HANDLED;
switch (musb->xceiv->state) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
usb_hcd_resume_root_hub(musb_to_hcd(musb));
@@ -757,8 +736,6 @@ b_host:
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
-#endif /* HOST */
-#ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_B_HOST:
/* REVISIT this behaves for "real disconnect"
* cases; make sure the other transitions from
@@ -777,13 +754,10 @@ b_host:
/* FALLTHROUGH */
case OTG_STATE_B_WAIT_ACON:
/* FALLTHROUGH */
-#endif /* OTG */
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb_g_disconnect(musb);
break;
-#endif /* GADGET */
default:
WARNING("unhandled DISCONNECT transition (%s)\n",
otg_state_string(musb->xceiv->state));
@@ -814,7 +788,6 @@ b_host:
dev_dbg(musb->controller, "BUS RESET as %s\n",
otg_state_string(musb->xceiv->state));
switch (musb->xceiv->state) {
-#ifdef CONFIG_USB_OTG
case OTG_STATE_A_SUSPEND:
/* We need to ignore disconnect on suspend
* otherwise tusb 2.0 won't reconnect after a
@@ -842,7 +815,6 @@ b_host:
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
musb_g_reset(musb);
break;
-#endif
case OTG_STATE_B_IDLE:
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
/* FALLTHROUGH */
@@ -927,7 +899,6 @@ void musb_start(struct musb *musb)
/* put into basic highspeed mode and start session */
musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
- | MUSB_POWER_SOFTCONN
| MUSB_POWER_HSENAB
/* ENSUSPEND wedges tusb */
/* | MUSB_POWER_ENSUSPEND */
@@ -1038,10 +1009,15 @@ static void musb_shutdown(struct platform_device *pdev)
* We don't currently use dynamic fifo setup capability to do anything
* more than selecting one of a bunch of predefined configurations.
*/
-#if defined(CONFIG_USB_MUSB_TUSB6010) || defined(CONFIG_USB_MUSB_OMAP2PLUS) \
- || defined(CONFIG_USB_MUSB_AM35X)
+#if defined(CONFIG_USB_MUSB_TUSB6010) \
+ || defined(CONFIG_USB_MUSB_TUSB6010_MODULE) \
+ || defined(CONFIG_USB_MUSB_OMAP2PLUS) \
+ || defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE) \
+ || defined(CONFIG_USB_MUSB_AM35X) \
+ || defined(CONFIG_USB_MUSB_AM35X_MODULE)
static ushort __initdata fifo_mode = 4;
-#elif defined(CONFIG_USB_MUSB_UX500)
+#elif defined(CONFIG_USB_MUSB_UX500) \
+ || defined(CONFIG_USB_MUSB_UX500_MODULE)
static ushort __initdata fifo_mode = 5;
#else
static ushort __initdata fifo_mode = 2;
@@ -1191,14 +1167,12 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
/* configure the FIFO */
musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum);
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* EP0 reserved endpoint for control, bidirectional;
* EP1 reserved for bulk, two unidirection halves.
*/
if (hw_ep->epnum == 1)
musb->bulk_ep = hw_ep;
/* REVISIT error check: be sure ep0 can both rx and tx ... */
-#endif
switch (cfg->style) {
case FIFO_TX:
musb_write_txfifosz(mbase, c_size);
@@ -1317,12 +1291,10 @@ done:
n + 1, musb->config->num_eps * 2 - 1,
offset, (1 << (musb->config->ram_bits + 2)));
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (!musb->bulk_ep) {
pr_debug("%s: missing bulk\n", musb_driver_name);
return -EINVAL;
}
-#endif
return 0;
}
@@ -1353,7 +1325,6 @@ static int __init ep_config_from_hw(struct musb *musb)
/* FIXME set up hw_ep->{rx,tx}_double_buffered */
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* pick an RX/TX endpoint for bulk */
if (hw_ep->max_packet_sz_tx < 512
|| hw_ep->max_packet_sz_rx < 512)
@@ -1365,15 +1336,12 @@ static int __init ep_config_from_hw(struct musb *musb)
if (musb->bulk_ep)
continue;
musb->bulk_ep = hw_ep;
-#endif
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (!musb->bulk_ep) {
pr_debug("%s: missing bulk\n", musb_driver_name);
return -EINVAL;
}
-#endif
return 0;
}
@@ -1429,13 +1397,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
} else {
musb->is_multipoint = 0;
type = "";
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
printk(KERN_ERR
"%s: kernel must blacklist external hubs\n",
musb_driver_name);
#endif
-#endif
}
/* log release info */
@@ -1479,11 +1445,9 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
#endif
hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
hw_ep->rx_reinit = 1;
hw_ep->tx_reinit = 1;
-#endif
if (hw_ep->max_packet_sz_tx) {
dev_dbg(musb->controller,
@@ -1561,14 +1525,6 @@ irqreturn_t musb_interrupt(struct musb *musb)
(devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx);
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
- if (!musb->gadget_driver) {
- dev_dbg(musb->controller, "No gadget driver loaded\n");
- return IRQ_HANDLED;
- }
-#endif
-
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
@@ -1767,8 +1723,6 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-
/* Gadget drivers can't know that a host is connected so they might want
* to start SRP, but users can. This allows userspace to trigger SRP.
*/
@@ -1792,14 +1746,10 @@ musb_srp_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
-#endif /* CONFIG_USB_GADGET_MUSB_HDRC */
-
static struct attribute *musb_attributes[] = {
&dev_attr_mode.attr,
&dev_attr_vbus.attr,
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
&dev_attr_srp.attr,
-#endif
NULL
};
@@ -1832,7 +1782,6 @@ allocate_instance(struct device *dev,
struct musb *musb;
struct musb_hw_ep *ep;
int epnum;
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
struct usb_hcd *hcd;
hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
@@ -1850,12 +1799,6 @@ allocate_instance(struct device *dev,
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
-#else
- musb = kzalloc(sizeof *musb, GFP_KERNEL);
- if (!musb)
- return NULL;
-
-#endif
dev_set_drvdata(dev, musb);
musb->mregs = mbase;
musb->ctrl_base = mbase;
@@ -1885,9 +1828,7 @@ static void musb_free(struct musb *musb)
sysfs_remove_group(&musb->controller->kobj, &musb_attr_group);
#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
musb_gadget_cleanup(musb);
-#endif
if (musb->nIrq >= 0) {
if (musb->irq_wake)
@@ -1901,11 +1842,7 @@ static void musb_free(struct musb *musb)
dma_controller_destroy(c);
}
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
- usb_put_hcd(musb_to_hcd(musb));
-#else
kfree(musb);
-#endif
}
/*
@@ -2000,9 +1937,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (status < 0)
goto fail3;
-#ifdef CONFIG_USB_MUSB_OTG
setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
-#endif
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
@@ -2214,7 +2149,16 @@ static void musb_save_context(struct musb *musb)
musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL);
for (i = 0; i < musb->config->num_eps; ++i) {
- epio = musb->endpoints[i].regs;
+ struct musb_hw_ep *hw_ep;
+
+ hw_ep = &musb->endpoints[i];
+ if (!hw_ep)
+ continue;
+
+ epio = hw_ep->regs;
+ if (!epio)
+ continue;
+
musb->context.index_regs[i].txmaxp =
musb_readw(epio, MUSB_TXMAXP);
musb->context.index_regs[i].txcsr =
@@ -2280,7 +2224,16 @@ static void musb_restore_context(struct musb *musb)
musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl);
for (i = 0; i < musb->config->num_eps; ++i) {
- epio = musb->endpoints[i].regs;
+ struct musb_hw_ep *hw_ep;
+
+ hw_ep = &musb->endpoints[i];
+ if (!hw_ep)
+ continue;
+
+ epio = hw_ep->regs;
+ if (!epio)
+ continue;
+
musb_writew(epio, MUSB_TXMAXP,
musb->context.index_regs[i].txmaxp);
musb_writew(epio, MUSB_TXCSR,
@@ -2329,13 +2282,13 @@ static void musb_restore_context(struct musb *musb)
musb->context.index_regs[i].rxhubport);
}
}
+ musb_writeb(musb_base, MUSB_INDEX, musb->context.index);
}
static int musb_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
+ struct musb *musb = dev_to_musb(dev);
unsigned long flags;
- struct musb *musb = dev_to_musb(&pdev->dev);
spin_lock_irqsave(&musb->lock, flags);
@@ -2357,8 +2310,7 @@ static int musb_suspend(struct device *dev)
static int musb_resume_noirq(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct musb *musb = dev_to_musb(&pdev->dev);
+ struct musb *musb = dev_to_musb(dev);
musb_restore_context(musb);
@@ -2426,34 +2378,13 @@ static struct platform_driver musb_driver = {
static int __init musb_init(void)
{
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (usb_disabled())
return 0;
-#endif
pr_info("%s: version " MUSB_VERSION ", "
-#ifdef CONFIG_MUSB_PIO_ONLY
- "pio"
-#elif defined(CONFIG_USB_TI_CPPI_DMA)
- "cppi-dma"
-#elif defined(CONFIG_USB_INVENTRA_DMA)
- "musb-dma"
-#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
- "tusb-omap-dma"
-#elif defined(CONFIG_USB_UX500_DMA)
- "ux500-dma"
-#else
"?dma?"
-#endif
", "
-#ifdef CONFIG_USB_MUSB_OTG
- "otg (peripheral+host)"
-#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
- "peripheral"
-#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
- "host"
-#endif
- ,
+ "otg (peripheral+host)",
musb_driver_name);
return platform_driver_probe(&musb_driver, musb_probe);
}
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 0e053b587960..668eeef601ae 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -72,10 +72,6 @@ struct musb_ep;
#include <linux/usb/hcd.h>
#include "musb_host.h"
-
-
-#ifdef CONFIG_USB_MUSB_OTG
-
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
@@ -86,24 +82,6 @@ struct musb_ep;
#define is_peripheral_active(m) (!(m)->is_host)
#define is_host_active(m) ((m)->is_host)
-#else
-#define is_peripheral_enabled(musb) is_peripheral_capable()
-#define is_host_enabled(musb) is_host_capable()
-#define is_otg_enabled(musb) 0
-
-#define is_peripheral_active(musb) is_peripheral_capable()
-#define is_host_active(musb) is_host_capable()
-#endif
-
-#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
-/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
- * override that choice selection (often USB_GADGET_DUMMY_HCD).
- */
-#ifndef CONFIG_USB_GADGET_MUSB_HDRC
-#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
-#endif
-#endif /* need MUSB gadget selection */
-
#ifndef CONFIG_HAVE_CLK
/* Dummy stub for clk framework */
#define clk_get(dev, id) NULL
@@ -119,8 +97,6 @@ struct musb_ep;
/****************************** PERIPHERAL ROLE *****************************/
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-
#define is_peripheral_capable() (1)
extern irqreturn_t musb_g_ep0_irq(struct musb *);
@@ -132,40 +108,14 @@ extern void musb_g_resume(struct musb *);
extern void musb_g_wakeup(struct musb *);
extern void musb_g_disconnect(struct musb *);
-#else
-
-#define is_peripheral_capable() (0)
-
-static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
-static inline void musb_g_reset(struct musb *m) {}
-static inline void musb_g_suspend(struct musb *m) {}
-static inline void musb_g_resume(struct musb *m) {}
-static inline void musb_g_wakeup(struct musb *m) {}
-static inline void musb_g_disconnect(struct musb *m) {}
-
-#endif
-
/****************************** HOST ROLE ***********************************/
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
-
#define is_host_capable() (1)
extern irqreturn_t musb_h_ep0_irq(struct musb *);
extern void musb_host_tx(struct musb *, u8);
extern void musb_host_rx(struct musb *, u8);
-#else
-
-#define is_host_capable() (0)
-
-static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
-static inline void musb_host_tx(struct musb *m, u8 e) {}
-static inline void musb_host_rx(struct musb *m, u8 e) {}
-
-#endif
-
-
/****************************** CONSTANTS ********************************/
#ifndef MUSB_C_NUM_EPS
@@ -261,7 +211,7 @@ enum musb_g_ep0_state {
* @try_ilde: tries to idle the IP
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
- * @channel_program: pre check for standard dma channel_program func
+ * @adjust_channel_params: pre check for standard dma channel_program func
*/
struct musb_platform_ops {
int (*init)(struct musb *musb);
@@ -315,7 +265,6 @@ struct musb_hw_ep {
void __iomem *fifo_sync_va;
#endif
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
void __iomem *target_regs;
/* currently scheduled peripheral endpoint */
@@ -324,31 +273,20 @@ struct musb_hw_ep {
u8 rx_reinit;
u8 tx_reinit;
-#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* peripheral side */
struct musb_ep ep_in; /* TX */
struct musb_ep ep_out; /* RX */
-#endif
};
static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep)
{
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_in);
-#else
- return NULL;
-#endif
}
static inline struct musb_request *next_out_request(struct musb_hw_ep *hw_ep)
{
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_out);
-#else
- return NULL;
-#endif
}
struct musb_csr_regs {
@@ -393,7 +331,6 @@ struct musb {
u32 port1_status;
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
unsigned long rh_timer;
enum musb_h_ep0_state ep0_stage;
@@ -411,7 +348,6 @@ struct musb {
struct list_head out_bulk; /* of musb_qh */
struct timer_list otg_timer;
-#endif
struct notifier_block nb;
struct dma_controller *dma_controller;
@@ -472,7 +408,6 @@ struct musb {
#define can_bulk_combine(musb,type) \
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
@@ -496,7 +431,6 @@ struct musb {
enum musb_g_ep0_state ep0_state;
struct usb_gadget g; /* the gadget */
struct usb_gadget_driver *gadget_driver; /* its driver */
-#endif
/*
* FIXME: Remove this flag.
@@ -518,12 +452,10 @@ struct musb {
#endif
};
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
{
return container_of(g, struct musb, g);
}
-#endif
#ifdef CONFIG_BLACKFIN
static inline int musb_read_fifosize(struct musb *musb,
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6aeb363e63e7..b67a062f556b 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1663,8 +1663,8 @@ static void musb_pullup(struct musb *musb, int is_on)
/* FIXME if on, HdrcStart; if off, HdrcStop */
- dev_dbg(musb->controller, "gadget %s D+ pullup %s\n",
- musb->gadget_driver->function, is_on ? "on" : "off");
+ dev_dbg(musb->controller, "gadget D+ pullup %s\n",
+ is_on ? "on" : "off");
musb_writeb(musb->mregs, MUSB_POWER, power);
}
@@ -1710,6 +1710,11 @@ static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int musb_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int musb_gadget_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+
static const struct usb_gadget_ops musb_gadget_operations = {
.get_frame = musb_gadget_get_frame,
.wakeup = musb_gadget_wakeup,
@@ -1717,6 +1722,8 @@ static const struct usb_gadget_ops musb_gadget_operations = {
/* .vbus_session = musb_gadget_vbus_session, */
.vbus_draw = musb_gadget_vbus_draw,
.pullup = musb_gadget_pullup,
+ .udc_start = musb_gadget_start,
+ .udc_stop = musb_gadget_stop,
};
/* ----------------------------------------------------------------------- */
@@ -1727,7 +1734,6 @@ static const struct usb_gadget_ops musb_gadget_operations = {
* about there being only one external upstream port. It assumes
* all peripheral ports are external...
*/
-static struct musb *the_gadget;
static void musb_gadget_release(struct device *dev)
{
@@ -1814,9 +1820,6 @@ int __init musb_gadget_setup(struct musb *musb)
* musb peripherals at the same time, only the bus lock
* is probably held.
*/
- if (the_gadget)
- return -EBUSY;
- the_gadget = musb;
musb->g.ops = &musb_gadget_operations;
musb->g.is_dualspeed = 1;
@@ -1840,18 +1843,22 @@ int __init musb_gadget_setup(struct musb *musb)
status = device_register(&musb->g.dev);
if (status != 0) {
put_device(&musb->g.dev);
- the_gadget = NULL;
+ return status;
}
+ status = usb_add_gadget_udc(musb->controller, &musb->g);
+ if (status)
+ goto err;
+
+ return 0;
+err:
+ device_unregister(&musb->g.dev);
return status;
}
void musb_gadget_cleanup(struct musb *musb)
{
- if (musb != the_gadget)
- return;
-
+ usb_del_gadget_udc(&musb->g);
device_unregister(&musb->g.dev);
- the_gadget = NULL;
}
/*
@@ -1863,59 +1870,30 @@ void musb_gadget_cleanup(struct musb *musb)
* -ENOMEM no memory to perform the operation
*
* @param driver the gadget driver
- * @param bind the driver's bind function
* @return <0 if error, 0 if everything is fine
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int musb_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct musb *musb = the_gadget;
+ struct musb *musb = gadget_to_musb(g);
unsigned long flags;
int retval = -EINVAL;
- if (!driver
- || driver->speed != USB_SPEED_HIGH
- || !bind || !driver->setup)
- goto err0;
-
- /* driver must be initialized to support peripheral mode */
- if (!musb) {
- dev_dbg(musb->controller, "no dev??\n");
- retval = -ENODEV;
+ if (driver->speed != USB_SPEED_HIGH)
goto err0;
- }
pm_runtime_get_sync(musb->controller);
dev_dbg(musb->controller, "registering driver %s\n", driver->function);
- if (musb->gadget_driver) {
- dev_dbg(musb->controller, "%s is already bound to %s\n",
- musb_driver_name,
- musb->gadget_driver->driver.name);
- retval = -EBUSY;
- goto err0;
- }
-
- spin_lock_irqsave(&musb->lock, flags);
+ musb->softconnect = 0;
musb->gadget_driver = driver;
- musb->g.dev.driver = &driver->driver;
- driver->driver.bus = NULL;
- musb->softconnect = 1;
- spin_unlock_irqrestore(&musb->lock, flags);
-
- retval = bind(&musb->g);
- if (retval) {
- dev_dbg(musb->controller, "bind to driver %s failed --> %d\n",
- driver->driver.name, retval);
- goto err1;
- }
spin_lock_irqsave(&musb->lock, flags);
+ musb->is_active = 1;
otg_set_peripheral(musb->xceiv, &musb->g);
musb->xceiv->state = OTG_STATE_B_IDLE;
- musb->is_active = 1;
/*
* FIXME this ignores the softconnect flag. Drivers are
@@ -1927,8 +1905,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
if (!is_otg_enabled(musb))
musb_start(musb);
- otg_set_peripheral(musb->xceiv, &musb->g);
-
spin_unlock_irqrestore(&musb->lock, flags);
if (is_otg_enabled(musb)) {
@@ -1960,15 +1936,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
err2:
if (!is_otg_enabled(musb))
musb_stop(musb);
-
-err1:
- musb->gadget_driver = NULL;
- musb->g.dev.driver = NULL;
-
err0:
return retval;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
{
@@ -2018,17 +1988,12 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
*
* @param driver the gadget driver to unregister
*/
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int musb_gadget_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct musb *musb = the_gadget;
+ struct musb *musb = gadget_to_musb(g);
unsigned long flags;
- if (!driver || !driver->unbind || !musb)
- return -EINVAL;
-
- if (!musb->gadget_driver)
- return -EINVAL;
-
if (musb->xceiv->last_event == USB_EVENT_NONE)
pm_runtime_get_sync(musb->controller);
@@ -2039,9 +2004,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
spin_lock_irqsave(&musb->lock, flags);
-#ifdef CONFIG_USB_MUSB_OTG
musb_hnp_stop(musb);
-#endif
(void) musb_gadget_vbus_draw(&musb->g, 0);
@@ -2051,13 +2014,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
dev_dbg(musb->controller, "unregistering driver %s\n", driver->function);
- spin_unlock_irqrestore(&musb->lock, flags);
- driver->unbind(&musb->g);
- spin_lock_irqsave(&musb->lock, flags);
-
- musb->gadget_driver = NULL;
- musb->g.dev.driver = NULL;
-
musb->is_active = 0;
musb_platform_try_idle(musb, 0);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -2077,8 +2033,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
/* ----------------------------------------------------------------------- */
@@ -2164,7 +2118,6 @@ void musb_g_disconnect(struct musb *musb)
switch (musb->xceiv->state) {
default:
-#ifdef CONFIG_USB_MUSB_OTG
dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
otg_state_string(musb->xceiv->state));
musb->xceiv->state = OTG_STATE_A_IDLE;
@@ -2176,7 +2129,6 @@ void musb_g_disconnect(struct musb *musb)
break;
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_HOST:
-#endif
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb->xceiv->state = OTG_STATE_B_IDLE;
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index b2faff235507..9378b359c1f0 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -88,7 +88,6 @@ static int service_tx_status_request(
case USB_RECIP_DEVICE:
result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
-#ifdef CONFIG_USB_MUSB_OTG
if (musb->g.is_otg) {
result[0] |= musb->g.b_hnp_enable
<< USB_DEVICE_B_HNP_ENABLE;
@@ -97,7 +96,6 @@ static int service_tx_status_request(
result[0] |= musb->g.a_hnp_support
<< USB_DEVICE_A_HNP_SUPPORT;
}
-#endif
break;
case USB_RECIP_INTERFACE:
@@ -392,7 +390,6 @@ __acquires(musb->lock)
if (handled > 0)
musb->test_mode = true;
break;
-#ifdef CONFIG_USB_MUSB_OTG
case USB_DEVICE_B_HNP_ENABLE:
if (!musb->g.is_otg)
goto stall;
@@ -409,7 +406,6 @@ __acquires(musb->lock)
goto stall;
musb->g.a_alt_hnp_support = 1;
break;
-#endif
case USB_DEVICE_DEBUG_MODE:
handled = 0;
break;
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 14b00776638d..622d09fb9aba 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -95,7 +95,6 @@ extern const struct hc_driver musb_hc_driver;
static inline struct urb *next_urb(struct musb_qh *qh)
{
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
struct list_head *queue;
if (!qh)
@@ -104,9 +103,6 @@ static inline struct urb *next_urb(struct musb_qh *qh)
if (list_empty(queue))
return NULL;
return list_entry(queue->next, struct urb, urb_list);
-#else
- return NULL;
-#endif
}
#endif /* _MUSB_HOST_H */
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 2d80a5758838..e9f80adc45a4 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -88,14 +88,12 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
OTG_TIME_A_AIDL_BDIS));
musb_platform_try_idle(musb, 0);
break;
-#ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_B_HOST:
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv->host->b_hnp_enable;
musb_platform_try_idle(musb, 0);
break;
-#endif
default:
dev_dbg(musb->controller, "bogus rh suspend? %s\n",
otg_state_string(musb->xceiv->state));
@@ -118,13 +116,11 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
u8 power;
void __iomem *mbase = musb->mregs;
-#ifdef CONFIG_USB_MUSB_OTG
if (musb->xceiv->state == OTG_STATE_B_IDLE) {
dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n");
musb->port1_status &= ~USB_PORT_STAT_RESET;
return;
}
-#endif
if (!is_host_active(musb))
return;
@@ -191,14 +187,12 @@ void musb_root_disconnect(struct musb *musb)
switch (musb->xceiv->state) {
case OTG_STATE_A_SUSPEND:
-#ifdef CONFIG_USB_MUSB_OTG
if (is_otg_enabled(musb)
&& musb->xceiv->host->b_hnp_enable) {
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
break;
}
-#endif
/* FALLTHROUGH */
case OTG_STATE_A_HOST:
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index c5d4c44d0ffa..ba85f273e487 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -51,9 +51,7 @@ static void musb_do_idle(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
unsigned long flags;
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
u8 power;
-#endif
u8 devctl;
spin_lock_irqsave(&musb->lock, flags);
@@ -70,7 +68,6 @@ static void musb_do_idle(unsigned long _musb)
MUSB_HST_MODE(musb);
}
break;
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_SUSPEND:
/* finish RESUME signaling? */
if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
@@ -87,15 +84,12 @@ static void musb_do_idle(unsigned long _musb)
musb->xceiv->state = OTG_STATE_A_HOST;
}
break;
-#endif
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
musb->xceiv->state = OTG_STATE_B_IDLE;
else
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
-#endif
default:
break;
}
@@ -243,13 +237,11 @@ static int musb_otg_notifications(struct notifier_block *nb,
dev_dbg(musb->controller, "ID GND\n");
if (is_otg_enabled(musb)) {
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (musb->gadget_driver) {
pm_runtime_get_sync(musb->controller);
otg_init(musb->xceiv);
omap2430_musb_set_vbus(musb, 1);
}
-#endif
} else {
pm_runtime_get_sync(musb->controller);
otg_init(musb->xceiv);
@@ -260,21 +252,16 @@ static int musb_otg_notifications(struct notifier_block *nb,
case USB_EVENT_VBUS:
dev_dbg(musb->controller, "VBUS Connect\n");
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (musb->gadget_driver)
pm_runtime_get_sync(musb->controller);
-#endif
otg_init(musb->xceiv);
break;
case USB_EVENT_NONE:
dev_dbg(musb->controller, "VBUS Disconnect\n");
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
- if (musb->gadget_driver)
-#endif
- {
+ if (musb->gadget_driver) {
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index b410357cf016..9eec41fbf3a4 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -269,8 +269,6 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
static struct musb *the_musb;
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-
/* This is used by gadget drivers, and OTG transceiver logic, allowing
* at most mA current to be drawn from VBUS during a Default-B session
* (that is, while VBUS exceeds 4.4V). In Default-A (including pure host
@@ -310,10 +308,6 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
return 0;
}
-#else
-#define tusb_draw_power NULL
-#endif
-
/* workaround for issue 13: change clock during chip idle
* (to be fixed in rev3 silicon) ... symptoms include disconnect
* or looping suspend/resume cycles
@@ -440,19 +434,15 @@ static void musb_do_idle(unsigned long _musb)
if (is_host_active(musb) && (musb->port1_status >> 16))
goto done;
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- if (is_peripheral_enabled(musb) && !musb->gadget_driver)
+ if (is_peripheral_enabled(musb) && !musb->gadget_driver) {
wakeups = 0;
- else {
+ } else {
wakeups = TUSB_PRCM_WHOSTDISCON
- | TUSB_PRCM_WBUS
+ | TUSB_PRCM_WBUS
| TUSB_PRCM_WVBUS;
if (is_otg_enabled(musb))
wakeups |= TUSB_PRCM_WID;
}
-#else
- wakeups = TUSB_PRCM_WHOSTDISCON | TUSB_PRCM_WBUS;
-#endif
tusb_allow_idle(musb, wakeups);
}
done:
@@ -610,30 +600,22 @@ static int tusb_musb_set_mode(struct musb *musb, u8 musb_mode)
switch (musb_mode) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
case MUSB_HOST: /* Disable PHY ID detect, ground ID */
phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
dev_conf |= TUSB_DEV_CONF_ID_SEL;
dev_conf &= ~TUSB_DEV_CONF_SOFT_ID;
break;
-#endif
-
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case MUSB_PERIPHERAL: /* Disable PHY ID detect, keep ID pull-up on */
phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
break;
-#endif
-
-#ifdef CONFIG_USB_MUSB_OTG
case MUSB_OTG: /* Use PHY ID detection */
phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
break;
-#endif
default:
dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode);
@@ -684,7 +666,6 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
/* B-dev state machine: no vbus ~= disconnect */
if ((is_otg_enabled(musb) && !musb->xceiv->default_a)
|| !is_host_enabled(musb)) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* ? musb_root_disconnect(musb); */
musb->port1_status &=
~(USB_PORT_STAT_CONNECTION
@@ -693,7 +674,6 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
| USB_PORT_STAT_HIGH_SPEED
| USB_PORT_STAT_TEST
);
-#endif
if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n");
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index cfb5aa72b196..b4d2c0972b3d 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -95,11 +95,15 @@ struct twl6030_usb {
struct regulator *usb3v3;
+ /* used to set vbus, in atomic path */
+ struct work_struct set_vbus_work;
+
int irq1;
int irq2;
u8 linkstat;
u8 asleep;
bool irq_enabled;
+ bool vbus_enable;
unsigned long features;
};
@@ -370,20 +374,31 @@ static int twl6030_enable_irq(struct otg_transceiver *x)
return 0;
}
-static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
+static void otg_set_vbus_work(struct work_struct *data)
{
- struct twl6030_usb *twl = xceiv_to_twl(x);
+ struct twl6030_usb *twl = container_of(data, struct twl6030_usb,
+ set_vbus_work);
/*
* Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
* register. This enables boost mode.
*/
- if (enabled)
+
+ if (twl->vbus_enable)
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
- CHARGERUSB_CTRL1);
- else
+ CHARGERUSB_CTRL1);
+ else
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
- CHARGERUSB_CTRL1);
+ CHARGERUSB_CTRL1);
+}
+
+static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
+{
+ struct twl6030_usb *twl = xceiv_to_twl(x);
+
+ twl->vbus_enable = enabled;
+ schedule_work(&twl->set_vbus_work);
+
return 0;
}
@@ -444,6 +459,8 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
+ INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
+
twl->irq_enabled = true;
status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
@@ -494,6 +511,7 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
regulator_put(twl->usb3v3);
pdata->phy_exit(twl->dev);
device_remove_file(twl->dev, &dev_attr_vbus);
+ cancel_work_sync(&twl->set_vbus_work);
kfree(twl);
return 0;
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
index b2e64918884c..286cbf1ca7de 100644
--- a/drivers/usb/renesas_usbhs/Kconfig
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -1,5 +1,5 @@
#
-# Renesas USB Controller Drivers
+# Renesas USBHS Controller Drivers
#
config USB_RENESAS_USBHS
@@ -7,10 +7,9 @@ config USB_RENESAS_USBHS
depends on SUPERH || ARCH_SHMOBILE
default n
help
- Renesas USBHS is a discrete USB host and peripheral controller chip
- that supports both full and high speed USB 2.0 data transfers.
- It has nine or more configurable endpoints, and endpoint zero.
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "renesas_usbhs" and force all
- gadget drivers to also be dynamically linked.
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs"
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index b8798ad16278..ce08345fa15a 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -4,6 +4,6 @@
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
-renesas_usbhs-y := common.o mod.o pipe.o
+renesas_usbhs-y := common.o mod.o pipe.o fifo.o
renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index f3664d6af661..d8239e5efa66 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -21,6 +21,29 @@
#include <linux/sysfs.h>
#include "./common.h"
+/*
+ * image of renesas_usbhs
+ *
+ * ex) gadget case
+
+ * mod.c
+ * mod_gadget.c
+ * mod_host.c pipe.c fifo.c
+ *
+ * +-------+ +-----------+
+ * | pipe0 |------>| fifo pio |
+ * +------------+ +-------+ +-----------+
+ * | mod_gadget |=====> | pipe1 |--+
+ * +------------+ +-------+ | +-----------+
+ * | pipe2 | | +-| fifo dma0 |
+ * +------------+ +-------+ | | +-----------+
+ * | mod_host | | pipe3 |<-|--+
+ * +------------+ +-------+ | +-----------+
+ * | .... | +--->| fifo dma1 |
+ * | .... | +-----------+
+ */
+
+
#define USBHSF_RUNTIME_PWCTRL (1 << 0)
/* status */
@@ -304,6 +327,8 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
priv->dparam->pipe_type = usbhsc_default_pipe_type;
priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
}
+ if (!priv->dparam->pio_dma_border)
+ priv->dparam->pio_dma_border = 64; /* 64byte */
/* FIXME */
/* runtime power control ? */
@@ -323,10 +348,14 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
if (ret < 0)
goto probe_end_iounmap;
- ret = usbhs_mod_probe(priv);
+ ret = usbhs_fifo_probe(priv);
if (ret < 0)
goto probe_end_pipe_exit;
+ ret = usbhs_mod_probe(priv);
+ if (ret < 0)
+ goto probe_end_fifo_exit;
+
/* dev_set_drvdata should be called after usbhs_mod_init */
dev_set_drvdata(&pdev->dev, priv);
@@ -374,6 +403,8 @@ probe_end_call_remove:
usbhs_platform_call(priv, hardware_exit, pdev);
probe_end_mod_exit:
usbhs_mod_remove(priv);
+probe_end_fifo_exit:
+ usbhs_fifo_remove(priv);
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
probe_end_iounmap:
@@ -404,6 +435,7 @@ static int __devexit usbhs_remove(struct platform_device *pdev)
usbhs_platform_call(priv, hardware_exit, pdev);
usbhs_mod_remove(priv);
+ usbhs_fifo_remove(priv);
usbhs_pipe_remove(priv);
iounmap(priv->base);
kfree(priv);
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index 0aadcb402764..b410463a1212 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -36,6 +36,12 @@ struct usbhs_priv;
#define CFIFO 0x0014
#define CFIFOSEL 0x0020
#define CFIFOCTR 0x0022
+#define D0FIFO 0x0100
+#define D0FIFOSEL 0x0028
+#define D0FIFOCTR 0x002A
+#define D1FIFO 0x0120
+#define D1FIFOSEL 0x002C
+#define D1FIFOCTR 0x002E
#define INTENB0 0x0030
#define INTENB1 0x0032
#define BRDYENB 0x0036
@@ -60,6 +66,30 @@ struct usbhs_priv;
#define PIPEMAXP 0x006C
#define PIPEPERI 0x006E
#define PIPEnCTR 0x0070
+#define PIPE1TRE 0x0090
+#define PIPE1TRN 0x0092
+#define PIPE2TRE 0x0094
+#define PIPE2TRN 0x0096
+#define PIPE3TRE 0x0098
+#define PIPE3TRN 0x009A
+#define PIPE4TRE 0x009C
+#define PIPE4TRN 0x009E
+#define PIPE5TRE 0x00A0
+#define PIPE5TRN 0x00A2
+#define PIPEBTRE 0x00A4
+#define PIPEBTRN 0x00A6
+#define PIPECTRE 0x00A8
+#define PIPECTRN 0x00AA
+#define PIPEDTRE 0x00AC
+#define PIPEDTRN 0x00AE
+#define PIPEETRE 0x00B0
+#define PIPEETRN 0x00B2
+#define PIPEFTRE 0x00B4
+#define PIPEFTRN 0x00B6
+#define PIPE9TRE 0x00B8
+#define PIPE9TRN 0x00BA
+#define PIPEATRE 0x00BC
+#define PIPEATRN 0x00BE
/* SYSCFG */
#define SCKE (1 << 10) /* USB Module Clock Enable */
@@ -78,6 +108,7 @@ struct usbhs_priv;
#define RHST_HIGH_SPEED 3 /* High-speed connection */
/* CFIFOSEL */
+#define DREQE (1 << 12) /* DMA Transfer Request Enable */
#define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */
/* CFIFOCTR */
@@ -164,6 +195,10 @@ struct usbhs_priv;
#define CCPL (1 << 2) /* Control Transfer End Enable */
+/* PIPEnTRE */
+#define TRENB (1 << 9) /* Transaction Counter Enable */
+#define TRCLR (1 << 8) /* Transaction Counter Clear */
+
/* FRMNUM */
#define FRNM_MASK (0x7FF)
@@ -194,6 +229,11 @@ struct usbhs_priv {
* pipe control
*/
struct usbhs_pipe_info pipe_info;
+
+ /*
+ * fifo control
+ */
+ struct usbhs_fifo_info fifo_info;
};
/*
@@ -204,6 +244,10 @@ void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev);
+
+#define usbhs_lock(p, f) spin_lock_irqsave(usbhs_priv_to_lock(p), f)
+#define usbhs_unlock(p, f) spin_unlock_irqrestore(usbhs_priv_to_lock(p), f)
+
/*
* sysconfig
*/
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
new file mode 100644
index 000000000000..406893e4422b
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -0,0 +1,1015 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "./common.h"
+#include "./pipe.h"
+
+#define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo))
+#define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo))
+#define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo))
+
+#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
+
+/*
+ * packet initialize
+ */
+void usbhs_pkt_init(struct usbhs_pkt *pkt)
+{
+ pkt->dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&pkt->node);
+}
+
+/*
+ * packet control function
+ */
+static int usbhsf_null_handle(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ dev_err(dev, "null handler\n");
+
+ return -EINVAL;
+}
+
+static struct usbhs_pkt_handle usbhsf_null_handler = {
+ .prepare = usbhsf_null_handle,
+ .try_run = usbhsf_null_handle,
+};
+
+void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
+ struct usbhs_pkt_handle *handler,
+ void *buf, int len, int zero)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
+
+ if (!handler) {
+ dev_err(dev, "no handler function\n");
+ handler = &usbhsf_null_handler;
+ }
+
+ list_del_init(&pkt->node);
+ list_add_tail(&pkt->node, &pipe->list);
+
+ pkt->pipe = pipe;
+ pkt->buf = buf;
+ pkt->handler = handler;
+ pkt->length = len;
+ pkt->zero = zero;
+ pkt->actual = 0;
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
+
+ usbhs_pkt_start(pipe);
+}
+
+static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
+{
+ list_del_init(&pkt->node);
+}
+
+static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
+{
+ if (list_empty(&pipe->list))
+ return NULL;
+
+ return list_entry(pipe->list.next, struct usbhs_pkt, node);
+}
+
+struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
+
+ if (!pkt)
+ pkt = __usbhsf_pkt_get(pipe);
+
+ if (pkt)
+ __usbhsf_pkt_del(pkt);
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
+
+ return pkt;
+}
+
+int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+ struct usbhs_pkt *pkt;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int (*func)(struct usbhs_pkt *pkt, int *is_done);
+ unsigned long flags;
+ int ret = 0;
+ int is_done = 0;
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
+
+ pkt = __usbhsf_pkt_get(pipe);
+ if (!pkt)
+ goto __usbhs_pkt_handler_end;
+
+ switch (type) {
+ case USBHSF_PKT_PREPARE:
+ func = pkt->handler->prepare;
+ break;
+ case USBHSF_PKT_TRY_RUN:
+ func = pkt->handler->try_run;
+ break;
+ case USBHSF_PKT_DMA_DONE:
+ func = pkt->handler->dma_done;
+ break;
+ default:
+ dev_err(dev, "unknown pkt hander\n");
+ goto __usbhs_pkt_handler_end;
+ }
+
+ ret = func(pkt, &is_done);
+
+ if (is_done)
+ __usbhsf_pkt_del(pkt);
+
+__usbhs_pkt_handler_end:
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
+
+ if (is_done) {
+ info->done(pkt);
+ usbhs_pkt_start(pipe);
+ }
+
+ return ret;
+}
+
+/*
+ * irq enable/disable function
+ */
+#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e)
+#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e)
+#define usbhsf_irq_callback_ctrl(pipe, status, enable) \
+ ({ \
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); \
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
+ u16 status = (1 << usbhs_pipe_number(pipe)); \
+ if (!mod) \
+ return; \
+ if (enable) \
+ mod->irq_##status |= status; \
+ else \
+ mod->irq_##status &= ~status; \
+ usbhs_irq_callback_update(priv, mod); \
+ })
+
+static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
+{
+ /*
+ * And DCP pipe can NOT use "ready interrupt" for "send"
+ * it should use "empty" interrupt.
+ * see
+ * "Operation" - "Interrupt Function" - "BRDY Interrupt"
+ *
+ * on the other hand, normal pipe can use "ready interrupt" for "send"
+ * even though it is single/double buffer
+ */
+ if (usbhs_pipe_is_dcp(pipe))
+ usbhsf_irq_empty_ctrl(pipe, enable);
+ else
+ usbhsf_irq_ready_ctrl(pipe, enable);
+}
+
+static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
+{
+ usbhsf_irq_ready_ctrl(pipe, enable);
+}
+
+/*
+ * FIFO ctrl
+ */
+static void usbhsf_send_terminator(struct usbhs_pipe *pipe,
+ struct usbhs_fifo *fifo)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+ usbhs_bset(priv, fifo->ctr, BVAL, BVAL);
+}
+
+static int usbhsf_fifo_barrier(struct usbhs_priv *priv,
+ struct usbhs_fifo *fifo)
+{
+ int timeout = 1024;
+
+ do {
+ /* The FIFO port is accessible */
+ if (usbhs_read(priv, fifo->ctr) & FRDY)
+ return 0;
+
+ udelay(10);
+ } while (timeout--);
+
+ return -EBUSY;
+}
+
+static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
+ struct usbhs_fifo *fifo)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+ if (!usbhs_pipe_is_dcp(pipe))
+ usbhsf_fifo_barrier(priv, fifo);
+
+ usbhs_write(priv, fifo->ctr, BCLR);
+}
+
+static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
+ struct usbhs_fifo *fifo)
+{
+ return usbhs_read(priv, fifo->ctr) & DTLN_MASK;
+}
+
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
+ struct usbhs_fifo *fifo)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+ usbhs_pipe_select_fifo(pipe, NULL);
+ usbhs_write(priv, fifo->sel, 0);
+}
+
+static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
+ struct usbhs_fifo *fifo,
+ int write)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int timeout = 1024;
+ u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */
+ u16 base = usbhs_pipe_number(pipe); /* CURPIPE */
+
+ if (usbhs_pipe_is_busy(pipe) ||
+ usbhsf_fifo_is_busy(fifo))
+ return -EBUSY;
+
+ if (usbhs_pipe_is_dcp(pipe))
+ base |= (1 == write) << 5; /* ISEL */
+
+ /* "base" will be used below */
+ usbhs_write(priv, fifo->sel, base | MBW_32);
+
+ /* check ISEL and CURPIPE value */
+ while (timeout--) {
+ if (base == (mask & usbhs_read(priv, fifo->sel))) {
+ usbhs_pipe_select_fifo(pipe, fifo);
+ return 0;
+ }
+ udelay(10);
+ }
+
+ dev_err(dev, "fifo select error\n");
+
+ return -EIO;
+}
+
+/*
+ * PIO push handler
+ */
+static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+ void __iomem *addr = priv->base + fifo->port;
+ u8 *buf;
+ int maxp = usbhs_pipe_get_maxpacket(pipe);
+ int total_len;
+ int i, ret, len;
+ int is_short;
+
+ ret = usbhsf_fifo_select(pipe, fifo, 1);
+ if (ret < 0)
+ return 0;
+
+ ret = usbhs_pipe_is_accessible(pipe);
+ if (ret < 0) {
+ /* inaccessible pipe is not an error */
+ ret = 0;
+ goto usbhs_fifo_write_busy;
+ }
+
+ ret = usbhsf_fifo_barrier(priv, fifo);
+ if (ret < 0)
+ goto usbhs_fifo_write_busy;
+
+ buf = pkt->buf + pkt->actual;
+ len = pkt->length - pkt->actual;
+ len = min(len, maxp);
+ total_len = len;
+ is_short = total_len < maxp;
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 && !((unsigned long)buf & 0x03)) {
+ iowrite32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += total_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++)
+ iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
+
+ /*
+ * variable update
+ */
+ pkt->actual += total_len;
+
+ if (pkt->actual < pkt->length)
+ *is_done = 0; /* there are remainder data */
+ else if (is_short)
+ *is_done = 1; /* short packet */
+ else
+ *is_done = !pkt->zero; /* send zero packet ? */
+
+ /*
+ * pipe/irq handling
+ */
+ if (is_short)
+ usbhsf_send_terminator(pipe, fifo);
+
+ usbhsf_tx_irq_ctrl(pipe, !*is_done);
+ usbhs_pipe_enable(pipe);
+
+ dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ pkt->length, pkt->actual, *is_done, pkt->zero);
+
+ /*
+ * Transmission end
+ */
+ if (*is_done) {
+ if (usbhs_pipe_is_dcp(pipe))
+ usbhs_dcp_control_transfer_done(pipe);
+ }
+
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ return 0;
+
+usbhs_fifo_write_busy:
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ /*
+ * pipe is busy.
+ * retry in interrupt
+ */
+ usbhsf_tx_irq_ctrl(pipe, 1);
+
+ return ret;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
+ .prepare = usbhsf_pio_try_push,
+ .try_run = usbhsf_pio_try_push,
+};
+
+/*
+ * PIO pop handler
+ */
+static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+
+ if (usbhs_pipe_is_busy(pipe))
+ return 0;
+
+ /*
+ * pipe enable to prepare packet receive
+ */
+
+ usbhs_pipe_enable(pipe);
+ usbhsf_rx_irq_ctrl(pipe, 1);
+
+ return 0;
+}
+
+static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+ void __iomem *addr = priv->base + fifo->port;
+ u8 *buf;
+ u32 data = 0;
+ int maxp = usbhs_pipe_get_maxpacket(pipe);
+ int rcv_len, len;
+ int i, ret;
+ int total_len = 0;
+
+ ret = usbhsf_fifo_select(pipe, fifo, 0);
+ if (ret < 0)
+ return 0;
+
+ ret = usbhsf_fifo_barrier(priv, fifo);
+ if (ret < 0)
+ goto usbhs_fifo_read_busy;
+
+ rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
+
+ buf = pkt->buf + pkt->actual;
+ len = pkt->length - pkt->actual;
+ len = min(len, rcv_len);
+ total_len = len;
+
+ /*
+ * Buffer clear if Zero-Length packet
+ *
+ * see
+ * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
+ */
+ if (0 == rcv_len) {
+ usbhsf_fifo_clear(pipe, fifo);
+ goto usbhs_fifo_read_end;
+ }
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 && !((unsigned long)buf & 0x03)) {
+ ioread32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += total_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++) {
+ if (!(i & 0x03))
+ data = ioread32(addr);
+
+ buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+ }
+
+ pkt->actual += total_len;
+
+usbhs_fifo_read_end:
+ if ((pkt->actual == pkt->length) || /* receive all data */
+ (total_len < maxp)) { /* short packet */
+ *is_done = 1;
+ usbhsf_rx_irq_ctrl(pipe, 0);
+ usbhs_pipe_disable(pipe);
+ }
+
+ dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ pkt->length, pkt->actual, *is_done, pkt->zero);
+
+usbhs_fifo_read_busy:
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ return ret;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler = {
+ .prepare = usbhsf_prepare_pop,
+ .try_run = usbhsf_pio_try_pop,
+};
+
+/*
+ * DCP ctrol statge handler
+ */
+static int usbhsf_ctrl_stage_end(struct usbhs_pkt *pkt, int *is_done)
+{
+ usbhs_dcp_control_transfer_done(pkt->pipe);
+
+ *is_done = 1;
+
+ return 0;
+}
+
+struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = {
+ .prepare = usbhsf_ctrl_stage_end,
+ .try_run = usbhsf_ctrl_stage_end,
+};
+
+/*
+ * DMA fifo functions
+ */
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
+ struct usbhs_pkt *pkt)
+{
+ if (&usbhs_fifo_dma_push_handler == pkt->handler)
+ return fifo->tx_chan;
+
+ if (&usbhs_fifo_dma_pop_handler == pkt->handler)
+ return fifo->rx_chan;
+
+ return NULL;
+}
+
+static struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv,
+ struct usbhs_pkt *pkt)
+{
+ struct usbhs_fifo *fifo;
+
+ /* DMA :: D0FIFO */
+ fifo = usbhsf_get_d0fifo(priv);
+ if (usbhsf_dma_chan_get(fifo, pkt) &&
+ !usbhsf_fifo_is_busy(fifo))
+ return fifo;
+
+ /* DMA :: D1FIFO */
+ fifo = usbhsf_get_d1fifo(priv);
+ if (usbhsf_dma_chan_get(fifo, pkt) &&
+ !usbhsf_fifo_is_busy(fifo))
+ return fifo;
+
+ return NULL;
+}
+
+#define usbhsf_dma_start(p, f) __usbhsf_dma_ctrl(p, f, DREQE)
+#define usbhsf_dma_stop(p, f) __usbhsf_dma_ctrl(p, f, 0)
+static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
+ struct usbhs_fifo *fifo,
+ u16 dreqe)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+ usbhs_bset(priv, fifo->sel, DREQE, dreqe);
+}
+
+#define usbhsf_dma_map(p) __usbhsf_dma_map_ctrl(p, 1)
+#define usbhsf_dma_unmap(p) __usbhsf_dma_map_ctrl(p, 0)
+static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+
+ return info->dma_map_ctrl(pkt, map);
+}
+
+static void usbhsf_dma_complete(void *arg);
+static void usbhsf_dma_prepare_tasklet(unsigned long data)
+{
+ struct usbhs_pkt *pkt = (struct usbhs_pkt *)data;
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct scatterlist sg;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ enum dma_data_direction dir;
+ dma_cookie_t cookie;
+
+ dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, virt_to_page(pkt->dma),
+ pkt->length, offset_in_page(pkt->dma));
+ sg_dma_address(&sg) = pkt->dma + pkt->actual;
+ sg_dma_len(&sg) = pkt->trans;
+
+ desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
+ if (!desc)
+ return;
+
+ desc->callback = usbhsf_dma_complete;
+ desc->callback_param = pipe;
+
+ cookie = desc->tx_submit(desc);
+ if (cookie < 0) {
+ dev_err(dev, "Failed to submit dma descriptor\n");
+ return;
+ }
+
+ dev_dbg(dev, " %s %d (%d/ %d)\n",
+ fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
+
+ usbhsf_dma_start(pipe, fifo);
+ dma_async_issue_pending(chan);
+}
+
+/*
+ * DMA push handler
+ */
+static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo;
+ int len = pkt->length - pkt->actual;
+ int ret;
+
+ if (usbhs_pipe_is_busy(pipe))
+ return 0;
+
+ /* use PIO if packet is less than pio_dma_border or pipe is DCP */
+ if ((len < usbhs_get_dparam(priv, pio_dma_border)) ||
+ usbhs_pipe_is_dcp(pipe))
+ goto usbhsf_pio_prepare_push;
+
+ if (len % 4) /* 32bit alignment */
+ goto usbhsf_pio_prepare_push;
+
+ if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+ goto usbhsf_pio_prepare_push;
+
+ /* get enable DMA fifo */
+ fifo = usbhsf_get_dma_fifo(priv, pkt);
+ if (!fifo)
+ goto usbhsf_pio_prepare_push;
+
+ if (usbhsf_dma_map(pkt) < 0)
+ goto usbhsf_pio_prepare_push;
+
+ ret = usbhsf_fifo_select(pipe, fifo, 0);
+ if (ret < 0)
+ goto usbhsf_pio_prepare_push_unmap;
+
+ pkt->trans = len;
+
+ tasklet_init(&fifo->tasklet,
+ usbhsf_dma_prepare_tasklet,
+ (unsigned long)pkt);
+
+ tasklet_schedule(&fifo->tasklet);
+
+ return 0;
+
+usbhsf_pio_prepare_push_unmap:
+ usbhsf_dma_unmap(pkt);
+usbhsf_pio_prepare_push:
+ /*
+ * change handler to PIO
+ */
+ pkt->handler = &usbhs_fifo_pio_push_handler;
+
+ return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+
+ pkt->actual = pkt->trans;
+
+ *is_done = !pkt->zero; /* send zero packet ? */
+
+ usbhsf_dma_stop(pipe, pipe->fifo);
+ usbhsf_dma_unmap(pkt);
+ usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+ return 0;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
+ .prepare = usbhsf_dma_prepare_push,
+ .dma_done = usbhsf_dma_push_done,
+};
+
+/*
+ * DMA pop handler
+ */
+static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo;
+ int len, ret;
+
+ if (usbhs_pipe_is_busy(pipe))
+ return 0;
+
+ if (usbhs_pipe_is_dcp(pipe))
+ goto usbhsf_pio_prepare_pop;
+
+ /* get enable DMA fifo */
+ fifo = usbhsf_get_dma_fifo(priv, pkt);
+ if (!fifo)
+ goto usbhsf_pio_prepare_pop;
+
+ if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+ goto usbhsf_pio_prepare_pop;
+
+ ret = usbhsf_fifo_select(pipe, fifo, 0);
+ if (ret < 0)
+ goto usbhsf_pio_prepare_pop;
+
+ /* use PIO if packet is less than pio_dma_border */
+ len = usbhsf_fifo_rcv_len(priv, fifo);
+ len = min(pkt->length - pkt->actual, len);
+ if (len % 4) /* 32bit alignment */
+ goto usbhsf_pio_prepare_pop_unselect;
+
+ if (len < usbhs_get_dparam(priv, pio_dma_border))
+ goto usbhsf_pio_prepare_pop_unselect;
+
+ ret = usbhsf_fifo_barrier(priv, fifo);
+ if (ret < 0)
+ goto usbhsf_pio_prepare_pop_unselect;
+
+ if (usbhsf_dma_map(pkt) < 0)
+ goto usbhsf_pio_prepare_pop_unselect;
+
+ /* DMA */
+
+ /*
+ * usbhs_fifo_dma_pop_handler :: prepare
+ * enabled irq to come here.
+ * but it is no longer needed for DMA. disable it.
+ */
+ usbhsf_rx_irq_ctrl(pipe, 0);
+
+ pkt->trans = len;
+
+ tasklet_init(&fifo->tasklet,
+ usbhsf_dma_prepare_tasklet,
+ (unsigned long)pkt);
+
+ tasklet_schedule(&fifo->tasklet);
+
+ return 0;
+
+usbhsf_pio_prepare_pop_unselect:
+ usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_pop:
+
+ /*
+ * change handler to PIO
+ */
+ pkt->handler = &usbhs_fifo_pio_pop_handler;
+
+ return pkt->handler->try_run(pkt, is_done);
+}
+
+static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+{
+ struct usbhs_pipe *pipe = pkt->pipe;
+ int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+ usbhsf_dma_stop(pipe, pipe->fifo);
+ usbhsf_dma_unmap(pkt);
+ usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+ pkt->actual += pkt->trans;
+
+ if ((pkt->actual == pkt->length) || /* receive all data */
+ (pkt->trans < maxp)) { /* short packet */
+ *is_done = 1;
+ } else {
+ /* re-enable */
+ usbhsf_prepare_pop(pkt, is_done);
+ }
+
+ return 0;
+}
+
+struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
+ .prepare = usbhsf_prepare_pop,
+ .try_run = usbhsf_dma_try_pop,
+ .dma_done = usbhsf_dma_pop_done
+};
+
+/*
+ * DMA setting
+ */
+static bool usbhsf_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct sh_dmae_slave *slave = param;
+
+ /*
+ * FIXME
+ *
+ * usbhs doesn't recognize id = 0 as valid DMA
+ */
+ if (0 == slave->slave_id)
+ return false;
+
+ chan->private = slave;
+
+ return true;
+}
+
+static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
+{
+ if (fifo->tx_chan)
+ dma_release_channel(fifo->tx_chan);
+ if (fifo->rx_chan)
+ dma_release_channel(fifo->rx_chan);
+
+ fifo->tx_chan = NULL;
+ fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv,
+ struct usbhs_fifo *fifo)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ fifo->tx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+ &fifo->tx_slave);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+ &fifo->rx_slave);
+
+ if (fifo->tx_chan || fifo->rx_chan)
+ dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
+ fifo->name,
+ fifo->tx_chan ? "[TX]" : " ",
+ fifo->rx_chan ? "[RX]" : " ");
+}
+
+/*
+ * irq functions
+ */
+static int usbhsf_irq_empty(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int i, ret;
+
+ if (!irq_state->bempsts) {
+ dev_err(dev, "debug %s !!\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
+
+ /*
+ * search interrupted "pipe"
+ * not "uep".
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (!(irq_state->bempsts & (1 << i)))
+ continue;
+
+ ret = usbhs_pkt_run(pipe);
+ if (ret < 0)
+ dev_err(dev, "irq_empty run_error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+static int usbhsf_irq_ready(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int i, ret;
+
+ if (!irq_state->brdysts) {
+ dev_err(dev, "debug %s !!\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
+
+ /*
+ * search interrupted "pipe"
+ * not "uep".
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (!(irq_state->brdysts & (1 << i)))
+ continue;
+
+ ret = usbhs_pkt_run(pipe);
+ if (ret < 0)
+ dev_err(dev, "irq_ready run_error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+static void usbhsf_dma_complete(void *arg)
+{
+ struct usbhs_pipe *pipe = arg;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret;
+
+ ret = usbhs_pkt_dmadone(pipe);
+ if (ret < 0)
+ dev_err(dev, "dma_complete run_error %d : %d\n",
+ usbhs_pipe_number(pipe), ret);
+}
+
+/*
+ * fifo init
+ */
+void usbhs_fifo_init(struct usbhs_priv *priv)
+{
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
+ struct usbhs_fifo *d0fifo = usbhsf_get_d0fifo(priv);
+ struct usbhs_fifo *d1fifo = usbhsf_get_d1fifo(priv);
+
+ mod->irq_empty = usbhsf_irq_empty;
+ mod->irq_ready = usbhsf_irq_ready;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+
+ cfifo->pipe = NULL;
+ cfifo->tx_chan = NULL;
+ cfifo->rx_chan = NULL;
+
+ d0fifo->pipe = NULL;
+ d0fifo->tx_chan = NULL;
+ d0fifo->rx_chan = NULL;
+
+ d1fifo->pipe = NULL;
+ d1fifo->tx_chan = NULL;
+ d1fifo->rx_chan = NULL;
+
+ usbhsf_dma_init(priv, usbhsf_get_d0fifo(priv));
+ usbhsf_dma_init(priv, usbhsf_get_d1fifo(priv));
+}
+
+void usbhs_fifo_quit(struct usbhs_priv *priv)
+{
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+ mod->irq_empty = NULL;
+ mod->irq_ready = NULL;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+
+ usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv));
+ usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv));
+}
+
+int usbhs_fifo_probe(struct usbhs_priv *priv)
+{
+ struct usbhs_fifo *fifo;
+
+ /* CFIFO */
+ fifo = usbhsf_get_cfifo(priv);
+ fifo->name = "CFIFO";
+ fifo->port = CFIFO;
+ fifo->sel = CFIFOSEL;
+ fifo->ctr = CFIFOCTR;
+
+ /* D0FIFO */
+ fifo = usbhsf_get_d0fifo(priv);
+ fifo->name = "D0FIFO";
+ fifo->port = D0FIFO;
+ fifo->sel = D0FIFOSEL;
+ fifo->ctr = D0FIFOCTR;
+ fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id);
+ fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id);
+
+ /* D1FIFO */
+ fifo = usbhsf_get_d1fifo(priv);
+ fifo->name = "D1FIFO";
+ fifo->port = D1FIFO;
+ fifo->sel = D1FIFOSEL;
+ fifo->ctr = D1FIFOCTR;
+ fifo->tx_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id);
+ fifo->rx_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id);
+
+ return 0;
+}
+
+void usbhs_fifo_remove(struct usbhs_priv *priv)
+{
+}
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
new file mode 100644
index 000000000000..ed6d8e56c13c
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -0,0 +1,104 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_FIFO_H
+#define RENESAS_USB_FIFO_H
+
+#include <linux/interrupt.h>
+#include <linux/sh_dma.h>
+#include <asm/dma.h>
+#include "pipe.h"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+struct usbhs_fifo {
+ char *name;
+ u32 port; /* xFIFO */
+ u32 sel; /* xFIFOSEL */
+ u32 ctr; /* xFIFOCTR */
+
+ struct usbhs_pipe *pipe;
+ struct tasklet_struct tasklet;
+
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+
+ struct sh_dmae_slave tx_slave;
+ struct sh_dmae_slave rx_slave;
+};
+
+struct usbhs_fifo_info {
+ struct usbhs_fifo cfifo;
+ struct usbhs_fifo d0fifo;
+ struct usbhs_fifo d1fifo;
+};
+
+struct usbhs_pkt_handle;
+struct usbhs_pkt {
+ struct list_head node;
+ struct usbhs_pipe *pipe;
+ struct usbhs_pkt_handle *handler;
+ dma_addr_t dma;
+ void *buf;
+ int length;
+ int trans;
+ int actual;
+ int zero;
+};
+
+struct usbhs_pkt_handle {
+ int (*prepare)(struct usbhs_pkt *pkt, int *is_done);
+ int (*try_run)(struct usbhs_pkt *pkt, int *is_done);
+ int (*dma_done)(struct usbhs_pkt *pkt, int *is_done);
+};
+
+/*
+ * fifo
+ */
+int usbhs_fifo_probe(struct usbhs_priv *priv);
+void usbhs_fifo_remove(struct usbhs_priv *priv);
+void usbhs_fifo_init(struct usbhs_priv *priv);
+void usbhs_fifo_quit(struct usbhs_priv *priv);
+
+/*
+ * packet info
+ */
+enum {
+ USBHSF_PKT_PREPARE,
+ USBHSF_PKT_TRY_RUN,
+ USBHSF_PKT_DMA_DONE,
+};
+
+extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler;
+extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler;
+extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler;
+
+extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler;
+extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler;
+
+
+void usbhs_pkt_init(struct usbhs_pkt *pkt);
+void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
+ struct usbhs_pkt_handle *handler,
+ void *buf, int len, int zero);
+struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt);
+int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type);
+
+#define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE)
+#define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN)
+#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE)
+
+#endif /* RENESAS_USB_FIFO_H */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 547486ccd059..ba79dbf5adbc 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -26,26 +26,25 @@
*/
struct usbhsg_request {
struct usb_request req;
- struct list_head node;
+ struct usbhs_pkt pkt;
};
#define EP_NAME_SIZE 8
struct usbhsg_gpriv;
-struct usbhsg_pipe_handle;
struct usbhsg_uep {
struct usb_ep ep;
struct usbhs_pipe *pipe;
- struct list_head list;
char ep_name[EP_NAME_SIZE];
struct usbhsg_gpriv *gpriv;
- struct usbhsg_pipe_handle *handler;
+ struct usbhs_pkt_handle *handler;
};
struct usbhsg_gpriv {
struct usb_gadget gadget;
struct usbhs_mod mod;
+ struct list_head link;
struct usbhsg_uep *uep;
int uep_size;
@@ -58,12 +57,6 @@ struct usbhsg_gpriv {
#define USBHSG_STATUS_WEDGE (1 << 2)
};
-struct usbhsg_pipe_handle {
- int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
- int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
- void (*irq_mask)(struct usbhsg_uep *uep, int enable);
-};
-
struct usbhsg_recip_handle {
char *name;
int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
@@ -100,7 +93,6 @@ struct usbhsg_recip_handle {
container_of(r, struct usbhsg_request, req)
#define usbhsg_ep_to_uep(e) container_of(e, struct usbhsg_uep, ep)
-#define usbhsg_gpriv_to_lock(gp) usbhs_priv_to_lock((gp)->mod.priv)
#define usbhsg_gpriv_to_dev(gp) usbhs_priv_to_dev((gp)->mod.priv)
#define usbhsg_gpriv_to_priv(gp) ((gp)->mod.priv)
#define usbhsg_gpriv_to_dcp(gp) ((gp)->uep)
@@ -110,6 +102,10 @@ struct usbhsg_recip_handle {
#define usbhsg_pipe_to_uep(p) ((p)->mod_private)
#define usbhsg_is_dcp(u) ((u) == usbhsg_gpriv_to_dcp((u)->gpriv))
+#define usbhsg_ureq_to_pkt(u) (&(u)->pkt)
+#define usbhsg_pkt_to_ureq(i) \
+ container_of(i, struct usbhsg_request, pkt)
+
#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN)
/* status */
@@ -118,37 +114,18 @@ struct usbhsg_recip_handle {
#define usbhsg_status_clr(gp, b) (gp->status &= ~b)
#define usbhsg_status_has(gp, b) (gp->status & b)
-/*
- * usbhsg_trylock
- *
- * This driver don't use spin_try_lock
- * to avoid warning of CONFIG_DEBUG_SPINLOCK
- */
-static spinlock_t *usbhsg_trylock(struct usbhsg_gpriv *gpriv,
- unsigned long *flags)
-{
- spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
-
- /* check spin lock status
- * to avoid deadlock/nest */
- if (spin_is_locked(lock))
- return NULL;
+/* controller */
+LIST_HEAD(the_controller_link);
- spin_lock_irqsave(lock, *flags);
-
- return lock;
-}
-
-static void usbhsg_unlock(spinlock_t *lock, unsigned long *flags)
-{
- if (!lock)
- return;
-
- spin_unlock_irqrestore(lock, *flags);
-}
+#define usbhsg_for_each_controller(gpriv)\
+ list_for_each_entry(gpriv, &the_controller_link, link)
+#define usbhsg_controller_register(gpriv)\
+ list_add_tail(&(gpriv)->link, &the_controller_link)
+#define usbhsg_controller_unregister(gpriv)\
+ list_del_init(&(gpriv)->link)
/*
- * list push/pop
+ * queue push/pop
*/
static void usbhsg_queue_push(struct usbhsg_uep *uep,
struct usbhsg_request *ureq)
@@ -156,79 +133,17 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep,
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
+ struct usb_request *req = &ureq->req;
- /*
- ********* assume under spin lock *********
- */
- list_del_init(&ureq->node);
- list_add_tail(&ureq->node, &uep->list);
- ureq->req.actual = 0;
- ureq->req.status = -EINPROGRESS;
+ req->actual = 0;
+ req->status = -EINPROGRESS;
+ usbhs_pkt_push(pipe, pkt, uep->handler,
+ req->buf, req->length, req->zero);
dev_dbg(dev, "pipe %d : queue push (%d)\n",
usbhs_pipe_number(pipe),
- ureq->req.length);
-}
-
-static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep)
-{
- /*
- ********* assume under spin lock *********
- */
- if (list_empty(&uep->list))
- return NULL;
-
- return list_entry(uep->list.next, struct usbhsg_request, node);
-}
-
-#define usbhsg_queue_prepare(uep) __usbhsg_queue_handler(uep, 1);
-#define usbhsg_queue_handle(uep) __usbhsg_queue_handler(uep, 0);
-static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare)
-{
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- struct usbhsg_request *ureq;
- spinlock_t *lock;
- unsigned long flags;
- int ret = 0;
-
- if (!uep->handler) {
- dev_err(dev, "no handler function\n");
- return -EIO;
- }
-
- /*
- * CAUTION [*queue handler*]
- *
- * This function will be called for start/restart queue operation.
- * OTOH the most much worry for USB driver is spinlock nest.
- * Specially it are
- * - usb_ep_ops :: queue
- * - usb_request :: complete
- *
- * But the caller of this function need not care about spinlock.
- * This function is using usbhsg_trylock for it.
- * if "is_locked" is 1, this mean this function lock it.
- * but if it is 0, this mean it is already under spin lock.
- * see also
- * CAUTION [*endpoint queue*]
- * CAUTION [*request complete*]
- */
-
- /****************** spin try lock *******************/
- lock = usbhsg_trylock(gpriv, &flags);
-
- ureq = usbhsg_queue_get(uep);
- if (ureq) {
- if (prepare)
- ret = uep->handler->prepare(uep, ureq);
- else
- ret = uep->handler->try_run(uep, ureq);
- }
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ******************/
-
- return ret;
+ req->length);
}
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
@@ -239,289 +154,91 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- /*
- ********* assume under spin lock *********
- */
-
- /*
- * CAUTION [*request complete*]
- *
- * There is a possibility not to be called in correct order
- * if "complete" is called without spinlock.
- *
- * So, this function assume it is under spinlock,
- * and call usb_request :: complete.
- *
- * But this "complete" will push next usb_request.
- * It mean "usb_ep_ops :: queue" which is using spinlock is called
- * under spinlock.
- *
- * To avoid dead-lock, this driver is using usbhsg_trylock.
- * CAUTION [*endpoint queue*]
- * CAUTION [*queue handler*]
- */
-
dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
- list_del_init(&ureq->node);
-
ureq->req.status = status;
ureq->req.complete(&uep->ep, &ureq->req);
-
- /* more request ? */
- if (0 == status)
- usbhsg_queue_prepare(uep);
}
-/*
- * irq enable/disable function
- */
-#define usbhsg_irq_callback_ctrl(uep, status, enable) \
- ({ \
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); \
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); \
- struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); \
- struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
- if (!mod) \
- return; \
- if (enable) \
- mod->irq_##status |= (1 << usbhs_pipe_number(pipe)); \
- else \
- mod->irq_##status &= ~(1 << usbhs_pipe_number(pipe)); \
- usbhs_irq_callback_update(priv, mod); \
- })
-
-static void usbhsg_irq_empty_ctrl(struct usbhsg_uep *uep, int enable)
+static void usbhsg_queue_done(struct usbhs_pkt *pkt)
{
- usbhsg_irq_callback_ctrl(uep, bempsts, enable);
-}
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
+ struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
-static void usbhsg_irq_ready_ctrl(struct usbhsg_uep *uep, int enable)
-{
- usbhsg_irq_callback_ctrl(uep, brdysts, enable);
-}
-
-/*
- * handler function
- */
-static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
-{
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
-
- /*
- ********* assume under spin lock *********
- */
+ ureq->req.actual = pkt->actual;
- usbhs_dcp_control_transfer_done(pipe);
usbhsg_queue_pop(uep, ureq, 0);
-
- return 0;
}
-static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
+/*
+ * dma map/unmap
+ */
+static int usbhsg_dma_map(struct device *dev,
+ struct usbhs_pkt *pkt,
+ enum dma_data_direction dir)
{
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
struct usb_request *req = &ureq->req;
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- void *buf;
- int remainder, send;
- int is_done = 0;
- int enable;
- int maxp;
- /*
- ********* assume under spin lock *********
- */
-
- maxp = usbhs_pipe_get_maxpacket(pipe);
- buf = req->buf + req->actual;
- remainder = req->length - req->actual;
-
- send = usbhs_fifo_write(pipe, buf, remainder);
-
- /*
- * send < 0 : pipe busy
- * send = 0 : send zero packet
- * send > 0 : send data
- *
- * send <= max_packet
- */
- if (send > 0)
- req->actual += send;
-
- /* send all packet ? */
- if (send < remainder)
- is_done = 0; /* there are remainder data */
- else if (send < maxp)
- is_done = 1; /* short packet */
- else
- is_done = !req->zero; /* send zero packet ? */
-
- dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
- usbhs_pipe_number(pipe),
- remainder, send, is_done, req->zero);
-
- /*
- * enable interrupt and send again in irq handler
- * if it still have remainder data which should be sent.
- */
- enable = !is_done;
- uep->handler->irq_mask(uep, enable);
-
- /*
- * usbhs_fifo_enable execute
- * - after callback_update,
- * - before queue_pop / stage_end
- */
- usbhs_fifo_enable(pipe);
-
- /*
- * all data were sent ?
- */
- if (is_done) {
- /* it care below call in
- "function mode" */
- if (usbhsg_is_dcp(uep))
- usbhs_dcp_control_transfer_done(pipe);
-
- usbhsg_queue_pop(uep, ureq, 0);
+ if (pkt->dma != DMA_ADDR_INVALID) {
+ dev_err(dev, "dma is already mapped\n");
+ return -EIO;
}
- return 0;
-}
-
-static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
-{
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
-
- /*
- ********* assume under spin lock *********
- */
+ if (req->dma == DMA_ADDR_INVALID) {
+ pkt->dma = dma_map_single(dev, pkt->buf, pkt->length, dir);
+ } else {
+ dma_sync_single_for_device(dev, req->dma, req->length, dir);
+ pkt->dma = req->dma;
+ }
- usbhs_fifo_prepare_write(pipe);
- usbhsg_try_run_send_packet(uep, ureq);
+ if (dma_mapping_error(dev, pkt->dma)) {
+ dev_err(dev, "dma mapping error %x\n", pkt->dma);
+ return -EIO;
+ }
return 0;
}
-static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
+static int usbhsg_dma_unmap(struct device *dev,
+ struct usbhs_pkt *pkt,
+ enum dma_data_direction dir)
{
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
struct usb_request *req = &ureq->req;
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- void *buf;
- int maxp;
- int remainder, recv;
- int is_done = 0;
-
- /*
- ********* assume under spin lock *********
- */
-
- maxp = usbhs_pipe_get_maxpacket(pipe);
- buf = req->buf + req->actual;
- remainder = req->length - req->actual;
-
- recv = usbhs_fifo_read(pipe, buf, remainder);
- /*
- * recv < 0 : pipe busy
- * recv >= 0 : receive data
- *
- * recv <= max_packet
- */
- if (recv < 0)
- return -EBUSY;
-
- /* update parameters */
- req->actual += recv;
-
- if ((recv == remainder) || /* receive all data */
- (recv < maxp)) /* short packet */
- is_done = 1;
- dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
- usbhs_pipe_number(pipe),
- remainder, recv, is_done, req->zero);
+ if (pkt->dma == DMA_ADDR_INVALID) {
+ dev_err(dev, "dma is not mapped\n");
+ return -EIO;
+ }
- /* read all data ? */
- if (is_done) {
- int disable = 0;
+ if (req->dma == DMA_ADDR_INVALID)
+ dma_unmap_single(dev, pkt->dma, pkt->length, dir);
+ else
+ dma_sync_single_for_cpu(dev, req->dma, req->length, dir);
- uep->handler->irq_mask(uep, disable);
- usbhs_fifo_disable(pipe);
- usbhsg_queue_pop(uep, ureq, 0);
- }
+ pkt->dma = DMA_ADDR_INVALID;
return 0;
}
-static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep,
- struct usbhsg_request *ureq)
+static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
{
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- int enable = 1;
- int ret;
-
- /*
- ********* assume under spin lock *********
- */
-
- ret = usbhs_fifo_prepare_read(pipe);
- if (ret < 0)
- return ret;
+ struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ enum dma_data_direction dir;
- /*
- * data will be read in interrupt handler
- */
- uep->handler->irq_mask(uep, enable);
+ dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- return ret;
+ if (map)
+ return usbhsg_dma_map(dev, pkt, dir);
+ else
+ return usbhsg_dma_unmap(dev, pkt, dir);
}
-static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = {
- .prepare = usbhsg_prepare_send_packet,
- .try_run = usbhsg_try_run_send_packet,
- .irq_mask = usbhsg_irq_empty_ctrl,
-};
-
-static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = {
- .prepare = usbhsg_prepare_send_packet,
- .try_run = usbhsg_try_run_send_packet,
- .irq_mask = usbhsg_irq_ready_ctrl,
-};
-
-static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = {
- .prepare = usbhsg_prepare_receive_packet,
- .try_run = usbhsg_try_run_receive_packet,
- .irq_mask = usbhsg_irq_ready_ctrl,
-};
-
-static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = {
- .prepare = usbhsg_try_run_ctrl_stage_end,
- .try_run = usbhsg_try_run_ctrl_stage_end,
-};
-
-/*
- * DCP pipe can NOT use "ready interrupt" for "send"
- * it should use "empty" interrupt.
- * see
- * "Operation" - "Interrupt Function" - "BRDY Interrupt"
- *
- * on the other hand, normal pipe can use "ready interrupt" for "send"
- * even though it is single/double buffer
- */
-#define usbhsg_handler_send_ctrl usbhsg_handler_send_by_empty
-#define usbhsg_handler_recv_ctrl usbhsg_handler_recv_by_ready
-
-#define usbhsg_handler_send_packet usbhsg_handler_send_by_ready
-#define usbhsg_handler_recv_packet usbhsg_handler_recv_by_ready
-
/*
* USB_TYPE_STANDARD / clear feature functions
*/
@@ -546,15 +263,13 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv,
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) {
- usbhs_fifo_disable(pipe);
+ usbhs_pipe_disable(pipe);
usbhs_pipe_clear_sequence(pipe);
- usbhs_fifo_enable(pipe);
+ usbhs_pipe_enable(pipe);
}
usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
- usbhsg_queue_prepare(uep);
-
return 0;
}
@@ -575,6 +290,7 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
struct usbhsg_uep *uep;
+ struct usbhs_pipe *pipe;
int recip = ctrl->bRequestType & USB_RECIP_MASK;
int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
int ret;
@@ -583,9 +299,11 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
char *msg;
uep = usbhsg_gpriv_to_nth_uep(gpriv, nth);
- if (!usbhsg_uep_to_pipe(uep)) {
+ pipe = usbhsg_uep_to_pipe(uep);
+ if (!pipe) {
dev_err(dev, "wrong recip request\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto usbhsg_recip_run_handle_end;
}
switch (recip) {
@@ -608,10 +326,20 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
}
if (func) {
+ unsigned long flags;
+
dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg);
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
ret = func(priv, uep, ctrl);
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
}
+usbhsg_recip_run_handle_end:
+ usbhs_pkt_start(pipe);
+
return ret;
}
@@ -660,13 +388,13 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
switch (stage) {
case READ_DATA_STAGE:
- dcp->handler = &usbhsg_handler_send_ctrl;
+ dcp->handler = &usbhs_fifo_pio_push_handler;
break;
case WRITE_DATA_STAGE:
- dcp->handler = &usbhsg_handler_recv_ctrl;
+ dcp->handler = &usbhs_fifo_pio_pop_handler;
break;
case NODATA_STATUS_STAGE:
- dcp->handler = &usbhsg_handler_ctrl_stage_end;
+ dcp->handler = &usbhs_ctrl_stage_end_handler;
break;
default:
return ret;
@@ -695,128 +423,27 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
ret = gpriv->driver->setup(&gpriv->gadget, &ctrl);
if (ret < 0)
- usbhs_fifo_stall(pipe);
+ usbhs_pipe_stall(pipe);
return ret;
}
-static int usbhsg_irq_empty(struct usbhs_priv *priv,
- struct usbhs_irq_state *irq_state)
-{
- struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
- struct usbhsg_uep *uep;
- struct usbhs_pipe *pipe;
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- int i, ret;
-
- if (!irq_state->bempsts) {
- dev_err(dev, "debug %s !!\n", __func__);
- return -EIO;
- }
-
- dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
-
- /*
- * search interrupted "pipe"
- * not "uep".
- */
- usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
- if (!(irq_state->bempsts & (1 << i)))
- continue;
-
- uep = usbhsg_pipe_to_uep(pipe);
- ret = usbhsg_queue_handle(uep);
- if (ret < 0)
- dev_err(dev, "send error %d : %d\n", i, ret);
- }
-
- return 0;
-}
-
-static int usbhsg_irq_ready(struct usbhs_priv *priv,
- struct usbhs_irq_state *irq_state)
-{
- struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
- struct usbhsg_uep *uep;
- struct usbhs_pipe *pipe;
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- int i, ret;
-
- if (!irq_state->brdysts) {
- dev_err(dev, "debug %s !!\n", __func__);
- return -EIO;
- }
-
- dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
-
- /*
- * search interrupted "pipe"
- * not "uep".
- */
- usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
- if (!(irq_state->brdysts & (1 << i)))
- continue;
-
- uep = usbhsg_pipe_to_uep(pipe);
- ret = usbhsg_queue_handle(uep);
- if (ret < 0)
- dev_err(dev, "receive error %d : %d\n", i, ret);
- }
-
- return 0;
-}
-
/*
*
* usb_dcp_ops
*
*/
-static int usbhsg_dcp_enable(struct usbhsg_uep *uep)
-{
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
- struct usbhs_pipe *pipe;
-
- /*
- ********* assume under spin lock *********
- */
-
- pipe = usbhs_dcp_malloc(priv);
- if (!pipe)
- return -EIO;
-
- uep->pipe = pipe;
- uep->pipe->mod_private = uep;
- INIT_LIST_HEAD(&uep->list);
-
- return 0;
-}
-
-#define usbhsg_dcp_disable usbhsg_pipe_disable
static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
{
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- struct usbhsg_request *ureq;
- int disable = 0;
-
- /*
- ********* assume under spin lock *********
- */
+ struct usbhs_pkt *pkt;
- usbhs_fifo_disable(pipe);
-
- /*
- * disable pipe irq
- */
- usbhsg_irq_empty_ctrl(uep, disable);
- usbhsg_irq_ready_ctrl(uep, disable);
+ usbhs_pipe_disable(pipe);
while (1) {
- ureq = usbhsg_queue_get(uep);
- if (!ureq)
+ pkt = usbhs_pkt_pop(pipe, NULL);
+ if (!pkt)
break;
-
- usbhsg_queue_pop(uep, ureq, -ECONNRESET);
}
return 0;
@@ -843,57 +470,44 @@ static int usbhsg_ep_enable(struct usb_ep *ep,
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
struct usbhs_pipe *pipe;
- spinlock_t *lock;
- unsigned long flags;
int ret = -EIO;
/*
* if it already have pipe,
* nothing to do
*/
- if (uep->pipe)
+ if (uep->pipe) {
+ usbhs_pipe_clear(uep->pipe);
+ usbhs_pipe_clear_sequence(uep->pipe);
return 0;
-
- /******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
+ }
pipe = usbhs_pipe_malloc(priv, desc);
if (pipe) {
uep->pipe = pipe;
pipe->mod_private = uep;
- INIT_LIST_HEAD(&uep->list);
+ /*
+ * usbhs_fifo_dma_push/pop_handler try to
+ * use dmaengine if possible.
+ * It will use pio handler if impossible.
+ */
if (usb_endpoint_dir_in(desc))
- uep->handler = &usbhsg_handler_send_packet;
+ uep->handler = &usbhs_fifo_dma_push_handler;
else
- uep->handler = &usbhsg_handler_recv_packet;
+ uep->handler = &usbhs_fifo_dma_pop_handler;
ret = 0;
}
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ******************/
-
return ret;
}
static int usbhsg_ep_disable(struct usb_ep *ep)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- spinlock_t *lock;
- unsigned long flags;
- int ret;
-
- /******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
- ret = usbhsg_pipe_disable(uep);
-
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ******************/
-
- return ret;
+ return usbhsg_pipe_disable(uep);
}
static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
@@ -905,7 +519,10 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
if (!ureq)
return NULL;
- INIT_LIST_HEAD(&ureq->node);
+ usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq));
+
+ ureq->req.dma = DMA_ADDR_INVALID;
+
return &ureq->req;
}
@@ -914,7 +531,7 @@ static void usbhsg_ep_free_request(struct usb_ep *ep,
{
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
- WARN_ON(!list_empty(&ureq->node));
+ WARN_ON(!list_empty(&ureq->pkt.node));
kfree(ureq);
}
@@ -925,69 +542,27 @@ static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req,
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- spinlock_t *lock;
- unsigned long flags;
- int ret = 0;
-
- /*
- * CAUTION [*endpoint queue*]
- *
- * This function will be called from usb_request :: complete
- * or usb driver timing.
- * If this function is called from usb_request :: complete,
- * it is already under spinlock on this driver.
- * but it is called frm usb driver, this function should call spinlock.
- *
- * This function is using usbshg_trylock to solve this issue.
- * if "is_locked" is 1, this mean this function lock it.
- * but if it is 0, this mean it is already under spin lock.
- * see also
- * CAUTION [*queue handler*]
- * CAUTION [*request complete*]
- */
-
- /******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
/* param check */
if (usbhsg_is_not_connected(gpriv) ||
unlikely(!gpriv->driver) ||
unlikely(!pipe))
- ret = -ESHUTDOWN;
- else
- usbhsg_queue_push(uep, ureq);
-
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ******************/
+ return -ESHUTDOWN;
- usbhsg_queue_prepare(uep);
+ usbhsg_queue_push(uep, ureq);
- return ret;
+ return 0;
}
static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
- struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- spinlock_t *lock;
- unsigned long flags;
-
- /*
- * see
- * CAUTION [*queue handler*]
- * CAUTION [*endpoint queue*]
- * CAUTION [*request complete*]
- */
-
- /******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
usbhsg_queue_pop(uep, ureq, -ECONNRESET);
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ******************/
-
return 0;
}
@@ -996,42 +571,32 @@ static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
- spinlock_t *lock;
unsigned long flags;
- int ret = -EAGAIN;
- /*
- * see
- * CAUTION [*queue handler*]
- * CAUTION [*endpoint queue*]
- * CAUTION [*request complete*]
- */
+ usbhsg_pipe_disable(uep);
- /******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
- if (!usbhsg_queue_get(uep)) {
+ dev_dbg(dev, "set halt %d (pipe %d)\n",
+ halt, usbhs_pipe_number(pipe));
- dev_dbg(dev, "set halt %d (pipe %d)\n",
- halt, usbhs_pipe_number(pipe));
-
- if (halt)
- usbhs_fifo_stall(pipe);
- else
- usbhs_fifo_disable(pipe);
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
- if (halt && wedge)
- usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
- else
- usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
+ if (halt)
+ usbhs_pipe_stall(pipe);
+ else
+ usbhs_pipe_disable(pipe);
- ret = 0;
- }
+ if (halt && wedge)
+ usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
+ else
+ usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
- usbhsg_unlock(lock, &flags);
+ usbhs_unlock(priv, flags);
/******************** spin unlock ******************/
- return ret;
+ return 0;
}
static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
@@ -1067,28 +632,40 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct device *dev = usbhs_priv_to_dev(priv);
- spinlock_t *lock;
unsigned long flags;
+ int ret = 0;
/******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
+ usbhs_lock(priv, flags);
- /*
- * enable interrupt and systems if ready
- */
usbhsg_status_set(gpriv, status);
if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)))
- goto usbhsg_try_start_unlock;
+ ret = -1; /* not ready */
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ********************/
+
+ if (ret < 0)
+ return 0; /* not ready is not error */
+ /*
+ * enable interrupt and systems if ready
+ */
dev_dbg(dev, "start gadget\n");
/*
* pipe initialize and enable DCP
*/
- usbhs_pipe_init(priv);
+ usbhs_pipe_init(priv,
+ usbhsg_queue_done,
+ usbhsg_dma_map_ctrl);
+ usbhs_fifo_init(priv);
usbhsg_uep_init(gpriv);
- usbhsg_dcp_enable(dcp);
+
+ /* dcp init */
+ dcp->pipe = usbhs_dcp_malloc(priv);
+ dcp->pipe->mod_private = dcp;
/*
* system config enble
@@ -1105,16 +682,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
*/
mod->irq_dev_state = usbhsg_irq_dev_state;
mod->irq_ctrl_stage = usbhsg_irq_ctrl_stage;
- mod->irq_empty = usbhsg_irq_empty;
- mod->irq_ready = usbhsg_irq_ready;
- mod->irq_bempsts = 0;
- mod->irq_brdysts = 0;
usbhs_irq_callback_update(priv, mod);
-usbhsg_try_start_unlock:
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ********************/
-
return 0;
}
@@ -1124,31 +693,33 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
struct device *dev = usbhs_priv_to_dev(priv);
- spinlock_t *lock;
unsigned long flags;
+ int ret = 0;
/******************** spin lock ********************/
- lock = usbhsg_trylock(gpriv, &flags);
+ usbhs_lock(priv, flags);
- /*
- * disable interrupt and systems if 1st try
- */
usbhsg_status_clr(gpriv, status);
if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
!usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))
- goto usbhsg_try_stop_unlock;
+ ret = -1; /* already done */
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ********************/
+
+ if (ret < 0)
+ return 0; /* already done is not error */
+
+ /*
+ * disable interrupt and systems if 1st try
+ */
+ usbhs_fifo_quit(priv);
/* disable all irq */
mod->irq_dev_state = NULL;
mod->irq_ctrl_stage = NULL;
- mod->irq_empty = NULL;
- mod->irq_ready = NULL;
- mod->irq_bempsts = 0;
- mod->irq_brdysts = 0;
usbhs_irq_callback_update(priv, mod);
- usbhsg_dcp_disable(dcp);
-
gpriv->gadget.speed = USB_SPEED_UNKNOWN;
/* disable sys */
@@ -1156,8 +727,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
usbhs_sys_function_ctrl(priv, 0);
usbhs_sys_usb_ctrl(priv, 0);
- usbhsg_unlock(lock, &flags);
- /******************** spin unlock ********************/
+ usbhsg_pipe_disable(dcp);
if (gpriv->driver &&
gpriv->driver->disconnect)
@@ -1166,11 +736,6 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
dev_dbg(dev, "stop gadget\n");
return 0;
-
-usbhsg_try_stop_unlock:
- usbhsg_unlock(lock, &flags);
-
- return 0;
}
/*
@@ -1178,11 +743,10 @@ usbhsg_try_stop_unlock:
* linux usb function
*
*/
-struct usbhsg_gpriv *the_controller;
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+static int usbhsg_gadget_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
- struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhsg_gpriv *gpriv;
struct usbhs_priv *priv;
struct device *dev;
int ret;
@@ -1192,10 +756,17 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
!driver->setup ||
driver->speed != USB_SPEED_HIGH)
return -EINVAL;
- if (!gpriv)
- return -ENODEV;
- if (gpriv->driver)
- return -EBUSY;
+
+ /*
+ * find unused controller
+ */
+ usbhsg_for_each_controller(gpriv) {
+ if (!gpriv->driver)
+ goto find_unused_controller;
+ }
+ return -ENODEV;
+
+find_unused_controller:
dev = usbhsg_gpriv_to_dev(gpriv);
priv = usbhsg_gpriv_to_priv(gpriv);
@@ -1229,22 +800,28 @@ add_fail:
return ret;
}
-EXPORT_SYMBOL(usb_gadget_probe_driver);
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static int usbhsg_gadget_stop(struct usb_gadget_driver *driver)
{
- struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhsg_gpriv *gpriv;
struct usbhs_priv *priv;
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
-
- if (!gpriv)
- return -ENODEV;
+ struct device *dev;
if (!driver ||
- !driver->unbind ||
- driver != gpriv->driver)
+ !driver->unbind)
return -EINVAL;
+ /*
+ * find controller
+ */
+ usbhsg_for_each_controller(gpriv) {
+ if (gpriv->driver == driver)
+ goto find_matching_controller;
+ }
+ return -ENODEV;
+
+find_matching_controller:
+
dev = usbhsg_gpriv_to_dev(gpriv);
priv = usbhsg_gpriv_to_priv(gpriv);
@@ -1260,7 +837,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return 0;
}
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*
* usb gadget ops
@@ -1275,6 +851,8 @@ static int usbhsg_get_frame(struct usb_gadget *gadget)
static struct usb_gadget_ops usbhsg_gadget_ops = {
.get_frame = usbhsg_get_frame,
+ .start = usbhsg_gadget_start,
+ .stop = usbhsg_gadget_stop,
};
static int usbhsg_start(struct usbhs_priv *priv)
@@ -1294,6 +872,7 @@ int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
struct device *dev = usbhs_priv_to_dev(priv);
int pipe_size = usbhs_get_dparam(priv, pipe_size);
int i;
+ int ret;
gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
if (!gpriv) {
@@ -1304,6 +883,7 @@ int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
if (!uep) {
dev_err(dev, "Could not allocate ep\n");
+ ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv;
}
@@ -1350,7 +930,6 @@ int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
uep->ep.name = uep->ep_name;
uep->ep.ops = &usbhsg_ep_ops;
INIT_LIST_HEAD(&uep->ep.ep_list);
- INIT_LIST_HEAD(&uep->list);
/* init DCP */
if (usbhsg_is_dcp(uep)) {
@@ -1364,22 +943,33 @@ int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
}
}
- the_controller = gpriv;
+ usbhsg_controller_register(gpriv);
+
+ ret = usb_add_gadget_udc(dev, &gpriv->gadget);
+ if (ret)
+ goto err_add_udc;
+
dev_info(dev, "gadget probed\n");
return 0;
+err_add_udc:
+ kfree(gpriv->uep);
usbhs_mod_gadget_probe_err_gpriv:
kfree(gpriv);
- return -ENOMEM;
+ return ret;
}
void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv)
{
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ usb_del_gadget_udc(&gpriv->gadget);
+
+ usbhsg_controller_unregister(gpriv);
+
kfree(gpriv->uep);
kfree(gpriv);
}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index bc4521c54261..1b14cae45704 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -15,7 +15,6 @@
*
*/
#include <linux/delay.h>
-#include <linux/io.h>
#include <linux/slab.h>
#include "./common.h"
#include "./pipe.h"
@@ -23,13 +22,8 @@
/*
* macros
*/
-#define usbhsp_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
-#define usbhsp_pipe_to_priv(p) ((p)->priv)
-
#define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2)
-#define usbhsp_is_dcp(p) ((p)->priv->pipe_info.pipe == (p))
-
#define usbhsp_flags_set(p, f) ((p)->flags |= USBHS_PIPE_FLAGS_##f)
#define usbhsp_flags_clr(p, f) ((p)->flags &= ~USBHS_PIPE_FLAGS_##f)
#define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f)
@@ -77,10 +71,10 @@ void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
*/
static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
int offset = usbhsp_addr_offset(pipe);
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
usbhs_bset(priv, DCPCTR, mask, val);
else
usbhs_bset(priv, PIPEnCTR + offset, mask, val);
@@ -88,10 +82,10 @@ static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
int offset = usbhsp_addr_offset(pipe);
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
return usbhs_read(priv, DCPCTR);
else
return usbhs_read(priv, PIPEnCTR + offset);
@@ -104,9 +98,9 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
u16 dcp_reg, u16 pipe_reg,
u16 mask, u16 val)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
usbhs_bset(priv, dcp_reg, mask, val);
else
usbhs_bset(priv, pipe_reg, mask, val);
@@ -115,9 +109,9 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
u16 dcp_reg, u16 pipe_reg)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
return usbhs_read(priv, dcp_reg);
else
return usbhs_read(priv, pipe_reg);
@@ -136,7 +130,7 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
*/
static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
{
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
return;
__usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val);
@@ -160,7 +154,7 @@ static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
*/
static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
/*
* On pipe, this is necessary before
@@ -182,7 +176,7 @@ static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
int timeout = 1024;
u16 val;
@@ -205,7 +199,7 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
* - "Pipe Control Registers Switching Procedure"
*/
usbhs_write(priv, CFIFOSEL, 0);
- usbhs_fifo_disable(pipe);
+ usbhs_pipe_disable(pipe);
do {
val = usbhsp_pipectrl_get(pipe);
@@ -220,7 +214,7 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
return -EBUSY;
}
-static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe)
+int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe)
{
u16 val;
@@ -253,7 +247,7 @@ static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
}
}
-void usbhs_fifo_disable(struct usbhs_pipe *pipe)
+void usbhs_pipe_disable(struct usbhs_pipe *pipe)
{
int timeout = 1024;
u16 val;
@@ -273,7 +267,7 @@ void usbhs_fifo_disable(struct usbhs_pipe *pipe)
} while (timeout--);
}
-void usbhs_fifo_enable(struct usbhs_pipe *pipe)
+void usbhs_pipe_enable(struct usbhs_pipe *pipe)
{
/* see "Pipe n Control Register" - "PID" */
__usbhsp_pid_try_nak_if_stall(pipe);
@@ -281,7 +275,7 @@ void usbhs_fifo_enable(struct usbhs_pipe *pipe)
usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF);
}
-void usbhs_fifo_stall(struct usbhs_pipe *pipe)
+void usbhs_pipe_stall(struct usbhs_pipe *pipe)
{
u16 pid = usbhsp_pipectrl_get(pipe);
@@ -302,191 +296,6 @@ void usbhs_fifo_stall(struct usbhs_pipe *pipe)
}
/*
- * CFIFO ctrl
- */
-void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe)
-{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
-
- usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
-}
-
-static void usbhsp_fifo_clear(struct usbhs_pipe *pipe)
-{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
-
- usbhs_write(priv, CFIFOCTR, BCLR);
-}
-
-static int usbhsp_fifo_barrier(struct usbhs_priv *priv)
-{
- int timeout = 1024;
-
- do {
- /* The FIFO port is accessible */
- if (usbhs_read(priv, CFIFOCTR) & FRDY)
- return 0;
-
- udelay(10);
- } while (timeout--);
-
- return -EBUSY;
-}
-
-static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv)
-{
- return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
-}
-
-static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write)
-{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
- struct device *dev = usbhs_priv_to_dev(priv);
- int timeout = 1024;
- u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */
- u16 base = usbhs_pipe_number(pipe); /* CURPIPE */
-
- if (usbhsp_is_dcp(pipe))
- base |= (1 == write) << 5; /* ISEL */
-
- /* "base" will be used below */
- usbhs_write(priv, CFIFOSEL, base | MBW_32);
-
- /* check ISEL and CURPIPE value */
- while (timeout--) {
- if (base == (mask & usbhs_read(priv, CFIFOSEL)))
- return 0;
- udelay(10);
- }
-
- dev_err(dev, "fifo select error\n");
-
- return -EIO;
-}
-
-int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
-{
- return usbhsp_fifo_select(pipe, 1);
-}
-
-int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len)
-{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
- void __iomem *addr = priv->base + CFIFO;
- int maxp = usbhs_pipe_get_maxpacket(pipe);
- int total_len;
- int i, ret;
-
- ret = usbhsp_pipe_is_accessible(pipe);
- if (ret < 0)
- return ret;
-
- ret = usbhsp_fifo_select(pipe, 1);
- if (ret < 0)
- return ret;
-
- ret = usbhsp_fifo_barrier(priv);
- if (ret < 0)
- return ret;
-
- len = min(len, maxp);
- total_len = len;
-
- /*
- * FIXME
- *
- * 32-bit access only
- */
- if (len >= 4 &&
- !((unsigned long)buf & 0x03)) {
- iowrite32_rep(addr, buf, len / 4);
- len %= 4;
- buf += total_len - len;
- }
-
- /* the rest operation */
- for (i = 0; i < len; i++)
- iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
-
- if (total_len < maxp)
- usbhs_fifo_send_terminator(pipe);
-
- return total_len;
-}
-
-int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
-{
- int ret;
-
- /*
- * select pipe and enable it to prepare packet receive
- */
- ret = usbhsp_fifo_select(pipe, 0);
- if (ret < 0)
- return ret;
-
- usbhs_fifo_enable(pipe);
-
- return ret;
-}
-
-int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len)
-{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
- void __iomem *addr = priv->base + CFIFO;
- int rcv_len;
- int i, ret;
- int total_len;
- u32 data = 0;
-
- ret = usbhsp_fifo_select(pipe, 0);
- if (ret < 0)
- return ret;
-
- ret = usbhsp_fifo_barrier(priv);
- if (ret < 0)
- return ret;
-
- rcv_len = usbhsp_fifo_rcv_len(priv);
-
- /*
- * Buffer clear if Zero-Length packet
- *
- * see
- * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
- */
- if (0 == rcv_len) {
- usbhsp_fifo_clear(pipe);
- return 0;
- }
-
- len = min(rcv_len, len);
- total_len = len;
-
- /*
- * FIXME
- *
- * 32-bit access only
- */
- if (len >= 4 &&
- !((unsigned long)buf & 0x03)) {
- ioread32_rep(addr, buf, len / 4);
- len %= 4;
- buf += rcv_len - len;
- }
-
- /* the rest operation */
- for (i = 0; i < len; i++) {
- if (!(i & 0x03))
- data = ioread32(addr);
-
- buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
- }
-
- return total_len;
-}
-
-/*
* pipe setup
*/
static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
@@ -519,7 +328,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
};
int is_double = usbhsp_possible_double_buffer(pipe);
- if (usbhsp_is_dcp(pipe))
+ if (usbhs_pipe_is_dcp(pipe))
return -EINVAL;
/*
@@ -550,12 +359,15 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
/* DIR */
if (usb_endpoint_dir_in(desc))
- usbhsp_flags_set(pipe, IS_DIR_IN);
+ usbhsp_flags_set(pipe, IS_DIR_HOST);
if ((is_host && usb_endpoint_dir_out(desc)) ||
(!is_host && usb_endpoint_dir_in(desc)))
dir |= DIR_OUT;
+ if (!dir)
+ usbhsp_flags_set(pipe, IS_DIR_IN);
+
/* SHTNAK */
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
!dir)
@@ -587,8 +399,8 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
const struct usb_endpoint_descriptor *desc,
int is_host)
{
- struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
- struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct device *dev = usbhs_priv_to_dev(priv);
int pipe_num = usbhs_pipe_number(pipe);
int is_double = usbhsp_possible_double_buffer(pipe);
@@ -666,7 +478,7 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
*/
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
{
- u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
+ u16 mask = usbhs_pipe_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
usbhsp_pipe_select(pipe);
@@ -678,11 +490,22 @@ int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
return usbhsp_flags_has(pipe, IS_DIR_IN);
}
+int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe)
+{
+ return usbhsp_flags_has(pipe, IS_DIR_HOST);
+}
+
void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
{
usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
}
+void usbhs_pipe_clear(struct usbhs_pipe *pipe)
+{
+ usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+ usbhsp_pipectrl_set(pipe, ACLRM, 0);
+}
+
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
{
struct usbhs_pipe *pos, *pipe;
@@ -714,12 +537,20 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
return pipe;
}
-void usbhs_pipe_init(struct usbhs_priv *priv)
+void usbhs_pipe_init(struct usbhs_priv *priv,
+ void (*done)(struct usbhs_pkt *pkt),
+ int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
{
- struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
struct usbhs_pipe *pipe;
int i;
+ if (!done) {
+ dev_err(dev, "no done function\n");
+ return;
+ }
+
/*
* FIXME
*
@@ -738,10 +569,16 @@ void usbhs_pipe_init(struct usbhs_priv *priv)
info->bufnmb_last++;
usbhsp_flags_init(pipe);
+ pipe->fifo = NULL;
pipe->mod_private = NULL;
+ INIT_LIST_HEAD(&pipe->list);
- usbhsp_fifo_clear(pipe);
+ /* pipe force init */
+ usbhs_pipe_clear(pipe);
}
+
+ info->done = done;
+ info->dma_map_ctrl = dma_map_ctrl;
}
struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
@@ -761,7 +598,9 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
return NULL;
}
- usbhs_fifo_disable(pipe);
+ INIT_LIST_HEAD(&pipe->list);
+
+ usbhs_pipe_disable(pipe);
/* make sure pipe is not busy */
ret = usbhsp_pipe_barrier(pipe);
@@ -774,11 +613,6 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host);
pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
- /* buffer clear
- * see PIPECFG :: BFRE */
- usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
- usbhsp_pipectrl_set(pipe, ACLRM, 0);
-
usbhsp_pipe_select(pipe);
usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
@@ -794,6 +628,18 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
return pipe;
}
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo)
+{
+ if (pipe->fifo)
+ pipe->fifo->pipe = NULL;
+
+ pipe->fifo = fifo;
+
+ if (fifo)
+ fifo->pipe = pipe;
+}
+
+
/*
* dcp control
*/
@@ -813,25 +659,25 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
usbhsp_pipe_select(pipe);
usbhs_pipe_clear_sequence(pipe);
+ INIT_LIST_HEAD(&pipe->list);
return pipe;
}
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
{
- WARN_ON(!usbhsp_is_dcp(pipe));
+ WARN_ON(!usbhs_pipe_is_dcp(pipe));
- usbhs_fifo_enable(pipe);
+ usbhs_pipe_enable(pipe);
usbhsp_pipectrl_set(pipe, CCPL, CCPL);
}
-
/*
* pipe module function
*/
int usbhs_pipe_probe(struct usbhs_priv *priv)
{
- struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct usbhs_pipe *pipe;
struct device *dev = usbhs_priv_to_dev(priv);
u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
@@ -868,7 +714,7 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
void usbhs_pipe_remove(struct usbhs_priv *priv)
{
- struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
kfree(info->pipe);
}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 1cca9b7fb266..41534cb0e734 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -18,6 +18,7 @@
#define RENESAS_USB_PIPE_H
#include "./common.h"
+#include "./fifo.h"
/*
* struct
@@ -26,10 +27,13 @@ struct usbhs_pipe {
u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */
struct usbhs_priv *priv;
+ struct usbhs_fifo *fifo;
+ struct list_head list;
u32 flags;
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
+#define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2)
void *mod_private;
};
@@ -38,6 +42,9 @@ struct usbhs_pipe_info {
struct usbhs_pipe *pipe;
int size; /* array size of "pipe" */
int bufnmb_last; /* FIXME : driver needs good allocator */
+
+ void (*done)(struct usbhs_pkt *pkt);
+ int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
};
/*
@@ -55,25 +62,9 @@ struct usbhs_pipe_info {
__usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
/*
- * pipe module probe / remove
+ * data
*/
-int usbhs_pipe_probe(struct usbhs_priv *priv);
-void usbhs_pipe_remove(struct usbhs_priv *priv);
-
-/*
- * cfifo
- */
-int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
-int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
-int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
-int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
-
-void usbhs_fifo_enable(struct usbhs_pipe *pipe);
-void usbhs_fifo_disable(struct usbhs_pipe *pipe);
-void usbhs_fifo_stall(struct usbhs_pipe *pipe);
-
-void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
-
+#define usbhs_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
/*
* usb request
@@ -87,13 +78,27 @@ void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
struct usbhs_pipe
*usbhs_pipe_malloc(struct usbhs_priv *priv,
const struct usb_endpoint_descriptor *desc);
-
+int usbhs_pipe_probe(struct usbhs_priv *priv);
+void usbhs_pipe_remove(struct usbhs_priv *priv);
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
-void usbhs_pipe_init(struct usbhs_priv *priv);
+int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe);
+void usbhs_pipe_init(struct usbhs_priv *priv,
+ void (*done)(struct usbhs_pkt *pkt),
+ int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map));
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
-
+void usbhs_pipe_clear(struct usbhs_pipe *pipe);
+int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
+void usbhs_pipe_enable(struct usbhs_pipe *pipe);
+void usbhs_pipe_disable(struct usbhs_pipe *pipe);
+void usbhs_pipe_stall(struct usbhs_pipe *pipe);
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
+
+#define usbhs_pipe_to_priv(p) ((p)->priv)
#define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe)
+#define usbhs_pipe_is_dcp(p) ((p)->priv->pipe_info.pipe == (p))
+#define usbhs_pipe_to_fifo(p) ((p)->fifo)
+#define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p)
/*
* dcp control
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 30461fcc2206..1d33260de014 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -91,6 +91,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
{ USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
{ USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) },
+ { USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) },
{ } /* Terminating entry */
};
@@ -342,10 +343,28 @@ static void pl2303_set_termios(struct tty_struct *tty,
baud = 6000000;
}
dbg("%s - baud set = %d", __func__, baud);
- buf[0] = baud & 0xff;
- buf[1] = (baud >> 8) & 0xff;
- buf[2] = (baud >> 16) & 0xff;
- buf[3] = (baud >> 24) & 0xff;
+ if (baud <= 115200) {
+ buf[0] = baud & 0xff;
+ buf[1] = (baud >> 8) & 0xff;
+ buf[2] = (baud >> 16) & 0xff;
+ buf[3] = (baud >> 24) & 0xff;
+ } else {
+ /* apparently the formula for higher speeds is:
+ * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
+ */
+ unsigned tmp = 12*1000*1000*32 / baud;
+ buf[3] = 0x80;
+ buf[2] = 0;
+ buf[1] = (tmp >= 256);
+ while (tmp >= 256) {
+ tmp >>= 2;
+ buf[1] <<= 1;
+ }
+ if (tmp > 256) {
+ tmp %= 256;
+ }
+ buf[0] = tmp;
+ }
}
/* For reference buf[4]=0 is 1 stop bits */
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 1b025f75dafd..ca0d237683b3 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -144,3 +144,7 @@
/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */
#define ADLINK_VENDOR_ID 0x0b63
#define ADLINK_ND6530_PRODUCT_ID 0x6530
+
+/* WinChipHead USB->RS 232 adapter */
+#define WINCHIPHEAD_VENDOR_ID 0x4348
+#define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 97987255be75..58aabe679c32 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -40,6 +40,10 @@ config USB_STORAGE_REALTEK
If this driver is compiled as a module, it will be named ums-realtek.
+config REALTEK_AUTOPM
+ bool "Realtek Card Reader autosuspend support"
+ depends on USB_STORAGE_REALTEK && CONFIG_PM_RUNTIME
+ default y
config USB_STORAGE_DATAFAB
tristate "Datafab Compact Flash Reader support"
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index d509a4a7d74f..34adc4b42ceb 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -24,7 +24,6 @@
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
-#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/version.h>
@@ -51,6 +50,35 @@ static int auto_delink_en = 1;
module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(auto_delink_en, "enable auto delink");
+#ifdef CONFIG_REALTEK_AUTOPM
+static int ss_en = 1;
+module_param(ss_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_en, "enable selective suspend");
+
+static int ss_delay = 50;
+module_param(ss_delay, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_delay,
+ "seconds to delay before entering selective suspend");
+
+enum RTS51X_STAT {
+ RTS51X_STAT_INIT,
+ RTS51X_STAT_IDLE,
+ RTS51X_STAT_RUN,
+ RTS51X_STAT_SS
+};
+
+#define POLLING_INTERVAL 50
+
+#define rts51x_set_stat(chip, stat) \
+ ((chip)->state = (enum RTS51X_STAT)(stat))
+#define rts51x_get_stat(chip) ((chip)->state)
+
+#define SET_LUN_READY(chip, lun) ((chip)->lun_ready |= ((u8)1 << (lun)))
+#define CLR_LUN_READY(chip, lun) ((chip)->lun_ready &= ~((u8)1 << (lun)))
+#define TST_LUN_READY(chip, lun) ((chip)->lun_ready & ((u8)1 << (lun)))
+
+#endif
+
struct rts51x_status {
u16 vid;
u16 pid;
@@ -70,14 +98,25 @@ struct rts51x_status {
};
struct rts51x_chip {
- u16 vendor_id;
- u16 product_id;
- char max_lun;
+ u16 vendor_id;
+ u16 product_id;
+ char max_lun;
- struct rts51x_status *status;
- int status_len;
+ struct rts51x_status *status;
+ int status_len;
- u32 flag;
+ u32 flag;
+#ifdef CONFIG_REALTEK_AUTOPM
+ struct us_data *us;
+ struct timer_list rts51x_suspend_timer;
+ unsigned long timer_expires;
+ int pwr_state;
+ u8 lun_ready;
+ enum RTS51X_STAT state;
+ int support_auto_delink;
+#endif
+ /* used to back up the protocal choosen in probe1 phase */
+ proto_cmnd proto_handler_backup;
};
/* flag definition */
@@ -97,9 +136,14 @@ struct rts51x_chip {
#define RTS51X_GET_VID(chip) ((chip)->vendor_id)
#define RTS51X_GET_PID(chip) ((chip)->product_id)
+#define VENDOR_ID(chip) ((chip)->status[0].vid)
+#define PRODUCT_ID(chip) ((chip)->status[0].pid)
#define FW_VERSION(chip) ((chip)->status[0].fw_ver)
#define STATUS_LEN(chip) ((chip)->status_len)
+#define STATUS_SUCCESS 0
+#define STATUS_FAIL 1
+
/* Check card reader function */
#define SUPPORT_DETAILED_TYPE1(chip) \
CHK_BIT((chip)->status[0].function[0], 1)
@@ -119,15 +163,6 @@ struct rts51x_chip {
#define CHECK_ID(chip, pid, fw_ver) \
(CHECK_PID((chip), (pid)) && CHECK_FW_VER((chip), (fw_ver)))
-#define wait_timeout_x(task_state, msecs) \
-do { \
- set_current_state((task_state)); \
- schedule_timeout((msecs) * HZ / 1000); \
-} while (0)
-
-#define wait_timeout(msecs) \
- wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
-
static int init_realtek_cr(struct us_data *us);
/*
@@ -143,8 +178,9 @@ static int init_realtek_cr(struct us_data *us);
static const struct usb_device_id realtek_cr_ids[] = {
# include "unusual_realtek.h"
- { } /* Terminating entry */
+ {} /* Terminating entry */
};
+
MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
#undef UNUSUAL_DEV
@@ -165,7 +201,7 @@ MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
static struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
# include "unusual_realtek.h"
- { } /* Terminating entry */
+ {} /* Terminating entry */
};
#undef UNUSUAL_DEV
@@ -174,8 +210,8 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
u8 *cmd, int cmd_len, u8 *buf, int buf_len,
enum dma_data_direction dir, int *act_len)
{
- struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
- struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
+ struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *)us->iobuf;
+ struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *)us->iobuf;
int result;
unsigned int residue;
unsigned int cswlen;
@@ -195,7 +231,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
/* send it to out endpoint */
result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
- bcb, cbwlen, NULL);
+ bcb, cbwlen, NULL);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -204,24 +240,23 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
if (buf && buf_len) {
unsigned int pipe = (dir == DMA_FROM_DEVICE) ?
- us->recv_bulk_pipe : us->send_bulk_pipe;
+ us->recv_bulk_pipe : us->send_bulk_pipe;
result = usb_stor_bulk_transfer_buf(us, pipe,
- buf, buf_len, NULL);
+ buf, buf_len, NULL);
if (result == USB_STOR_XFER_ERROR)
return USB_STOR_TRANSPORT_ERROR;
}
/* get CSW for device status */
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
- bcs, US_BULK_CS_WRAP_LEN, &cswlen);
+ bcs, US_BULK_CS_WRAP_LEN, &cswlen);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
/* check bulk status */
if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n",
- le32_to_cpu(bcs->Signature),
- US_BULK_CS_SIGN);
+ le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN);
return USB_STOR_TRANSPORT_ERROR;
}
@@ -249,8 +284,8 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
case US_BULK_STAT_PHASE:
/* phase error -- note that a transport reset will be
- * invoked by the invoke_transport() function
- */
+ * invoked by the invoke_transport() function
+ */
return USB_STOR_TRANSPORT_ERROR;
}
@@ -266,10 +301,10 @@ static int rts51x_get_max_lun(struct us_data *us)
/* issue the command */
us->iobuf[0] = 0;
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
- US_BULK_GET_MAX_LUN,
- USB_DIR_IN | USB_TYPE_CLASS |
- USB_RECIP_INTERFACE,
- 0, us->ifnum, us->iobuf, 1, 10*HZ);
+ US_BULK_GET_MAX_LUN,
+ USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE,
+ 0, us->ifnum, us->iobuf, 1, 10 * HZ);
US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",
result, us->iobuf[0]);
@@ -284,16 +319,16 @@ static int rts51x_get_max_lun(struct us_data *us)
static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
{
int retval;
- u8 cmnd[12] = {0};
+ u8 cmnd[12] = { 0 };
US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
cmnd[0] = 0xF0;
cmnd[1] = 0x0D;
- cmnd[2] = (u8)(addr >> 8);
- cmnd[3] = (u8)addr;
- cmnd[4] = (u8)(len >> 8);
- cmnd[5] = (u8)len;
+ cmnd[2] = (u8) (addr >> 8);
+ cmnd[3] = (u8) addr;
+ cmnd[4] = (u8) (len >> 8);
+ cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
data, len, DMA_FROM_DEVICE, NULL);
@@ -306,16 +341,16 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
{
int retval;
- u8 cmnd[12] = {0};
+ u8 cmnd[12] = { 0 };
US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
cmnd[0] = 0xF0;
cmnd[1] = 0x0E;
- cmnd[2] = (u8)(addr >> 8);
- cmnd[3] = (u8)addr;
- cmnd[4] = (u8)(len >> 8);
- cmnd[5] = (u8)len;
+ cmnd[2] = (u8) (addr >> 8);
+ cmnd[3] = (u8) addr;
+ cmnd[4] = (u8) (len >> 8);
+ cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
data, len, DMA_TO_DEVICE, NULL);
@@ -329,7 +364,7 @@ static int rts51x_read_status(struct us_data *us,
u8 lun, u8 *status, int len, int *actlen)
{
int retval;
- u8 cmnd[12] = {0};
+ u8 cmnd[12] = { 0 };
US_DEBUGP("%s, lun = %d\n", __func__, lun);
@@ -356,12 +391,12 @@ static int rts51x_check_status(struct us_data *us, u8 lun)
US_DEBUGP("chip->status_len = %d\n", chip->status_len);
- chip->status[lun].vid = ((u16)buf[0] << 8) | buf[1];
- chip->status[lun].pid = ((u16)buf[2] << 8) | buf[3];
+ chip->status[lun].vid = ((u16) buf[0] << 8) | buf[1];
+ chip->status[lun].pid = ((u16) buf[2] << 8) | buf[3];
chip->status[lun].cur_lun = buf[4];
chip->status[lun].card_type = buf[5];
chip->status[lun].total_lun = buf[6];
- chip->status[lun].fw_ver = ((u16)buf[7] << 8) | buf[8];
+ chip->status[lun].fw_ver = ((u16) buf[7] << 8) | buf[8];
chip->status[lun].phy_exist = buf[9];
chip->status[lun].multi_flag = buf[10];
chip->status[lun].multi_card = buf[11];
@@ -432,6 +467,8 @@ static int config_autodelink_after_power_on(struct us_data *us)
int retval;
u8 value;
+ US_DEBUGP("%s: <---\n", __func__);
+
if (!CHK_AUTO_DELINK(chip))
return 0;
@@ -465,7 +502,7 @@ static int config_autodelink_after_power_on(struct us_data *us)
CLR_BIT(value, 2);
if (CHECK_ID(chip, 0x0159, 0x5889) ||
- CHECK_ID(chip, 0x0138, 0x3880)) {
+ CHECK_ID(chip, 0x0138, 0x3880)) {
CLR_BIT(value, 0);
CLR_BIT(value, 7);
}
@@ -487,6 +524,8 @@ static int config_autodelink_after_power_on(struct us_data *us)
}
}
+ US_DEBUGP("%s: --->\n", __func__);
+
return 0;
}
@@ -496,6 +535,8 @@ static int config_autodelink_before_power_down(struct us_data *us)
int retval;
u8 value;
+ US_DEBUGP("%s: <---\n", __func__);
+
if (!CHK_AUTO_DELINK(chip))
return 0;
@@ -528,14 +569,14 @@ static int config_autodelink_before_power_down(struct us_data *us)
return -EIO;
} else {
if (CHECK_ID(chip, 0x0159, 0x5889) ||
- CHECK_ID(chip, 0x0138, 0x3880) ||
- CHECK_ID(chip, 0x0138, 0x3882)) {
+ CHECK_ID(chip, 0x0138, 0x3880) ||
+ CHECK_ID(chip, 0x0138, 0x3882)) {
retval = rts51x_read_mem(us, 0xFE47, &value, 1);
if (retval < 0)
return -EIO;
if (CHECK_ID(chip, 0x0159, 0x5889) ||
- CHECK_ID(chip, 0x0138, 0x3880)) {
+ CHECK_ID(chip, 0x0138, 0x3880)) {
SET_BIT(value, 0);
SET_BIT(value, 7);
}
@@ -556,25 +597,323 @@ static int config_autodelink_before_power_down(struct us_data *us)
}
}
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
+}
+
+static void fw5895_init(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 val;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) {
+ US_DEBUGP("Not the specified device, return immediately!\n");
+ } else {
+ retval = rts51x_read_mem(us, 0xFD6F, &val, 1);
+ if (retval == STATUS_SUCCESS && (val & 0x1F) == 0) {
+ val = 0x1F;
+ retval = rts51x_write_mem(us, 0xFD70, &val, 1);
+ if (retval != STATUS_SUCCESS)
+ US_DEBUGP("Write memory fail\n");
+ } else {
+ US_DEBUGP("Read memory fail, OR (val & 0x1F) != 0\n");
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+#ifdef CONFIG_REALTEK_AUTOPM
+static void fw5895_set_mmc_wp(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 buf[13];
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) {
+ US_DEBUGP("Not the specified device, return immediately!\n");
+ } else {
+ retval = rts51x_read_mem(us, 0xFD6F, buf, 1);
+ if (retval == STATUS_SUCCESS && (buf[0] & 0x24) == 0x24) {
+ /* SD Exist and SD WP */
+ retval = rts51x_read_mem(us, 0xD04E, buf, 1);
+ if (retval == STATUS_SUCCESS) {
+ buf[0] |= 0x04;
+ retval = rts51x_write_mem(us, 0xFD70, buf, 1);
+ if (retval != STATUS_SUCCESS)
+ US_DEBUGP("Write memory fail\n");
+ } else {
+ US_DEBUGP("Read memory fail\n");
+ }
+ } else {
+ US_DEBUGP("Read memory fail, OR (buf[0]&0x24)!=0x24\n");
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static void rts51x_modi_suspend_timer(struct rts51x_chip *chip)
+{
+ US_DEBUGP("%s: <---, state:%d\n", __func__, rts51x_get_stat(chip));
+
+ chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay);
+ mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires);
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static void rts51x_suspend_timer_fn(unsigned long data)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)data;
+ struct us_data *us = chip->us;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ switch (rts51x_get_stat(chip)) {
+ case RTS51X_STAT_INIT:
+ case RTS51X_STAT_RUN:
+ rts51x_modi_suspend_timer(chip);
+ break;
+ case RTS51X_STAT_IDLE:
+ case RTS51X_STAT_SS:
+ US_DEBUGP("%s: RTS51X_STAT_SS, intf->pm_usage_cnt:%d,"
+ "power.usage:%d\n", __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(&us->pusb_intf->dev.power.usage_count));
+
+ if (atomic_read(&us->pusb_intf->pm_usage_cnt) > 0) {
+ US_DEBUGP("%s: Ready to enter SS state.\n",
+ __func__);
+ rts51x_set_stat(chip, RTS51X_STAT_SS);
+ /* ignore mass storage interface's children */
+ pm_suspend_ignore_children(&us->pusb_intf->dev, true);
+ usb_autopm_put_interface(us->pusb_intf);
+ US_DEBUGP("%s: RTS51X_STAT_SS 01,"
+ "intf->pm_usage_cnt:%d, power.usage:%d\n",
+ __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(
+ &us->pusb_intf->dev.power.usage_count));
+ }
+ break;
+ default:
+ US_DEBUGP("%s: Unknonwn state !!!\n", __func__);
+ break;
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static inline int working_scsi(struct scsi_cmnd *srb)
+{
+ if ((srb->cmnd[0] == TEST_UNIT_READY) ||
+ (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ static int card_first_show = 1;
+ static u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0
+ };
+ static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0
+ };
+ int ret;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if (working_scsi(srb)) {
+ US_DEBUGP("%s: working scsi, intf->pm_usage_cnt:%d,"
+ "power.usage:%d\n", __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(&us->pusb_intf->dev.power.usage_count));
+
+ if (atomic_read(&us->pusb_intf->pm_usage_cnt) <= 0) {
+ ret = usb_autopm_get_interface(us->pusb_intf);
+ US_DEBUGP("%s: working scsi, ret=%d\n", __func__, ret);
+ }
+ if (rts51x_get_stat(chip) != RTS51X_STAT_RUN)
+ rts51x_set_stat(chip, RTS51X_STAT_RUN);
+ chip->proto_handler_backup(srb, us);
+ } else {
+ if (rts51x_get_stat(chip) == RTS51X_STAT_SS) {
+ US_DEBUGP("%s: NOT working scsi\n", __func__);
+ if ((srb->cmnd[0] == TEST_UNIT_READY) &&
+ (chip->pwr_state == US_SUSPEND)) {
+ if (TST_LUN_READY(chip, srb->device->lun)) {
+ srb->result = SAM_STAT_GOOD;
+ } else {
+ srb->result = SAM_STAT_CHECK_CONDITION;
+ memcpy(srb->sense_buffer,
+ media_not_present,
+ US_SENSE_SIZE);
+ }
+ US_DEBUGP("%s: TEST_UNIT_READY--->\n",
+ __func__);
+ goto out;
+ }
+ if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
+ int prevent = srb->cmnd[4] & 0x1;
+ if (prevent) {
+ srb->result = SAM_STAT_CHECK_CONDITION;
+ memcpy(srb->sense_buffer,
+ invalid_cmd_field,
+ US_SENSE_SIZE);
+ } else {
+ srb->result = SAM_STAT_GOOD;
+ }
+ US_DEBUGP("%s: ALLOW_MEDIUM_REMOVAL--->\n",
+ __func__);
+ goto out;
+ }
+ } else {
+ US_DEBUGP("%s: NOT working scsi, not SS\n", __func__);
+ chip->proto_handler_backup(srb, us);
+ /* Check wether card is plugged in */
+ if (srb->cmnd[0] == TEST_UNIT_READY) {
+ if (srb->result == SAM_STAT_GOOD) {
+ SET_LUN_READY(chip, srb->device->lun);
+ if (card_first_show) {
+ card_first_show = 0;
+ fw5895_set_mmc_wp(us);
+ }
+ } else {
+ CLR_LUN_READY(chip, srb->device->lun);
+ card_first_show = 1;
+ }
+ }
+ if (rts51x_get_stat(chip) != RTS51X_STAT_IDLE)
+ rts51x_set_stat(chip, RTS51X_STAT_IDLE);
+ }
+ }
+out:
+ US_DEBUGP("%s: state:%d\n", __func__, rts51x_get_stat(chip));
+ if (rts51x_get_stat(chip) == RTS51X_STAT_RUN)
+ rts51x_modi_suspend_timer(chip);
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static int realtek_cr_autosuspend_setup(struct us_data *us)
+{
+ struct rts51x_chip *chip;
+ struct rts51x_status *status = NULL;
+ u8 buf[16];
+ int retval;
+
+ chip = (struct rts51x_chip *)us->extra;
+ chip->support_auto_delink = 0;
+ chip->pwr_state = US_RESUME;
+ chip->lun_ready = 0;
+ rts51x_set_stat(chip, RTS51X_STAT_INIT);
+
+ retval = rts51x_read_status(us, 0, buf, 16, &(chip->status_len));
+ if (retval != STATUS_SUCCESS) {
+ US_DEBUGP("Read status fail\n");
+ return -EIO;
+ }
+ status = chip->status;
+ status->vid = ((u16) buf[0] << 8) | buf[1];
+ status->pid = ((u16) buf[2] << 8) | buf[3];
+ status->cur_lun = buf[4];
+ status->card_type = buf[5];
+ status->total_lun = buf[6];
+ status->fw_ver = ((u16) buf[7] << 8) | buf[8];
+ status->phy_exist = buf[9];
+ status->multi_flag = buf[10];
+ status->multi_card = buf[11];
+ status->log_exist = buf[12];
+ if (chip->status_len == 16) {
+ status->detailed_type.detailed_type1 = buf[13];
+ status->function[0] = buf[14];
+ status->function[1] = buf[15];
+ }
+
+ /* back up the proto_handler in us->extra */
+ chip = (struct rts51x_chip *)(us->extra);
+ chip->proto_handler_backup = us->proto_handler;
+ /* Set the autosuspend_delay to 0 */
+ pm_runtime_set_autosuspend_delay(&us->pusb_dev->dev, 0);
+ /* override us->proto_handler setted in get_protocol() */
+ us->proto_handler = rts51x_invoke_transport;
+
+ chip->timer_expires = 0;
+ setup_timer(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn,
+ (unsigned long)chip);
+ fw5895_init(us);
+
+ /* enable autosuspend funciton of the usb device */
+ usb_enable_autosuspend(us->pusb_dev);
+
return 0;
}
+#endif
static void realtek_cr_destructor(void *extra)
{
struct rts51x_chip *chip = (struct rts51x_chip *)extra;
+ US_DEBUGP("%s: <---\n", __func__);
+
if (!chip)
return;
-
+#ifdef CONFIG_REALTEK_AUTOPM
+ if (ss_en) {
+ del_timer(&chip->rts51x_suspend_timer);
+ chip->timer_expires = 0;
+ }
+#endif
kfree(chip->status);
}
#ifdef CONFIG_PM
-static void realtek_pm_hook(struct us_data *us, int pm_state)
+int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ /* wait until no command is running */
+ mutex_lock(&us->dev_mutex);
+
+ config_autodelink_before_power_down(us);
+
+ mutex_unlock(&us->dev_mutex);
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
+}
+
+static int realtek_cr_resume(struct usb_interface *iface)
{
- if (pm_state == US_SUSPEND)
- (void)config_autodelink_before_power_down(us);
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ fw5895_init(us);
+ config_autodelink_after_power_on(us);
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
}
+#else
+#define realtek_cr_suspend NULL
+#define realtek_cr_resume NULL
#endif
static int init_realtek_cr(struct us_data *us)
@@ -588,10 +927,6 @@ static int init_realtek_cr(struct us_data *us)
us->extra = chip;
us->extra_destructor = realtek_cr_destructor;
-#ifdef CONFIG_PM
- us->suspend_resume_hook = realtek_pm_hook;
-#endif
-
us->max_lun = chip->max_lun = rts51x_get_max_lun(us);
US_DEBUGP("chip->max_lun = %d\n", chip->max_lun);
@@ -602,18 +937,24 @@ static int init_realtek_cr(struct us_data *us)
goto INIT_FAIL;
for (i = 0; i <= (int)(chip->max_lun); i++) {
- retval = rts51x_check_status(us, (u8)i);
+ retval = rts51x_check_status(us, (u8) i);
if (retval < 0)
goto INIT_FAIL;
}
if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) ||
- CHECK_FW_VER(chip, 0x5901))
+ CHECK_FW_VER(chip, 0x5901))
SET_AUTO_DELINK(chip);
if (STATUS_LEN(chip) == 16) {
if (SUPPORT_AUTO_DELINK(chip))
SET_AUTO_DELINK(chip);
}
+#ifdef CONFIG_REALTEK_AUTOPM
+ if (ss_en) {
+ chip->us = us;
+ realtek_cr_autosuspend_setup(us);
+ }
+#endif
US_DEBUGP("chip->flag = 0x%x\n", chip->flag);
@@ -632,7 +973,7 @@ INIT_FAIL:
}
static int realtek_cr_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+ const struct usb_device_id *id)
{
struct us_data *us;
int result;
@@ -640,25 +981,30 @@ static int realtek_cr_probe(struct usb_interface *intf,
US_DEBUGP("Probe Realtek Card Reader!\n");
result = usb_stor_probe1(&us, intf, id,
- (id - realtek_cr_ids) + realtek_cr_unusual_dev_list);
+ (id - realtek_cr_ids) +
+ realtek_cr_unusual_dev_list);
if (result)
return result;
result = usb_stor_probe2(us);
+
return result;
}
static struct usb_driver realtek_cr_driver = {
- .name = "ums-realtek",
- .probe = realtek_cr_probe,
- .disconnect = usb_stor_disconnect,
- .suspend = usb_stor_suspend,
- .resume = usb_stor_resume,
- .reset_resume = usb_stor_reset_resume,
- .pre_reset = usb_stor_pre_reset,
- .post_reset = usb_stor_post_reset,
- .id_table = realtek_cr_ids,
- .soft_unbind = 1,
+ .name = "ums-realtek",
+ .probe = realtek_cr_probe,
+ .disconnect = usb_stor_disconnect,
+ /* .suspend = usb_stor_suspend, */
+ /* .resume = usb_stor_resume, */
+ .reset_resume = usb_stor_reset_resume,
+ .suspend = realtek_cr_suspend,
+ .resume = realtek_cr_resume,
+ .pre_reset = usb_stor_pre_reset,
+ .post_reset = usb_stor_post_reset,
+ .id_table = realtek_cr_ids,
+ .soft_unbind = 1,
+ .supports_autosuspend = 1,
};
static int __init realtek_cr_init(void)
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c
index c0c5665e60a9..200fd7c6c7d5 100644
--- a/drivers/usb/wusbcore/cbaf.c
+++ b/drivers/usb/wusbcore/cbaf.c
@@ -298,7 +298,7 @@ static int cbaf_cdid_get(struct cbaf *cbaf)
if (result < needed) {
dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs "
"%zu bytes needed)\n", (size_t)result, needed);
- return result;
+ return -ENOENT;
}
strlcpy(cbaf->device_name, di->DeviceFriendlyName, CBA_NAME_LEN);
@@ -350,7 +350,7 @@ static ssize_t cbaf_wusb_chid_store(struct device *dev,
return result;
result = cbaf_cdid_get(cbaf);
if (result < 0)
- return -result;
+ return result;
return size;
}
static DEVICE_ATTR(wusb_chid, 0600, cbaf_wusb_chid_show, cbaf_wusb_chid_store);
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 6ccd93a9b909..419334568be6 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -83,6 +83,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/hash.h>
+#include <linux/ratelimit.h>
#include "wa-hc.h"
#include "wusbhc.h"
@@ -1217,16 +1218,14 @@ static int wa_xfer_status_to_errno(u8 status)
if (status == 0)
return 0;
if (status >= ARRAY_SIZE(xlat)) {
- if (printk_ratelimit())
- printk(KERN_ERR "%s(): BUG? "
+ printk_ratelimited(KERN_ERR "%s(): BUG? "
"Unknown WA transfer status 0x%02x\n",
__func__, real_status);
return -EINVAL;
}
errno = xlat[status];
if (unlikely(errno > 0)) {
- if (printk_ratelimit())
- printk(KERN_ERR "%s(): BUG? "
+ printk_ratelimited(KERN_ERR "%s(): BUG? "
"Inconsistent WA status: 0x%02x\n",
__func__, real_status);
errno = -errno;