v4.19.13 snapshot.
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
new file mode 100644
index 0000000..31cce78
--- /dev/null
+++ b/drivers/usb/gadget/Kconfig
@@ -0,0 +1,515 @@
+#
+# USB Gadget support on a system involves
+#    (a) a peripheral controller, and
+#    (b) the gadget driver using it.
+#
+# NOTE:  Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+#  - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+#  - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+#  - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+menuconfig USB_GADGET
+	tristate "USB Gadget Support"
+	select USB_COMMON
+	select NLS
+	help
+	   USB is a master/slave protocol, organized with one master
+	   host (such as a PC) controlling up to 127 peripheral devices.
+	   The USB hardware is asymmetric, which makes it easier to set up:
+	   you can't connect a "to-the-host" connector to a peripheral.
+
+	   Linux can run in the host, or in the peripheral.  In both cases
+	   you need a low level bus controller driver, and some software
+	   talking to it.  Peripheral controllers are often discrete silicon,
+	   or are integrated with the CPU in a microcontroller.  The more
+	   familiar host side controllers have names like "EHCI", "OHCI",
+	   or "UHCI", and are usually integrated into southbridges on PC
+	   motherboards.
+
+	   Enable this configuration option if you want to run Linux inside
+	   a USB peripheral device.  Configure one hardware driver for your
+	   peripheral/device side bus controller, and a "gadget driver" for
+	   your peripheral protocol.  (If you use modular gadget drivers,
+	   you may configure more than one.)
+
+	   If in doubt, say "N" and don't enable these drivers; most people
+	   don't have this kind of hardware (except maybe inside Linux PDAs).
+
+	   For more information, see <http://www.linux-usb.org/gadget> and
+	   the kernel documentation for this API.
+
+if USB_GADGET
+
+config USB_GADGET_DEBUG
+	bool "Debugging messages (DEVELOPMENT)"
+	depends on DEBUG_KERNEL
+	help
+	   Many controller and gadget drivers will print some debugging
+	   messages if you use this option to ask for those messages.
+
+	   Avoid enabling these messages, even if you're actively
+	   debugging such a driver.  Many drivers will emit so many
+	   messages that the driver timings are affected, which will
+	   either create new failure modes or remove the one you're
+	   trying to track down.  Never enable these messages for a
+	   production build.
+
+config USB_GADGET_VERBOSE
+	bool "Verbose debugging Messages (DEVELOPMENT)"
+	depends on USB_GADGET_DEBUG
+	help
+	   Many controller and gadget drivers will print verbose debugging
+	   messages if you use this option to ask for those messages.
+
+	   Avoid enabling these messages, even if you're actively
+	   debugging such a driver.  Many drivers will emit so many
+	   messages that the driver timings are affected, which will
+	   either create new failure modes or remove the one you're
+	   trying to track down.  Never enable these messages for a
+	   production build.
+
+config USB_GADGET_DEBUG_FILES
+	bool "Debugging information files (DEVELOPMENT)"
+	depends on PROC_FS
+	help
+	   Some of the drivers in the "gadget" framework can expose
+	   debugging information in files such as /proc/driver/udc
+	   (for a peripheral controller).  The information in these
+	   files may help when you're troubleshooting or bringing up a
+	   driver on a new board.   Enable these files by choosing "Y"
+	   here.  If in doubt, or to conserve kernel memory, say "N".
+
+config USB_GADGET_DEBUG_FS
+	bool "Debugging information files in debugfs (DEVELOPMENT)"
+	depends on DEBUG_FS
+	help
+	   Some of the drivers in the "gadget" framework can expose
+	   debugging information in files under /sys/kernel/debug/.
+	   The information in these files may help when you're
+	   troubleshooting or bringing up a driver on a new board.
+	   Enable these files by choosing "Y" here.  If in doubt, or
+	   to conserve kernel memory, say "N".
+
+config USB_GADGET_VBUS_DRAW
+	int "Maximum VBUS Power usage (2-500 mA)"
+	range 2 500
+	default 2
+	help
+	   Some devices need to draw power from USB when they are
+	   configured, perhaps to operate circuitry or to recharge
+	   batteries.  This is in addition to any local power supply,
+	   such as an AC adapter or batteries.
+
+	   Enter the maximum power your device draws through USB, in
+	   milliAmperes.  The permitted range of values is 2 - 500 mA;
+	   0 mA would be legal, but can make some hosts misbehave.
+
+	   This value will be used except for system-specific gadget
+	   drivers that have more specific information.
+
+config USB_GADGET_STORAGE_NUM_BUFFERS
+	int "Number of storage pipeline buffers"
+	range 2 256
+	default 2
+	help
+	   Usually 2 buffers are enough to establish a good buffering
+	   pipeline. The number may be increased in order to compensate
+	   for a bursty VFS behaviour. For instance there may be CPU wake up
+	   latencies that makes the VFS to appear bursty in a system with
+	   an CPU on-demand governor. Especially if DMA is doing IO to
+	   offload the CPU. In this case the CPU will go into power
+	   save often and spin up occasionally to move data within VFS.
+	   If selecting USB_GADGET_DEBUG_FILES this value may be set by
+	   a module parameter as well.
+	   If unsure, say 2.
+
+config U_SERIAL_CONSOLE
+	bool "Serial gadget console support"
+	depends on USB_U_SERIAL
+	help
+	   It supports the serial gadget can be used as a console.
+
+source "drivers/usb/gadget/udc/Kconfig"
+
+#
+# USB Gadget Drivers
+#
+
+# composite based drivers
+config USB_LIBCOMPOSITE
+	tristate
+	select CONFIGFS_FS
+	depends on USB_GADGET
+
+config USB_F_ACM
+	tristate
+
+config USB_F_SS_LB
+	tristate
+
+config USB_U_SERIAL
+	tristate
+
+config USB_U_ETHER
+	tristate
+
+config USB_U_AUDIO
+	tristate
+
+config USB_F_SERIAL
+	tristate
+
+config USB_F_OBEX
+	tristate
+
+config USB_F_NCM
+	tristate
+
+config USB_F_ECM
+	tristate
+
+config USB_F_PHONET
+	tristate
+
+config USB_F_EEM
+	tristate
+
+config USB_F_SUBSET
+	tristate
+
+config USB_F_RNDIS
+	tristate
+
+config USB_F_MASS_STORAGE
+	tristate
+
+config USB_F_FS
+	tristate
+
+config USB_F_UAC1
+	tristate
+
+config USB_F_UAC1_LEGACY
+	tristate
+
+config USB_F_UAC2
+	tristate
+
+config USB_F_UVC
+	tristate
+
+config USB_F_MIDI
+	tristate
+
+config USB_F_HID
+	tristate
+
+config USB_F_PRINTER
+	tristate
+
+config USB_F_TCM
+	tristate
+
+# this first set of drivers all depend on bulk-capable hardware.
+
+config USB_CONFIGFS
+	tristate "USB Gadget functions configurable through configfs"
+	select USB_LIBCOMPOSITE
+	help
+	  A Linux USB "gadget" can be set up through configfs.
+	  If this is the case, the USB functions (which from the host's
+	  perspective are seen as interfaces) and configurations are
+	  specified simply by creating appropriate directories in configfs.
+	  Associating functions with configurations is done by creating
+	  appropriate symbolic links.
+	  For more information see Documentation/usb/gadget_configfs.txt.
+
+config USB_CONFIGFS_SERIAL
+	bool "Generic serial bulk in/out"
+	depends on USB_CONFIGFS
+	depends on TTY
+	select USB_U_SERIAL
+	select USB_F_SERIAL
+	help
+	  The function talks to the Linux-USB generic serial driver.
+
+config USB_CONFIGFS_ACM
+	bool "Abstract Control Model (CDC ACM)"
+	depends on USB_CONFIGFS
+	depends on TTY
+	select USB_U_SERIAL
+	select USB_F_ACM
+	help
+	  ACM serial link.  This function can be used to interoperate with
+	  MS-Windows hosts or with the Linux-USB "cdc-acm" driver.
+
+config USB_CONFIGFS_OBEX
+	bool "Object Exchange Model (CDC OBEX)"
+	depends on USB_CONFIGFS
+	depends on TTY
+	select USB_U_SERIAL
+	select USB_F_OBEX
+	help
+	  You will need a user space OBEX server talking to /dev/ttyGS*,
+	  since the kernel itself doesn't implement the OBEX protocol.
+
+config USB_CONFIGFS_NCM
+	bool "Network Control Model (CDC NCM)"
+	depends on USB_CONFIGFS
+	depends on NET
+	select USB_U_ETHER
+	select USB_F_NCM
+	help
+	  NCM is an advanced protocol for Ethernet encapsulation, allows
+	  grouping of several ethernet frames into one USB transfer and
+	  different alignment possibilities.
+
+config USB_CONFIGFS_ECM
+	bool "Ethernet Control Model (CDC ECM)"
+	depends on USB_CONFIGFS
+	depends on NET
+	select USB_U_ETHER
+	select USB_F_ECM
+	help
+	  The "Communication Device Class" (CDC) Ethernet Control Model.
+	  That protocol is often avoided with pure Ethernet adapters, in
+	  favor of simpler vendor-specific hardware, but is widely
+	  supported by firmware for smart network devices.
+
+config USB_CONFIGFS_ECM_SUBSET
+	bool "Ethernet Control Model (CDC ECM) subset"
+	depends on USB_CONFIGFS
+	depends on NET
+	select USB_U_ETHER
+	select USB_F_SUBSET
+	help
+	  On hardware that can't implement the full protocol,
+	  a simple CDC subset is used, placing fewer demands on USB.
+
+config USB_CONFIGFS_RNDIS
+	bool "RNDIS"
+	depends on USB_CONFIGFS
+	depends on NET
+	select USB_U_ETHER
+	select USB_F_RNDIS
+	help
+	   Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+	   and Microsoft provides redistributable binary RNDIS drivers for
+	   older versions of Windows.
+
+	   To make MS-Windows work with this, use Documentation/usb/linux.inf
+	   as the "driver info file".  For versions of MS-Windows older than
+	   XP, you'll need to download drivers from Microsoft's website; a URL
+	   is given in comments found in that info file.
+
+config USB_CONFIGFS_EEM
+	bool "Ethernet Emulation Model (EEM)"
+	depends on USB_CONFIGFS
+	depends on NET
+	select USB_U_ETHER
+	select USB_F_EEM
+	help
+	  CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+	  and therefore can be supported by more hardware.  Technically ECM and
+	  EEM are designed for different applications.  The ECM model extends
+	  the network interface to the target (e.g. a USB cable modem), and the
+	  EEM model is for mobile devices to communicate with hosts using
+	  ethernet over USB.  For Linux gadgets, however, the interface with
+	  the host is the same (a usbX device), so the differences are minimal.
+
+config USB_CONFIGFS_PHONET
+	bool "Phonet protocol"
+	depends on USB_CONFIGFS
+	depends on NET
+	depends on PHONET
+	select USB_U_ETHER
+	select USB_F_PHONET
+	help
+	  The Phonet protocol implementation for USB device.
+
+config USB_CONFIGFS_MASS_STORAGE
+	bool "Mass storage"
+	depends on USB_CONFIGFS
+	depends on BLOCK
+	select USB_F_MASS_STORAGE
+	help
+	  The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+	  As its storage repository it can use a regular file or a block
+	  device (in much the same way as the "loop" device driver),
+	  specified as a module parameter or sysfs option.
+
+config USB_CONFIGFS_F_LB_SS
+	bool "Loopback and sourcesink function (for testing)"
+	depends on USB_CONFIGFS
+	select USB_F_SS_LB
+	help
+	  Loopback function loops back a configurable number of transfers.
+	  Sourcesink function either sinks and sources bulk data.
+	  It also implements control requests, for "chapter 9" conformance.
+	  Make this be the first driver you try using on top of any new
+	  USB peripheral controller driver.  Then you can use host-side
+	  test software, like the "usbtest" driver, to put your hardware
+	  and its driver through a basic set of functional tests.
+
+config USB_CONFIGFS_F_FS
+	bool "Function filesystem (FunctionFS)"
+	depends on USB_CONFIGFS
+	select USB_F_FS
+	help
+	  The Function Filesystem (FunctionFS) lets one create USB
+	  composite functions in user space in the same way GadgetFS
+	  lets one create USB gadgets in user space.  This allows creation
+	  of composite gadgets such that some of the functions are
+	  implemented in kernel space (for instance Ethernet, serial or
+	  mass storage) and other are implemented in user space.
+
+config USB_CONFIGFS_F_UAC1
+	bool "Audio Class 1.0"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_PCM
+	select USB_U_AUDIO
+	select USB_F_UAC1
+	help
+	  This Audio function implements 1 AudioControl interface,
+	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
+	  This driver doesn't expect any real Audio codec to be present
+	  on the device - the audio streams are simply sinked to and
+	  sourced from a virtual ALSA sound card created. The user-space
+	  application may choose to do whatever it wants with the data
+	  received from the USB Host and choose to provide whatever it
+	  wants as audio data to the USB Host.
+
+config USB_CONFIGFS_F_UAC1_LEGACY
+	bool "Audio Class 1.0 (legacy implementation)"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_PCM
+	select USB_F_UAC1_LEGACY
+	help
+	  This Audio function implements 1 AudioControl interface,
+	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
+	  This is a legacy driver and requires a real Audio codec
+	  to be present on the device.
+
+config USB_CONFIGFS_F_UAC2
+	bool "Audio Class 2.0"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_PCM
+	select USB_U_AUDIO
+	select USB_F_UAC2
+	help
+	  This Audio function is compatible with USB Audio Class
+	  specification 2.0. It implements 1 AudioControl interface,
+	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
+	  This driver doesn't expect any real Audio codec to be present
+	  on the device - the audio streams are simply sinked to and
+	  sourced from a virtual ALSA sound card created. The user-space
+	  application may choose to do whatever it wants with the data
+	  received from the USB Host and choose to provide whatever it
+	  wants as audio data to the USB Host.
+
+config USB_CONFIGFS_F_MIDI
+	bool "MIDI function"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_RAWMIDI
+	select USB_F_MIDI
+	help
+	  The MIDI Function acts as a USB Audio device, with one MIDI
+	  input and one MIDI output. These MIDI jacks appear as
+	  a sound "card" in the ALSA sound system. Other MIDI
+	  connections can then be made on the gadget system, using
+	  ALSA's aconnect utility etc.
+
+config USB_CONFIGFS_F_HID
+	bool "HID function"
+	depends on USB_CONFIGFS
+	select USB_F_HID
+	help
+	  The HID function driver provides generic emulation of USB
+	  Human Interface Devices (HID).
+
+	  For more information, see Documentation/usb/gadget_hid.txt.
+
+config USB_CONFIGFS_F_UVC
+	bool "USB Webcam function"
+	depends on USB_CONFIGFS
+	depends on VIDEO_V4L2
+	depends on VIDEO_DEV
+	select VIDEOBUF2_VMALLOC
+	select USB_F_UVC
+	help
+	  The Webcam function acts as a composite USB Audio and Video Class
+	  device. It provides a userspace API to process UVC control requests
+	  and stream video data to the host.
+
+config USB_CONFIGFS_F_PRINTER
+	bool "Printer function"
+	select USB_F_PRINTER
+	depends on USB_CONFIGFS
+	help
+	  The Printer function channels data between the USB host and a
+	  userspace program driving the print engine. The user space
+	  program reads and writes the device file /dev/g_printer<X> to
+	  receive or send printer data. It can use ioctl calls to
+	  the device file to get or set printer status.
+
+	  For more information, see Documentation/usb/gadget_printer.txt
+	  which includes sample code for accessing the device file.
+
+config USB_CONFIGFS_F_TCM
+	bool "USB Gadget Target Fabric"
+	depends on TARGET_CORE
+	depends on USB_CONFIGFS
+	select USB_LIBCOMPOSITE
+	select USB_F_TCM
+	help
+	  This fabric is a USB gadget component. Two USB protocols are
+	  supported that is BBB or BOT (Bulk Only Transport) and UAS
+	  (USB Attached SCSI). BOT is advertised on alternative
+	  interface 0 (primary) and UAS is on alternative interface 1.
+	  Both protocols can work on USB2.0 and USB3.0.
+	  UAS utilizes the USB 3.0 feature called streams support.
+
+choice
+	tristate "USB Gadget precomposed configurations"
+	default USB_ETH
+	optional
+	help
+	  A Linux "Gadget Driver" talks to the USB Peripheral Controller
+	  driver through the abstract "gadget" API.  Some other operating
+	  systems call these "client" drivers, of which "class drivers"
+	  are a subset (implementing a USB device class specification).
+	  A gadget driver implements one or more USB functions using
+	  the peripheral hardware.
+
+	  Gadget drivers are hardware-neutral, or "platform independent",
+	  except that they sometimes must understand quirks or limitations
+	  of the particular controllers they work with.  For example, when
+	  a controller doesn't support alternate configurations or provide
+	  enough of the right types of endpoints, the gadget driver might
+	  not be able work with that controller, or might need to implement
+	  a less common variant of a device class protocol.
+
+	  The available choices each represent a single precomposed USB
+	  gadget configuration. In the device model, each option contains
+	  both the device instantiation as a child for a USB gadget
+	  controller, and the relevant drivers for each function declared
+	  by the device.
+
+source "drivers/usb/gadget/legacy/Kconfig"
+
+endchoice
+
+endif # USB_GADGET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
new file mode 100644
index 0000000..130dad7
--- /dev/null
+++ b/drivers/usb/gadget/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# USB peripheral controller drivers
+#
+subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG)	:= -DDEBUG
+subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE)	+= -DVERBOSE_DEBUG
+ccflags-y				+= -I$(srctree)/drivers/usb/gadget/udc
+
+obj-$(CONFIG_USB_LIBCOMPOSITE)	+= libcomposite.o
+libcomposite-y			:= usbstring.o config.o epautoconf.o
+libcomposite-y			+= composite.o functions.o configfs.o u_f.o
+
+obj-$(CONFIG_USB_GADGET)	+= udc/ function/ legacy/
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
new file mode 100644
index 0000000..b8a1584
--- /dev/null
+++ b/drivers/usb/gadget/composite.c
@@ -0,0 +1,2437 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/otg.h>
+#include <asm/unaligned.h>
+
+#include "u_os_desc.h"
+
+/**
+ * struct usb_os_string - represents OS String to be reported by a gadget
+ * @bLength: total length of the entire descritor, always 0x12
+ * @bDescriptorType: USB_DT_STRING
+ * @qwSignature: the OS String proper
+ * @bMS_VendorCode: code used by the host for subsequent requests
+ * @bPad: not used, must be zero
+ */
+struct usb_os_string {
+	__u8	bLength;
+	__u8	bDescriptorType;
+	__u8	qwSignature[OS_STRING_QW_SIGN_LEN];
+	__u8	bMS_VendorCode;
+	__u8	bPad;
+} __packed;
+
+/*
+ * The code in this file is utility code, used to build a gadget driver
+ * from one or more "function" drivers, one or more "configuration"
+ * objects, and a "usb_composite_driver" by gluing them together along
+ * with the relevant device-wide data.
+ */
+
+static struct usb_gadget_strings **get_containers_gs(
+		struct usb_gadget_string_container *uc)
+{
+	return (struct usb_gadget_strings **)uc->stash;
+}
+
+/**
+ * function_descriptors() - get function descriptors for speed
+ * @f: the function
+ * @speed: the speed
+ *
+ * Returns the descriptors or NULL if not set.
+ */
+static struct usb_descriptor_header **
+function_descriptors(struct usb_function *f,
+		     enum usb_device_speed speed)
+{
+	struct usb_descriptor_header **descriptors;
+
+	/*
+	 * NOTE: we try to help gadget drivers which might not be setting
+	 * max_speed appropriately.
+	 */
+
+	switch (speed) {
+	case USB_SPEED_SUPER_PLUS:
+		descriptors = f->ssp_descriptors;
+		if (descriptors)
+			break;
+		/* FALLTHROUGH */
+	case USB_SPEED_SUPER:
+		descriptors = f->ss_descriptors;
+		if (descriptors)
+			break;
+		/* FALLTHROUGH */
+	case USB_SPEED_HIGH:
+		descriptors = f->hs_descriptors;
+		if (descriptors)
+			break;
+		/* FALLTHROUGH */
+	default:
+		descriptors = f->fs_descriptors;
+	}
+
+	/*
+	 * if we can't find any descriptors at all, then this gadget deserves to
+	 * Oops with a NULL pointer dereference
+	 */
+
+	return descriptors;
+}
+
+/**
+ * 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_PLUS:
+		if (gadget_is_superspeed_plus(g)) {
+			speed_desc = f->ssp_descriptors;
+			want_comp_desc = 1;
+			break;
+		}
+		/* fall through */
+	case USB_SPEED_SUPER:
+		if (gadget_is_superspeed(g)) {
+			speed_desc = f->ss_descriptors;
+			want_comp_desc = 1;
+			break;
+		}
+		/* fall through */
+	case USB_SPEED_HIGH:
+		if (gadget_is_dualspeed(g)) {
+			speed_desc = f->hs_descriptors;
+			break;
+		}
+		/* fall through */
+	default:
+		speed_desc = f->fs_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 = usb_endpoint_maxp(chosen_desc);
+	_ep->desc = chosen_desc;
+	_ep->comp_desc = NULL;
+	_ep->maxburst = 0;
+	_ep->mult = 1;
+
+	if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+				usb_endpoint_xfer_int(_ep->desc)))
+		_ep->mult = usb_endpoint_maxp_mult(_ep->desc);
+
+	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_ISOC:
+			/* mult: bits 1:0 of bmAttributes */
+			_ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
+			/* fall through */
+		case USB_ENDPOINT_XFER_BULK:
+		case USB_ENDPOINT_XFER_INT:
+			_ep->maxburst = comp_desc->bMaxBurst + 1;
+			break;
+		default:
+			if (comp_desc->bMaxBurst != 0) {
+				struct usb_composite_dev *cdev;
+
+				cdev = get_gadget_data(g);
+				ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+			}
+			_ep->maxburst = 1;
+			break;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(config_ep_by_speed);
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it.  Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int usb_add_function(struct usb_configuration *config,
+		struct usb_function *function)
+{
+	int	value = -EINVAL;
+
+	DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+			function->name, function,
+			config->label, config);
+
+	if (!function->set_alt || !function->disable)
+		goto done;
+
+	function->config = config;
+	list_add_tail(&function->list, &config->functions);
+
+	if (function->bind_deactivated) {
+		value = usb_function_deactivate(function);
+		if (value)
+			goto done;
+	}
+
+	/* REVISIT *require* function->bind? */
+	if (function->bind) {
+		value = function->bind(config, function);
+		if (value < 0) {
+			list_del(&function->list);
+			function->config = NULL;
+		}
+	} else
+		value = 0;
+
+	/* We allow configurations that don't work at both speeds.
+	 * If we run into a lowspeed Linux system, treat it the same
+	 * as full speed ... it's the function drivers that will need
+	 * to avoid bulk and ISO transfers.
+	 */
+	if (!config->fullspeed && function->fs_descriptors)
+		config->fullspeed = true;
+	if (!config->highspeed && function->hs_descriptors)
+		config->highspeed = true;
+	if (!config->superspeed && function->ss_descriptors)
+		config->superspeed = true;
+	if (!config->superspeed_plus && function->ssp_descriptors)
+		config->superspeed_plus = true;
+
+done:
+	if (value)
+		DBG(config->cdev, "adding '%s'/%p --> %d\n",
+				function->name, function, value);
+	return value;
+}
+EXPORT_SYMBOL_GPL(usb_add_function);
+
+void usb_remove_function(struct usb_configuration *c, struct usb_function *f)
+{
+	if (f->disable)
+		f->disable(f);
+
+	bitmap_zero(f->endpoints, 32);
+	list_del(&f->list);
+	if (f->unbind)
+		f->unbind(c, f);
+
+	if (f->bind_deactivated)
+		usb_function_activate(f);
+}
+EXPORT_SYMBOL_GPL(usb_remove_function);
+
+/**
+ * usb_function_deactivate - prevent function and gadget enumeration
+ * @function: the function that isn't yet ready to respond
+ *
+ * Blocks response of the gadget driver to host enumeration by
+ * preventing the data line pullup from being activated.  This is
+ * normally called during @bind() processing to change from the
+ * initial "ready to respond" state, or when a required resource
+ * becomes available.
+ *
+ * For example, drivers that serve as a passthrough to a userspace
+ * daemon can block enumeration unless that daemon (such as an OBEX,
+ * MTP, or print server) is ready to handle host requests.
+ *
+ * Not all systems support software control of their USB peripheral
+ * data pullups.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_deactivate(struct usb_function *function)
+{
+	struct usb_composite_dev	*cdev = function->config->cdev;
+	unsigned long			flags;
+	int				status = 0;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+
+	if (cdev->deactivations == 0)
+		status = usb_gadget_deactivate(cdev->gadget);
+	if (status == 0)
+		cdev->deactivations++;
+
+	spin_unlock_irqrestore(&cdev->lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(usb_function_deactivate);
+
+/**
+ * usb_function_activate - allow function and gadget enumeration
+ * @function: function on which usb_function_activate() was called
+ *
+ * Reverses effect of usb_function_deactivate().  If no more functions
+ * are delaying their activation, the gadget driver will respond to
+ * host enumeration procedures.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_activate(struct usb_function *function)
+{
+	struct usb_composite_dev	*cdev = function->config->cdev;
+	unsigned long			flags;
+	int				status = 0;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+
+	if (WARN_ON(cdev->deactivations == 0))
+		status = -EINVAL;
+	else {
+		cdev->deactivations--;
+		if (cdev->deactivations == 0)
+			status = usb_gadget_activate(cdev->gadget);
+	}
+
+	spin_unlock_irqrestore(&cdev->lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(usb_function_activate);
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs.  The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors.  It
+ * will also handle any control requests targeted at that interface,
+ * particularly changing its altsetting via set_alt().  There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier.  Note that since interface
+ * identifiers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int usb_interface_id(struct usb_configuration *config,
+		struct usb_function *function)
+{
+	unsigned id = config->next_interface_id;
+
+	if (id < MAX_CONFIG_INTERFACES) {
+		config->interface[id] = function;
+		config->next_interface_id = id + 1;
+		return id;
+	}
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(usb_interface_id);
+
+static u8 encode_bMaxPower(enum usb_device_speed speed,
+		struct usb_configuration *c)
+{
+	unsigned val;
+
+	if (c->MaxPower)
+		val = c->MaxPower;
+	else
+		val = CONFIG_USB_GADGET_VBUS_DRAW;
+	if (!val)
+		return 0;
+	switch (speed) {
+	case USB_SPEED_SUPER:
+		return DIV_ROUND_UP(val, 8);
+	default:
+		return DIV_ROUND_UP(val, 2);
+	}
+}
+
+static int config_buf(struct usb_configuration *config,
+		enum usb_device_speed speed, void *buf, u8 type)
+{
+	struct usb_config_descriptor	*c = buf;
+	void				*next = buf + USB_DT_CONFIG_SIZE;
+	int				len;
+	struct usb_function		*f;
+	int				status;
+
+	len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE;
+	/* write the config descriptor */
+	c = buf;
+	c->bLength = USB_DT_CONFIG_SIZE;
+	c->bDescriptorType = type;
+	/* wTotalLength is written later */
+	c->bNumInterfaces = config->next_interface_id;
+	c->bConfigurationValue = config->bConfigurationValue;
+	c->iConfiguration = config->iConfiguration;
+	c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+	c->bMaxPower = encode_bMaxPower(speed, config);
+
+	/* There may be e.g. OTG descriptors */
+	if (config->descriptors) {
+		status = usb_descriptor_fillbuf(next, len,
+				config->descriptors);
+		if (status < 0)
+			return status;
+		len -= status;
+		next += status;
+	}
+
+	/* add each function's descriptors */
+	list_for_each_entry(f, &config->functions, list) {
+		struct usb_descriptor_header **descriptors;
+
+		descriptors = function_descriptors(f, speed);
+		if (!descriptors)
+			continue;
+		status = usb_descriptor_fillbuf(next, len,
+			(const struct usb_descriptor_header **) descriptors);
+		if (status < 0)
+			return status;
+		len -= status;
+		next += status;
+	}
+
+	len = next - buf;
+	c->wTotalLength = cpu_to_le16(len);
+	return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+	struct usb_gadget		*gadget = cdev->gadget;
+	struct usb_configuration	*c;
+	struct list_head		*pos;
+	u8				type = w_value >> 8;
+	enum usb_device_speed		speed = USB_SPEED_UNKNOWN;
+
+	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)
+			hs = !hs;
+		if (hs)
+			speed = USB_SPEED_HIGH;
+
+	}
+
+	/* This is a lookup by config *INDEX* */
+	w_value &= 0xff;
+
+	pos = &cdev->configs;
+	c = cdev->os_desc_config;
+	if (c)
+		goto check_config;
+
+	while ((pos = pos->next) !=  &cdev->configs) {
+		c = list_entry(pos, typeof(*c), list);
+
+		/* skip OS Descriptors config which is handled separately */
+		if (c == cdev->os_desc_config)
+			continue;
+
+check_config:
+		/* ignore configs that won't work at this speed */
+		switch (speed) {
+		case USB_SPEED_SUPER_PLUS:
+			if (!c->superspeed_plus)
+				continue;
+			break;
+		case USB_SPEED_SUPER:
+			if (!c->superspeed)
+				continue;
+			break;
+		case USB_SPEED_HIGH:
+			if (!c->highspeed)
+				continue;
+			break;
+		default:
+			if (!c->fullspeed)
+				continue;
+		}
+
+		if (w_value == 0)
+			return config_buf(c, speed, cdev->req->buf, type);
+		w_value--;
+	}
+	return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+	struct usb_gadget		*gadget = cdev->gadget;
+	struct usb_configuration	*c;
+	unsigned			count = 0;
+	int				hs = 0;
+	int				ss = 0;
+	int				ssp = 0;
+
+	if (gadget_is_dualspeed(gadget)) {
+		if (gadget->speed == USB_SPEED_HIGH)
+			hs = 1;
+		if (gadget->speed == USB_SPEED_SUPER)
+			ss = 1;
+		if (gadget->speed == USB_SPEED_SUPER_PLUS)
+			ssp = 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 (ssp) {
+			if (!c->superspeed_plus)
+				continue;
+		} else if (ss) {
+			if (!c->superspeed)
+				continue;
+		} else if (hs) {
+			if (!c->highspeed)
+				continue;
+		} else {
+			if (!c->fullspeed)
+				continue;
+		}
+		count++;
+	}
+	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_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 | USB_BESL_SUPPORT);
+
+	/*
+	 * The Superspeed USB Capability descriptor shall be implemented by all
+	 * SuperSpeed devices.
+	 */
+	if (gadget_is_superspeed(cdev->gadget)) {
+		struct usb_ss_cap_descriptor *ss_cap;
+
+		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_DEFAULT_U1_DEV_EXIT_LAT;
+			dcd_config_params.bU2DevExitLat =
+				cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+		}
+		ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+		ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+	}
+
+	/* The SuperSpeedPlus USB Device Capability descriptor */
+	if (gadget_is_superspeed_plus(cdev->gadget)) {
+		struct usb_ssp_cap_descriptor *ssp_cap;
+
+		ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+		bos->bNumDeviceCaps++;
+
+		/*
+		 * Report typical values.
+		 */
+
+		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
+		ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
+		ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+		ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
+		ssp_cap->bReserved = 0;
+		ssp_cap->wReserved = 0;
+
+		/* SSAC = 1 (2 attributes) */
+		ssp_cap->bmAttributes = cpu_to_le32(1);
+
+		/* Min RX/TX Lane Count = 1 */
+		ssp_cap->wFunctionalitySupport =
+			cpu_to_le16((1 << 8) | (1 << 12));
+
+		/*
+		 * bmSublinkSpeedAttr[0]:
+		 *   ST  = Symmetric, RX
+		 *   LSE =  3 (Gbps)
+		 *   LP  =  1 (SuperSpeedPlus)
+		 *   LSM = 10 (10 Gbps)
+		 */
+		ssp_cap->bmSublinkSpeedAttr[0] =
+			cpu_to_le32((3 << 4) | (1 << 14) | (0xa << 16));
+		/*
+		 * bmSublinkSpeedAttr[1] =
+		 *   ST  = Symmetric, TX
+		 *   LSE =  3 (Gbps)
+		 *   LP  =  1 (SuperSpeedPlus)
+		 *   LSM = 10 (10 Gbps)
+		 */
+		ssp_cap->bmSublinkSpeedAttr[1] =
+			cpu_to_le32((3 << 4) | (1 << 14) |
+				    (0xa << 16) | (1 << 7));
+	}
+
+	return le16_to_cpu(bos->wTotalLength);
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+	struct usb_qualifier_descriptor	*qual = cdev->req->buf;
+
+	qual->bLength = sizeof(*qual);
+	qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+	/* POLICY: same bcdUSB and device type info at both speeds */
+	qual->bcdUSB = cdev->desc.bcdUSB;
+	qual->bDeviceClass = cdev->desc.bDeviceClass;
+	qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+	qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+	/* ASSUME same EP0 fifo size at both speeds */
+	qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket;
+	qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+	qual->bRESERVED = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+	struct usb_function		*f;
+
+	DBG(cdev, "reset config\n");
+
+	list_for_each_entry(f, &cdev->config->functions, list) {
+		if (f->disable)
+			f->disable(f);
+
+		bitmap_zero(f->endpoints, 32);
+	}
+	cdev->config = NULL;
+	cdev->delayed_status = 0;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+		const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct usb_configuration *c = NULL;
+	int			result = -EINVAL;
+	unsigned		power = gadget_is_otg(gadget) ? 8 : 100;
+	int			tmp;
+
+	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 { /* Zero configuration value - need to reset the config */
+		if (cdev->config)
+			reset_config(cdev);
+		result = 0;
+	}
+
+	INFO(cdev, "%s config #%d: %s\n",
+	     usb_speed_string(gadget->speed),
+	     number, c ? c->label : "unconfigured");
+
+	if (!c)
+		goto done;
+
+	usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
+	cdev->config = c;
+
+	/* Initialize all interfaces by setting them to altsetting zero. */
+	for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+		struct usb_function	*f = c->interface[tmp];
+		struct usb_descriptor_header **descriptors;
+
+		if (!f)
+			break;
+
+		/*
+		 * Record which endpoints are used by the function. This is used
+		 * to dispatch control requests targeted at that endpoint to the
+		 * function's setup callback instead of the current
+		 * configuration's setup callback.
+		 */
+		descriptors = function_descriptors(f, gadget->speed);
+
+		for (; *descriptors; ++descriptors) {
+			struct usb_endpoint_descriptor *ep;
+			int addr;
+
+			if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+				continue;
+
+			ep = (struct usb_endpoint_descriptor *)*descriptors;
+			addr = ((ep->bEndpointAddress & 0x80) >> 3)
+			     |  (ep->bEndpointAddress & 0x0f);
+			set_bit(addr, f->endpoints);
+		}
+
+		result = f->set_alt(f, tmp, 0);
+		if (result < 0) {
+			DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
+					tmp, f->name, f, result);
+
+			reset_config(cdev);
+			goto done;
+		}
+
+		if (result == USB_GADGET_DELAYED_STATUS) {
+			DBG(cdev,
+			 "%s: interface %d (%s) requested delayed status\n",
+					__func__, tmp, f->name);
+			cdev->delayed_status++;
+			DBG(cdev, "delayed_status count %d\n",
+					cdev->delayed_status);
+		}
+	}
+
+	/* when we return, be sure our power usage is valid */
+	power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
+done:
+	usb_gadget_vbus_draw(gadget, power);
+	if (result >= 0 && cdev->delayed_status)
+		result = USB_GADGET_DELAYED_STATUS;
+	return result;
+}
+
+int usb_add_config_only(struct usb_composite_dev *cdev,
+		struct usb_configuration *config)
+{
+	struct usb_configuration *c;
+
+	if (!config->bConfigurationValue)
+		return -EINVAL;
+
+	/* Prevent duplicate configuration identifiers */
+	list_for_each_entry(c, &cdev->configs, list) {
+		if (c->bConfigurationValue == config->bConfigurationValue)
+			return -EBUSY;
+	}
+
+	config->cdev = cdev;
+	list_add_tail(&config->list, &cdev->configs);
+
+	INIT_LIST_HEAD(&config->functions);
+	config->next_interface_id = 0;
+	memset(config->interface, 0, sizeof(config->interface));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_add_config_only);
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * @bind: the configuration's bind function
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite @bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's @bind(), which
+ * is zero for success else a negative errno value.  Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int usb_add_config(struct usb_composite_dev *cdev,
+		struct usb_configuration *config,
+		int (*bind)(struct usb_configuration *))
+{
+	int				status = -EINVAL;
+
+	if (!bind)
+		goto done;
+
+	DBG(cdev, "adding config #%u '%s'/%p\n",
+			config->bConfigurationValue,
+			config->label, config);
+
+	status = usb_add_config_only(cdev, config);
+	if (status)
+		goto done;
+
+	status = bind(config);
+	if (status < 0) {
+		while (!list_empty(&config->functions)) {
+			struct usb_function		*f;
+
+			f = list_first_entry(&config->functions,
+					struct usb_function, list);
+			list_del(&f->list);
+			if (f->unbind) {
+				DBG(cdev, "unbind function '%s'/%p\n",
+					f->name, f);
+				f->unbind(config, f);
+				/* may free memory for "f" */
+			}
+		}
+		list_del(&config->list);
+		config->cdev = NULL;
+	} else {
+		unsigned	i;
+
+		DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
+			config->bConfigurationValue, config,
+			config->superspeed_plus ? " superplus" : "",
+			config->superspeed ? " super" : "",
+			config->highspeed ? " high" : "",
+			config->fullspeed
+				? (gadget_is_dualspeed(cdev->gadget)
+					? " full"
+					: " full/low")
+				: "");
+
+		for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+			struct usb_function	*f = config->interface[i];
+
+			if (!f)
+				continue;
+			DBG(cdev, "  interface %d = %s/%p\n",
+				i, f->name, f);
+		}
+	}
+
+	/* set_alt(), or next bind(), sets up ep->claimed as needed */
+	usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+	if (status)
+		DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
+				config->bConfigurationValue, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(usb_add_config);
+
+static void remove_config(struct usb_composite_dev *cdev,
+			      struct usb_configuration *config)
+{
+	while (!list_empty(&config->functions)) {
+		struct usb_function		*f;
+
+		f = list_first_entry(&config->functions,
+				struct usb_function, list);
+
+		usb_remove_function(config, f);
+	}
+	list_del(&config->list);
+	if (config->unbind) {
+		DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
+		config->unbind(config);
+			/* may free memory for "c" */
+	}
+}
+
+/**
+ * usb_remove_config() - remove a configuration from a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration
+ *
+ * Drivers must call usb_gadget_disconnect before calling this function
+ * to disconnect the device from the host and make sure the host will not
+ * try to enumerate the device while we are changing the config list.
+ */
+void usb_remove_config(struct usb_composite_dev *cdev,
+		      struct usb_configuration *config)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+
+	if (cdev->config == config)
+		reset_config(cdev);
+
+	spin_unlock_irqrestore(&cdev->lock, flags);
+
+	remove_config(cdev, config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported.  The typical case will be that
+ * only one language (probably English) is used, with i18n handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+	const struct usb_gadget_strings	*s;
+	__le16				language;
+	__le16				*tmp;
+
+	while (*sp) {
+		s = *sp;
+		language = cpu_to_le16(s->language);
+		for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+			if (*tmp == language)
+				goto repeat;
+		}
+		*tmp++ = language;
+repeat:
+		sp++;
+	}
+}
+
+static int lookup_string(
+	struct usb_gadget_strings	**sp,
+	void				*buf,
+	u16				language,
+	int				id
+)
+{
+	struct usb_gadget_strings	*s;
+	int				value;
+
+	while (*sp) {
+		s = *sp++;
+		if (s->language != language)
+			continue;
+		value = usb_gadget_get_string(s, id, buf);
+		if (value > 0)
+			return value;
+	}
+	return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+		void *buf, u16 language, int id)
+{
+	struct usb_composite_driver	*composite = cdev->driver;
+	struct usb_gadget_string_container *uc;
+	struct usb_configuration	*c;
+	struct usb_function		*f;
+	int				len;
+
+	/* Yes, not only is USB's i18n support probably more than most
+	 * folk will ever care about ... also, it's all supported here.
+	 * (Except for UTF8 support for Unicode's "Astral Planes".)
+	 */
+
+	/* 0 == report all available language codes */
+	if (id == 0) {
+		struct usb_string_descriptor	*s = buf;
+		struct usb_gadget_strings	**sp;
+
+		memset(s, 0, 256);
+		s->bDescriptorType = USB_DT_STRING;
+
+		sp = composite->strings;
+		if (sp)
+			collect_langs(sp, s->wData);
+
+		list_for_each_entry(c, &cdev->configs, list) {
+			sp = c->strings;
+			if (sp)
+				collect_langs(sp, s->wData);
+
+			list_for_each_entry(f, &c->functions, list) {
+				sp = f->strings;
+				if (sp)
+					collect_langs(sp, s->wData);
+			}
+		}
+		list_for_each_entry(uc, &cdev->gstrings, list) {
+			struct usb_gadget_strings **sp;
+
+			sp = get_containers_gs(uc);
+			collect_langs(sp, s->wData);
+		}
+
+		for (len = 0; len <= 126 && s->wData[len]; len++)
+			continue;
+		if (!len)
+			return -EINVAL;
+
+		s->bLength = 2 * (len + 1);
+		return s->bLength;
+	}
+
+	if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) {
+		struct usb_os_string *b = buf;
+		b->bLength = sizeof(*b);
+		b->bDescriptorType = USB_DT_STRING;
+		compiletime_assert(
+			sizeof(b->qwSignature) == sizeof(cdev->qw_sign),
+			"qwSignature size must be equal to qw_sign");
+		memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature));
+		b->bMS_VendorCode = cdev->b_vendor_code;
+		b->bPad = 0;
+		return sizeof(*b);
+	}
+
+	list_for_each_entry(uc, &cdev->gstrings, list) {
+		struct usb_gadget_strings **sp;
+
+		sp = get_containers_gs(uc);
+		len = lookup_string(sp, buf, language, id);
+		if (len > 0)
+			return len;
+	}
+
+	/* String IDs are device-scoped, so we look up each string
+	 * table we're told about.  These lookups are infrequent;
+	 * simpler-is-better here.
+	 */
+	if (composite->strings) {
+		len = lookup_string(composite->strings, buf, language, id);
+		if (len > 0)
+			return len;
+	}
+	list_for_each_entry(c, &cdev->configs, list) {
+		if (c->strings) {
+			len = lookup_string(c->strings, buf, language, id);
+			if (len > 0)
+				return len;
+		}
+		list_for_each_entry(f, &c->functions, list) {
+			if (!f->strings)
+				continue;
+			len = lookup_string(f->strings, buf, language, id);
+			if (len > 0)
+				return len;
+		}
+	}
+	return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs.  Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
+ */
+int usb_string_id(struct usb_composite_dev *cdev)
+{
+	if (cdev->next_string_id < 254) {
+		/* string id 0 is reserved by USB spec for list of
+		 * supported languages */
+		/* 255 reserved as well? -- mina86 */
+		cdev->next_string_id++;
+		return cdev->next_string_id;
+	}
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(usb_string_id);
+
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs.  Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+	int next = cdev->next_string_id;
+
+	for (; str->s; ++str) {
+		if (unlikely(next >= 254))
+			return -ENODEV;
+		str->id = ++next;
+	}
+
+	cdev->next_string_id = next;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_string_ids_tab);
+
+static struct usb_gadget_string_container *copy_gadget_strings(
+		struct usb_gadget_strings **sp, unsigned n_gstrings,
+		unsigned n_strings)
+{
+	struct usb_gadget_string_container *uc;
+	struct usb_gadget_strings **gs_array;
+	struct usb_gadget_strings *gs;
+	struct usb_string *s;
+	unsigned mem;
+	unsigned n_gs;
+	unsigned n_s;
+	void *stash;
+
+	mem = sizeof(*uc);
+	mem += sizeof(void *) * (n_gstrings + 1);
+	mem += sizeof(struct usb_gadget_strings) * n_gstrings;
+	mem += sizeof(struct usb_string) * (n_strings + 1) * (n_gstrings);
+	uc = kmalloc(mem, GFP_KERNEL);
+	if (!uc)
+		return ERR_PTR(-ENOMEM);
+	gs_array = get_containers_gs(uc);
+	stash = uc->stash;
+	stash += sizeof(void *) * (n_gstrings + 1);
+	for (n_gs = 0; n_gs < n_gstrings; n_gs++) {
+		struct usb_string *org_s;
+
+		gs_array[n_gs] = stash;
+		gs = gs_array[n_gs];
+		stash += sizeof(struct usb_gadget_strings);
+		gs->language = sp[n_gs]->language;
+		gs->strings = stash;
+		org_s = sp[n_gs]->strings;
+
+		for (n_s = 0; n_s < n_strings; n_s++) {
+			s = stash;
+			stash += sizeof(struct usb_string);
+			if (org_s->s)
+				s->s = org_s->s;
+			else
+				s->s = "";
+			org_s++;
+		}
+		s = stash;
+		s->s = NULL;
+		stash += sizeof(struct usb_string);
+
+	}
+	gs_array[n_gs] = NULL;
+	return uc;
+}
+
+/**
+ * usb_gstrings_attach() - attach gadget strings to a cdev and assign ids
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * and attached.
+ * @sp: an array of usb_gadget_strings to attach.
+ * @n_strings: number of entries in each usb_strings array (sp[]->strings)
+ *
+ * This function will create a deep copy of usb_gadget_strings and usb_string
+ * and attach it to the cdev. The actual string (usb_string.s) will not be
+ * copied but only a referenced will be made. The struct usb_gadget_strings
+ * array may contain multiple languages and should be NULL terminated.
+ * The ->language pointer of each struct usb_gadget_strings has to contain the
+ * same amount of entries.
+ * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
+ * usb_string entry of es-ES contains the translation of the first usb_string
+ * entry of en-US. Therefore both entries become the same id assign.
+ */
+struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
+		struct usb_gadget_strings **sp, unsigned n_strings)
+{
+	struct usb_gadget_string_container *uc;
+	struct usb_gadget_strings **n_gs;
+	unsigned n_gstrings = 0;
+	unsigned i;
+	int ret;
+
+	for (i = 0; sp[i]; i++)
+		n_gstrings++;
+
+	if (!n_gstrings)
+		return ERR_PTR(-EINVAL);
+
+	uc = copy_gadget_strings(sp, n_gstrings, n_strings);
+	if (IS_ERR(uc))
+		return ERR_CAST(uc);
+
+	n_gs = get_containers_gs(uc);
+	ret = usb_string_ids_tab(cdev, n_gs[0]->strings);
+	if (ret)
+		goto err;
+
+	for (i = 1; i < n_gstrings; i++) {
+		struct usb_string *m_s;
+		struct usb_string *s;
+		unsigned n;
+
+		m_s = n_gs[0]->strings;
+		s = n_gs[i]->strings;
+		for (n = 0; n < n_strings; n++) {
+			s->id = m_s->id;
+			s++;
+			m_s++;
+		}
+	}
+	list_add_tail(&uc->list, &cdev->gstrings);
+	return n_gs[0]->strings;
+err:
+	kfree(uc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_gstrings_attach);
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @c: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID.  This ID and next @n-1 IDs are now
+ * valid IDs.  At least provided that @n is non-zero because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs.  Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+	unsigned next = c->next_string_id;
+	if (unlikely(n > 254 || (unsigned)next + n > 254))
+		return -ENODEV;
+	c->next_string_id += n;
+	return next + 1;
+}
+EXPORT_SYMBOL_GPL(usb_string_ids_n);
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usb_composite_dev *cdev;
+
+	if (req->status || req->actual != req->length)
+		DBG((struct usb_composite_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+
+	/*
+	 * REVIST The same ep0 requests are shared with function drivers
+	 * so they don't have to maintain the same ->complete() stubs.
+	 *
+	 * Because of that, we need to check for the validity of ->context
+	 * here, even though we know we've set it to something useful.
+	 */
+	if (!req->context)
+		return;
+
+	cdev = req->context;
+
+	if (cdev->req == req)
+		cdev->setup_pending = false;
+	else if (cdev->os_desc_req == req)
+		cdev->os_desc_pending = false;
+	else
+		WARN(1, "unknown request %p\n", req);
+}
+
+static int composite_ep0_queue(struct usb_composite_dev *cdev,
+		struct usb_request *req, gfp_t gfp_flags)
+{
+	int ret;
+
+	ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);
+	if (ret == 0) {
+		if (cdev->req == req)
+			cdev->setup_pending = true;
+		else if (cdev->os_desc_req == req)
+			cdev->os_desc_pending = true;
+		else
+			WARN(1, "unknown request %p\n", req);
+	}
+
+	return ret;
+}
+
+static int count_ext_compat(struct usb_configuration *c)
+{
+	int i, res;
+
+	res = 0;
+	for (i = 0; i < c->next_interface_id; ++i) {
+		struct usb_function *f;
+		int j;
+
+		f = c->interface[i];
+		for (j = 0; j < f->os_desc_n; ++j) {
+			struct usb_os_desc *d;
+
+			if (i != f->os_desc_table[j].if_id)
+				continue;
+			d = f->os_desc_table[j].os_desc;
+			if (d && d->ext_compat_id)
+				++res;
+		}
+	}
+	BUG_ON(res > 255);
+	return res;
+}
+
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
+{
+	int i, count;
+
+	count = 16;
+	buf += 16;
+	for (i = 0; i < c->next_interface_id; ++i) {
+		struct usb_function *f;
+		int j;
+
+		f = c->interface[i];
+		for (j = 0; j < f->os_desc_n; ++j) {
+			struct usb_os_desc *d;
+
+			if (i != f->os_desc_table[j].if_id)
+				continue;
+			d = f->os_desc_table[j].os_desc;
+			if (d && d->ext_compat_id) {
+				*buf++ = i;
+				*buf++ = 0x01;
+				memcpy(buf, d->ext_compat_id, 16);
+				buf += 22;
+			} else {
+				++buf;
+				*buf = 0x01;
+				buf += 23;
+			}
+			count += 24;
+			if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+				return count;
+		}
+	}
+
+	return count;
+}
+
+static int count_ext_prop(struct usb_configuration *c, int interface)
+{
+	struct usb_function *f;
+	int j;
+
+	f = c->interface[interface];
+	for (j = 0; j < f->os_desc_n; ++j) {
+		struct usb_os_desc *d;
+
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d && d->ext_compat_id)
+			return d->ext_prop_count;
+	}
+	return 0;
+}
+
+static int len_ext_prop(struct usb_configuration *c, int interface)
+{
+	struct usb_function *f;
+	struct usb_os_desc *d;
+	int j, res;
+
+	res = 10; /* header length */
+	f = c->interface[interface];
+	for (j = 0; j < f->os_desc_n; ++j) {
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d)
+			return min(res + d->ext_prop_len, 4096);
+	}
+	return res;
+}
+
+static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
+{
+	struct usb_function *f;
+	struct usb_os_desc *d;
+	struct usb_os_desc_ext_prop *ext_prop;
+	int j, count, n, ret;
+
+	f = c->interface[interface];
+	count = 10; /* header length */
+	buf += 10;
+	for (j = 0; j < f->os_desc_n; ++j) {
+		if (interface != f->os_desc_table[j].if_id)
+			continue;
+		d = f->os_desc_table[j].os_desc;
+		if (d)
+			list_for_each_entry(ext_prop, &d->ext_prop, entry) {
+				n = ext_prop->data_len +
+					ext_prop->name_len + 14;
+				if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+					return count;
+				usb_ext_prop_put_size(buf, n);
+				usb_ext_prop_put_type(buf, ext_prop->type);
+				ret = usb_ext_prop_put_name(buf, ext_prop->name,
+							    ext_prop->name_len);
+				if (ret < 0)
+					return ret;
+				switch (ext_prop->type) {
+				case USB_EXT_PROP_UNICODE:
+				case USB_EXT_PROP_UNICODE_ENV:
+				case USB_EXT_PROP_UNICODE_LINK:
+					usb_ext_prop_put_unicode(buf, ret,
+							 ext_prop->data,
+							 ext_prop->data_len);
+					break;
+				case USB_EXT_PROP_BINARY:
+					usb_ext_prop_put_binary(buf, ret,
+							ext_prop->data,
+							ext_prop->data_len);
+					break;
+				case USB_EXT_PROP_LE32:
+					/* not implemented */
+				case USB_EXT_PROP_BE32:
+					/* not implemented */
+				default:
+					return -EINVAL;
+				}
+				buf += n;
+				count += n;
+			}
+	}
+
+	return count;
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config and function specific setup.
+ */
+int
+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);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+	struct usb_function		*f = NULL;
+	u8				endp;
+
+	/* partial re-init of the response message; the function or the
+	 * gadget might need to intercept e.g. a control-OUT completion
+	 * when we delegate to it.
+	 */
+	req->zero = 0;
+	req->context = cdev;
+	req->complete = composite_setup_complete;
+	req->length = 0;
+	gadget->ep0->driver_data = cdev;
+
+	/*
+	 * Don't let non-standard requests match any of the cases below
+	 * by accident.
+	 */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+		goto unknown;
+
+	switch (ctrl->bRequest) {
+
+	/* we handle all standard USB descriptors */
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			goto unknown;
+		switch (w_value >> 8) {
+
+		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(0x0320);
+					cdev->desc.bMaxPacketSize0 = 9;
+				} else {
+					cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+				}
+			} else {
+				if (gadget->lpm_capable)
+					cdev->desc.bcdUSB = cpu_to_le16(0x0201);
+				else
+					cdev->desc.bcdUSB = cpu_to_le16(0x0200);
+			}
+
+			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) ||
+			    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) ||
+			    gadget->speed >= USB_SPEED_SUPER)
+				break;
+			/* FALLTHROUGH */
+		case USB_DT_CONFIG:
+			value = config_desc(cdev, w_value);
+			if (value >= 0)
+				value = min(w_length, (u16) value);
+			break;
+		case USB_DT_STRING:
+			value = get_string(cdev, req->buf,
+					w_index, w_value & 0xff);
+			if (value >= 0)
+				value = min(w_length, (u16) value);
+			break;
+		case USB_DT_BOS:
+			if (gadget_is_superspeed(gadget) ||
+			    gadget->lpm_capable) {
+				value = bos_desc(cdev);
+				value = min(w_length, (u16) value);
+			}
+			break;
+		case USB_DT_OTG:
+			if (gadget_is_otg(gadget)) {
+				struct usb_configuration *config;
+				int otg_desc_len = 0;
+
+				if (cdev->config)
+					config = cdev->config;
+				else
+					config = list_first_entry(
+							&cdev->configs,
+						struct usb_configuration, list);
+				if (!config)
+					goto done;
+
+				if (gadget->otg_caps &&
+					(gadget->otg_caps->otg_rev >= 0x0200))
+					otg_desc_len += sizeof(
+						struct usb_otg20_descriptor);
+				else
+					otg_desc_len += sizeof(
+						struct usb_otg_descriptor);
+
+				value = min_t(int, w_length, otg_desc_len);
+				memcpy(req->buf, config->descriptors[0], value);
+			}
+			break;
+		}
+		break;
+
+	/* any number of configs can work */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			goto unknown;
+		if (gadget_is_otg(gadget)) {
+			if (gadget->a_hnp_support)
+				DBG(cdev, "HNP available\n");
+			else if (gadget->a_alt_hnp_support)
+				DBG(cdev, "HNP on another port\n");
+			else
+				VDBG(cdev, "HNP inactive\n");
+		}
+		spin_lock(&cdev->lock);
+		value = set_config(cdev, ctrl, w_value);
+		spin_unlock(&cdev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			goto unknown;
+		if (cdev->config)
+			*(u8 *)req->buf = cdev->config->bConfigurationValue;
+		else
+			*(u8 *)req->buf = 0;
+		value = min(w_length, (u16) 1);
+		break;
+
+	/* function drivers must handle get/set altsetting */
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+			goto unknown;
+		if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+			break;
+		f = cdev->config->interface[intf];
+		if (!f)
+			break;
+
+		/*
+		 * If there's no get_alt() method, we know only altsetting zero
+		 * works. There is no need to check if set_alt() is not NULL
+		 * as we check this in usb_add_function().
+		 */
+		if (w_value && !f->get_alt)
+			break;
+
+		spin_lock(&cdev->lock);
+		value = f->set_alt(f, w_index, w_value);
+		if (value == USB_GADGET_DELAYED_STATUS) {
+			DBG(cdev,
+			 "%s: interface %d (%s) requested delayed status\n",
+					__func__, intf, f->name);
+			cdev->delayed_status++;
+			DBG(cdev, "delayed_status count %d\n",
+					cdev->delayed_status);
+		}
+		spin_unlock(&cdev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+			goto unknown;
+		if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+			break;
+		f = cdev->config->interface[intf];
+		if (!f)
+			break;
+		/* lots of interfaces only need altsetting zero... */
+		value = f->get_alt ? f->get_alt(f, w_index) : 0;
+		if (value < 0)
+			break;
+		*((u8 *)req->buf) = value;
+		value = min(w_length, (u16) 1);
+		break;
+	case USB_REQ_GET_STATUS:
+		if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
+						(w_index == OTG_STS_SELECTOR)) {
+			if (ctrl->bRequestType != (USB_DIR_IN |
+							USB_RECIP_DEVICE))
+				goto unknown;
+			*((u8 *)req->buf) = gadget->host_request_flag;
+			value = 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
+		 */
+		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:
+		/*
+		 * OS descriptors handling
+		 */
+		if (cdev->use_os_string && cdev->os_desc_config &&
+		    (ctrl->bRequestType & USB_TYPE_VENDOR) &&
+		    ctrl->bRequest == cdev->b_vendor_code) {
+			struct usb_configuration	*os_desc_cfg;
+			u8				*buf;
+			int				interface;
+			int				count = 0;
+
+			req = cdev->os_desc_req;
+			req->context = cdev;
+			req->complete = composite_setup_complete;
+			buf = req->buf;
+			os_desc_cfg = cdev->os_desc_config;
+			w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
+			memset(buf, 0, w_length);
+			buf[5] = 0x01;
+			switch (ctrl->bRequestType & USB_RECIP_MASK) {
+			case USB_RECIP_DEVICE:
+				if (w_index != 0x4 || (w_value >> 8))
+					break;
+				buf[6] = w_index;
+				/* Number of ext compat interfaces */
+				count = count_ext_compat(os_desc_cfg);
+				buf[8] = count;
+				count *= 24; /* 24 B/ext compat desc */
+				count += 16; /* header */
+				put_unaligned_le32(count, buf);
+				value = w_length;
+				if (w_length > 0x10) {
+					value = fill_ext_compat(os_desc_cfg, buf);
+					value = min_t(u16, w_length, value);
+				}
+				break;
+			case USB_RECIP_INTERFACE:
+				if (w_index != 0x5 || (w_value >> 8))
+					break;
+				interface = w_value & 0xFF;
+				buf[6] = w_index;
+				count = count_ext_prop(os_desc_cfg,
+					interface);
+				put_unaligned_le16(count, buf + 8);
+				count = len_ext_prop(os_desc_cfg,
+					interface);
+				put_unaligned_le32(count, buf);
+				value = w_length;
+				if (w_length > 0x0A) {
+					value = fill_ext_prop(os_desc_cfg,
+							      interface, buf);
+					if (value >= 0)
+						value = min_t(u16, w_length, value);
+				}
+				break;
+			}
+
+			goto check_value;
+		}
+
+		VDBG(cdev,
+			"non-core control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+
+		/* functions always handle their interfaces and endpoints...
+		 * punt other recipients (other, WUSB, ...) to the current
+		 * configuration code.
+		 */
+		if (cdev->config) {
+			list_for_each_entry(f, &cdev->config->functions, list)
+				if (f->req_match &&
+				    f->req_match(f, ctrl, false))
+					goto try_fun_setup;
+		} else {
+			struct usb_configuration *c;
+			list_for_each_entry(c, &cdev->configs, list)
+				list_for_each_entry(f, &c->functions, list)
+					if (f->req_match &&
+					    f->req_match(f, ctrl, true))
+						goto try_fun_setup;
+		}
+		f = NULL;
+
+		switch (ctrl->bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_INTERFACE:
+			if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+				break;
+			f = cdev->config->interface[intf];
+			break;
+
+		case USB_RECIP_ENDPOINT:
+			if (!cdev->config)
+				break;
+			endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+			list_for_each_entry(f, &cdev->config->functions, list) {
+				if (test_bit(endp, f->endpoints))
+					break;
+			}
+			if (&f->list == &cdev->config->functions)
+				f = NULL;
+			break;
+		}
+try_fun_setup:
+		if (f && f->setup)
+			value = f->setup(f, ctrl);
+		else {
+			struct usb_configuration	*c;
+
+			c = cdev->config;
+			if (!c)
+				goto done;
+
+			/* try current config's setup */
+			if (c->setup) {
+				value = c->setup(c, ctrl);
+				goto done;
+			}
+
+			/* try the only function in the current config */
+			if (!list_is_singular(&c->functions))
+				goto done;
+			f = list_first_entry(&c->functions, struct usb_function,
+					     list);
+			if (f->setup)
+				value = f->setup(f, ctrl);
+		}
+
+		goto done;
+	}
+
+check_value:
+	/* respond with data transfer before status phase? */
+	if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
+		req->length = value;
+		req->context = cdev;
+		req->zero = value < w_length;
+		value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG(cdev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			composite_setup_complete(gadget->ep0, req);
+		}
+	} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
+		WARN(cdev,
+			"%s: Delayed status not supported for w_length != 0",
+			__func__);
+	}
+
+done:
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+void composite_disconnect(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	unsigned long			flags;
+
+	/* REVISIT:  should we have config and device level
+	 * disconnect callbacks?
+	 */
+	spin_lock_irqsave(&cdev->lock, flags);
+	if (cdev->config)
+		reset_config(cdev);
+	if (cdev->driver->disconnect)
+		cdev->driver->disconnect(cdev);
+	spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static ssize_t suspended_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+	struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+	return sprintf(buf, "%d\n", cdev->suspended);
+}
+static DEVICE_ATTR_RO(suspended);
+
+static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_gadget_strings	*gstr = cdev->driver->strings[0];
+	struct usb_string		*dev_str = gstr->strings;
+
+	/* composite_disconnect() must already have been called
+	 * by the underlying peripheral controller driver!
+	 * so there's no i/o concurrency that could affect the
+	 * state protected by cdev->lock.
+	 */
+	WARN_ON(cdev->config);
+
+	while (!list_empty(&cdev->configs)) {
+		struct usb_configuration	*c;
+		c = list_first_entry(&cdev->configs,
+				struct usb_configuration, list);
+		remove_config(cdev, c);
+	}
+	if (cdev->driver->unbind && unbind_driver)
+		cdev->driver->unbind(cdev);
+
+	composite_dev_cleanup(cdev);
+
+	if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
+		dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";
+
+	kfree(cdev->def_manufacturer);
+	kfree(cdev);
+	set_gadget_data(gadget, NULL);
+}
+
+static void composite_unbind(struct usb_gadget *gadget)
+{
+	__composite_unbind(gadget, true);
+}
+
+static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
+		const struct usb_device_descriptor *old)
+{
+	__le16 idVendor;
+	__le16 idProduct;
+	__le16 bcdDevice;
+	u8 iSerialNumber;
+	u8 iManufacturer;
+	u8 iProduct;
+
+	/*
+	 * these variables may have been set in
+	 * usb_composite_overwrite_options()
+	 */
+	idVendor = new->idVendor;
+	idProduct = new->idProduct;
+	bcdDevice = new->bcdDevice;
+	iSerialNumber = new->iSerialNumber;
+	iManufacturer = new->iManufacturer;
+	iProduct = new->iProduct;
+
+	*new = *old;
+	if (idVendor)
+		new->idVendor = idVendor;
+	if (idProduct)
+		new->idProduct = idProduct;
+	if (bcdDevice)
+		new->bcdDevice = bcdDevice;
+	else
+		new->bcdDevice = cpu_to_le16(get_default_bcdDevice());
+	if (iSerialNumber)
+		new->iSerialNumber = iSerialNumber;
+	if (iManufacturer)
+		new->iManufacturer = iManufacturer;
+	if (iProduct)
+		new->iProduct = iProduct;
+}
+
+int composite_dev_prepare(struct usb_composite_driver *composite,
+		struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+	int ret = -ENOMEM;
+
+	/* preallocate control response and buffer */
+	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!cdev->req)
+		return -ENOMEM;
+
+	cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
+	if (!cdev->req->buf)
+		goto fail;
+
+	ret = device_create_file(&gadget->dev, &dev_attr_suspended);
+	if (ret)
+		goto fail_dev;
+
+	cdev->req->complete = composite_setup_complete;
+	cdev->req->context = cdev;
+	gadget->ep0->driver_data = cdev;
+
+	cdev->driver = composite;
+
+	/*
+	 * As per USB compliance update, a device that is actively drawing
+	 * more than 100mA from USB must report itself as bus-powered in
+	 * the GetStatus(DEVICE) call.
+	 */
+	if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
+		usb_gadget_set_selfpowered(gadget);
+
+	/* interface and string IDs start at zero via kzalloc.
+	 * we force endpoints to start unassigned; few controller
+	 * drivers will zero ep->driver_data.
+	 */
+	usb_ep_autoconfig_reset(gadget);
+	return 0;
+fail_dev:
+	kfree(cdev->req->buf);
+fail:
+	usb_ep_free_request(gadget->ep0, cdev->req);
+	cdev->req = NULL;
+	return ret;
+}
+
+int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+				  struct usb_ep *ep0)
+{
+	int ret = 0;
+
+	cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL);
+	if (!cdev->os_desc_req) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+					 GFP_KERNEL);
+	if (!cdev->os_desc_req->buf) {
+		ret = -ENOMEM;
+		usb_ep_free_request(ep0, cdev->os_desc_req);
+		goto end;
+	}
+	cdev->os_desc_req->context = cdev;
+	cdev->os_desc_req->complete = composite_setup_complete;
+end:
+	return ret;
+}
+
+void composite_dev_cleanup(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget_string_container *uc, *tmp;
+	struct usb_ep			   *ep, *tmp_ep;
+
+	list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
+		list_del(&uc->list);
+		kfree(uc);
+	}
+	if (cdev->os_desc_req) {
+		if (cdev->os_desc_pending)
+			usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
+
+		kfree(cdev->os_desc_req->buf);
+		usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
+	}
+	if (cdev->req) {
+		if (cdev->setup_pending)
+			usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
+
+		kfree(cdev->req->buf);
+		usb_ep_free_request(cdev->gadget->ep0, cdev->req);
+	}
+	cdev->next_string_id = 0;
+	device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
+
+	/*
+	 * Some UDC backends have a dynamic EP allocation scheme.
+	 *
+	 * In that case, the dispose() callback is used to notify the
+	 * backend that the EPs are no longer in use.
+	 *
+	 * Note: The UDC backend can remove the EP from the ep_list as
+	 *	 a result, so we need to use the _safe list iterator.
+	 */
+	list_for_each_entry_safe(ep, tmp_ep,
+				 &cdev->gadget->ep_list, ep_list) {
+		if (ep->ops->dispose)
+			ep->ops->dispose(ep);
+	}
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+		struct usb_gadget_driver *gdriver)
+{
+	struct usb_composite_dev	*cdev;
+	struct usb_composite_driver	*composite = to_cdriver(gdriver);
+	int				status = -ENOMEM;
+
+	cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+	if (!cdev)
+		return status;
+
+	spin_lock_init(&cdev->lock);
+	cdev->gadget = gadget;
+	set_gadget_data(gadget, cdev);
+	INIT_LIST_HEAD(&cdev->configs);
+	INIT_LIST_HEAD(&cdev->gstrings);
+
+	status = composite_dev_prepare(composite, cdev);
+	if (status)
+		goto fail;
+
+	/* composite gadget needs to assign strings for whole device (like
+	 * serial number), register function drivers, potentially update
+	 * power state and consumption, etc
+	 */
+	status = composite->bind(cdev);
+	if (status < 0)
+		goto fail;
+
+	if (cdev->use_os_string) {
+		status = composite_os_desc_req_prepare(cdev, gadget->ep0);
+		if (status)
+			goto fail;
+	}
+
+	update_unchanged_dev_desc(&cdev->desc, composite->dev);
+
+	/* has userspace failed to provide a serial number? */
+	if (composite->needs_serial && !cdev->desc.iSerialNumber)
+		WARNING(cdev, "userspace failed to provide iSerialNumber\n");
+
+	INFO(cdev, "%s ready\n", composite->name);
+	return 0;
+
+fail:
+	__composite_unbind(gadget, false);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+void composite_suspend(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_function		*f;
+
+	/* REVISIT:  should we have config level
+	 * suspend/resume callbacks?
+	 */
+	DBG(cdev, "suspend\n");
+	if (cdev->config) {
+		list_for_each_entry(f, &cdev->config->functions, list) {
+			if (f->suspend)
+				f->suspend(f);
+		}
+	}
+	if (cdev->driver->suspend)
+		cdev->driver->suspend(cdev);
+
+	cdev->suspended = 1;
+
+	usb_gadget_vbus_draw(gadget, 2);
+}
+
+void composite_resume(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_function		*f;
+	u16				maxpower;
+
+	/* REVISIT:  should we have config level
+	 * suspend/resume callbacks?
+	 */
+	DBG(cdev, "resume\n");
+	if (cdev->driver->resume)
+		cdev->driver->resume(cdev);
+	if (cdev->config) {
+		list_for_each_entry(f, &cdev->config->functions, list) {
+			if (f->resume)
+				f->resume(f);
+		}
+
+		maxpower = cdev->config->MaxPower;
+
+		usb_gadget_vbus_draw(gadget, maxpower ?
+			maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
+	}
+
+	cdev->suspended = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_gadget_driver composite_driver_template = {
+	.bind		= composite_bind,
+	.unbind		= composite_unbind,
+
+	.setup		= composite_setup,
+	.reset		= composite_disconnect,
+	.disconnect	= composite_disconnect,
+
+	.suspend	= composite_suspend,
+	.resume		= composite_resume,
+
+	.driver	= {
+		.owner		= THIS_MODULE,
+	},
+};
+
+/**
+ * usb_composite_probe() - register a composite driver
+ * @driver: the driver to register
+ *
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework.  The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding.  That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int usb_composite_probe(struct usb_composite_driver *driver)
+{
+	struct usb_gadget_driver *gadget_driver;
+
+	if (!driver || !driver->dev || !driver->bind)
+		return -EINVAL;
+
+	if (!driver->name)
+		driver->name = "composite";
+
+	driver->gadget_driver = composite_driver_template;
+	gadget_driver = &driver->gadget_driver;
+
+	gadget_driver->function =  (char *) driver->name;
+	gadget_driver->driver.name = driver->name;
+	gadget_driver->max_speed = driver->max_speed;
+
+	return usb_gadget_probe_driver(gadget_driver);
+}
+EXPORT_SYMBOL_GPL(usb_composite_probe);
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void usb_composite_unregister(struct usb_composite_driver *driver)
+{
+	usb_gadget_unregister_driver(&driver->gadget_driver);
+}
+EXPORT_SYMBOL_GPL(usb_composite_unregister);
+
+/**
+ * usb_composite_setup_continue() - Continue with the control transfer
+ * @cdev: the composite device who's control transfer was kept waiting
+ *
+ * This function must be called by the USB function driver to continue
+ * with the control transfer's data/status stage in case it had requested to
+ * delay the data/status stages. A USB function's setup handler (e.g. set_alt())
+ * can request the composite framework to delay the setup request's data/status
+ * stages by returning USB_GADGET_DELAYED_STATUS.
+ */
+void usb_composite_setup_continue(struct usb_composite_dev *cdev)
+{
+	int			value;
+	struct usb_request	*req = cdev->req;
+	unsigned long		flags;
+
+	DBG(cdev, "%s\n", __func__);
+	spin_lock_irqsave(&cdev->lock, flags);
+
+	if (cdev->delayed_status == 0) {
+		WARN(cdev, "%s: Unexpected call\n", __func__);
+
+	} else if (--cdev->delayed_status == 0) {
+		DBG(cdev, "%s: Completing delayed status\n", __func__);
+		req->length = 0;
+		req->context = cdev;
+		value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG(cdev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			composite_setup_complete(cdev->gadget->ep0, req);
+		}
+	}
+
+	spin_unlock_irqrestore(&cdev->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
+
+static char *composite_default_mfr(struct usb_gadget *gadget)
+{
+	return kasprintf(GFP_KERNEL, "%s %s with %s", init_utsname()->sysname,
+			 init_utsname()->release, gadget->name);
+}
+
+void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
+		struct usb_composite_overwrite *covr)
+{
+	struct usb_device_descriptor	*desc = &cdev->desc;
+	struct usb_gadget_strings	*gstr = cdev->driver->strings[0];
+	struct usb_string		*dev_str = gstr->strings;
+
+	if (covr->idVendor)
+		desc->idVendor = cpu_to_le16(covr->idVendor);
+
+	if (covr->idProduct)
+		desc->idProduct = cpu_to_le16(covr->idProduct);
+
+	if (covr->bcdDevice)
+		desc->bcdDevice = cpu_to_le16(covr->bcdDevice);
+
+	if (covr->serial_number) {
+		desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id;
+		dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number;
+	}
+	if (covr->manufacturer) {
+		desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+		dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer;
+
+	} else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) {
+		desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+		cdev->def_manufacturer = composite_default_mfr(cdev->gadget);
+		dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer;
+	}
+
+	if (covr->product) {
+		desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id;
+		dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product;
+	}
+}
+EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
new file mode 100644
index 0000000..2d11535
--- /dev/null
+++ b/drivers/usb/gadget/config.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/otg.h>
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied.  Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+		const struct usb_descriptor_header **src)
+{
+	u8	*dest = buf;
+
+	if (!src)
+		return -EINVAL;
+
+	/* fill buffer from src[] until null descriptor ptr */
+	for (; NULL != *src; src++) {
+		unsigned		len = (*src)->bLength;
+
+		if (len > buflen)
+			return -EINVAL;
+		memcpy(dest, *src, len);
+		buflen -= len;
+		dest += len;
+	}
+	return dest - (u8 *)buf;
+}
+EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ *	as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ *	endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer.  If this is not big enough to hold the
+ *	entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration.  It returns the buffer length or a negative
+ * status code.  The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR.  They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+	const struct usb_config_descriptor	*config,
+	void					*buf,
+	unsigned				length,
+	const struct usb_descriptor_header	**desc
+)
+{
+	struct usb_config_descriptor		*cp = buf;
+	int					len;
+
+	/* config descriptor first */
+	if (length < USB_DT_CONFIG_SIZE || !desc)
+		return -EINVAL;
+	*cp = *config;
+
+	/* then interface/endpoint/class/vendor/... */
+	len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
+			length - USB_DT_CONFIG_SIZE, desc);
+	if (len < 0)
+		return len;
+	len += USB_DT_CONFIG_SIZE;
+	if (len > 0xffff)
+		return -EINVAL;
+
+	/* patch up the config descriptor */
+	cp->bLength = USB_DT_CONFIG_SIZE;
+	cp->bDescriptorType = USB_DT_CONFIG;
+	cp->wTotalLength = cpu_to_le16(len);
+	cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+	return len;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
+
+/**
+ * usb_copy_descriptors - copy a vector of USB descriptors
+ * @src: null-terminated vector to copy
+ * Context: initialization code, which may sleep
+ *
+ * This makes a copy of a vector of USB descriptors.  Its primary use
+ * is to support usb_function objects which can have multiple copies,
+ * each needing different descriptors.  Functions may have static
+ * tables of descriptors, which are used as templates and customized
+ * with identifiers (for interfaces, strings, endpoints, and more)
+ * as needed by a given function instance.
+ */
+struct usb_descriptor_header **
+usb_copy_descriptors(struct usb_descriptor_header **src)
+{
+	struct usb_descriptor_header **tmp;
+	unsigned bytes;
+	unsigned n_desc;
+	void *mem;
+	struct usb_descriptor_header **ret;
+
+	/* count descriptors and their sizes; then add vector size */
+	for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
+		bytes += (*tmp)->bLength;
+	bytes += (n_desc + 1) * sizeof(*tmp);
+
+	mem = kmalloc(bytes, GFP_KERNEL);
+	if (!mem)
+		return NULL;
+
+	/* fill in pointers starting at "tmp",
+	 * to descriptors copied starting at "mem";
+	 * and return "ret"
+	 */
+	tmp = mem;
+	ret = mem;
+	mem += (n_desc + 1) * sizeof(*tmp);
+	while (*src) {
+		memcpy(mem, *src, (*src)->bLength);
+		*tmp = mem;
+		tmp++;
+		mem += (*src)->bLength;
+		src++;
+	}
+	*tmp = NULL;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_copy_descriptors);
+
+int usb_assign_descriptors(struct usb_function *f,
+		struct usb_descriptor_header **fs,
+		struct usb_descriptor_header **hs,
+		struct usb_descriptor_header **ss,
+		struct usb_descriptor_header **ssp)
+{
+	struct usb_gadget *g = f->config->cdev->gadget;
+
+	if (fs) {
+		f->fs_descriptors = usb_copy_descriptors(fs);
+		if (!f->fs_descriptors)
+			goto err;
+	}
+	if (hs && gadget_is_dualspeed(g)) {
+		f->hs_descriptors = usb_copy_descriptors(hs);
+		if (!f->hs_descriptors)
+			goto err;
+	}
+	if (ss && gadget_is_superspeed(g)) {
+		f->ss_descriptors = usb_copy_descriptors(ss);
+		if (!f->ss_descriptors)
+			goto err;
+	}
+	if (ssp && gadget_is_superspeed_plus(g)) {
+		f->ssp_descriptors = usb_copy_descriptors(ssp);
+		if (!f->ssp_descriptors)
+			goto err;
+	}
+	return 0;
+err:
+	usb_free_all_descriptors(f);
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(usb_assign_descriptors);
+
+void usb_free_all_descriptors(struct usb_function *f)
+{
+	usb_free_descriptors(f->fs_descriptors);
+	usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->ss_descriptors);
+	usb_free_descriptors(f->ssp_descriptors);
+}
+EXPORT_SYMBOL_GPL(usb_free_all_descriptors);
+
+struct usb_descriptor_header *usb_otg_descriptor_alloc(
+				struct usb_gadget *gadget)
+{
+	struct usb_descriptor_header *otg_desc;
+	unsigned length = 0;
+
+	if (gadget->otg_caps && (gadget->otg_caps->otg_rev >= 0x0200))
+		length = sizeof(struct usb_otg20_descriptor);
+	else
+		length = sizeof(struct usb_otg_descriptor);
+
+	otg_desc = kzalloc(length, GFP_KERNEL);
+	return otg_desc;
+}
+EXPORT_SYMBOL_GPL(usb_otg_descriptor_alloc);
+
+int usb_otg_descriptor_init(struct usb_gadget *gadget,
+		struct usb_descriptor_header *otg_desc)
+{
+	struct usb_otg_descriptor *otg1x_desc;
+	struct usb_otg20_descriptor *otg20_desc;
+	struct usb_otg_caps *otg_caps = gadget->otg_caps;
+	u8 otg_attributes = 0;
+
+	if (!otg_desc)
+		return -EINVAL;
+
+	if (otg_caps && otg_caps->otg_rev) {
+		if (otg_caps->hnp_support)
+			otg_attributes |= USB_OTG_HNP;
+		if (otg_caps->srp_support)
+			otg_attributes |= USB_OTG_SRP;
+		if (otg_caps->adp_support && (otg_caps->otg_rev >= 0x0200))
+			otg_attributes |= USB_OTG_ADP;
+	} else {
+		otg_attributes = USB_OTG_SRP | USB_OTG_HNP;
+	}
+
+	if (otg_caps && (otg_caps->otg_rev >= 0x0200)) {
+		otg20_desc = (struct usb_otg20_descriptor *)otg_desc;
+		otg20_desc->bLength = sizeof(struct usb_otg20_descriptor);
+		otg20_desc->bDescriptorType = USB_DT_OTG;
+		otg20_desc->bmAttributes = otg_attributes;
+		otg20_desc->bcdOTG = cpu_to_le16(otg_caps->otg_rev);
+	} else {
+		otg1x_desc = (struct usb_otg_descriptor *)otg_desc;
+		otg1x_desc->bLength = sizeof(struct usb_otg_descriptor);
+		otg1x_desc->bDescriptorType = USB_DT_OTG;
+		otg1x_desc->bmAttributes = otg_attributes;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_descriptor_init);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
new file mode 100644
index 0000000..0251299
--- /dev/null
+++ b/drivers/usb/gadget/configfs.c
@@ -0,0 +1,1520 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/nls.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget_configfs.h>
+#include "configfs.h"
+#include "u_f.h"
+#include "u_os_desc.h"
+
+int check_user_usb_string(const char *name,
+		struct usb_gadget_strings *stringtab_dev)
+{
+	unsigned primary_lang;
+	unsigned sub_lang;
+	u16 num;
+	int ret;
+
+	ret = kstrtou16(name, 0, &num);
+	if (ret)
+		return ret;
+
+	primary_lang = num & 0x3ff;
+	sub_lang = num >> 10;
+
+	/* simple sanity check for valid langid */
+	switch (primary_lang) {
+	case 0:
+	case 0x62 ... 0xfe:
+	case 0x100 ... 0x3ff:
+		return -EINVAL;
+	}
+	if (!sub_lang)
+		return -EINVAL;
+
+	stringtab_dev->language = num;
+	return 0;
+}
+
+#define MAX_NAME_LEN	40
+#define MAX_USB_STRING_LANGS 2
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+struct gadget_info {
+	struct config_group group;
+	struct config_group functions_group;
+	struct config_group configs_group;
+	struct config_group strings_group;
+	struct config_group os_desc_group;
+
+	struct mutex lock;
+	struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1];
+	struct list_head string_list;
+	struct list_head available_func;
+
+	struct usb_composite_driver composite;
+	struct usb_composite_dev cdev;
+	bool use_os_desc;
+	char b_vendor_code;
+	char qw_sign[OS_STRING_QW_SIGN_LEN];
+};
+
+static inline struct gadget_info *to_gadget_info(struct config_item *item)
+{
+	 return container_of(to_config_group(item), struct gadget_info, group);
+}
+
+struct config_usb_cfg {
+	struct config_group group;
+	struct config_group strings_group;
+	struct list_head string_list;
+	struct usb_configuration c;
+	struct list_head func_list;
+	struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1];
+};
+
+static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct config_usb_cfg,
+			group);
+}
+
+struct gadget_strings {
+	struct usb_gadget_strings stringtab_dev;
+	struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX];
+	char *manufacturer;
+	char *product;
+	char *serialnumber;
+
+	struct config_group group;
+	struct list_head list;
+};
+
+struct os_desc {
+	struct config_group group;
+};
+
+struct gadget_config_name {
+	struct usb_gadget_strings stringtab_dev;
+	struct usb_string strings;
+	char *configuration;
+
+	struct config_group group;
+	struct list_head list;
+};
+
+static int usb_string_copy(const char *s, char **s_copy)
+{
+	int ret;
+	char *str;
+	char *copy = *s_copy;
+	ret = strlen(s);
+	if (ret > 126)
+		return -EOVERFLOW;
+
+	str = kstrdup(s, GFP_KERNEL);
+	if (!str)
+		return -ENOMEM;
+	if (str[ret - 1] == '\n')
+		str[ret - 1] = '\0';
+	kfree(copy);
+	*s_copy = str;
+	return 0;
+}
+
+#define GI_DEVICE_DESC_SIMPLE_R_u8(__name)	\
+static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \
+			char *page)	\
+{	\
+	return sprintf(page, "0x%02x\n", \
+		to_gadget_info(item)->cdev.desc.__name); \
+}
+
+#define GI_DEVICE_DESC_SIMPLE_R_u16(__name)	\
+static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \
+			char *page)	\
+{	\
+	return sprintf(page, "0x%04x\n", \
+		le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \
+}
+
+
+#define GI_DEVICE_DESC_SIMPLE_W_u8(_name)		\
+static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \
+		const char *page, size_t len)		\
+{							\
+	u8 val;						\
+	int ret;					\
+	ret = kstrtou8(page, 0, &val);			\
+	if (ret)					\
+		return ret;				\
+	to_gadget_info(item)->cdev.desc._name = val;	\
+	return len;					\
+}
+
+#define GI_DEVICE_DESC_SIMPLE_W_u16(_name)	\
+static ssize_t gadget_dev_desc_##_name##_store(struct config_item *item, \
+		const char *page, size_t len)		\
+{							\
+	u16 val;					\
+	int ret;					\
+	ret = kstrtou16(page, 0, &val);			\
+	if (ret)					\
+		return ret;				\
+	to_gadget_info(item)->cdev.desc._name = cpu_to_le16p(&val);	\
+	return len;					\
+}
+
+#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type)	\
+	GI_DEVICE_DESC_SIMPLE_R_##_type(_name)	\
+	GI_DEVICE_DESC_SIMPLE_W_##_type(_name)
+
+GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB);
+GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8);
+GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8);
+GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8);
+GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8);
+GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16);
+GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16);
+GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice);
+
+static ssize_t is_valid_bcd(u16 bcd_val)
+{
+	if ((bcd_val & 0xf) > 9)
+		return -EINVAL;
+	if (((bcd_val >> 4) & 0xf) > 9)
+		return -EINVAL;
+	if (((bcd_val >> 8) & 0xf) > 9)
+		return -EINVAL;
+	if (((bcd_val >> 12) & 0xf) > 9)
+		return -EINVAL;
+	return 0;
+}
+
+static ssize_t gadget_dev_desc_bcdDevice_store(struct config_item *item,
+		const char *page, size_t len)
+{
+	u16 bcdDevice;
+	int ret;
+
+	ret = kstrtou16(page, 0, &bcdDevice);
+	if (ret)
+		return ret;
+	ret = is_valid_bcd(bcdDevice);
+	if (ret)
+		return ret;
+
+	to_gadget_info(item)->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice);
+	return len;
+}
+
+static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item,
+		const char *page, size_t len)
+{
+	u16 bcdUSB;
+	int ret;
+
+	ret = kstrtou16(page, 0, &bcdUSB);
+	if (ret)
+		return ret;
+	ret = is_valid_bcd(bcdUSB);
+	if (ret)
+		return ret;
+
+	to_gadget_info(item)->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB);
+	return len;
+}
+
+static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page)
+{
+	char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name;
+
+	return sprintf(page, "%s\n", udc_name ?: "");
+}
+
+static int unregister_gadget(struct gadget_info *gi)
+{
+	int ret;
+
+	if (!gi->composite.gadget_driver.udc_name)
+		return -ENODEV;
+
+	ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver);
+	if (ret)
+		return ret;
+	kfree(gi->composite.gadget_driver.udc_name);
+	gi->composite.gadget_driver.udc_name = NULL;
+	return 0;
+}
+
+static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
+		const char *page, size_t len)
+{
+	struct gadget_info *gi = to_gadget_info(item);
+	char *name;
+	int ret;
+
+	name = kstrdup(page, GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	if (name[len - 1] == '\n')
+		name[len - 1] = '\0';
+
+	mutex_lock(&gi->lock);
+
+	if (!strlen(name)) {
+		ret = unregister_gadget(gi);
+		if (ret)
+			goto err;
+		kfree(name);
+	} else {
+		if (gi->composite.gadget_driver.udc_name) {
+			ret = -EBUSY;
+			goto err;
+		}
+		gi->composite.gadget_driver.udc_name = name;
+		ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
+		if (ret) {
+			gi->composite.gadget_driver.udc_name = NULL;
+			goto err;
+		}
+	}
+	mutex_unlock(&gi->lock);
+	return len;
+err:
+	kfree(name);
+	mutex_unlock(&gi->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass);
+CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass);
+CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol);
+CONFIGFS_ATTR(gadget_dev_desc_, bMaxPacketSize0);
+CONFIGFS_ATTR(gadget_dev_desc_, idVendor);
+CONFIGFS_ATTR(gadget_dev_desc_, idProduct);
+CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice);
+CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB);
+CONFIGFS_ATTR(gadget_dev_desc_, UDC);
+
+static struct configfs_attribute *gadget_root_attrs[] = {
+	&gadget_dev_desc_attr_bDeviceClass,
+	&gadget_dev_desc_attr_bDeviceSubClass,
+	&gadget_dev_desc_attr_bDeviceProtocol,
+	&gadget_dev_desc_attr_bMaxPacketSize0,
+	&gadget_dev_desc_attr_idVendor,
+	&gadget_dev_desc_attr_idProduct,
+	&gadget_dev_desc_attr_bcdDevice,
+	&gadget_dev_desc_attr_bcdUSB,
+	&gadget_dev_desc_attr_UDC,
+	NULL,
+};
+
+static inline struct gadget_strings *to_gadget_strings(struct config_item *item)
+{
+	 return container_of(to_config_group(item), struct gadget_strings,
+			 group);
+}
+
+static inline struct gadget_config_name *to_gadget_config_name(
+		struct config_item *item)
+{
+	 return container_of(to_config_group(item), struct gadget_config_name,
+			 group);
+}
+
+static inline struct usb_function_instance *to_usb_function_instance(
+		struct config_item *item)
+{
+	 return container_of(to_config_group(item),
+			 struct usb_function_instance, group);
+}
+
+static void gadget_info_attr_release(struct config_item *item)
+{
+	struct gadget_info *gi = to_gadget_info(item);
+
+	WARN_ON(!list_empty(&gi->cdev.configs));
+	WARN_ON(!list_empty(&gi->string_list));
+	WARN_ON(!list_empty(&gi->available_func));
+	kfree(gi->composite.gadget_driver.function);
+	kfree(gi);
+}
+
+static struct configfs_item_operations gadget_root_item_ops = {
+	.release                = gadget_info_attr_release,
+};
+
+static void gadget_config_attr_release(struct config_item *item)
+{
+	struct config_usb_cfg *cfg = to_config_usb_cfg(item);
+
+	WARN_ON(!list_empty(&cfg->c.functions));
+	list_del(&cfg->c.list);
+	kfree(cfg->c.label);
+	kfree(cfg);
+}
+
+static int config_usb_cfg_link(
+	struct config_item *usb_cfg_ci,
+	struct config_item *usb_func_ci)
+{
+	struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
+	struct usb_composite_dev *cdev = cfg->c.cdev;
+	struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
+
+	struct config_group *group = to_config_group(usb_func_ci);
+	struct usb_function_instance *fi = container_of(group,
+			struct usb_function_instance, group);
+	struct usb_function_instance *a_fi;
+	struct usb_function *f;
+	int ret;
+
+	mutex_lock(&gi->lock);
+	/*
+	 * Make sure this function is from within our _this_ gadget and not
+	 * from another gadget or a random directory.
+	 * Also a function instance can only be linked once.
+	 */
+	list_for_each_entry(a_fi, &gi->available_func, cfs_list) {
+		if (a_fi == fi)
+			break;
+	}
+	if (a_fi != fi) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(f, &cfg->func_list, list) {
+		if (f->fi == fi) {
+			ret = -EEXIST;
+			goto out;
+		}
+	}
+
+	f = usb_get_function(fi);
+	if (IS_ERR(f)) {
+		ret = PTR_ERR(f);
+		goto out;
+	}
+
+	/* stash the function until we bind it to the gadget */
+	list_add_tail(&f->list, &cfg->func_list);
+	ret = 0;
+out:
+	mutex_unlock(&gi->lock);
+	return ret;
+}
+
+static void config_usb_cfg_unlink(
+	struct config_item *usb_cfg_ci,
+	struct config_item *usb_func_ci)
+{
+	struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
+	struct usb_composite_dev *cdev = cfg->c.cdev;
+	struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
+
+	struct config_group *group = to_config_group(usb_func_ci);
+	struct usb_function_instance *fi = container_of(group,
+			struct usb_function_instance, group);
+	struct usb_function *f;
+
+	/*
+	 * ideally I would like to forbid to unlink functions while a gadget is
+	 * bound to an UDC. Since this isn't possible at the moment, we simply
+	 * force an unbind, the function is available here and then we can
+	 * remove the function.
+	 */
+	mutex_lock(&gi->lock);
+	if (gi->composite.gadget_driver.udc_name)
+		unregister_gadget(gi);
+	WARN_ON(gi->composite.gadget_driver.udc_name);
+
+	list_for_each_entry(f, &cfg->func_list, list) {
+		if (f->fi == fi) {
+			list_del(&f->list);
+			usb_put_function(f);
+			mutex_unlock(&gi->lock);
+			return;
+		}
+	}
+	mutex_unlock(&gi->lock);
+	WARN(1, "Unable to locate function to unbind\n");
+}
+
+static struct configfs_item_operations gadget_config_item_ops = {
+	.release                = gadget_config_attr_release,
+	.allow_link             = config_usb_cfg_link,
+	.drop_link              = config_usb_cfg_unlink,
+};
+
+
+static ssize_t gadget_config_desc_MaxPower_show(struct config_item *item,
+		char *page)
+{
+	return sprintf(page, "%u\n", to_config_usb_cfg(item)->c.MaxPower);
+}
+
+static ssize_t gadget_config_desc_MaxPower_store(struct config_item *item,
+		const char *page, size_t len)
+{
+	u16 val;
+	int ret;
+	ret = kstrtou16(page, 0, &val);
+	if (ret)
+		return ret;
+	if (DIV_ROUND_UP(val, 8) > 0xff)
+		return -ERANGE;
+	to_config_usb_cfg(item)->c.MaxPower = val;
+	return len;
+}
+
+static ssize_t gadget_config_desc_bmAttributes_show(struct config_item *item,
+		char *page)
+{
+	return sprintf(page, "0x%02x\n",
+		to_config_usb_cfg(item)->c.bmAttributes);
+}
+
+static ssize_t gadget_config_desc_bmAttributes_store(struct config_item *item,
+		const char *page, size_t len)
+{
+	u8 val;
+	int ret;
+	ret = kstrtou8(page, 0, &val);
+	if (ret)
+		return ret;
+	if (!(val & USB_CONFIG_ATT_ONE))
+		return -EINVAL;
+	if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER |
+				USB_CONFIG_ATT_WAKEUP))
+		return -EINVAL;
+	to_config_usb_cfg(item)->c.bmAttributes = val;
+	return len;
+}
+
+CONFIGFS_ATTR(gadget_config_desc_, MaxPower);
+CONFIGFS_ATTR(gadget_config_desc_, bmAttributes);
+
+static struct configfs_attribute *gadget_config_attrs[] = {
+	&gadget_config_desc_attr_MaxPower,
+	&gadget_config_desc_attr_bmAttributes,
+	NULL,
+};
+
+static const struct config_item_type gadget_config_type = {
+	.ct_item_ops	= &gadget_config_item_ops,
+	.ct_attrs	= gadget_config_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type gadget_root_type = {
+	.ct_item_ops	= &gadget_root_item_ops,
+	.ct_attrs	= gadget_root_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void composite_init_dev(struct usb_composite_dev *cdev)
+{
+	spin_lock_init(&cdev->lock);
+	INIT_LIST_HEAD(&cdev->configs);
+	INIT_LIST_HEAD(&cdev->gstrings);
+}
+
+static struct config_group *function_make(
+		struct config_group *group,
+		const char *name)
+{
+	struct gadget_info *gi;
+	struct usb_function_instance *fi;
+	char buf[MAX_NAME_LEN];
+	char *func_name;
+	char *instance_name;
+	int ret;
+
+	ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
+	if (ret >= MAX_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	func_name = buf;
+	instance_name = strchr(func_name, '.');
+	if (!instance_name) {
+		pr_err("Unable to locate . in FUNC.INSTANCE\n");
+		return ERR_PTR(-EINVAL);
+	}
+	*instance_name = '\0';
+	instance_name++;
+
+	fi = usb_get_function_instance(func_name);
+	if (IS_ERR(fi))
+		return ERR_CAST(fi);
+
+	ret = config_item_set_name(&fi->group.cg_item, "%s", name);
+	if (ret) {
+		usb_put_function_instance(fi);
+		return ERR_PTR(ret);
+	}
+	if (fi->set_inst_name) {
+		ret = fi->set_inst_name(fi, instance_name);
+		if (ret) {
+			usb_put_function_instance(fi);
+			return ERR_PTR(ret);
+		}
+	}
+
+	gi = container_of(group, struct gadget_info, functions_group);
+
+	mutex_lock(&gi->lock);
+	list_add_tail(&fi->cfs_list, &gi->available_func);
+	mutex_unlock(&gi->lock);
+	return &fi->group;
+}
+
+static void function_drop(
+		struct config_group *group,
+		struct config_item *item)
+{
+	struct usb_function_instance *fi = to_usb_function_instance(item);
+	struct gadget_info *gi;
+
+	gi = container_of(group, struct gadget_info, functions_group);
+
+	mutex_lock(&gi->lock);
+	list_del(&fi->cfs_list);
+	mutex_unlock(&gi->lock);
+	config_item_put(item);
+}
+
+static struct configfs_group_operations functions_ops = {
+	.make_group     = &function_make,
+	.drop_item      = &function_drop,
+};
+
+static const struct config_item_type functions_type = {
+	.ct_group_ops   = &functions_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+GS_STRINGS_RW(gadget_config_name, configuration);
+
+static struct configfs_attribute *gadget_config_name_langid_attrs[] = {
+	&gadget_config_name_attr_configuration,
+	NULL,
+};
+
+static void gadget_config_name_attr_release(struct config_item *item)
+{
+	struct gadget_config_name *cn = to_gadget_config_name(item);
+
+	kfree(cn->configuration);
+
+	list_del(&cn->list);
+	kfree(cn);
+}
+
+USB_CONFIG_STRING_RW_OPS(gadget_config_name);
+USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg);
+
+static struct config_group *config_desc_make(
+		struct config_group *group,
+		const char *name)
+{
+	struct gadget_info *gi;
+	struct config_usb_cfg *cfg;
+	char buf[MAX_NAME_LEN];
+	char *num_str;
+	u8 num;
+	int ret;
+
+	gi = container_of(group, struct gadget_info, configs_group);
+	ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
+	if (ret >= MAX_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	num_str = strchr(buf, '.');
+	if (!num_str) {
+		pr_err("Unable to locate . in name.bConfigurationValue\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	*num_str = '\0';
+	num_str++;
+
+	if (!strlen(buf))
+		return ERR_PTR(-EINVAL);
+
+	ret = kstrtou8(num_str, 0, &num);
+	if (ret)
+		return ERR_PTR(ret);
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+	cfg->c.label = kstrdup(buf, GFP_KERNEL);
+	if (!cfg->c.label) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	cfg->c.bConfigurationValue = num;
+	cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW;
+	cfg->c.bmAttributes = USB_CONFIG_ATT_ONE;
+	INIT_LIST_HEAD(&cfg->string_list);
+	INIT_LIST_HEAD(&cfg->func_list);
+
+	config_group_init_type_name(&cfg->group, name,
+				&gadget_config_type);
+
+	config_group_init_type_name(&cfg->strings_group, "strings",
+			&gadget_config_name_strings_type);
+	configfs_add_default_group(&cfg->strings_group, &cfg->group);
+
+	ret = usb_add_config_only(&gi->cdev, &cfg->c);
+	if (ret)
+		goto err;
+
+	return &cfg->group;
+err:
+	kfree(cfg->c.label);
+	kfree(cfg);
+	return ERR_PTR(ret);
+}
+
+static void config_desc_drop(
+		struct config_group *group,
+		struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations config_desc_ops = {
+	.make_group     = &config_desc_make,
+	.drop_item      = &config_desc_drop,
+};
+
+static const struct config_item_type config_desc_type = {
+	.ct_group_ops   = &config_desc_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+GS_STRINGS_RW(gadget_strings, manufacturer);
+GS_STRINGS_RW(gadget_strings, product);
+GS_STRINGS_RW(gadget_strings, serialnumber);
+
+static struct configfs_attribute *gadget_strings_langid_attrs[] = {
+	&gadget_strings_attr_manufacturer,
+	&gadget_strings_attr_product,
+	&gadget_strings_attr_serialnumber,
+	NULL,
+};
+
+static void gadget_strings_attr_release(struct config_item *item)
+{
+	struct gadget_strings *gs = to_gadget_strings(item);
+
+	kfree(gs->manufacturer);
+	kfree(gs->product);
+	kfree(gs->serialnumber);
+
+	list_del(&gs->list);
+	kfree(gs);
+}
+
+USB_CONFIG_STRING_RW_OPS(gadget_strings);
+USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info);
+
+static inline struct os_desc *to_os_desc(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct os_desc, group);
+}
+
+static inline struct gadget_info *os_desc_item_to_gadget_info(
+		struct config_item *item)
+{
+	return to_gadget_info(to_os_desc(item)->group.cg_item.ci_parent);
+}
+
+static ssize_t os_desc_use_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "%d\n",
+			os_desc_item_to_gadget_info(item)->use_os_desc);
+}
+
+static ssize_t os_desc_use_store(struct config_item *item, const char *page,
+				 size_t len)
+{
+	struct gadget_info *gi = os_desc_item_to_gadget_info(item);
+	int ret;
+	bool use;
+
+	mutex_lock(&gi->lock);
+	ret = strtobool(page, &use);
+	if (!ret) {
+		gi->use_os_desc = use;
+		ret = len;
+	}
+	mutex_unlock(&gi->lock);
+
+	return ret;
+}
+
+static ssize_t os_desc_b_vendor_code_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "0x%02x\n",
+			os_desc_item_to_gadget_info(item)->b_vendor_code);
+}
+
+static ssize_t os_desc_b_vendor_code_store(struct config_item *item,
+					   const char *page, size_t len)
+{
+	struct gadget_info *gi = os_desc_item_to_gadget_info(item);
+	int ret;
+	u8 b_vendor_code;
+
+	mutex_lock(&gi->lock);
+	ret = kstrtou8(page, 0, &b_vendor_code);
+	if (!ret) {
+		gi->b_vendor_code = b_vendor_code;
+		ret = len;
+	}
+	mutex_unlock(&gi->lock);
+
+	return ret;
+}
+
+static ssize_t os_desc_qw_sign_show(struct config_item *item, char *page)
+{
+	struct gadget_info *gi = os_desc_item_to_gadget_info(item);
+	int res;
+
+	res = utf16s_to_utf8s((wchar_t *) gi->qw_sign, OS_STRING_QW_SIGN_LEN,
+			      UTF16_LITTLE_ENDIAN, page, PAGE_SIZE - 1);
+	page[res++] = '\n';
+
+	return res;
+}
+
+static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page,
+				     size_t len)
+{
+	struct gadget_info *gi = os_desc_item_to_gadget_info(item);
+	int res, l;
+
+	l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1);
+	if (page[l - 1] == '\n')
+		--l;
+
+	mutex_lock(&gi->lock);
+	res = utf8s_to_utf16s(page, l,
+			      UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign,
+			      OS_STRING_QW_SIGN_LEN);
+	if (res > 0)
+		res = len;
+	mutex_unlock(&gi->lock);
+
+	return res;
+}
+
+CONFIGFS_ATTR(os_desc_, use);
+CONFIGFS_ATTR(os_desc_, b_vendor_code);
+CONFIGFS_ATTR(os_desc_, qw_sign);
+
+static struct configfs_attribute *os_desc_attrs[] = {
+	&os_desc_attr_use,
+	&os_desc_attr_b_vendor_code,
+	&os_desc_attr_qw_sign,
+	NULL,
+};
+
+static void os_desc_attr_release(struct config_item *item)
+{
+	struct os_desc *os_desc = to_os_desc(item);
+	kfree(os_desc);
+}
+
+static int os_desc_link(struct config_item *os_desc_ci,
+			struct config_item *usb_cfg_ci)
+{
+	struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
+					struct gadget_info, os_desc_group);
+	struct usb_composite_dev *cdev = &gi->cdev;
+	struct config_usb_cfg *c_target =
+		container_of(to_config_group(usb_cfg_ci),
+			     struct config_usb_cfg, group);
+	struct usb_configuration *c;
+	int ret;
+
+	mutex_lock(&gi->lock);
+	list_for_each_entry(c, &cdev->configs, list) {
+		if (c == &c_target->c)
+			break;
+	}
+	if (c != &c_target->c) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (cdev->os_desc_config) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	cdev->os_desc_config = &c_target->c;
+	ret = 0;
+
+out:
+	mutex_unlock(&gi->lock);
+	return ret;
+}
+
+static void os_desc_unlink(struct config_item *os_desc_ci,
+			  struct config_item *usb_cfg_ci)
+{
+	struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
+					struct gadget_info, os_desc_group);
+	struct usb_composite_dev *cdev = &gi->cdev;
+
+	mutex_lock(&gi->lock);
+	if (gi->composite.gadget_driver.udc_name)
+		unregister_gadget(gi);
+	cdev->os_desc_config = NULL;
+	WARN_ON(gi->composite.gadget_driver.udc_name);
+	mutex_unlock(&gi->lock);
+}
+
+static struct configfs_item_operations os_desc_ops = {
+	.release                = os_desc_attr_release,
+	.allow_link		= os_desc_link,
+	.drop_link		= os_desc_unlink,
+};
+
+static struct config_item_type os_desc_type = {
+	.ct_item_ops	= &os_desc_ops,
+	.ct_attrs	= os_desc_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static inline struct usb_os_desc_ext_prop
+*to_usb_os_desc_ext_prop(struct config_item *item)
+{
+	return container_of(item, struct usb_os_desc_ext_prop, item);
+}
+
+static ssize_t ext_prop_type_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "%d\n", to_usb_os_desc_ext_prop(item)->type);
+}
+
+static ssize_t ext_prop_type_store(struct config_item *item,
+				   const char *page, size_t len)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+	struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
+	u8 type;
+	int ret;
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	ret = kstrtou8(page, 0, &type);
+	if (ret)
+		goto end;
+	if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if ((ext_prop->type == USB_EXT_PROP_BINARY ||
+	    ext_prop->type == USB_EXT_PROP_LE32 ||
+	    ext_prop->type == USB_EXT_PROP_BE32) &&
+	    (type == USB_EXT_PROP_UNICODE ||
+	    type == USB_EXT_PROP_UNICODE_ENV ||
+	    type == USB_EXT_PROP_UNICODE_LINK))
+		ext_prop->data_len <<= 1;
+	else if ((ext_prop->type == USB_EXT_PROP_UNICODE ||
+		   ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+		   ext_prop->type == USB_EXT_PROP_UNICODE_LINK) &&
+		   (type == USB_EXT_PROP_BINARY ||
+		   type == USB_EXT_PROP_LE32 ||
+		   type == USB_EXT_PROP_BE32))
+		ext_prop->data_len >>= 1;
+	ext_prop->type = type;
+	ret = len;
+
+end:
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	return ret;
+}
+
+static ssize_t ext_prop_data_show(struct config_item *item, char *page)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+	int len = ext_prop->data_len;
+
+	if (ext_prop->type == USB_EXT_PROP_UNICODE ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_LINK)
+		len >>= 1;
+	memcpy(page, ext_prop->data, len);
+
+	return len;
+}
+
+static ssize_t ext_prop_data_store(struct config_item *item,
+				   const char *page, size_t len)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+	struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
+	char *new_data;
+	size_t ret_len = len;
+
+	if (page[len - 1] == '\n' || page[len - 1] == '\0')
+		--len;
+	new_data = kmemdup(page, len, GFP_KERNEL);
+	if (!new_data)
+		return -ENOMEM;
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	kfree(ext_prop->data);
+	ext_prop->data = new_data;
+	desc->ext_prop_len -= ext_prop->data_len;
+	ext_prop->data_len = len;
+	desc->ext_prop_len += ext_prop->data_len;
+	if (ext_prop->type == USB_EXT_PROP_UNICODE ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
+	    ext_prop->type == USB_EXT_PROP_UNICODE_LINK) {
+		desc->ext_prop_len -= ext_prop->data_len;
+		ext_prop->data_len <<= 1;
+		ext_prop->data_len += 2;
+		desc->ext_prop_len += ext_prop->data_len;
+	}
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	return ret_len;
+}
+
+CONFIGFS_ATTR(ext_prop_, type);
+CONFIGFS_ATTR(ext_prop_, data);
+
+static struct configfs_attribute *ext_prop_attrs[] = {
+	&ext_prop_attr_type,
+	&ext_prop_attr_data,
+	NULL,
+};
+
+static void usb_os_desc_ext_prop_release(struct config_item *item)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+
+	kfree(ext_prop); /* frees a whole chunk */
+}
+
+static struct configfs_item_operations ext_prop_ops = {
+	.release		= usb_os_desc_ext_prop_release,
+};
+
+static struct config_item *ext_prop_make(
+		struct config_group *group,
+		const char *name)
+{
+	struct usb_os_desc_ext_prop *ext_prop;
+	struct config_item_type *ext_prop_type;
+	struct usb_os_desc *desc;
+	char *vlabuf;
+
+	vla_group(data_chunk);
+	vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1);
+	vla_item(data_chunk, struct config_item_type, ext_prop_type, 1);
+
+	vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
+	if (!vlabuf)
+		return ERR_PTR(-ENOMEM);
+
+	ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop);
+	ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type);
+
+	desc = container_of(group, struct usb_os_desc, group);
+	ext_prop_type->ct_item_ops = &ext_prop_ops;
+	ext_prop_type->ct_attrs = ext_prop_attrs;
+	ext_prop_type->ct_owner = desc->owner;
+
+	config_item_init_type_name(&ext_prop->item, name, ext_prop_type);
+
+	ext_prop->name = kstrdup(name, GFP_KERNEL);
+	if (!ext_prop->name) {
+		kfree(vlabuf);
+		return ERR_PTR(-ENOMEM);
+	}
+	desc->ext_prop_len += 14;
+	ext_prop->name_len = 2 * strlen(ext_prop->name) + 2;
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	desc->ext_prop_len += ext_prop->name_len;
+	list_add_tail(&ext_prop->entry, &desc->ext_prop);
+	++desc->ext_prop_count;
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+
+	return &ext_prop->item;
+}
+
+static void ext_prop_drop(struct config_group *group, struct config_item *item)
+{
+	struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
+	struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item);
+
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	list_del(&ext_prop->entry);
+	--desc->ext_prop_count;
+	kfree(ext_prop->name);
+	desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14);
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+	config_item_put(item);
+}
+
+static struct configfs_group_operations interf_grp_ops = {
+	.make_item	= &ext_prop_make,
+	.drop_item	= &ext_prop_drop,
+};
+
+static ssize_t interf_grp_compatible_id_show(struct config_item *item,
+					     char *page)
+{
+	memcpy(page, to_usb_os_desc(item)->ext_compat_id, 8);
+	return 8;
+}
+
+static ssize_t interf_grp_compatible_id_store(struct config_item *item,
+					      const char *page, size_t len)
+{
+	struct usb_os_desc *desc = to_usb_os_desc(item);
+	int l;
+
+	l = min_t(int, 8, len);
+	if (page[l - 1] == '\n')
+		--l;
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	memcpy(desc->ext_compat_id, page, l);
+
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+
+	return len;
+}
+
+static ssize_t interf_grp_sub_compatible_id_show(struct config_item *item,
+						 char *page)
+{
+	memcpy(page, to_usb_os_desc(item)->ext_compat_id + 8, 8);
+	return 8;
+}
+
+static ssize_t interf_grp_sub_compatible_id_store(struct config_item *item,
+						  const char *page, size_t len)
+{
+	struct usb_os_desc *desc = to_usb_os_desc(item);
+	int l;
+
+	l = min_t(int, 8, len);
+	if (page[l - 1] == '\n')
+		--l;
+	if (desc->opts_mutex)
+		mutex_lock(desc->opts_mutex);
+	memcpy(desc->ext_compat_id + 8, page, l);
+
+	if (desc->opts_mutex)
+		mutex_unlock(desc->opts_mutex);
+
+	return len;
+}
+
+CONFIGFS_ATTR(interf_grp_, compatible_id);
+CONFIGFS_ATTR(interf_grp_, sub_compatible_id);
+
+static struct configfs_attribute *interf_grp_attrs[] = {
+	&interf_grp_attr_compatible_id,
+	&interf_grp_attr_sub_compatible_id,
+	NULL
+};
+
+struct config_group *usb_os_desc_prepare_interf_dir(
+		struct config_group *parent,
+		int n_interf,
+		struct usb_os_desc **desc,
+		char **names,
+		struct module *owner)
+{
+	struct config_group *os_desc_group;
+	struct config_item_type *os_desc_type, *interface_type;
+
+	vla_group(data_chunk);
+	vla_item(data_chunk, struct config_group, os_desc_group, 1);
+	vla_item(data_chunk, struct config_item_type, os_desc_type, 1);
+	vla_item(data_chunk, struct config_item_type, interface_type, 1);
+
+	char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
+	if (!vlabuf)
+		return ERR_PTR(-ENOMEM);
+
+	os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
+	os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
+	interface_type = vla_ptr(vlabuf, data_chunk, interface_type);
+
+	os_desc_type->ct_owner = owner;
+	config_group_init_type_name(os_desc_group, "os_desc", os_desc_type);
+	configfs_add_default_group(os_desc_group, parent);
+
+	interface_type->ct_group_ops = &interf_grp_ops;
+	interface_type->ct_attrs = interf_grp_attrs;
+	interface_type->ct_owner = owner;
+
+	while (n_interf--) {
+		struct usb_os_desc *d;
+
+		d = desc[n_interf];
+		d->owner = owner;
+		config_group_init_type_name(&d->group, "", interface_type);
+		config_item_set_name(&d->group.cg_item, "interface.%s",
+				     names[n_interf]);
+		configfs_add_default_group(&d->group, os_desc_group);
+	}
+
+	return os_desc_group;
+}
+EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
+
+static int configfs_do_nothing(struct usb_composite_dev *cdev)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+int composite_dev_prepare(struct usb_composite_driver *composite,
+		struct usb_composite_dev *dev);
+
+int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+				  struct usb_ep *ep0);
+
+static void purge_configs_funcs(struct gadget_info *gi)
+{
+	struct usb_configuration	*c;
+
+	list_for_each_entry(c, &gi->cdev.configs, list) {
+		struct usb_function *f, *tmp;
+		struct config_usb_cfg *cfg;
+
+		cfg = container_of(c, struct config_usb_cfg, c);
+
+		list_for_each_entry_safe(f, tmp, &c->functions, list) {
+
+			list_move_tail(&f->list, &cfg->func_list);
+			if (f->unbind) {
+				dev_dbg(&gi->cdev.gadget->dev,
+					"unbind function '%s'/%p\n",
+					f->name, f);
+				f->unbind(c, f);
+			}
+		}
+		c->next_interface_id = 0;
+		memset(c->interface, 0, sizeof(c->interface));
+		c->superspeed_plus = 0;
+		c->superspeed = 0;
+		c->highspeed = 0;
+		c->fullspeed = 0;
+	}
+}
+
+static int configfs_composite_bind(struct usb_gadget *gadget,
+		struct usb_gadget_driver *gdriver)
+{
+	struct usb_composite_driver     *composite = to_cdriver(gdriver);
+	struct gadget_info		*gi = container_of(composite,
+						struct gadget_info, composite);
+	struct usb_composite_dev	*cdev = &gi->cdev;
+	struct usb_configuration	*c;
+	struct usb_string		*s;
+	unsigned			i;
+	int				ret;
+
+	/* the gi->lock is hold by the caller */
+	cdev->gadget = gadget;
+	set_gadget_data(gadget, cdev);
+	ret = composite_dev_prepare(composite, cdev);
+	if (ret)
+		return ret;
+	/* and now the gadget bind */
+	ret = -EINVAL;
+
+	if (list_empty(&gi->cdev.configs)) {
+		pr_err("Need at least one configuration in %s.\n",
+				gi->composite.name);
+		goto err_comp_cleanup;
+	}
+
+
+	list_for_each_entry(c, &gi->cdev.configs, list) {
+		struct config_usb_cfg *cfg;
+
+		cfg = container_of(c, struct config_usb_cfg, c);
+		if (list_empty(&cfg->func_list)) {
+			pr_err("Config %s/%d of %s needs at least one function.\n",
+			      c->label, c->bConfigurationValue,
+			      gi->composite.name);
+			goto err_comp_cleanup;
+		}
+	}
+
+	/* init all strings */
+	if (!list_empty(&gi->string_list)) {
+		struct gadget_strings *gs;
+
+		i = 0;
+		list_for_each_entry(gs, &gi->string_list, list) {
+
+			gi->gstrings[i] = &gs->stringtab_dev;
+			gs->stringtab_dev.strings = gs->strings;
+			gs->strings[USB_GADGET_MANUFACTURER_IDX].s =
+				gs->manufacturer;
+			gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product;
+			gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber;
+			i++;
+		}
+		gi->gstrings[i] = NULL;
+		s = usb_gstrings_attach(&gi->cdev, gi->gstrings,
+				USB_GADGET_FIRST_AVAIL_IDX);
+		if (IS_ERR(s)) {
+			ret = PTR_ERR(s);
+			goto err_comp_cleanup;
+		}
+
+		gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id;
+		gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id;
+		gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id;
+	}
+
+	if (gi->use_os_desc) {
+		cdev->use_os_string = true;
+		cdev->b_vendor_code = gi->b_vendor_code;
+		memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
+	}
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc) {
+			ret = -ENOMEM;
+			goto err_comp_cleanup;
+		}
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* Go through all configs, attach all functions */
+	list_for_each_entry(c, &gi->cdev.configs, list) {
+		struct config_usb_cfg *cfg;
+		struct usb_function *f;
+		struct usb_function *tmp;
+		struct gadget_config_name *cn;
+
+		if (gadget_is_otg(gadget))
+			c->descriptors = otg_desc;
+
+		cfg = container_of(c, struct config_usb_cfg, c);
+		if (!list_empty(&cfg->string_list)) {
+			i = 0;
+			list_for_each_entry(cn, &cfg->string_list, list) {
+				cfg->gstrings[i] = &cn->stringtab_dev;
+				cn->stringtab_dev.strings = &cn->strings;
+				cn->strings.s = cn->configuration;
+				i++;
+			}
+			cfg->gstrings[i] = NULL;
+			s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1);
+			if (IS_ERR(s)) {
+				ret = PTR_ERR(s);
+				goto err_comp_cleanup;
+			}
+			c->iConfiguration = s[0].id;
+		}
+
+		list_for_each_entry_safe(f, tmp, &cfg->func_list, list) {
+			list_del(&f->list);
+			ret = usb_add_function(c, f);
+			if (ret) {
+				list_add(&f->list, &cfg->func_list);
+				goto err_purge_funcs;
+			}
+		}
+		usb_ep_autoconfig_reset(cdev->gadget);
+	}
+	if (cdev->use_os_string) {
+		ret = composite_os_desc_req_prepare(cdev, gadget->ep0);
+		if (ret)
+			goto err_purge_funcs;
+	}
+
+	usb_ep_autoconfig_reset(cdev->gadget);
+	return 0;
+
+err_purge_funcs:
+	purge_configs_funcs(gi);
+err_comp_cleanup:
+	composite_dev_cleanup(cdev);
+	return ret;
+}
+
+static void configfs_composite_unbind(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev;
+	struct gadget_info		*gi;
+
+	/* the gi->lock is hold by the caller */
+
+	cdev = get_gadget_data(gadget);
+	gi = container_of(cdev, struct gadget_info, cdev);
+
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+	purge_configs_funcs(gi);
+	composite_dev_cleanup(cdev);
+	usb_ep_autoconfig_reset(cdev->gadget);
+	cdev->gadget = NULL;
+	set_gadget_data(gadget, NULL);
+}
+
+static const struct usb_gadget_driver configfs_driver_template = {
+	.bind           = configfs_composite_bind,
+	.unbind         = configfs_composite_unbind,
+
+	.setup          = composite_setup,
+	.reset          = composite_disconnect,
+	.disconnect     = composite_disconnect,
+
+	.suspend	= composite_suspend,
+	.resume		= composite_resume,
+
+	.max_speed	= USB_SPEED_SUPER,
+	.driver = {
+		.owner          = THIS_MODULE,
+		.name		= "configfs-gadget",
+	},
+	.match_existing_only = 1,
+};
+
+static struct config_group *gadgets_make(
+		struct config_group *group,
+		const char *name)
+{
+	struct gadget_info *gi;
+
+	gi = kzalloc(sizeof(*gi), GFP_KERNEL);
+	if (!gi)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&gi->group, name, &gadget_root_type);
+
+	config_group_init_type_name(&gi->functions_group, "functions",
+			&functions_type);
+	configfs_add_default_group(&gi->functions_group, &gi->group);
+
+	config_group_init_type_name(&gi->configs_group, "configs",
+			&config_desc_type);
+	configfs_add_default_group(&gi->configs_group, &gi->group);
+
+	config_group_init_type_name(&gi->strings_group, "strings",
+			&gadget_strings_strings_type);
+	configfs_add_default_group(&gi->strings_group, &gi->group);
+
+	config_group_init_type_name(&gi->os_desc_group, "os_desc",
+			&os_desc_type);
+	configfs_add_default_group(&gi->os_desc_group, &gi->group);
+
+	gi->composite.bind = configfs_do_nothing;
+	gi->composite.unbind = configfs_do_nothing;
+	gi->composite.suspend = NULL;
+	gi->composite.resume = NULL;
+	gi->composite.max_speed = USB_SPEED_SUPER;
+
+	mutex_init(&gi->lock);
+	INIT_LIST_HEAD(&gi->string_list);
+	INIT_LIST_HEAD(&gi->available_func);
+
+	composite_init_dev(&gi->cdev);
+	gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
+	gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
+	gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
+
+	gi->composite.gadget_driver = configfs_driver_template;
+
+	gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
+	gi->composite.name = gi->composite.gadget_driver.function;
+
+	if (!gi->composite.gadget_driver.function)
+		goto err;
+
+	return &gi->group;
+err:
+	kfree(gi);
+	return ERR_PTR(-ENOMEM);
+}
+
+static void gadgets_drop(struct config_group *group, struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations gadgets_ops = {
+	.make_group     = &gadgets_make,
+	.drop_item      = &gadgets_drop,
+};
+
+static const struct config_item_type gadgets_type = {
+	.ct_group_ops   = &gadgets_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem gadget_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "usb_gadget",
+			.ci_type = &gadgets_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
+};
+
+void unregister_gadget_item(struct config_item *item)
+{
+	struct gadget_info *gi = to_gadget_info(item);
+
+	mutex_lock(&gi->lock);
+	unregister_gadget(gi);
+	mutex_unlock(&gi->lock);
+}
+EXPORT_SYMBOL_GPL(unregister_gadget_item);
+
+static int __init gadget_cfs_init(void)
+{
+	int ret;
+
+	config_group_init(&gadget_subsys.su_group);
+
+	ret = configfs_register_subsystem(&gadget_subsys);
+	return ret;
+}
+module_init(gadget_cfs_init);
+
+static void __exit gadget_cfs_exit(void)
+{
+	configfs_unregister_subsystem(&gadget_subsys);
+}
+module_exit(gadget_cfs_exit);
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
new file mode 100644
index 0000000..3b6f529
--- /dev/null
+++ b/drivers/usb/gadget/configfs.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef USB__GADGET__CONFIGFS__H
+#define USB__GADGET__CONFIGFS__H
+
+#include <linux/configfs.h>
+
+void unregister_gadget_item(struct config_item *item);
+
+struct config_group *usb_os_desc_prepare_interf_dir(
+		struct config_group *parent,
+		int n_interf,
+		struct usb_os_desc **desc,
+		char **names,
+		struct module *owner);
+
+static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct usb_os_desc, group);
+}
+
+#endif /*  USB__GADGET__CONFIGFS__H */
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
new file mode 100644
index 0000000..71b15c6
--- /dev/null
+++ b/drivers/usb/gadget/epautoconf.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/**
+ * 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.
+ * @ep_comp: Endpoint companion descriptor, with the required
+ *    number of streams. Will be modified when the chosen EP
+ *    supports a different number of streams.
+ *
+ * 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.
+ *
+ * On success, this returns an 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 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, claims it by
+ * assigning ep->claimed to true.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig_ss(
+	struct usb_gadget		*gadget,
+	struct usb_endpoint_descriptor	*desc,
+	struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+	struct usb_ep	*ep;
+	u8		type;
+
+	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+	if (gadget->ops->match_ep) {
+		ep = gadget->ops->match_ep(gadget, desc, ep_comp);
+		if (ep)
+			goto found_ep;
+	}
+
+	/* Second, look at endpoints until an unclaimed one looks usable */
+	list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+		if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))
+			goto found_ep;
+	}
+
+	/* Fail */
+	return NULL;
+found_ep:
+
+	/*
+	 * If the protocol driver hasn't yet decided on wMaxPacketSize
+	 * and wants to know the maximum possible, provide the info.
+	 */
+	if (desc->wMaxPacketSize == 0)
+		desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
+
+	/* report address */
+	desc->bEndpointAddress &= USB_DIR_IN;
+	if (isdigit(ep->name[2])) {
+		u8 num = simple_strtoul(&ep->name[2], NULL, 10);
+		desc->bEndpointAddress |= num;
+	} else if (desc->bEndpointAddress & USB_DIR_IN) {
+		if (++gadget->in_epnum > 15)
+			return NULL;
+		desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
+	} else {
+		if (++gadget->out_epnum > 15)
+			return NULL;
+		desc->bEndpointAddress |= gadget->out_epnum;
+	}
+
+	/* report (variable) full speed bulk maxpacket */
+	if ((type == USB_ENDPOINT_XFER_BULK) && !ep_comp) {
+		int size = ep->maxpacket_limit;
+
+		/* min() doesn't work on bitfields with gcc-3.5 */
+		if (size > 64)
+			size = 64;
+		desc->wMaxPacketSize = cpu_to_le16(size);
+	}
+
+	ep->address = desc->bEndpointAddress;
+	ep->desc = NULL;
+	ep->comp_desc = NULL;
+	ep->claimed = true;
+	return ep;
+}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
+
+/**
+ * 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 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, claims it
+ * by assigning ep->claimed to true.
+ *
+ * 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);
+}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
+
+/**
+ * usb_ep_autoconfig_release - releases endpoint and set it to initial state
+ * @ep: endpoint which should be released
+ *
+ * This function can be used during function bind for endpoints obtained
+ * from usb_ep_autoconfig(). It unclaims endpoint claimed by
+ * usb_ep_autoconfig() to make it available for other functions. Endpoint
+ * which was released is no longer invalid and shouldn't be used in
+ * context of function which released it.
+ */
+void usb_ep_autoconfig_release(struct usb_ep *ep)
+{
+	ep->claimed = false;
+	ep->driver_data = NULL;
+}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_release);
+
+/**
+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state
+ * @gadget: device for which autoconfig state will be reset
+ *
+ * Use this for devices where one configuration may need to assign
+ * endpoint resources very differently from the next one.  It clears
+ * state such as ep->claimed and the record of assigned endpoints
+ * used by usb_ep_autoconfig().
+ */
+void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+{
+	struct usb_ep	*ep;
+
+	list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+		ep->claimed = false;
+		ep->driver_data = NULL;
+	}
+	gadget->in_epnum = 0;
+	gadget->out_epnum = 0;
+}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
new file mode 100644
index 0000000..5d3a6cf
--- /dev/null
+++ b/drivers/usb/gadget/function/Makefile
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# USB peripheral controller drivers
+#
+
+ccflags-y			:= -I$(srctree)/drivers/usb/gadget/
+ccflags-y			+= -I$(srctree)/drivers/usb/gadget/udc/
+
+# USB Functions
+usb_f_acm-y			:= f_acm.o
+obj-$(CONFIG_USB_F_ACM)		+= usb_f_acm.o
+usb_f_ss_lb-y			:= f_loopback.o f_sourcesink.o
+obj-$(CONFIG_USB_F_SS_LB)	+= usb_f_ss_lb.o
+obj-$(CONFIG_USB_U_SERIAL)	+= u_serial.o
+usb_f_serial-y			:= f_serial.o
+obj-$(CONFIG_USB_F_SERIAL)	+= usb_f_serial.o
+usb_f_obex-y			:= f_obex.o
+obj-$(CONFIG_USB_F_OBEX)	+= usb_f_obex.o
+obj-$(CONFIG_USB_U_ETHER)	+= u_ether.o
+usb_f_ncm-y			:= f_ncm.o
+obj-$(CONFIG_USB_F_NCM)		+= usb_f_ncm.o
+usb_f_ecm-y			:= f_ecm.o
+obj-$(CONFIG_USB_F_ECM)		+= usb_f_ecm.o
+usb_f_phonet-y			:= f_phonet.o
+obj-$(CONFIG_USB_F_PHONET)	+= usb_f_phonet.o
+usb_f_eem-y			:= f_eem.o
+obj-$(CONFIG_USB_F_EEM)		+= usb_f_eem.o
+usb_f_ecm_subset-y		:= f_subset.o
+obj-$(CONFIG_USB_F_SUBSET)	+= usb_f_ecm_subset.o
+usb_f_rndis-y			:= f_rndis.o rndis.o
+obj-$(CONFIG_USB_F_RNDIS)	+= usb_f_rndis.o
+usb_f_mass_storage-y		:= f_mass_storage.o storage_common.o
+obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
+usb_f_fs-y			:= f_fs.o
+obj-$(CONFIG_USB_F_FS)		+= usb_f_fs.o
+obj-$(CONFIG_USB_U_AUDIO)	+= u_audio.o
+usb_f_uac1-y			:= f_uac1.o
+obj-$(CONFIG_USB_F_UAC1)	+= usb_f_uac1.o
+usb_f_uac1_legacy-y		:= f_uac1_legacy.o u_uac1_legacy.o
+obj-$(CONFIG_USB_F_UAC1_LEGACY)	+= usb_f_uac1_legacy.o
+usb_f_uac2-y			:= f_uac2.o
+obj-$(CONFIG_USB_F_UAC2)	+= usb_f_uac2.o
+usb_f_uvc-y			:= f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
+obj-$(CONFIG_USB_F_UVC)		+= usb_f_uvc.o
+usb_f_midi-y			:= f_midi.o
+obj-$(CONFIG_USB_F_MIDI)	+= usb_f_midi.o
+usb_f_hid-y			:= f_hid.o
+obj-$(CONFIG_USB_F_HID)		+= usb_f_hid.o
+usb_f_printer-y			:= f_printer.o
+obj-$(CONFIG_USB_F_PRINTER)	+= usb_f_printer.o
+usb_f_tcm-y			:= f_tcm.o
+obj-$(CONFIG_USB_F_TCM)		+= usb_f_tcm.o
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
new file mode 100644
index 0000000..9fc98de
--- /dev/null
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -0,0 +1,820 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_acm.c -- USB CDC serial (ACM) function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 2009 by Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This CDC ACM function support just wraps control functions and
+ * notifications around the generic serial-over-usb code.
+ *
+ * Because CDC ACM is standardized by the USB-IF, many host operating
+ * systems have drivers for it.  Accordingly, ACM is the preferred
+ * interop solution for serial-port type connections.  The control
+ * models are often not necessary, and in any case don't do much in
+ * this bare-bones implementation.
+ *
+ * Note that even MS-Windows has some support for ACM.  However, that
+ * support is somewhat broken because when you use ACM in a composite
+ * device, having multiple interfaces confuses the poor OS.  It doesn't
+ * seem to understand CDC Union descriptors.  The new "association"
+ * descriptors (roughly equivalent to CDC Unions) may sometimes help.
+ */
+
+struct f_acm {
+	struct gserial			port;
+	u8				ctrl_id, data_id;
+	u8				port_num;
+
+	u8				pending;
+
+	/* lock is mostly for pending and notify_req ... they get accessed
+	 * by callbacks both from tty (open/close/break) under its spinlock,
+	 * and notify_req.complete() which can't use that lock.
+	 */
+	spinlock_t			lock;
+
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+
+	struct usb_cdc_line_coding	port_line_coding;	/* 8-N-1 etc */
+
+	/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
+	u16				port_handshake_bits;
+#define ACM_CTRL_RTS	(1 << 1)	/* unused with full duplex */
+#define ACM_CTRL_DTR	(1 << 0)	/* host is ready for data r/w */
+
+	/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
+	u16				serial_state;
+#define ACM_CTRL_OVERRUN	(1 << 6)
+#define ACM_CTRL_PARITY		(1 << 5)
+#define ACM_CTRL_FRAMING	(1 << 4)
+#define ACM_CTRL_RI		(1 << 3)
+#define ACM_CTRL_BRK		(1 << 2)
+#define ACM_CTRL_DSR		(1 << 1)
+#define ACM_CTRL_DCD		(1 << 0)
+};
+
+static inline struct f_acm *func_to_acm(struct usb_function *f)
+{
+	return container_of(f, struct f_acm, port.func);
+}
+
+static inline struct f_acm *port_to_acm(struct gserial *p)
+{
+	return container_of(p, struct f_acm, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* notification endpoint uses smallish and infrequent fixed-size messages */
+
+#define GS_NOTIFY_INTERVAL_MS		32
+#define GS_NOTIFY_MAXPACKET		10	/* notification + 2 bytes */
+
+/* interface and class descriptors: */
+
+static struct usb_interface_assoc_descriptor
+acm_iad_descriptor = {
+	.bLength =		sizeof acm_iad_descriptor,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount = 	2,	// control + data
+	.bFunctionClass =	USB_CLASS_COMM,
+	.bFunctionSubClass =	USB_CDC_SUBCLASS_ACM,
+	.bFunctionProtocol =	USB_CDC_ACM_PROTO_AT_V25TER,
+	/* .iFunction =		DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor acm_control_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ACM,
+	.bInterfaceProtocol =	USB_CDC_ACM_PROTO_AT_V25TER,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_interface_descriptor acm_data_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc acm_header_desc = {
+	.bLength =		sizeof(acm_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+acm_call_mgmt_descriptor = {
+	.bLength =		sizeof(acm_call_mgmt_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+	.bmCapabilities =	0,
+	/* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor acm_descriptor = {
+	.bLength =		sizeof(acm_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+	.bmCapabilities =	USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc acm_union_desc = {
+	.bLength =		sizeof(acm_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor acm_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_NOTIFY_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor acm_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acm_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *acm_fs_function[] = {
+	(struct usb_descriptor_header *) &acm_iad_descriptor,
+	(struct usb_descriptor_header *) &acm_control_interface_desc,
+	(struct usb_descriptor_header *) &acm_header_desc,
+	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &acm_descriptor,
+	(struct usb_descriptor_header *) &acm_union_desc,
+	(struct usb_descriptor_header *) &acm_fs_notify_desc,
+	(struct usb_descriptor_header *) &acm_data_interface_desc,
+	(struct usb_descriptor_header *) &acm_fs_in_desc,
+	(struct usb_descriptor_header *) &acm_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor acm_hs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
+};
+
+static struct usb_endpoint_descriptor acm_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acm_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *acm_hs_function[] = {
+	(struct usb_descriptor_header *) &acm_iad_descriptor,
+	(struct usb_descriptor_header *) &acm_control_interface_desc,
+	(struct usb_descriptor_header *) &acm_header_desc,
+	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &acm_descriptor,
+	(struct usb_descriptor_header *) &acm_union_desc,
+	(struct usb_descriptor_header *) &acm_hs_notify_desc,
+	(struct usb_descriptor_header *) &acm_data_interface_desc,
+	(struct usb_descriptor_header *) &acm_hs_in_desc,
+	(struct usb_descriptor_header *) &acm_hs_out_desc,
+	NULL,
+};
+
+static struct usb_endpoint_descriptor acm_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor acm_ss_out_desc = {
+	.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 acm_ss_bulk_comp_desc = {
+	.bLength =              sizeof acm_ss_bulk_comp_desc,
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *acm_ss_function[] = {
+	(struct usb_descriptor_header *) &acm_iad_descriptor,
+	(struct usb_descriptor_header *) &acm_control_interface_desc,
+	(struct usb_descriptor_header *) &acm_header_desc,
+	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &acm_descriptor,
+	(struct usb_descriptor_header *) &acm_union_desc,
+	(struct usb_descriptor_header *) &acm_hs_notify_desc,
+	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *) &acm_data_interface_desc,
+	(struct usb_descriptor_header *) &acm_ss_in_desc,
+	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *) &acm_ss_out_desc,
+	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+#define ACM_CTRL_IDX	0
+#define ACM_DATA_IDX	1
+#define ACM_IAD_IDX	2
+
+/* static strings, in UTF-8 */
+static struct usb_string acm_string_defs[] = {
+	[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
+	[ACM_DATA_IDX].s = "CDC ACM Data",
+	[ACM_IAD_IDX ].s = "CDC Serial",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings acm_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		acm_string_defs,
+};
+
+static struct usb_gadget_strings *acm_strings[] = {
+	&acm_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM control ... data handling is delegated to tty library code.
+ * The main task of this function is to activate and deactivate
+ * that code based on device state; track parameters like line
+ * speed, handshake state, and so on; and issue notifications.
+ */
+
+static void acm_complete_set_line_coding(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_acm	*acm = ep->driver_data;
+	struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+
+	if (req->status != 0) {
+		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n",
+			acm->port_num, req->status);
+		return;
+	}
+
+	/* normal completion */
+	if (req->actual != sizeof(acm->port_line_coding)) {
+		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n",
+			acm->port_num, req->actual);
+		usb_ep_set_halt(ep);
+	} else {
+		struct usb_cdc_line_coding	*value = req->buf;
+
+		/* REVISIT:  we currently just remember this data.
+		 * If we change that, (a) validate it first, then
+		 * (b) update whatever hardware needs updating,
+		 * (c) worry about locking.  This is information on
+		 * the order of 9600-8-N-1 ... most of which means
+		 * nothing unless we control a real RS232 line.
+		 */
+		acm->port_line_coding = *value;
+	}
+}
+
+static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_acm		*acm = func_to_acm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 *
+	 * Note CDC spec table 4 lists the ACM request profile.  It requires
+	 * encapsulated command support ... we don't handle any, and respond
+	 * to them by stalling.  Options include get/set/clear comm features
+	 * (not that useful) and SEND_BREAK.
+	 */
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	/* SET_LINE_CODING ... just read and save what the host sends */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_LINE_CODING:
+		if (w_length != sizeof(struct usb_cdc_line_coding)
+				|| w_index != acm->ctrl_id)
+			goto invalid;
+
+		value = w_length;
+		cdev->gadget->ep0->driver_data = acm;
+		req->complete = acm_complete_set_line_coding;
+		break;
+
+	/* GET_LINE_CODING ... return what host sent, or initial value */
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_GET_LINE_CODING:
+		if (w_index != acm->ctrl_id)
+			goto invalid;
+
+		value = min_t(unsigned, w_length,
+				sizeof(struct usb_cdc_line_coding));
+		memcpy(req->buf, &acm->port_line_coding, value);
+		break;
+
+	/* SET_CONTROL_LINE_STATE ... save what the host sent */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		if (w_index != acm->ctrl_id)
+			goto invalid;
+
+		value = 0;
+
+		/* FIXME we should not allow data to flow until the
+		 * host sets the ACM_CTRL_DTR bit; and when it clears
+		 * that bit, we should return to that no-flow state.
+		 */
+		acm->port_handshake_bits = w_value;
+		break;
+
+	default:
+invalid:
+		dev_vdbg(&cdev->gadget->dev,
+			 "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			 ctrl->bRequestType, ctrl->bRequest,
+			 w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		dev_dbg(&cdev->gadget->dev,
+			"acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+			acm->port_num, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "acm response on ttyGS%d, err %d\n",
+					acm->port_num, value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_acm		*acm = func_to_acm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* we know alt == 0, so this is an activation or a reset */
+
+	if (intf == acm->ctrl_id) {
+		dev_vdbg(&cdev->gadget->dev,
+				"reset acm control interface %d\n", intf);
+		usb_ep_disable(acm->notify);
+
+		if (!acm->notify->desc)
+			if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+				return -EINVAL;
+
+		usb_ep_enable(acm->notify);
+
+	} else if (intf == acm->data_id) {
+		if (acm->notify->enabled) {
+			dev_dbg(&cdev->gadget->dev,
+				"reset acm ttyGS%d\n", acm->port_num);
+			gserial_disconnect(&acm->port);
+		}
+		if (!acm->port.in->desc || !acm->port.out->desc) {
+			dev_dbg(&cdev->gadget->dev,
+				"activate acm ttyGS%d\n", acm->port_num);
+			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);
+
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static void acm_disable(struct usb_function *f)
+{
+	struct f_acm	*acm = func_to_acm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num);
+	gserial_disconnect(&acm->port);
+	usb_ep_disable(acm->notify);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * acm_cdc_notify - issue CDC notification to host
+ * @acm: wraps host to be notified
+ * @type: notification type
+ * @value: Refer to cdc specs, wValue field.
+ * @data: data to be sent
+ * @length: size of data
+ * Context: irqs blocked, acm->lock held, acm_notify_req non-null
+ *
+ * Returns zero on success or a negative errno.
+ *
+ * See section 6.3.5 of the CDC 1.1 specification for information
+ * about the only notification we issue:  SerialState change.
+ */
+static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
+		void *data, unsigned length)
+{
+	struct usb_ep			*ep = acm->notify;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*notify;
+	const unsigned			len = sizeof(*notify) + length;
+	void				*buf;
+	int				status;
+
+	req = acm->notify_req;
+	acm->notify_req = NULL;
+	acm->pending = false;
+
+	req->length = len;
+	notify = req->buf;
+	buf = notify + 1;
+
+	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	notify->bNotificationType = type;
+	notify->wValue = cpu_to_le16(value);
+	notify->wIndex = cpu_to_le16(acm->ctrl_id);
+	notify->wLength = cpu_to_le16(length);
+	memcpy(buf, data, length);
+
+	/* ep_queue() can complete immediately if it fills the fifo... */
+	spin_unlock(&acm->lock);
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	spin_lock(&acm->lock);
+
+	if (status < 0) {
+		ERROR(acm->port.func.config->cdev,
+				"acm ttyGS%d can't notify serial state, %d\n",
+				acm->port_num, status);
+		acm->notify_req = req;
+	}
+
+	return status;
+}
+
+static int acm_notify_serial_state(struct f_acm *acm)
+{
+	struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+	int			status;
+	__le16			serial_state;
+
+	spin_lock(&acm->lock);
+	if (acm->notify_req) {
+		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
+			acm->port_num, acm->serial_state);
+		serial_state = cpu_to_le16(acm->serial_state);
+		status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
+				0, &serial_state, sizeof(acm->serial_state));
+	} else {
+		acm->pending = true;
+		status = 0;
+	}
+	spin_unlock(&acm->lock);
+	return status;
+}
+
+static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_acm		*acm = req->context;
+	u8			doit = false;
+
+	/* on this call path we do NOT hold the port spinlock,
+	 * which is why ACM needs its own spinlock
+	 */
+	spin_lock(&acm->lock);
+	if (req->status != -ESHUTDOWN)
+		doit = acm->pending;
+	acm->notify_req = req;
+	spin_unlock(&acm->lock);
+
+	if (doit)
+		acm_notify_serial_state(acm);
+}
+
+/* connect == the TTY link is open */
+
+static void acm_connect(struct gserial *port)
+{
+	struct f_acm		*acm = port_to_acm(port);
+
+	acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+	acm_notify_serial_state(acm);
+}
+
+static void acm_disconnect(struct gserial *port)
+{
+	struct f_acm		*acm = port_to_acm(port);
+
+	acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+	acm_notify_serial_state(acm);
+}
+
+static int acm_send_break(struct gserial *port, int duration)
+{
+	struct f_acm		*acm = port_to_acm(port);
+	u16			state;
+
+	state = acm->serial_state;
+	state &= ~ACM_CTRL_BRK;
+	if (duration)
+		state |= ACM_CTRL_BRK;
+
+	acm->serial_state = state;
+	return acm_notify_serial_state(acm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM function driver setup/binding */
+static int
+acm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_acm		*acm = func_to_acm(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	/* REVISIT might want instance-specific strings to help
+	 * distinguish instances ...
+	 */
+
+	/* maybe allocate device-global string IDs, and patch descriptors */
+	us = usb_gstrings_attach(cdev, acm_strings,
+			ARRAY_SIZE(acm_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
+	acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
+	acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	acm->ctrl_id = status;
+	acm_iad_descriptor.bFirstInterface = status;
+
+	acm_control_interface_desc.bInterfaceNumber = status;
+	acm_union_desc .bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	acm->data_id = status;
+
+	acm_data_interface_desc.bInterfaceNumber = status;
+	acm_union_desc.bSlaveInterface0 = status;
+	acm_call_mgmt_descriptor.bDataInterface = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
+	if (!ep)
+		goto fail;
+	acm->port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
+	if (!ep)
+		goto fail;
+	acm->port.out = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
+	if (!ep)
+		goto fail;
+	acm->notify = ep;
+
+	/* allocate notification */
+	acm->notify_req = gs_alloc_req(ep,
+			sizeof(struct usb_cdc_notification) + 2,
+			GFP_KERNEL);
+	if (!acm->notify_req)
+		goto fail;
+
+	acm->notify_req->complete = acm_cdc_notify_complete;
+	acm->notify_req->context = acm;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
+	acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
+	acm_hs_notify_desc.bEndpointAddress =
+		acm_fs_notify_desc.bEndpointAddress;
+
+	acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
+	acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
+			acm_ss_function, NULL);
+	if (status)
+		goto fail;
+
+	dev_dbg(&cdev->gadget->dev,
+		"acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+		acm->port_num,
+		gadget_is_superspeed(c->cdev->gadget) ? "super" :
+		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+		acm->port.in->name, acm->port.out->name,
+		acm->notify->name);
+	return 0;
+
+fail:
+	if (acm->notify_req)
+		gs_free_req(acm->notify, acm->notify_req);
+
+	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+	return status;
+}
+
+static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_acm		*acm = func_to_acm(f);
+
+	acm_string_defs[0].id = 0;
+	usb_free_all_descriptors(f);
+	if (acm->notify_req)
+		gs_free_req(acm->notify, acm->notify_req);
+}
+
+static void acm_free_func(struct usb_function *f)
+{
+	struct f_acm		*acm = func_to_acm(f);
+
+	kfree(acm);
+}
+
+static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
+{
+	struct f_serial_opts *opts;
+	struct f_acm *acm;
+
+	acm = kzalloc(sizeof(*acm), GFP_KERNEL);
+	if (!acm)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&acm->lock);
+
+	acm->port.connect = acm_connect;
+	acm->port.disconnect = acm_disconnect;
+	acm->port.send_break = acm_send_break;
+
+	acm->port.func.name = "acm";
+	acm->port.func.strings = acm_strings;
+	/* descriptors are per-instance copies */
+	acm->port.func.bind = acm_bind;
+	acm->port.func.set_alt = acm_set_alt;
+	acm->port.func.setup = acm_setup;
+	acm->port.func.disable = acm_disable;
+
+	opts = container_of(fi, struct f_serial_opts, func_inst);
+	acm->port_num = opts->port_num;
+	acm->port.func.unbind = acm_unbind;
+	acm->port.func.free_func = acm_free_func;
+
+	return &acm->port.func;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_serial_opts,
+			func_inst.group);
+}
+
+static void acm_attr_release(struct config_item *item)
+{
+	struct f_serial_opts *opts = to_f_serial_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations acm_item_ops = {
+	.release                = acm_attr_release,
+};
+
+static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
+}
+
+CONFIGFS_ATTR_RO(f_acm_, port_num);
+
+static struct configfs_attribute *acm_attrs[] = {
+	&f_acm_attr_port_num,
+	NULL,
+};
+
+static const struct config_item_type acm_func_type = {
+	.ct_item_ops    = &acm_item_ops,
+	.ct_attrs	= acm_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static void acm_free_instance(struct usb_function_instance *fi)
+{
+	struct f_serial_opts *opts;
+
+	opts = container_of(fi, struct f_serial_opts, func_inst);
+	gserial_free_line(opts->port_num);
+	kfree(opts);
+}
+
+static struct usb_function_instance *acm_alloc_instance(void)
+{
+	struct f_serial_opts *opts;
+	int ret;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	opts->func_inst.free_func_inst = acm_free_instance;
+	ret = gserial_alloc_line(&opts->port_num);
+	if (ret) {
+		kfree(opts);
+		return ERR_PTR(ret);
+	}
+	config_group_init_type_name(&opts->func_inst.group, "",
+			&acm_func_type);
+	return &opts->func_inst;
+}
+DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
new file mode 100644
index 0000000..6ce0440
--- /dev/null
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -0,0 +1,953 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_ecm.c -- USB CDC Ethernet (ECM) link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ecm.h"
+
+
+/*
+ * This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
+ * Ethernet link.  The data transfer model is simple (packets sent and
+ * received over bulk endpoints using normal short packet termination),
+ * and the control model exposes various data and optional notifications.
+ *
+ * ECM is well standardized and (except for Microsoft) supported by most
+ * operating systems with USB host support.  It's the preferred interop
+ * solution for Ethernet over USB, at least for firmware based solutions.
+ * (Hardware solutions tend to be more minimalist.)  A newer and simpler
+ * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
+ *
+ * Note that ECM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+
+enum ecm_notify_state {
+	ECM_NOTIFY_NONE,		/* don't notify */
+	ECM_NOTIFY_CONNECT,		/* issue CONNECT next */
+	ECM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
+};
+
+struct f_ecm {
+	struct gether			port;
+	u8				ctrl_id, data_id;
+
+	char				ethaddr[14];
+
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	u8				notify_state;
+	bool				is_open;
+
+	/* FIXME is_open needs some irq-ish locking
+	 * ... possibly the same as port.ioport
+	 */
+};
+
+static inline struct f_ecm *func_to_ecm(struct usb_function *f)
+{
+	return container_of(f, struct f_ecm, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned ecm_bitrate(struct usb_gadget *g)
+{
+	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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Include the status endpoint if we can, even though it's optional.
+ *
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ *
+ * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
+ * if they ignore the connect/disconnect notifications that real aether
+ * can provide.  More advanced cdc configurations might want to support
+ * encapsulated commands (vendor-specific, using control-OUT).
+ */
+
+#define ECM_STATUS_INTERVAL_MS		32
+#define ECM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_assoc_descriptor
+ecm_iad_descriptor = {
+	.bLength =		sizeof ecm_iad_descriptor,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	USB_CLASS_COMM,
+	.bFunctionSubClass =	USB_CDC_SUBCLASS_ETHERNET,
+	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
+	/* .iFunction =		DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor ecm_control_intf = {
+	.bLength =		sizeof ecm_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	/* status endpoint is optional; this could be patched later */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ETHERNET,
+	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ecm_header_desc = {
+	.bLength =		sizeof ecm_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ecm_union_desc = {
+	.bLength =		sizeof(ecm_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_desc = {
+	.bLength =		sizeof ecm_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
+
+	/* this descriptor actually adds value, surprise! */
+	/* .iMACAddress = DYNAMIC */
+	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
+	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
+	.wNumberMCFilters =	cpu_to_le16(0),
+	.bNumberPowerFilters =	0,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ecm_data_nop_intf = {
+	.bLength =		sizeof ecm_data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ecm_data_intf = {
+	.bLength =		sizeof ecm_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_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 =		ECM_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_ecm_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_ecm_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ecm_fs_function[] = {
+	/* CDC ECM control descriptors */
+	(struct usb_descriptor_header *) &ecm_iad_descriptor,
+	(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 *) &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,
+	(struct usb_descriptor_header *) &fs_ecm_in_desc,
+	(struct usb_descriptor_header *) &fs_ecm_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_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 =		USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
+};
+
+static struct usb_endpoint_descriptor hs_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(512),
+};
+
+static struct usb_endpoint_descriptor hs_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(512),
+};
+
+static struct usb_descriptor_header *ecm_hs_function[] = {
+	/* CDC ECM control descriptors */
+	(struct usb_descriptor_header *) &ecm_iad_descriptor,
+	(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 *) &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,
+	(struct usb_descriptor_header *) &hs_ecm_in_desc,
+	(struct usb_descriptor_header *) &hs_ecm_out_desc,
+	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 =		USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
+};
+
+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_iad_descriptor,
+	(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[] = {
+	[0].s = "CDC Ethernet Control Model (ECM)",
+	[1].s = "",
+	[2].s = "CDC Ethernet Data",
+	[3].s = "CDC ECM",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings ecm_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		ecm_string_defs,
+};
+
+static struct usb_gadget_strings *ecm_strings[] = {
+	&ecm_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void ecm_do_notify(struct f_ecm *ecm)
+{
+	struct usb_request		*req = ecm->notify_req;
+	struct usb_cdc_notification	*event;
+	struct usb_composite_dev	*cdev = ecm->port.func.config->cdev;
+	__le32				*data;
+	int				status;
+
+	/* notification already in flight? */
+	if (!req)
+		return;
+
+	event = req->buf;
+	switch (ecm->notify_state) {
+	case ECM_NOTIFY_NONE:
+		return;
+
+	case ECM_NOTIFY_CONNECT:
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		if (ecm->is_open)
+			event->wValue = cpu_to_le16(1);
+		else
+			event->wValue = cpu_to_le16(0);
+		event->wLength = 0;
+		req->length = sizeof *event;
+
+		DBG(cdev, "notify connect %s\n",
+				ecm->is_open ? "true" : "false");
+		ecm->notify_state = ECM_NOTIFY_SPEED;
+		break;
+
+	case ECM_NOTIFY_SPEED:
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+		req->length = ECM_STATUS_BYTECOUNT;
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof *event;
+		data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
+		ecm->notify_state = ECM_NOTIFY_NONE;
+		break;
+	}
+	event->bmRequestType = 0xA1;
+	event->wIndex = cpu_to_le16(ecm->ctrl_id);
+
+	ecm->notify_req = NULL;
+	status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
+	if (status < 0) {
+		ecm->notify_req = req;
+		DBG(cdev, "notify --> %d\n", status);
+	}
+}
+
+static void ecm_notify(struct f_ecm *ecm)
+{
+	/* NOTE on most versions of Linux, host side cdc-ethernet
+	 * won't listen for notifications until its netdevice opens.
+	 * The first notification then sits in the FIFO for a long
+	 * time, and the second one is queued.
+	 */
+	ecm->notify_state = ECM_NOTIFY_CONNECT;
+	ecm_do_notify(ecm);
+}
+
+static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ecm			*ecm = req->context;
+	struct usb_composite_dev	*cdev = ecm->port.func.config->cdev;
+	struct usb_cdc_notification	*event = req->buf;
+
+	switch (req->status) {
+	case 0:
+		/* no fault */
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		ecm->notify_state = ECM_NOTIFY_NONE;
+		break;
+	default:
+		DBG(cdev, "event %02x --> %d\n",
+			event->bNotificationType, req->status);
+		break;
+	}
+	ecm->notify_req = req;
+	ecm_do_notify(ecm);
+}
+
+static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_ecm		*ecm = func_to_ecm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
+		/* see 6.2.30: no data, wIndex = interface,
+		 * wValue = packet filter bitmap
+		 */
+		if (w_length != 0 || w_index != ecm->ctrl_id)
+			goto invalid;
+		DBG(cdev, "packet filter %02x\n", w_value);
+		/* REVISIT locking of cdc_filter.  This assumes the UDC
+		 * driver won't have a concurrent packet TX irq running on
+		 * another CPU; or that if it does, this write is atomic...
+		 */
+		ecm->port.cdc_filter = w_value;
+		value = 0;
+		break;
+
+	/* and optionally:
+	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+	 * case USB_CDC_GET_ETHERNET_STATISTIC:
+	 */
+
+	default:
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "ecm req %02x.%02x response err %d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+
+static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_ecm		*ecm = func_to_ecm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* Control interface has only altsetting 0 */
+	if (intf == ecm->ctrl_id) {
+		if (alt != 0)
+			goto fail;
+
+		VDBG(cdev, "reset ecm control %d\n", intf);
+		usb_ep_disable(ecm->notify);
+		if (!(ecm->notify->desc)) {
+			VDBG(cdev, "init ecm ctrl %d\n", intf);
+			if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
+				goto fail;
+		}
+		usb_ep_enable(ecm->notify);
+
+	/* Data interface has two altsettings, 0 and 1 */
+	} else if (intf == ecm->data_id) {
+		if (alt > 1)
+			goto fail;
+
+		if (ecm->port.in_ep->enabled) {
+			DBG(cdev, "reset ecm\n");
+			gether_disconnect(&ecm->port);
+		}
+
+		if (!ecm->port.in_ep->desc ||
+		    !ecm->port.out_ep->desc) {
+			DBG(cdev, "init ecm\n");
+			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.
+		 * Changing altsettings resets filters, statistics, etc.
+		 */
+		if (alt == 1) {
+			struct net_device	*net;
+
+			/* Enable zlps by default for ECM conformance;
+			 * override for musb_hdrc (avoids txdma ovhead).
+			 */
+			ecm->port.is_zlp_ok =
+				gadget_is_zlp_supported(cdev->gadget);
+			ecm->port.cdc_filter = DEFAULT_FILTER;
+			DBG(cdev, "activate ecm\n");
+			net = gether_connect(&ecm->port);
+			if (IS_ERR(net))
+				return PTR_ERR(net);
+		}
+
+		/* NOTE this can be a minor disagreement with the ECM spec,
+		 * which says speed notifications will "always" follow
+		 * connection notifications.  But we allow one connect to
+		 * follow another (if the first is in flight), and instead
+		 * just guarantee that a speed notification is always sent.
+		 */
+		ecm_notify(ecm);
+	} else
+		goto fail;
+
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+/* Because the data interface supports multiple altsettings,
+ * this ECM function *MUST* implement a get_alt() method.
+ */
+static int ecm_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_ecm		*ecm = func_to_ecm(f);
+
+	if (intf == ecm->ctrl_id)
+		return 0;
+	return ecm->port.in_ep->enabled ? 1 : 0;
+}
+
+static void ecm_disable(struct usb_function *f)
+{
+	struct f_ecm		*ecm = func_to_ecm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	DBG(cdev, "ecm deactivated\n");
+
+	if (ecm->port.in_ep->enabled)
+		gether_disconnect(&ecm->port);
+
+	usb_ep_disable(ecm->notify);
+	ecm->notify->desc = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Callbacks let us notify the host about connect/disconnect when the
+ * net device is opened or closed.
+ *
+ * For testing, note that link states on this side include both opened
+ * and closed variants of:
+ *
+ *   - disconnected/unconfigured
+ *   - configured but inactive (data alt 0)
+ *   - configured and active (data alt 1)
+ *
+ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
+ * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
+ * imply the host is actually polling the notification endpoint, and
+ * likewise that "active" doesn't imply it's actually using the data
+ * endpoints for traffic.
+ */
+
+static void ecm_open(struct gether *geth)
+{
+	struct f_ecm		*ecm = func_to_ecm(&geth->func);
+
+	DBG(ecm->port.func.config->cdev, "%s\n", __func__);
+
+	ecm->is_open = true;
+	ecm_notify(ecm);
+}
+
+static void ecm_close(struct gether *geth)
+{
+	struct f_ecm		*ecm = func_to_ecm(&geth->func);
+
+	DBG(ecm->port.func.config->cdev, "%s\n", __func__);
+
+	ecm->is_open = false;
+	ecm_notify(ecm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int
+ecm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_ecm		*ecm = func_to_ecm(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	struct f_ecm_opts	*ecm_opts;
+
+	if (!can_support_ecm(cdev->gadget))
+		return -EINVAL;
+
+	ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to ecm_opts->bound access
+	 */
+	if (!ecm_opts->bound) {
+		mutex_lock(&ecm_opts->lock);
+		gether_set_gadget(ecm_opts->net, cdev->gadget);
+		status = gether_register_netdev(ecm_opts->net);
+		mutex_unlock(&ecm_opts->lock);
+		if (status)
+			return status;
+		ecm_opts->bound = true;
+	}
+
+	ecm_string_defs[1].s = ecm->ethaddr;
+
+	us = usb_gstrings_attach(cdev, ecm_strings,
+				 ARRAY_SIZE(ecm_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	ecm_control_intf.iInterface = us[0].id;
+	ecm_data_intf.iInterface = us[2].id;
+	ecm_desc.iMACAddress = us[1].id;
+	ecm_iad_descriptor.iFunction = us[3].id;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ecm->ctrl_id = status;
+	ecm_iad_descriptor.bFirstInterface = status;
+
+	ecm_control_intf.bInterfaceNumber = status;
+	ecm_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ecm->data_id = status;
+
+	ecm_data_nop_intf.bInterfaceNumber = status;
+	ecm_data_intf.bInterfaceNumber = status;
+	ecm_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
+	if (!ep)
+		goto fail;
+	ecm->port.in_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
+	if (!ep)
+		goto fail;
+	ecm->port.out_ep = ep;
+
+	/* NOTE:  a status/notification endpoint is *OPTIONAL* but we
+	 * don't treat it that way.  It's simpler, and some newer CDC
+	 * profiles (wireless handsets) no longer treat it as optional.
+	 */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
+	if (!ep)
+		goto fail;
+	ecm->notify = ep;
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!ecm->notify_req)
+		goto fail;
+	ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
+	if (!ecm->notify_req->buf)
+		goto fail;
+	ecm->notify_req->context = ecm;
+	ecm->notify_req->complete = ecm_notify_complete;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
+	hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
+	hs_ecm_notify_desc.bEndpointAddress =
+		fs_ecm_notify_desc.bEndpointAddress;
+
+	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;
+
+	status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
+			ecm_ss_function, NULL);
+	if (status)
+		goto fail;
+
+	/* NOTE:  all that is done without knowing or caring about
+	 * the network link ... which is unavailable to this code
+	 * until we're activated via set_alt().
+	 */
+
+	ecm->port.open = ecm_open;
+	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);
+	return 0;
+
+fail:
+	if (ecm->notify_req) {
+		kfree(ecm->notify_req->buf);
+		usb_ep_free_request(ecm->notify, ecm->notify_req);
+	}
+
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_ecm_opts,
+			    func_inst.group);
+}
+
+/* f_ecm_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(ecm);
+
+/* f_ecm_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
+
+/* f_ecm_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
+
+/* f_ecm_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
+
+/* f_ecm_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
+
+static struct configfs_attribute *ecm_attrs[] = {
+	&ecm_opts_attr_dev_addr,
+	&ecm_opts_attr_host_addr,
+	&ecm_opts_attr_qmult,
+	&ecm_opts_attr_ifname,
+	NULL,
+};
+
+static const struct config_item_type ecm_func_type = {
+	.ct_item_ops	= &ecm_item_ops,
+	.ct_attrs	= ecm_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void ecm_free_inst(struct usb_function_instance *f)
+{
+	struct f_ecm_opts *opts;
+
+	opts = container_of(f, struct f_ecm_opts, func_inst);
+	if (opts->bound)
+		gether_cleanup(netdev_priv(opts->net));
+	else
+		free_netdev(opts->net);
+	kfree(opts);
+}
+
+static struct usb_function_instance *ecm_alloc_inst(void)
+{
+	struct f_ecm_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = ecm_free_inst;
+	opts->net = gether_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+
+	config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
+
+	return &opts->func_inst;
+}
+
+static void ecm_free(struct usb_function *f)
+{
+	struct f_ecm *ecm;
+	struct f_ecm_opts *opts;
+
+	ecm = func_to_ecm(f);
+	opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+	kfree(ecm);
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+}
+
+static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_ecm		*ecm = func_to_ecm(f);
+
+	DBG(c->cdev, "ecm unbind\n");
+
+	usb_free_all_descriptors(f);
+
+	kfree(ecm->notify_req->buf);
+	usb_ep_free_request(ecm->notify, ecm->notify_req);
+}
+
+static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
+{
+	struct f_ecm	*ecm;
+	struct f_ecm_opts *opts;
+	int status;
+
+	/* allocate and initialize one new instance */
+	ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
+	if (!ecm)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_ecm_opts, func_inst);
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+
+	/* export host's Ethernet address in CDC format */
+	status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
+					  sizeof(ecm->ethaddr));
+	if (status < 12) {
+		kfree(ecm);
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ecm->port.ioport = netdev_priv(opts->net);
+	mutex_unlock(&opts->lock);
+	ecm->port.cdc_filter = DEFAULT_FILTER;
+
+	ecm->port.func.name = "cdc_ethernet";
+	/* descriptors are per-instance copies */
+	ecm->port.func.bind = ecm_bind;
+	ecm->port.func.unbind = ecm_unbind;
+	ecm->port.func.set_alt = ecm_set_alt;
+	ecm->port.func.get_alt = ecm_get_alt;
+	ecm->port.func.setup = ecm_setup;
+	ecm->port.func.disable = ecm_disable;
+	ecm->port.func.free_func = ecm_free;
+
+	return &ecm->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
new file mode 100644
index 0000000..c13befa
--- /dev/null
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -0,0 +1,648 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_eem.c -- USB CDC Ethernet (EEM) link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 EF Johnson Technologies
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_eem.h"
+
+#define EEM_HLEN 2
+
+/*
+ * This function is a "CDC Ethernet Emulation Model" (CDC EEM)
+ * Ethernet link.
+ */
+
+struct f_eem {
+	struct gether			port;
+	u8				ctrl_id;
+};
+
+static inline struct f_eem *func_to_eem(struct usb_function *f)
+{
+	return container_of(f, struct f_eem, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor eem_intf = {
+	.bLength =		sizeof eem_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_EEM,
+	.bInterfaceProtocol =	USB_CDC_PROTO_EEM,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor eem_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor eem_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *eem_fs_function[] = {
+	/* CDC EEM control descriptors */
+	(struct usb_descriptor_header *) &eem_intf,
+	(struct usb_descriptor_header *) &eem_fs_in_desc,
+	(struct usb_descriptor_header *) &eem_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor eem_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor eem_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *eem_hs_function[] = {
+	/* CDC EEM control descriptors */
+	(struct usb_descriptor_header *) &eem_intf,
+	(struct usb_descriptor_header *) &eem_hs_in_desc,
+	(struct usb_descriptor_header *) &eem_hs_out_desc,
+	NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor eem_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 eem_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 eem_ss_bulk_comp_desc = {
+	.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[] = {
+	/* 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[] = {
+	[0].s = "CDC Ethernet Emulation Model (EEM)",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings eem_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		eem_string_defs,
+};
+
+static struct usb_gadget_strings *eem_strings[] = {
+	&eem_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+
+static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_eem		*eem = func_to_eem(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct net_device	*net;
+
+	/* we know alt == 0, so this is an activation or a reset */
+	if (alt != 0)
+		goto fail;
+
+	if (intf == eem->ctrl_id) {
+		DBG(cdev, "reset eem\n");
+		gether_disconnect(&eem->port);
+
+		if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
+			DBG(cdev, "init eem\n");
+			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
+		 * will be inserted in those cases where they would occur
+		 */
+		eem->port.is_zlp_ok = 1;
+		eem->port.cdc_filter = DEFAULT_FILTER;
+		DBG(cdev, "activate eem\n");
+		net = gether_connect(&eem->port);
+		if (IS_ERR(net))
+			return PTR_ERR(net);
+	} else
+		goto fail;
+
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+static void eem_disable(struct usb_function *f)
+{
+	struct f_eem		*eem = func_to_eem(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	DBG(cdev, "eem deactivated\n");
+
+	if (eem->port.in_ep->enabled)
+		gether_disconnect(&eem->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EEM function driver setup/binding */
+
+static int eem_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_eem		*eem = func_to_eem(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	struct f_eem_opts	*eem_opts;
+
+	eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to eem_opts->bound access
+	 */
+	if (!eem_opts->bound) {
+		mutex_lock(&eem_opts->lock);
+		gether_set_gadget(eem_opts->net, cdev->gadget);
+		status = gether_register_netdev(eem_opts->net);
+		mutex_unlock(&eem_opts->lock);
+		if (status)
+			return status;
+		eem_opts->bound = true;
+	}
+
+	us = usb_gstrings_attach(cdev, eem_strings,
+				 ARRAY_SIZE(eem_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	eem_intf.iInterface = us[0].id;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	eem->ctrl_id = status;
+	eem_intf.bInterfaceNumber = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
+	if (!ep)
+		goto fail;
+	eem->port.in_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
+	if (!ep)
+		goto fail;
+	eem->port.out_ep = ep;
+
+	status = -ENOMEM;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
+	eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
+
+	eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
+	eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
+			eem_ss_function, NULL);
+	if (status)
+		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;
+
+fail:
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff *skb = (struct sk_buff *)req->context;
+
+	dev_kfree_skb_any(skb);
+}
+
+/*
+ * Add the EEM header and ethernet checksum.
+ * We currently do not attempt to put multiple ethernet frames
+ * into a single USB transfer
+ */
+static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
+{
+	struct sk_buff	*skb2 = NULL;
+	struct usb_ep	*in = port->in_ep;
+	int		headroom, tailroom, padlen = 0;
+	u16		len;
+
+	if (!skb)
+		return NULL;
+
+	len = skb->len;
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	/* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
+	 * stick two bytes of zero-length EEM packet on the end.
+	 */
+	if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
+		padlen += 2;
+
+	if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
+			(headroom >= EEM_HLEN) && !skb_cloned(skb))
+		goto done;
+
+	skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
+	dev_kfree_skb_any(skb);
+	skb = skb2;
+	if (!skb)
+		return skb;
+
+done:
+	/* use the "no CRC" option */
+	put_unaligned_be32(0xdeadbeef, skb_put(skb, 4));
+
+	/* EEM packet header format:
+	 * b0..13:	length of ethernet frame
+	 * b14:		bmCRC (0 == sentinel CRC)
+	 * b15:		bmType (0 == data)
+	 */
+	len = skb->len;
+	put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
+
+	/* add a zero-length EEM packet, if needed */
+	if (padlen)
+		put_unaligned_le16(0, skb_put(skb, 2));
+
+	return skb;
+}
+
+/*
+ * Remove the EEM header.  Note that there can be many EEM packets in a single
+ * USB transfer, so we need to break them out and handle them independently.
+ */
+static int eem_unwrap(struct gether *port,
+			struct sk_buff *skb,
+			struct sk_buff_head *list)
+{
+	struct usb_composite_dev	*cdev = port->func.config->cdev;
+	int				status = 0;
+
+	do {
+		struct sk_buff	*skb2;
+		u16		header;
+		u16		len = 0;
+
+		if (skb->len < EEM_HLEN) {
+			status = -EINVAL;
+			DBG(cdev, "invalid EEM header\n");
+			goto error;
+		}
+
+		/* remove the EEM header */
+		header = get_unaligned_le16(skb->data);
+		skb_pull(skb, EEM_HLEN);
+
+		/* EEM packet header format:
+		 * b0..14:	EEM type dependent (data or command)
+		 * b15:		bmType (0 == data, 1 == command)
+		 */
+		if (header & BIT(15)) {
+			struct usb_request	*req = cdev->req;
+			u16			bmEEMCmd;
+
+			/* EEM command packet format:
+			 * b0..10:	bmEEMCmdParam
+			 * b11..13:	bmEEMCmd
+			 * b14:		reserved (must be zero)
+			 * b15:		bmType (1 == command)
+			 */
+			if (header & BIT(14))
+				continue;
+
+			bmEEMCmd = (header >> 11) & 0x7;
+			switch (bmEEMCmd) {
+			case 0: /* echo */
+				len = header & 0x7FF;
+				if (skb->len < len) {
+					status = -EOVERFLOW;
+					goto error;
+				}
+
+				skb2 = skb_clone(skb, GFP_ATOMIC);
+				if (unlikely(!skb2)) {
+					DBG(cdev, "EEM echo response error\n");
+					goto next;
+				}
+				skb_trim(skb2, len);
+				put_unaligned_le16(BIT(15) | BIT(11) | len,
+							skb_push(skb2, 2));
+				skb_copy_bits(skb2, 0, req->buf, skb2->len);
+				req->length = skb2->len;
+				req->complete = eem_cmd_complete;
+				req->zero = 1;
+				req->context = skb2;
+				if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
+					DBG(cdev, "echo response queue fail\n");
+				break;
+
+			case 1:  /* echo response */
+			case 2:  /* suspend hint */
+			case 3:  /* response hint */
+			case 4:  /* response complete hint */
+			case 5:  /* tickle */
+			default: /* reserved */
+				continue;
+			}
+		} else {
+			u32		crc, crc2;
+			struct sk_buff	*skb3;
+
+			/* check for zero-length EEM packet */
+			if (header == 0)
+				continue;
+
+			/* EEM data packet format:
+			 * b0..13:	length of ethernet frame
+			 * b14:		bmCRC (0 == sentinel, 1 == calculated)
+			 * b15:		bmType (0 == data)
+			 */
+			len = header & 0x3FFF;
+			if ((skb->len < len)
+					|| (len < (ETH_HLEN + ETH_FCS_LEN))) {
+				status = -EINVAL;
+				goto error;
+			}
+
+			/* validate CRC */
+			if (header & BIT(14)) {
+				crc = get_unaligned_le32(skb->data + len
+							- ETH_FCS_LEN);
+				crc2 = ~crc32_le(~0,
+						skb->data, len - ETH_FCS_LEN);
+			} else {
+				crc = get_unaligned_be32(skb->data + len
+							- ETH_FCS_LEN);
+				crc2 = 0xdeadbeef;
+			}
+			if (crc != crc2) {
+				DBG(cdev, "invalid EEM CRC\n");
+				goto next;
+			}
+
+			skb2 = skb_clone(skb, GFP_ATOMIC);
+			if (unlikely(!skb2)) {
+				DBG(cdev, "unable to unframe EEM packet\n");
+				continue;
+			}
+			skb_trim(skb2, len - ETH_FCS_LEN);
+
+			skb3 = skb_copy_expand(skb2,
+						NET_IP_ALIGN,
+						0,
+						GFP_ATOMIC);
+			if (unlikely(!skb3)) {
+				dev_kfree_skb_any(skb2);
+				continue;
+			}
+			dev_kfree_skb_any(skb2);
+			skb_queue_tail(list, skb3);
+		}
+next:
+		skb_pull(skb, len);
+	} while (skb->len);
+
+error:
+	dev_kfree_skb_any(skb);
+	return status;
+}
+
+static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_eem_opts,
+			    func_inst.group);
+}
+
+/* f_eem_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(eem);
+
+/* f_eem_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
+
+/* f_eem_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
+
+/* f_eem_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
+
+/* f_eem_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
+
+static struct configfs_attribute *eem_attrs[] = {
+	&eem_opts_attr_dev_addr,
+	&eem_opts_attr_host_addr,
+	&eem_opts_attr_qmult,
+	&eem_opts_attr_ifname,
+	NULL,
+};
+
+static const struct config_item_type eem_func_type = {
+	.ct_item_ops	= &eem_item_ops,
+	.ct_attrs	= eem_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void eem_free_inst(struct usb_function_instance *f)
+{
+	struct f_eem_opts *opts;
+
+	opts = container_of(f, struct f_eem_opts, func_inst);
+	if (opts->bound)
+		gether_cleanup(netdev_priv(opts->net));
+	else
+		free_netdev(opts->net);
+	kfree(opts);
+}
+
+static struct usb_function_instance *eem_alloc_inst(void)
+{
+	struct f_eem_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = eem_free_inst;
+	opts->net = gether_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+
+	config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
+
+	return &opts->func_inst;
+}
+
+static void eem_free(struct usb_function *f)
+{
+	struct f_eem *eem;
+	struct f_eem_opts *opts;
+
+	eem = func_to_eem(f);
+	opts = container_of(f->fi, struct f_eem_opts, func_inst);
+	kfree(eem);
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+}
+
+static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	DBG(c->cdev, "eem unbind\n");
+
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *eem_alloc(struct usb_function_instance *fi)
+{
+	struct f_eem	*eem;
+	struct f_eem_opts *opts;
+
+	/* allocate and initialize one new instance */
+	eem = kzalloc(sizeof(*eem), GFP_KERNEL);
+	if (!eem)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_eem_opts, func_inst);
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+
+	eem->port.ioport = netdev_priv(opts->net);
+	mutex_unlock(&opts->lock);
+	eem->port.cdc_filter = DEFAULT_FILTER;
+
+	eem->port.func.name = "cdc_eem";
+	/* descriptors are per-instance copies */
+	eem->port.func.bind = eem_bind;
+	eem->port.func.unbind = eem_unbind;
+	eem->port.func.set_alt = eem_set_alt;
+	eem->port.func.setup = eem_setup;
+	eem->port.func.disable = eem_disable;
+	eem->port.func.free_func = eem_free;
+	eem->port.wrap = eem_wrap;
+	eem->port.unwrap = eem_unwrap;
+	eem->port.header_len = EEM_HLEN;
+
+	return &eem->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
new file mode 100644
index 0000000..31e8bf3
--- /dev/null
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -0,0 +1,3734 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_fs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ *
+ * Based on inode.c (GadgetFS) which was:
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ */
+
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
+#include <linux/uio.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+#include <linux/aio.h>
+#include <linux/mmu_context.h>
+#include <linux/poll.h>
+#include <linux/eventfd.h>
+
+#include "u_fs.h"
+#include "u_f.h"
+#include "u_os_desc.h"
+#include "configfs.h"
+
+#define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
+
+/* Reference counter handling */
+static void ffs_data_get(struct ffs_data *ffs);
+static void ffs_data_put(struct ffs_data *ffs);
+/* Creates new ffs_data object. */
+static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
+	__attribute__((malloc));
+
+/* Opened counter handling. */
+static void ffs_data_opened(struct ffs_data *ffs);
+static void ffs_data_closed(struct ffs_data *ffs);
+
+/* Called with ffs->mutex held; take over ownership of data. */
+static int __must_check
+__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
+static int __must_check
+__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
+
+
+/* The function structure ***************************************************/
+
+struct ffs_ep;
+
+struct ffs_function {
+	struct usb_configuration	*conf;
+	struct usb_gadget		*gadget;
+	struct ffs_data			*ffs;
+
+	struct ffs_ep			*eps;
+	u8				eps_revmap[16];
+	short				*interfaces_nums;
+
+	struct usb_function		function;
+};
+
+
+static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
+{
+	return container_of(f, struct ffs_function, function);
+}
+
+
+static inline enum ffs_setup_state
+ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
+{
+	return (enum ffs_setup_state)
+		cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
+}
+
+
+static void ffs_func_eps_disable(struct ffs_function *func);
+static int __must_check ffs_func_eps_enable(struct ffs_function *func);
+
+static int ffs_func_bind(struct usb_configuration *,
+			 struct usb_function *);
+static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static void ffs_func_disable(struct usb_function *);
+static int ffs_func_setup(struct usb_function *,
+			  const struct usb_ctrlrequest *);
+static bool ffs_func_req_match(struct usb_function *,
+			       const struct usb_ctrlrequest *,
+			       bool config0);
+static void ffs_func_suspend(struct usb_function *);
+static void ffs_func_resume(struct usb_function *);
+
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
+
+
+/* The endpoints structures *************************************************/
+
+struct ffs_ep {
+	struct usb_ep			*ep;	/* P: ffs->eps_lock */
+	struct usb_request		*req;	/* P: epfile->mutex */
+
+	/* [0]: full speed, [1]: high speed, [2]: super speed */
+	struct usb_endpoint_descriptor	*descs[3];
+
+	u8				num;
+
+	int				status;	/* P: epfile->mutex */
+};
+
+struct ffs_epfile {
+	/* Protects ep->ep and ep->req. */
+	struct mutex			mutex;
+
+	struct ffs_data			*ffs;
+	struct ffs_ep			*ep;	/* P: ffs->eps_lock */
+
+	struct dentry			*dentry;
+
+	/*
+	 * Buffer for holding data from partial reads which may happen since
+	 * we’re rounding user read requests to a multiple of a max packet size.
+	 *
+	 * The pointer is initialised with NULL value and may be set by
+	 * __ffs_epfile_read_data function to point to a temporary buffer.
+	 *
+	 * In normal operation, calls to __ffs_epfile_read_buffered will consume
+	 * data from said buffer and eventually free it.  Importantly, while the
+	 * function is using the buffer, it sets the pointer to NULL.  This is
+	 * all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
+	 * can never run concurrently (they are synchronised by epfile->mutex)
+	 * so the latter will not assign a new value to the pointer.
+	 *
+	 * Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
+	 * valid) and sets the pointer to READ_BUFFER_DROP value.  This special
+	 * value is crux of the synchronisation between ffs_func_eps_disable and
+	 * __ffs_epfile_read_data.
+	 *
+	 * Once __ffs_epfile_read_data is about to finish it will try to set the
+	 * pointer back to its old value (as described above), but seeing as the
+	 * pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
+	 * the buffer.
+	 *
+	 * == State transitions ==
+	 *
+	 * • ptr == NULL:  (initial state)
+	 *   â—¦ __ffs_epfile_read_buffer_free: go to ptr == DROP
+	 *   â—¦ __ffs_epfile_read_buffered:    nop
+	 *   â—¦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
+	 *   â—¦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == DROP:
+	 *   â—¦ __ffs_epfile_read_buffer_free: nop
+	 *   â—¦ __ffs_epfile_read_buffered:    go to ptr == NULL
+	 *   â—¦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
+	 *   â—¦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == buf:
+	 *   â—¦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
+	 *   â—¦ __ffs_epfile_read_buffered:    go to ptr == NULL and reading
+	 *   â—¦ __ffs_epfile_read_data:        n/a, __ffs_epfile_read_buffered
+	 *                                    is always called first
+	 *   â—¦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == NULL and reading:
+	 *   â—¦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
+	 *   â—¦ __ffs_epfile_read_buffered:    n/a, mutex is held
+	 *   â—¦ __ffs_epfile_read_data:        n/a, mutex is held
+	 *   â—¦ reading finishes and …
+	 *     … all data read:               free buf, go to ptr == NULL
+	 *     … otherwise:                   go to ptr == buf and reading
+	 * • ptr == DROP and reading:
+	 *   â—¦ __ffs_epfile_read_buffer_free: nop
+	 *   â—¦ __ffs_epfile_read_buffered:    n/a, mutex is held
+	 *   â—¦ __ffs_epfile_read_data:        n/a, mutex is held
+	 *   â—¦ reading finishes:              free buf, go to ptr == DROP
+	 */
+	struct ffs_buffer		*read_buffer;
+#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
+
+	char				name[5];
+
+	unsigned char			in;	/* P: ffs->eps_lock */
+	unsigned char			isoc;	/* P: ffs->eps_lock */
+
+	unsigned char			_pad;
+};
+
+struct ffs_buffer {
+	size_t length;
+	char *data;
+	char storage[];
+};
+
+/*  ffs_io_data structure ***************************************************/
+
+struct ffs_io_data {
+	bool aio;
+	bool read;
+
+	struct kiocb *kiocb;
+	struct iov_iter data;
+	const void *to_free;
+	char *buf;
+
+	struct mm_struct *mm;
+	struct work_struct work;
+
+	struct usb_ep *ep;
+	struct usb_request *req;
+
+	struct ffs_data *ffs;
+};
+
+struct ffs_desc_helper {
+	struct ffs_data *ffs;
+	unsigned interfaces_count;
+	unsigned eps_count;
+};
+
+static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+
+static struct dentry *
+ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+		   const struct file_operations *fops);
+
+/* Devices management *******************************************************/
+
+DEFINE_MUTEX(ffs_lock);
+EXPORT_SYMBOL_GPL(ffs_lock);
+
+static struct ffs_dev *_ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_alloc_dev(void);
+static void _ffs_free_dev(struct ffs_dev *dev);
+static void *ffs_acquire_dev(const char *dev_name);
+static void ffs_release_dev(struct ffs_data *ffs_data);
+static int ffs_ready(struct ffs_data *ffs);
+static void ffs_closed(struct ffs_data *ffs);
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+	__attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
+	__attribute__((warn_unused_result, nonnull));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ffs_data *ffs = req->context;
+
+	complete(&ffs->ep0req_completion);
+}
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+	__releases(&ffs->ev.waitq.lock)
+{
+	struct usb_request *req = ffs->ep0req;
+	int ret;
+
+	req->zero     = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+
+	req->buf      = data;
+	req->length   = len;
+
+	/*
+	 * UDC layer requires to provide a buffer even for ZLP, but should
+	 * not use it at all. Let's provide some poisoned pointer to catch
+	 * possible bug in the driver.
+	 */
+	if (req->buf == NULL)
+		req->buf = (void *)0xDEADBABE;
+
+	reinit_completion(&ffs->ep0req_completion);
+
+	ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+	if (unlikely(ret < 0))
+		return ret;
+
+	ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+	if (unlikely(ret)) {
+		usb_ep_dequeue(ffs->gadget->ep0, req);
+		return -EINTR;
+	}
+
+	ffs->setup_state = FFS_NO_SETUP;
+	return req->status ? req->status : req->actual;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+	if (ffs->ev.can_stall) {
+		pr_vdebug("ep0 stall\n");
+		usb_ep_set_halt(ffs->gadget->ep0);
+		ffs->setup_state = FFS_NO_SETUP;
+		return -EL2HLT;
+	} else {
+		pr_debug("bogus ep0 stall!\n");
+		return -ESRCH;
+	}
+}
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+			     size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	ssize_t ret;
+	char *data;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+	/* Check state */
+	switch (ffs->state) {
+	case FFS_READ_DESCRIPTORS:
+	case FFS_READ_STRINGS:
+		/* Copy data */
+		if (unlikely(len < 16)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		data = ffs_prepare_buffer(buf, len);
+		if (IS_ERR(data)) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		/* Handle data */
+		if (ffs->state == FFS_READ_DESCRIPTORS) {
+			pr_info("read descriptors\n");
+			ret = __ffs_data_got_descs(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ffs->state = FFS_READ_STRINGS;
+			ret = len;
+		} else {
+			pr_info("read strings\n");
+			ret = __ffs_data_got_strings(ffs, data, len);
+			if (unlikely(ret < 0))
+				break;
+
+			ret = ffs_epfiles_create(ffs);
+			if (unlikely(ret)) {
+				ffs->state = FFS_CLOSING;
+				break;
+			}
+
+			ffs->state = FFS_ACTIVE;
+			mutex_unlock(&ffs->mutex);
+
+			ret = ffs_ready(ffs);
+			if (unlikely(ret < 0)) {
+				ffs->state = FFS_CLOSING;
+				return ret;
+			}
+
+			return len;
+		}
+		break;
+
+	case FFS_ACTIVE:
+		data = NULL;
+		/*
+		 * We're called from user space, we can use _irq
+		 * rather then _irqsave
+		 */
+		spin_lock_irq(&ffs->ev.waitq.lock);
+		switch (ffs_setup_state_clear_cancelled(ffs)) {
+		case FFS_SETUP_CANCELLED:
+			ret = -EIDRM;
+			goto done_spin;
+
+		case FFS_NO_SETUP:
+			ret = -ESRCH;
+			goto done_spin;
+
+		case FFS_SETUP_PENDING:
+			break;
+		}
+
+		/* FFS_SETUP_PENDING */
+		if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+			spin_unlock_irq(&ffs->ev.waitq.lock);
+			ret = __ffs_ep0_stall(ffs);
+			break;
+		}
+
+		/* FFS_SETUP_PENDING and not stall */
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		data = ffs_prepare_buffer(buf, len);
+		if (IS_ERR(data)) {
+			ret = PTR_ERR(data);
+			break;
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/*
+		 * We are guaranteed to be still in FFS_ACTIVE state
+		 * but the state of setup could have changed from
+		 * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
+		 * to check for that.  If that happened we copied data
+		 * from user space in vain but it's unlikely.
+		 *
+		 * For sure we are not in FFS_NO_SETUP since this is
+		 * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+		 * transition can be performed and it's protected by
+		 * mutex.
+		 */
+		if (ffs_setup_state_clear_cancelled(ffs) ==
+		    FFS_SETUP_CANCELLED) {
+			ret = -EIDRM;
+done_spin:
+			spin_unlock_irq(&ffs->ev.waitq.lock);
+		} else {
+			/* unlocks spinlock */
+			ret = __ffs_ep0_queue_wait(ffs, data, len);
+		}
+		kfree(data);
+		break;
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+	mutex_unlock(&ffs->mutex);
+	return ret;
+}
+
+/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+				     size_t n)
+	__releases(&ffs->ev.waitq.lock)
+{
+	/*
+	 * n cannot be bigger than ffs->ev.count, which cannot be bigger than
+	 * size of ffs->ev.types array (which is four) so that's how much space
+	 * we reserve.
+	 */
+	struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
+	const size_t size = n * sizeof *events;
+	unsigned i = 0;
+
+	memset(events, 0, size);
+
+	do {
+		events[i].type = ffs->ev.types[i];
+		if (events[i].type == FUNCTIONFS_SETUP) {
+			events[i].u.setup = ffs->ev.setup;
+			ffs->setup_state = FFS_SETUP_PENDING;
+		}
+	} while (++i < n);
+
+	ffs->ev.count -= n;
+	if (ffs->ev.count)
+		memmove(ffs->ev.types, ffs->ev.types + n,
+			ffs->ev.count * sizeof *ffs->ev.types);
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+	mutex_unlock(&ffs->mutex);
+
+	return unlikely(copy_to_user(buf, events, size)) ? -EFAULT : size;
+}
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+			    size_t len, loff_t *ptr)
+{
+	struct ffs_data *ffs = file->private_data;
+	char *data = NULL;
+	size_t n;
+	int ret;
+
+	ENTER();
+
+	/* Fast check if setup was canceled */
+	if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
+		return -EIDRM;
+
+	/* Acquire mutex */
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return ret;
+
+	/* Check state */
+	if (ffs->state != FFS_ACTIVE) {
+		ret = -EBADFD;
+		goto done_mutex;
+	}
+
+	/*
+	 * We're called from user space, we can use _irq rather then
+	 * _irqsave
+	 */
+	spin_lock_irq(&ffs->ev.waitq.lock);
+
+	switch (ffs_setup_state_clear_cancelled(ffs)) {
+	case FFS_SETUP_CANCELLED:
+		ret = -EIDRM;
+		break;
+
+	case FFS_NO_SETUP:
+		n = len / sizeof(struct usb_functionfs_event);
+		if (unlikely(!n)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
+							ffs->ev.count)) {
+			ret = -EINTR;
+			break;
+		}
+
+		/* unlocks spinlock */
+		return __ffs_ep0_read_events(ffs, buf,
+					     min(n, (size_t)ffs->ev.count));
+
+	case FFS_SETUP_PENDING:
+		if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+			spin_unlock_irq(&ffs->ev.waitq.lock);
+			ret = __ffs_ep0_stall(ffs);
+			goto done_mutex;
+		}
+
+		len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+		spin_unlock_irq(&ffs->ev.waitq.lock);
+
+		if (likely(len)) {
+			data = kmalloc(len, GFP_KERNEL);
+			if (unlikely(!data)) {
+				ret = -ENOMEM;
+				goto done_mutex;
+			}
+		}
+
+		spin_lock_irq(&ffs->ev.waitq.lock);
+
+		/* See ffs_ep0_write() */
+		if (ffs_setup_state_clear_cancelled(ffs) ==
+		    FFS_SETUP_CANCELLED) {
+			ret = -EIDRM;
+			break;
+		}
+
+		/* unlocks spinlock */
+		ret = __ffs_ep0_queue_wait(ffs, data, len);
+		if (likely(ret > 0) && unlikely(copy_to_user(buf, data, len)))
+			ret = -EFAULT;
+		goto done_mutex;
+
+	default:
+		ret = -EBADFD;
+		break;
+	}
+
+	spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+	mutex_unlock(&ffs->mutex);
+	kfree(data);
+	return ret;
+}
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = inode->i_private;
+
+	ENTER();
+
+	if (unlikely(ffs->state == FFS_CLOSING))
+		return -EBUSY;
+
+	file->private_data = ffs;
+	ffs_data_opened(ffs);
+
+	return 0;
+}
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+	struct ffs_data *ffs = file->private_data;
+
+	ENTER();
+
+	ffs_data_closed(ffs);
+
+	return 0;
+}
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+	struct ffs_data *ffs = file->private_data;
+	struct usb_gadget *gadget = ffs->gadget;
+	long ret;
+
+	ENTER();
+
+	if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+		struct ffs_function *func = ffs->func;
+		ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+	} else if (gadget && gadget->ops->ioctl) {
+		ret = gadget->ops->ioctl(gadget, code, value);
+	} else {
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait)
+{
+	struct ffs_data *ffs = file->private_data;
+	__poll_t mask = EPOLLWRNORM;
+	int ret;
+
+	poll_wait(file, &ffs->ev.waitq, wait);
+
+	ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret < 0))
+		return mask;
+
+	switch (ffs->state) {
+	case FFS_READ_DESCRIPTORS:
+	case FFS_READ_STRINGS:
+		mask |= EPOLLOUT;
+		break;
+
+	case FFS_ACTIVE:
+		switch (ffs->setup_state) {
+		case FFS_NO_SETUP:
+			if (ffs->ev.count)
+				mask |= EPOLLIN;
+			break;
+
+		case FFS_SETUP_PENDING:
+		case FFS_SETUP_CANCELLED:
+			mask |= (EPOLLIN | EPOLLOUT);
+			break;
+		}
+	case FFS_CLOSING:
+		break;
+	case FFS_DEACTIVATED:
+		break;
+	}
+
+	mutex_unlock(&ffs->mutex);
+
+	return mask;
+}
+
+static const struct file_operations ffs_ep0_operations = {
+	.llseek =	no_llseek,
+
+	.open =		ffs_ep0_open,
+	.write =	ffs_ep0_write,
+	.read =		ffs_ep0_read,
+	.release =	ffs_ep0_release,
+	.unlocked_ioctl =	ffs_ep0_ioctl,
+	.poll =		ffs_ep0_poll,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+	ENTER();
+	if (likely(req->context)) {
+		struct ffs_ep *ep = _ep->driver_data;
+		ep->status = req->status ? req->status : req->actual;
+		complete(req->context);
+	}
+}
+
+static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
+{
+	ssize_t ret = copy_to_iter(data, data_len, iter);
+	if (likely(ret == data_len))
+		return ret;
+
+	if (unlikely(iov_iter_count(iter)))
+		return -EFAULT;
+
+	/*
+	 * Dear user space developer!
+	 *
+	 * TL;DR: To stop getting below error message in your kernel log, change
+	 * user space code using functionfs to align read buffers to a max
+	 * packet size.
+	 *
+	 * Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max
+	 * packet size.  When unaligned buffer is passed to functionfs, it
+	 * internally uses a larger, aligned buffer so that such UDCs are happy.
+	 *
+	 * Unfortunately, this means that host may send more data than was
+	 * requested in read(2) system call.  f_fs doesn’t know what to do with
+	 * that excess data so it simply drops it.
+	 *
+	 * Was the buffer aligned in the first place, no such problem would
+	 * happen.
+	 *
+	 * Data may be dropped only in AIO reads.  Synchronous reads are handled
+	 * by splitting a request into multiple parts.  This splitting may still
+	 * be a problem though so it’s likely best to align the buffer
+	 * regardless of it being AIO or not..
+	 *
+	 * This only affects OUT endpoints, i.e. reading data with a read(2),
+	 * aio_read(2) etc. system calls.  Writing data to an IN endpoint is not
+	 * affected.
+	 */
+	pr_err("functionfs read size %d > requested size %zd, dropping excess data. "
+	       "Align read buffer size to max packet size to avoid the problem.\n",
+	       data_len, ret);
+
+	return ret;
+}
+
+static void ffs_user_copy_worker(struct work_struct *work)
+{
+	struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
+						   work);
+	int ret = io_data->req->status ? io_data->req->status :
+					 io_data->req->actual;
+	bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
+
+	if (io_data->read && ret > 0) {
+		mm_segment_t oldfs = get_fs();
+
+		set_fs(USER_DS);
+		use_mm(io_data->mm);
+		ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
+		unuse_mm(io_data->mm);
+		set_fs(oldfs);
+	}
+
+	io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
+
+	if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
+		eventfd_signal(io_data->ffs->ffs_eventfd, 1);
+
+	usb_ep_free_request(io_data->ep, io_data->req);
+
+	if (io_data->read)
+		kfree(io_data->to_free);
+	kfree(io_data->buf);
+	kfree(io_data);
+}
+
+static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
+					 struct usb_request *req)
+{
+	struct ffs_io_data *io_data = req->context;
+	struct ffs_data *ffs = io_data->ffs;
+
+	ENTER();
+
+	INIT_WORK(&io_data->work, ffs_user_copy_worker);
+	queue_work(ffs->io_completion_wq, &io_data->work);
+}
+
+static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
+{
+	/*
+	 * See comment in struct ffs_epfile for full read_buffer pointer
+	 * synchronisation story.
+	 */
+	struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
+	if (buf && buf != READ_BUFFER_DROP)
+		kfree(buf);
+}
+
+/* Assumes epfile->mutex is held. */
+static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
+					  struct iov_iter *iter)
+{
+	/*
+	 * Null out epfile->read_buffer so ffs_func_eps_disable does not free
+	 * the buffer while we are using it.  See comment in struct ffs_epfile
+	 * for full read_buffer pointer synchronisation story.
+	 */
+	struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
+	ssize_t ret;
+	if (!buf || buf == READ_BUFFER_DROP)
+		return 0;
+
+	ret = copy_to_iter(buf->data, buf->length, iter);
+	if (buf->length == ret) {
+		kfree(buf);
+		return ret;
+	}
+
+	if (unlikely(iov_iter_count(iter))) {
+		ret = -EFAULT;
+	} else {
+		buf->length -= ret;
+		buf->data += ret;
+	}
+
+	if (cmpxchg(&epfile->read_buffer, NULL, buf))
+		kfree(buf);
+
+	return ret;
+}
+
+/* Assumes epfile->mutex is held. */
+static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
+				      void *data, int data_len,
+				      struct iov_iter *iter)
+{
+	struct ffs_buffer *buf;
+
+	ssize_t ret = copy_to_iter(data, data_len, iter);
+	if (likely(data_len == ret))
+		return ret;
+
+	if (unlikely(iov_iter_count(iter)))
+		return -EFAULT;
+
+	/* See ffs_copy_to_iter for more context. */
+	pr_warn("functionfs read size %d > requested size %zd, splitting request into multiple reads.",
+		data_len, ret);
+
+	data_len -= ret;
+	buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	buf->length = data_len;
+	buf->data = buf->storage;
+	memcpy(buf->storage, data + ret, data_len);
+
+	/*
+	 * At this point read_buffer is NULL or READ_BUFFER_DROP (if
+	 * ffs_func_eps_disable has been called in the meanwhile).  See comment
+	 * in struct ffs_epfile for full read_buffer pointer synchronisation
+	 * story.
+	 */
+	if (unlikely(cmpxchg(&epfile->read_buffer, NULL, buf)))
+		kfree(buf);
+
+	return ret;
+}
+
+static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	struct usb_request *req;
+	struct ffs_ep *ep;
+	char *data = NULL;
+	ssize_t ret, data_len = -EINVAL;
+	int halt;
+
+	/* Are we still active? */
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	/* Wait for endpoint to be enabled */
+	ep = epfile->ep;
+	if (!ep) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible(
+				epfile->ffs->wait, (ep = epfile->ep));
+		if (ret)
+			return -EINTR;
+	}
+
+	/* Do we halt? */
+	halt = (!io_data->read == !epfile->in);
+	if (halt && epfile->isoc)
+		return -EINVAL;
+
+	/* We will be using request and read_buffer */
+	ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
+	if (unlikely(ret))
+		goto error;
+
+	/* Allocate & copy */
+	if (!halt) {
+		struct usb_gadget *gadget;
+
+		/*
+		 * Do we have buffered data from previous partial read?  Check
+		 * that for synchronous case only because we do not have
+		 * facility to ‘wake up’ a pending asynchronous read and push
+		 * buffered data to it which we would need to make things behave
+		 * consistently.
+		 */
+		if (!io_data->aio && io_data->read) {
+			ret = __ffs_epfile_read_buffered(epfile, &io_data->data);
+			if (ret)
+				goto error_mutex;
+		}
+
+		/*
+		 * if we _do_ wait above, the epfile->ffs->gadget might be NULL
+		 * before the waiting completes, so do not assign to 'gadget'
+		 * earlier
+		 */
+		gadget = epfile->ffs->gadget;
+
+		spin_lock_irq(&epfile->ffs->eps_lock);
+		/* In the meantime, endpoint got disabled or changed. */
+		if (epfile->ep != ep) {
+			ret = -ESHUTDOWN;
+			goto error_lock;
+		}
+		data_len = iov_iter_count(&io_data->data);
+		/*
+		 * Controller may require buffer size to be aligned to
+		 * maxpacketsize of an out endpoint.
+		 */
+		if (io_data->read)
+			data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+
+		data = kmalloc(data_len, GFP_KERNEL);
+		if (unlikely(!data)) {
+			ret = -ENOMEM;
+			goto error_mutex;
+		}
+		if (!io_data->read &&
+		    !copy_from_iter_full(data, data_len, &io_data->data)) {
+			ret = -EFAULT;
+			goto error_mutex;
+		}
+	}
+
+	spin_lock_irq(&epfile->ffs->eps_lock);
+
+	if (epfile->ep != ep) {
+		/* In the meantime, endpoint got disabled or changed. */
+		ret = -ESHUTDOWN;
+	} else if (halt) {
+		ret = usb_ep_set_halt(ep->ep);
+		if (!ret)
+			ret = -EBADMSG;
+	} else if (unlikely(data_len == -EINVAL)) {
+		/*
+		 * Sanity Check: even though data_len can't be used
+		 * uninitialized at the time I write this comment, some
+		 * compilers complain about this situation.
+		 * In order to keep the code clean from warnings, data_len is
+		 * being initialized to -EINVAL during its declaration, which
+		 * means we can't rely on compiler anymore to warn no future
+		 * changes won't result in data_len being used uninitialized.
+		 * For such reason, we're adding this redundant sanity check
+		 * here.
+		 */
+		WARN(1, "%s: data_len == -EINVAL\n", __func__);
+		ret = -EINVAL;
+	} else if (!io_data->aio) {
+		DECLARE_COMPLETION_ONSTACK(done);
+		bool interrupted = false;
+
+		req = ep->req;
+		req->buf      = data;
+		req->length   = data_len;
+
+		req->context  = &done;
+		req->complete = ffs_epfile_io_complete;
+
+		ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+		if (unlikely(ret < 0))
+			goto error_lock;
+
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+
+		if (unlikely(wait_for_completion_interruptible(&done))) {
+			/*
+			 * To avoid race condition with ffs_epfile_io_complete,
+			 * dequeue the request first then check
+			 * status. usb_ep_dequeue API should guarantee no race
+			 * condition with req->complete callback.
+			 */
+			usb_ep_dequeue(ep->ep, req);
+			interrupted = ep->status < 0;
+		}
+
+		if (interrupted)
+			ret = -EINTR;
+		else if (io_data->read && ep->status > 0)
+			ret = __ffs_epfile_read_data(epfile, data, ep->status,
+						     &io_data->data);
+		else
+			ret = ep->status;
+		goto error_mutex;
+	} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
+		ret = -ENOMEM;
+	} else {
+		req->buf      = data;
+		req->length   = data_len;
+
+		io_data->buf = data;
+		io_data->ep = ep->ep;
+		io_data->req = req;
+		io_data->ffs = epfile->ffs;
+
+		req->context  = io_data;
+		req->complete = ffs_epfile_async_io_complete;
+
+		ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+		if (unlikely(ret)) {
+			usb_ep_free_request(ep->ep, req);
+			goto error_lock;
+		}
+
+		ret = -EIOCBQUEUED;
+		/*
+		 * Do not kfree the buffer in this function.  It will be freed
+		 * by ffs_user_copy_worker.
+		 */
+		data = NULL;
+	}
+
+error_lock:
+	spin_unlock_irq(&epfile->ffs->eps_lock);
+error_mutex:
+	mutex_unlock(&epfile->mutex);
+error:
+	kfree(data);
+	return ret;
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	file->private_data = epfile;
+	ffs_data_opened(epfile->ffs);
+
+	return 0;
+}
+
+static int ffs_aio_cancel(struct kiocb *kiocb)
+{
+	struct ffs_io_data *io_data = kiocb->private;
+	struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
+	int value;
+
+	ENTER();
+
+	spin_lock_irq(&epfile->ffs->eps_lock);
+
+	if (likely(io_data && io_data->ep && io_data->req))
+		value = usb_ep_dequeue(io_data->ep, io_data->req);
+	else
+		value = -EINVAL;
+
+	spin_unlock_irq(&epfile->ffs->eps_lock);
+
+	return value;
+}
+
+static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
+{
+	struct ffs_io_data io_data, *p = &io_data;
+	ssize_t res;
+
+	ENTER();
+
+	if (!is_sync_kiocb(kiocb)) {
+		p = kmalloc(sizeof(io_data), GFP_KERNEL);
+		if (unlikely(!p))
+			return -ENOMEM;
+		p->aio = true;
+	} else {
+		p->aio = false;
+	}
+
+	p->read = false;
+	p->kiocb = kiocb;
+	p->data = *from;
+	p->mm = current->mm;
+
+	kiocb->private = p;
+
+	if (p->aio)
+		kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+	res = ffs_epfile_io(kiocb->ki_filp, p);
+	if (res == -EIOCBQUEUED)
+		return res;
+	if (p->aio)
+		kfree(p);
+	else
+		*from = p->data;
+	return res;
+}
+
+static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
+{
+	struct ffs_io_data io_data, *p = &io_data;
+	ssize_t res;
+
+	ENTER();
+
+	if (!is_sync_kiocb(kiocb)) {
+		p = kmalloc(sizeof(io_data), GFP_KERNEL);
+		if (unlikely(!p))
+			return -ENOMEM;
+		p->aio = true;
+	} else {
+		p->aio = false;
+	}
+
+	p->read = true;
+	p->kiocb = kiocb;
+	if (p->aio) {
+		p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
+		if (!p->to_free) {
+			kfree(p);
+			return -ENOMEM;
+		}
+	} else {
+		p->data = *to;
+		p->to_free = NULL;
+	}
+	p->mm = current->mm;
+
+	kiocb->private = p;
+
+	if (p->aio)
+		kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+	res = ffs_epfile_io(kiocb->ki_filp, p);
+	if (res == -EIOCBQUEUED)
+		return res;
+
+	if (p->aio) {
+		kfree(p->to_free);
+		kfree(p);
+	} else {
+		*to = p->data;
+	}
+	return res;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+	struct ffs_epfile *epfile = inode->i_private;
+
+	ENTER();
+
+	__ffs_epfile_read_buffer_free(epfile);
+	ffs_data_closed(epfile->ffs);
+
+	return 0;
+}
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+			     unsigned long value)
+{
+	struct ffs_epfile *epfile = file->private_data;
+	struct ffs_ep *ep;
+	int ret;
+
+	ENTER();
+
+	if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+		return -ENODEV;
+
+	/* Wait for endpoint to be enabled */
+	ep = epfile->ep;
+	if (!ep) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible(
+				epfile->ffs->wait, (ep = epfile->ep));
+		if (ret)
+			return -EINTR;
+	}
+
+	spin_lock_irq(&epfile->ffs->eps_lock);
+
+	/* In the meantime, endpoint got disabled or changed. */
+	if (epfile->ep != ep) {
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		return -ESHUTDOWN;
+	}
+
+	switch (code) {
+	case FUNCTIONFS_FIFO_STATUS:
+		ret = usb_ep_fifo_status(epfile->ep->ep);
+		break;
+	case FUNCTIONFS_FIFO_FLUSH:
+		usb_ep_fifo_flush(epfile->ep->ep);
+		ret = 0;
+		break;
+	case FUNCTIONFS_CLEAR_HALT:
+		ret = usb_ep_clear_halt(epfile->ep->ep);
+		break;
+	case FUNCTIONFS_ENDPOINT_REVMAP:
+		ret = epfile->ep->num;
+		break;
+	case FUNCTIONFS_ENDPOINT_DESC:
+	{
+		int desc_idx;
+		struct usb_endpoint_descriptor *desc;
+
+		switch (epfile->ffs->gadget->speed) {
+		case USB_SPEED_SUPER:
+			desc_idx = 2;
+			break;
+		case USB_SPEED_HIGH:
+			desc_idx = 1;
+			break;
+		default:
+			desc_idx = 0;
+		}
+		desc = epfile->ep->descs[desc_idx];
+
+		spin_unlock_irq(&epfile->ffs->eps_lock);
+		ret = copy_to_user((void __user *)value, desc, desc->bLength);
+		if (ret)
+			ret = -EFAULT;
+		return ret;
+	}
+	default:
+		ret = -ENOTTY;
+	}
+	spin_unlock_irq(&epfile->ffs->eps_lock);
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long ffs_epfile_compat_ioctl(struct file *file, unsigned code,
+		unsigned long value)
+{
+	return ffs_epfile_ioctl(file, code, value);
+}
+#endif
+
+static const struct file_operations ffs_epfile_operations = {
+	.llseek =	no_llseek,
+
+	.open =		ffs_epfile_open,
+	.write_iter =	ffs_epfile_write_iter,
+	.read_iter =	ffs_epfile_read_iter,
+	.release =	ffs_epfile_release,
+	.unlocked_ioctl =	ffs_epfile_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ffs_epfile_compat_ioctl,
+#endif
+};
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the file system creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+		  const struct file_operations *fops,
+		  const struct inode_operations *iops,
+		  struct ffs_file_perms *perms)
+{
+	struct inode *inode;
+
+	ENTER();
+
+	inode = new_inode(sb);
+
+	if (likely(inode)) {
+		struct timespec64 ts = current_time(inode);
+
+		inode->i_ino	 = get_next_ino();
+		inode->i_mode    = perms->mode;
+		inode->i_uid     = perms->uid;
+		inode->i_gid     = perms->gid;
+		inode->i_atime   = ts;
+		inode->i_mtime   = ts;
+		inode->i_ctime   = ts;
+		inode->i_private = data;
+		if (fops)
+			inode->i_fop = fops;
+		if (iops)
+			inode->i_op  = iops;
+	}
+
+	return inode;
+}
+
+/* Create "regular" file */
+static struct dentry *ffs_sb_create_file(struct super_block *sb,
+					const char *name, void *data,
+					const struct file_operations *fops)
+{
+	struct ffs_data	*ffs = sb->s_fs_info;
+	struct dentry	*dentry;
+	struct inode	*inode;
+
+	ENTER();
+
+	dentry = d_alloc_name(sb->s_root, name);
+	if (unlikely(!dentry))
+		return NULL;
+
+	inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+	if (unlikely(!inode)) {
+		dput(dentry);
+		return NULL;
+	}
+
+	d_add(dentry, inode);
+	return dentry;
+}
+
+/* Super block */
+static const struct super_operations ffs_sb_operations = {
+	.statfs =	simple_statfs,
+	.drop_inode =	generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+	struct ffs_file_perms perms;
+	umode_t root_mode;
+	const char *dev_name;
+	bool no_disconnect;
+	struct ffs_data *ffs_data;
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+	struct ffs_sb_fill_data *data = _data;
+	struct inode	*inode;
+	struct ffs_data	*ffs = data->ffs_data;
+
+	ENTER();
+
+	ffs->sb              = sb;
+	data->ffs_data       = NULL;
+	sb->s_fs_info        = ffs;
+	sb->s_blocksize      = PAGE_SIZE;
+	sb->s_blocksize_bits = PAGE_SHIFT;
+	sb->s_magic          = FUNCTIONFS_MAGIC;
+	sb->s_op             = &ffs_sb_operations;
+	sb->s_time_gran      = 1;
+
+	/* Root inode */
+	data->perms.mode = data->root_mode;
+	inode = ffs_sb_make_inode(sb, NULL,
+				  &simple_dir_operations,
+				  &simple_dir_inode_operations,
+				  &data->perms);
+	sb->s_root = d_make_root(inode);
+	if (unlikely(!sb->s_root))
+		return -ENOMEM;
+
+	/* EP0 file */
+	if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+					 &ffs_ep0_operations)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+	ENTER();
+
+	if (!opts || !*opts)
+		return 0;
+
+	for (;;) {
+		unsigned long value;
+		char *eq, *comma;
+
+		/* Option limit */
+		comma = strchr(opts, ',');
+		if (comma)
+			*comma = 0;
+
+		/* Value limit */
+		eq = strchr(opts, '=');
+		if (unlikely(!eq)) {
+			pr_err("'=' missing in %s\n", opts);
+			return -EINVAL;
+		}
+		*eq = 0;
+
+		/* Parse value */
+		if (kstrtoul(eq + 1, 0, &value)) {
+			pr_err("%s: invalid value: %s\n", opts, eq + 1);
+			return -EINVAL;
+		}
+
+		/* Interpret option */
+		switch (eq - opts) {
+		case 13:
+			if (!memcmp(opts, "no_disconnect", 13))
+				data->no_disconnect = !!value;
+			else
+				goto invalid;
+			break;
+		case 5:
+			if (!memcmp(opts, "rmode", 5))
+				data->root_mode  = (value & 0555) | S_IFDIR;
+			else if (!memcmp(opts, "fmode", 5))
+				data->perms.mode = (value & 0666) | S_IFREG;
+			else
+				goto invalid;
+			break;
+
+		case 4:
+			if (!memcmp(opts, "mode", 4)) {
+				data->root_mode  = (value & 0555) | S_IFDIR;
+				data->perms.mode = (value & 0666) | S_IFREG;
+			} else {
+				goto invalid;
+			}
+			break;
+
+		case 3:
+			if (!memcmp(opts, "uid", 3)) {
+				data->perms.uid = make_kuid(current_user_ns(), value);
+				if (!uid_valid(data->perms.uid)) {
+					pr_err("%s: unmapped value: %lu\n", opts, value);
+					return -EINVAL;
+				}
+			} else if (!memcmp(opts, "gid", 3)) {
+				data->perms.gid = make_kgid(current_user_ns(), value);
+				if (!gid_valid(data->perms.gid)) {
+					pr_err("%s: unmapped value: %lu\n", opts, value);
+					return -EINVAL;
+				}
+			} else {
+				goto invalid;
+			}
+			break;
+
+		default:
+invalid:
+			pr_err("%s: invalid option\n", opts);
+			return -EINVAL;
+		}
+
+		/* Next iteration */
+		if (!comma)
+			break;
+		opts = comma + 1;
+	}
+
+	return 0;
+}
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static struct dentry *
+ffs_fs_mount(struct file_system_type *t, int flags,
+	      const char *dev_name, void *opts)
+{
+	struct ffs_sb_fill_data data = {
+		.perms = {
+			.mode = S_IFREG | 0600,
+			.uid = GLOBAL_ROOT_UID,
+			.gid = GLOBAL_ROOT_GID,
+		},
+		.root_mode = S_IFDIR | 0500,
+		.no_disconnect = false,
+	};
+	struct dentry *rv;
+	int ret;
+	void *ffs_dev;
+	struct ffs_data	*ffs;
+
+	ENTER();
+
+	ret = ffs_fs_parse_opts(&data, opts);
+	if (unlikely(ret < 0))
+		return ERR_PTR(ret);
+
+	ffs = ffs_data_new(dev_name);
+	if (unlikely(!ffs))
+		return ERR_PTR(-ENOMEM);
+	ffs->file_perms = data.perms;
+	ffs->no_disconnect = data.no_disconnect;
+
+	ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+	if (unlikely(!ffs->dev_name)) {
+		ffs_data_put(ffs);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	ffs_dev = ffs_acquire_dev(dev_name);
+	if (IS_ERR(ffs_dev)) {
+		ffs_data_put(ffs);
+		return ERR_CAST(ffs_dev);
+	}
+	ffs->private_data = ffs_dev;
+	data.ffs_data = ffs;
+
+	rv = mount_nodev(t, flags, &data, ffs_sb_fill);
+	if (IS_ERR(rv) && data.ffs_data) {
+		ffs_release_dev(data.ffs_data);
+		ffs_data_put(data.ffs_data);
+	}
+	return rv;
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+	ENTER();
+
+	kill_litter_super(sb);
+	if (sb->s_fs_info) {
+		ffs_release_dev(sb->s_fs_info);
+		ffs_data_closed(sb->s_fs_info);
+	}
+}
+
+static struct file_system_type ffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "functionfs",
+	.mount		= ffs_fs_mount,
+	.kill_sb	= ffs_fs_kill_sb,
+};
+MODULE_ALIAS_FS("functionfs");
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+static int functionfs_init(void)
+{
+	int ret;
+
+	ENTER();
+
+	ret = register_filesystem(&ffs_fs_type);
+	if (likely(!ret))
+		pr_info("file system registered\n");
+	else
+		pr_err("failed registering file system (%d)\n", ret);
+
+	return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+	ENTER();
+
+	pr_info("unloading\n");
+	unregister_filesystem(&ffs_fs_type);
+}
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+	ENTER();
+
+	refcount_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+	ENTER();
+
+	refcount_inc(&ffs->ref);
+	if (atomic_add_return(1, &ffs->opened) == 1 &&
+			ffs->state == FFS_DEACTIVATED) {
+		ffs->state = FFS_CLOSING;
+		ffs_data_reset(ffs);
+	}
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (unlikely(refcount_dec_and_test(&ffs->ref))) {
+		pr_info("%s(): freeing\n", __func__);
+		ffs_data_clear(ffs);
+		BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
+		       waitqueue_active(&ffs->ep0req_completion.wait) ||
+		       waitqueue_active(&ffs->wait));
+		destroy_workqueue(ffs->io_completion_wq);
+		kfree(ffs->dev_name);
+		kfree(ffs);
+	}
+}
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (atomic_dec_and_test(&ffs->opened)) {
+		if (ffs->no_disconnect) {
+			ffs->state = FFS_DEACTIVATED;
+			if (ffs->epfiles) {
+				ffs_epfiles_destroy(ffs->epfiles,
+						   ffs->eps_count);
+				ffs->epfiles = NULL;
+			}
+			if (ffs->setup_state == FFS_SETUP_PENDING)
+				__ffs_ep0_stall(ffs);
+		} else {
+			ffs->state = FFS_CLOSING;
+			ffs_data_reset(ffs);
+		}
+	}
+	if (atomic_read(&ffs->opened) < 0) {
+		ffs->state = FFS_CLOSING;
+		ffs_data_reset(ffs);
+	}
+
+	ffs_data_put(ffs);
+}
+
+static struct ffs_data *ffs_data_new(const char *dev_name)
+{
+	struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+	if (unlikely(!ffs))
+		return NULL;
+
+	ENTER();
+
+	ffs->io_completion_wq = alloc_ordered_workqueue("%s", 0, dev_name);
+	if (!ffs->io_completion_wq) {
+		kfree(ffs);
+		return NULL;
+	}
+
+	refcount_set(&ffs->ref, 1);
+	atomic_set(&ffs->opened, 0);
+	ffs->state = FFS_READ_DESCRIPTORS;
+	mutex_init(&ffs->mutex);
+	spin_lock_init(&ffs->eps_lock);
+	init_waitqueue_head(&ffs->ev.waitq);
+	init_waitqueue_head(&ffs->wait);
+	init_completion(&ffs->ep0req_completion);
+
+	/* XXX REVISIT need to update it in some places, or do we? */
+	ffs->ev.can_stall = 1;
+
+	return ffs;
+}
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+	ENTER();
+
+	ffs_closed(ffs);
+
+	BUG_ON(ffs->gadget);
+
+	if (ffs->epfiles)
+		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+	if (ffs->ffs_eventfd)
+		eventfd_ctx_put(ffs->ffs_eventfd);
+
+	kfree(ffs->raw_descs_data);
+	kfree(ffs->raw_strings);
+	kfree(ffs->stringtabs);
+}
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+	ENTER();
+
+	ffs_data_clear(ffs);
+
+	ffs->epfiles = NULL;
+	ffs->raw_descs_data = NULL;
+	ffs->raw_descs = NULL;
+	ffs->raw_strings = NULL;
+	ffs->stringtabs = NULL;
+
+	ffs->raw_descs_length = 0;
+	ffs->fs_descs_count = 0;
+	ffs->hs_descs_count = 0;
+	ffs->ss_descs_count = 0;
+
+	ffs->strings_count = 0;
+	ffs->interfaces_count = 0;
+	ffs->eps_count = 0;
+
+	ffs->ev.count = 0;
+
+	ffs->state = FFS_READ_DESCRIPTORS;
+	ffs->setup_state = FFS_NO_SETUP;
+	ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+	struct usb_gadget_strings **lang;
+	int first_id;
+
+	ENTER();
+
+	if (WARN_ON(ffs->state != FFS_ACTIVE
+		 || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+		return -EBADFD;
+
+	first_id = usb_string_ids_n(cdev, ffs->strings_count);
+	if (unlikely(first_id < 0))
+		return first_id;
+
+	ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+	if (unlikely(!ffs->ep0req))
+		return -ENOMEM;
+	ffs->ep0req->complete = ffs_ep0_complete;
+	ffs->ep0req->context = ffs;
+
+	lang = ffs->stringtabs;
+	if (lang) {
+		for (; *lang; ++lang) {
+			struct usb_string *str = (*lang)->strings;
+			int id = first_id;
+			for (; str->s; ++id, ++str)
+				str->id = id;
+		}
+	}
+
+	ffs->gadget = cdev->gadget;
+	ffs_data_get(ffs);
+	return 0;
+}
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+	ENTER();
+
+	if (!WARN_ON(!ffs->gadget)) {
+		usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+		ffs->ep0req = NULL;
+		ffs->gadget = NULL;
+		clear_bit(FFS_FL_BOUND, &ffs->flags);
+		ffs_data_put(ffs);
+	}
+}
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+	struct ffs_epfile *epfile, *epfiles;
+	unsigned i, count;
+
+	ENTER();
+
+	count = ffs->eps_count;
+	epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
+	if (!epfiles)
+		return -ENOMEM;
+
+	epfile = epfiles;
+	for (i = 1; i <= count; ++i, ++epfile) {
+		epfile->ffs = ffs;
+		mutex_init(&epfile->mutex);
+		if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+			sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
+		else
+			sprintf(epfile->name, "ep%u", i);
+		epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
+						 epfile,
+						 &ffs_epfile_operations);
+		if (unlikely(!epfile->dentry)) {
+			ffs_epfiles_destroy(epfiles, i - 1);
+			return -ENOMEM;
+		}
+	}
+
+	ffs->epfiles = epfiles;
+	return 0;
+}
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+	struct ffs_epfile *epfile = epfiles;
+
+	ENTER();
+
+	for (; count; --count, ++epfile) {
+		BUG_ON(mutex_is_locked(&epfile->mutex));
+		if (epfile->dentry) {
+			d_delete(epfile->dentry);
+			dput(epfile->dentry);
+			epfile->dentry = NULL;
+		}
+	}
+
+	kfree(epfiles);
+}
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = func->ffs->epfiles;
+	unsigned count            = func->ffs->eps_count;
+	unsigned long flags;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	while (count--) {
+		/* pending requests get nuked */
+		if (likely(ep->ep))
+			usb_ep_disable(ep->ep);
+		++ep;
+
+		if (epfile) {
+			epfile->ep = NULL;
+			__ffs_epfile_read_buffer_free(epfile);
+			++epfile;
+		}
+	}
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+	struct ffs_data *ffs      = func->ffs;
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = ffs->epfiles;
+	unsigned count            = ffs->eps_count;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	while(count--) {
+		ep->ep->driver_data = ep;
+
+		ret = config_ep_by_speed(func->gadget, &func->function, ep->ep);
+		if (ret) {
+			pr_err("%s: config_ep_by_speed(%s) returned %d\n",
+					__func__, ep->ep->name, ret);
+			break;
+		}
+
+		ret = usb_ep_enable(ep->ep);
+		if (likely(!ret)) {
+			epfile->ep = ep;
+			epfile->in = usb_endpoint_dir_in(ep->ep->desc);
+			epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc);
+		} else {
+			break;
+		}
+
+		++ep;
+		++epfile;
+	}
+
+	wake_up_interruptible(&ffs->wait);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+	return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+/*
+ * This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration.  Returns address after the
+ * descriptor or NULL if data is invalid.
+ */
+
+enum ffs_entity_type {
+	FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+enum ffs_os_desc_type {
+	FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+				   u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv);
+
+typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
+				    struct usb_os_desc_header *h, void *data,
+				    unsigned len, void *priv);
+
+static int __must_check ffs_do_single_desc(char *data, unsigned len,
+					   ffs_entity_callback entity,
+					   void *priv)
+{
+	struct usb_descriptor_header *_ds = (void *)data;
+	u8 length;
+	int ret;
+
+	ENTER();
+
+	/* At least two bytes are required: length and type */
+	if (len < 2) {
+		pr_vdebug("descriptor too short\n");
+		return -EINVAL;
+	}
+
+	/* If we have at least as many bytes as the descriptor takes? */
+	length = _ds->bLength;
+	if (len < length) {
+		pr_vdebug("descriptor longer then available data\n");
+		return -EINVAL;
+	}
+
+#define __entity_check_INTERFACE(val)  1
+#define __entity_check_STRING(val)     (val)
+#define __entity_check_ENDPOINT(val)   ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do {					\
+		pr_vdebug("entity " #type "(%02x)\n", (val));		\
+		if (unlikely(!__entity_check_ ##type(val))) {		\
+			pr_vdebug("invalid entity's value\n");		\
+			return -EINVAL;					\
+		}							\
+		ret = entity(FFS_ ##type, &val, _ds, priv);		\
+		if (unlikely(ret < 0)) {				\
+			pr_debug("entity " #type "(%02x); ret = %d\n",	\
+				 (val), ret);				\
+			return ret;					\
+		}							\
+	} while (0)
+
+	/* Parse descriptor depending on type. */
+	switch (_ds->bDescriptorType) {
+	case USB_DT_DEVICE:
+	case USB_DT_CONFIG:
+	case USB_DT_STRING:
+	case USB_DT_DEVICE_QUALIFIER:
+		/* function can't have any of those */
+		pr_vdebug("descriptor reserved for gadget: %d\n",
+		      _ds->bDescriptorType);
+		return -EINVAL;
+
+	case USB_DT_INTERFACE: {
+		struct usb_interface_descriptor *ds = (void *)_ds;
+		pr_vdebug("interface descriptor\n");
+		if (length != sizeof *ds)
+			goto inv_length;
+
+		__entity(INTERFACE, ds->bInterfaceNumber);
+		if (ds->iInterface)
+			__entity(STRING, ds->iInterface);
+	}
+		break;
+
+	case USB_DT_ENDPOINT: {
+		struct usb_endpoint_descriptor *ds = (void *)_ds;
+		pr_vdebug("endpoint descriptor\n");
+		if (length != USB_DT_ENDPOINT_SIZE &&
+		    length != USB_DT_ENDPOINT_AUDIO_SIZE)
+			goto inv_length;
+		__entity(ENDPOINT, ds->bEndpointAddress);
+	}
+		break;
+
+	case HID_DT_HID:
+		pr_vdebug("hid descriptor\n");
+		if (length != sizeof(struct hid_descriptor))
+			goto inv_length;
+		break;
+
+	case USB_DT_OTG:
+		if (length != sizeof(struct usb_otg_descriptor))
+			goto inv_length;
+		break;
+
+	case USB_DT_INTERFACE_ASSOCIATION: {
+		struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+		pr_vdebug("interface association descriptor\n");
+		if (length != sizeof *ds)
+			goto inv_length;
+		if (ds->iFunction)
+			__entity(STRING, ds->iFunction);
+	}
+		break;
+
+	case USB_DT_SS_ENDPOINT_COMP:
+		pr_vdebug("EP SS companion descriptor\n");
+		if (length != sizeof(struct usb_ss_ep_comp_descriptor))
+			goto inv_length;
+		break;
+
+	case USB_DT_OTHER_SPEED_CONFIG:
+	case USB_DT_INTERFACE_POWER:
+	case USB_DT_DEBUG:
+	case USB_DT_SECURITY:
+	case USB_DT_CS_RADIO_CONTROL:
+		/* TODO */
+		pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
+		return -EINVAL;
+
+	default:
+		/* We should never be here */
+		pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
+		return -EINVAL;
+
+inv_length:
+		pr_vdebug("invalid length: %d (descriptor %d)\n",
+			  _ds->bLength, _ds->bDescriptorType);
+		return -EINVAL;
+	}
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+	return length;
+}
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+				     ffs_entity_callback entity, void *priv)
+{
+	const unsigned _len = len;
+	unsigned long num = 0;
+
+	ENTER();
+
+	for (;;) {
+		int ret;
+
+		if (num == count)
+			data = NULL;
+
+		/* Record "descriptor" entity */
+		ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+		if (unlikely(ret < 0)) {
+			pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
+				 num, ret);
+			return ret;
+		}
+
+		if (!data)
+			return _len - len;
+
+		ret = ffs_do_single_desc(data, len, entity, priv);
+		if (unlikely(ret < 0)) {
+			pr_debug("%s returns %d\n", __func__, ret);
+			return ret;
+		}
+
+		len -= ret;
+		data += ret;
+		++num;
+	}
+}
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+				u8 *valuep, struct usb_descriptor_header *desc,
+				void *priv)
+{
+	struct ffs_desc_helper *helper = priv;
+	struct usb_endpoint_descriptor *d;
+
+	ENTER();
+
+	switch (type) {
+	case FFS_DESCRIPTOR:
+		break;
+
+	case FFS_INTERFACE:
+		/*
+		 * Interfaces are indexed from zero so if we
+		 * encountered interface "n" then there are at least
+		 * "n+1" interfaces.
+		 */
+		if (*valuep >= helper->interfaces_count)
+			helper->interfaces_count = *valuep + 1;
+		break;
+
+	case FFS_STRING:
+		/*
+		 * Strings are indexed from 1 (0 is reserved
+		 * for languages list)
+		 */
+		if (*valuep > helper->ffs->strings_count)
+			helper->ffs->strings_count = *valuep;
+		break;
+
+	case FFS_ENDPOINT:
+		d = (void *)desc;
+		helper->eps_count++;
+		if (helper->eps_count >= FFS_MAX_EPS_COUNT)
+			return -EINVAL;
+		/* Check if descriptors for any speed were already parsed */
+		if (!helper->ffs->eps_count && !helper->ffs->interfaces_count)
+			helper->ffs->eps_addrmap[helper->eps_count] =
+				d->bEndpointAddress;
+		else if (helper->ffs->eps_addrmap[helper->eps_count] !=
+				d->bEndpointAddress)
+			return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
+				   struct usb_os_desc_header *desc)
+{
+	u16 bcd_version = le16_to_cpu(desc->bcdVersion);
+	u16 w_index = le16_to_cpu(desc->wIndex);
+
+	if (bcd_version != 1) {
+		pr_vdebug("unsupported os descriptors version: %d",
+			  bcd_version);
+		return -EINVAL;
+	}
+	switch (w_index) {
+	case 0x4:
+		*next_type = FFS_OS_DESC_EXT_COMPAT;
+		break;
+	case 0x5:
+		*next_type = FFS_OS_DESC_EXT_PROP;
+		break;
+	default:
+		pr_vdebug("unsupported os descriptor type: %d", w_index);
+		return -EINVAL;
+	}
+
+	return sizeof(*desc);
+}
+
+/*
+ * Process all extended compatibility/extended property descriptors
+ * of a feature descriptor
+ */
+static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
+					      enum ffs_os_desc_type type,
+					      u16 feature_count,
+					      ffs_os_desc_callback entity,
+					      void *priv,
+					      struct usb_os_desc_header *h)
+{
+	int ret;
+	const unsigned _len = len;
+
+	ENTER();
+
+	/* loop over all ext compat/ext prop descriptors */
+	while (feature_count--) {
+		ret = entity(type, h, data, len, priv);
+		if (unlikely(ret < 0)) {
+			pr_debug("bad OS descriptor, type: %d\n", type);
+			return ret;
+		}
+		data += ret;
+		len -= ret;
+	}
+	return _len - len;
+}
+
+/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
+static int __must_check ffs_do_os_descs(unsigned count,
+					char *data, unsigned len,
+					ffs_os_desc_callback entity, void *priv)
+{
+	const unsigned _len = len;
+	unsigned long num = 0;
+
+	ENTER();
+
+	for (num = 0; num < count; ++num) {
+		int ret;
+		enum ffs_os_desc_type type;
+		u16 feature_count;
+		struct usb_os_desc_header *desc = (void *)data;
+
+		if (len < sizeof(*desc))
+			return -EINVAL;
+
+		/*
+		 * Record "descriptor" entity.
+		 * Process dwLength, bcdVersion, wIndex, get b/wCount.
+		 * Move the data pointer to the beginning of extended
+		 * compatibilities proper or extended properties proper
+		 * portions of the data
+		 */
+		if (le32_to_cpu(desc->dwLength) > len)
+			return -EINVAL;
+
+		ret = __ffs_do_os_desc_header(&type, desc);
+		if (unlikely(ret < 0)) {
+			pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
+				 num, ret);
+			return ret;
+		}
+		/*
+		 * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
+		 */
+		feature_count = le16_to_cpu(desc->wCount);
+		if (type == FFS_OS_DESC_EXT_COMPAT &&
+		    (feature_count > 255 || desc->Reserved))
+				return -EINVAL;
+		len -= ret;
+		data += ret;
+
+		/*
+		 * Process all function/property descriptors
+		 * of this Feature Descriptor
+		 */
+		ret = ffs_do_single_os_desc(data, len, type,
+					    feature_count, entity, priv, desc);
+		if (unlikely(ret < 0)) {
+			pr_debug("%s returns %d\n", __func__, ret);
+			return ret;
+		}
+
+		len -= ret;
+		data += ret;
+	}
+	return _len - len;
+}
+
+/**
+ * Validate contents of the buffer from userspace related to OS descriptors.
+ */
+static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
+				 struct usb_os_desc_header *h, void *data,
+				 unsigned len, void *priv)
+{
+	struct ffs_data *ffs = priv;
+	u8 length;
+
+	ENTER();
+
+	switch (type) {
+	case FFS_OS_DESC_EXT_COMPAT: {
+		struct usb_ext_compat_desc *d = data;
+		int i;
+
+		if (len < sizeof(*d) ||
+		    d->bFirstInterfaceNumber >= ffs->interfaces_count)
+			return -EINVAL;
+		if (d->Reserved1 != 1) {
+			/*
+			 * According to the spec, Reserved1 must be set to 1
+			 * but older kernels incorrectly rejected non-zero
+			 * values.  We fix it here to avoid returning EINVAL
+			 * in response to values we used to accept.
+			 */
+			pr_debug("usb_ext_compat_desc::Reserved1 forced to 1\n");
+			d->Reserved1 = 1;
+		}
+		for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
+			if (d->Reserved2[i])
+				return -EINVAL;
+
+		length = sizeof(struct usb_ext_compat_desc);
+	}
+		break;
+	case FFS_OS_DESC_EXT_PROP: {
+		struct usb_ext_prop_desc *d = data;
+		u32 type, pdl;
+		u16 pnl;
+
+		if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
+			return -EINVAL;
+		length = le32_to_cpu(d->dwSize);
+		if (len < length)
+			return -EINVAL;
+		type = le32_to_cpu(d->dwPropertyDataType);
+		if (type < USB_EXT_PROP_UNICODE ||
+		    type > USB_EXT_PROP_UNICODE_MULTI) {
+			pr_vdebug("unsupported os descriptor property type: %d",
+				  type);
+			return -EINVAL;
+		}
+		pnl = le16_to_cpu(d->wPropertyNameLength);
+		if (length < 14 + pnl) {
+			pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
+				  length, pnl, type);
+			return -EINVAL;
+		}
+		pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl));
+		if (length != 14 + pnl + pdl) {
+			pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
+				  length, pnl, pdl, type);
+			return -EINVAL;
+		}
+		++ffs->ms_os_descs_ext_prop_count;
+		/* property name reported to the host as "WCHAR"s */
+		ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
+		ffs->ms_os_descs_ext_prop_data_len += pdl;
+	}
+		break;
+	default:
+		pr_vdebug("unknown descriptor: %d\n", type);
+		return -EINVAL;
+	}
+	return length;
+}
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+				char *const _data, size_t len)
+{
+	char *data = _data, *raw_descs;
+	unsigned os_descs_count = 0, counts[3], flags;
+	int ret = -EINVAL, i;
+	struct ffs_desc_helper helper;
+
+	ENTER();
+
+	if (get_unaligned_le32(data + 4) != len)
+		goto error;
+
+	switch (get_unaligned_le32(data)) {
+	case FUNCTIONFS_DESCRIPTORS_MAGIC:
+		flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+		data += 8;
+		len  -= 8;
+		break;
+	case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+		flags = get_unaligned_le32(data + 8);
+		ffs->user_flags = flags;
+		if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
+			      FUNCTIONFS_HAS_HS_DESC |
+			      FUNCTIONFS_HAS_SS_DESC |
+			      FUNCTIONFS_HAS_MS_OS_DESC |
+			      FUNCTIONFS_VIRTUAL_ADDR |
+			      FUNCTIONFS_EVENTFD |
+			      FUNCTIONFS_ALL_CTRL_RECIP |
+			      FUNCTIONFS_CONFIG0_SETUP)) {
+			ret = -ENOSYS;
+			goto error;
+		}
+		data += 12;
+		len  -= 12;
+		break;
+	default:
+		goto error;
+	}
+
+	if (flags & FUNCTIONFS_EVENTFD) {
+		if (len < 4)
+			goto error;
+		ffs->ffs_eventfd =
+			eventfd_ctx_fdget((int)get_unaligned_le32(data));
+		if (IS_ERR(ffs->ffs_eventfd)) {
+			ret = PTR_ERR(ffs->ffs_eventfd);
+			ffs->ffs_eventfd = NULL;
+			goto error;
+		}
+		data += 4;
+		len  -= 4;
+	}
+
+	/* Read fs_count, hs_count and ss_count (if present) */
+	for (i = 0; i < 3; ++i) {
+		if (!(flags & (1 << i))) {
+			counts[i] = 0;
+		} else if (len < 4) {
+			goto error;
+		} else {
+			counts[i] = get_unaligned_le32(data);
+			data += 4;
+			len  -= 4;
+		}
+	}
+	if (flags & (1 << i)) {
+		if (len < 4) {
+			goto error;
+		}
+		os_descs_count = get_unaligned_le32(data);
+		data += 4;
+		len -= 4;
+	};
+
+	/* Read descriptors */
+	raw_descs = data;
+	helper.ffs = ffs;
+	for (i = 0; i < 3; ++i) {
+		if (!counts[i])
+			continue;
+		helper.interfaces_count = 0;
+		helper.eps_count = 0;
+		ret = ffs_do_descs(counts[i], data, len,
+				   __ffs_data_do_entity, &helper);
+		if (ret < 0)
+			goto error;
+		if (!ffs->eps_count && !ffs->interfaces_count) {
+			ffs->eps_count = helper.eps_count;
+			ffs->interfaces_count = helper.interfaces_count;
+		} else {
+			if (ffs->eps_count != helper.eps_count) {
+				ret = -EINVAL;
+				goto error;
+			}
+			if (ffs->interfaces_count != helper.interfaces_count) {
+				ret = -EINVAL;
+				goto error;
+			}
+		}
+		data += ret;
+		len  -= ret;
+	}
+	if (os_descs_count) {
+		ret = ffs_do_os_descs(os_descs_count, data, len,
+				      __ffs_data_do_os_desc, ffs);
+		if (ret < 0)
+			goto error;
+		data += ret;
+		len -= ret;
+	}
+
+	if (raw_descs == data || len) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ffs->raw_descs_data	= _data;
+	ffs->raw_descs		= raw_descs;
+	ffs->raw_descs_length	= data - raw_descs;
+	ffs->fs_descs_count	= counts[0];
+	ffs->hs_descs_count	= counts[1];
+	ffs->ss_descs_count	= counts[2];
+	ffs->ms_os_descs_count	= os_descs_count;
+
+	return 0;
+
+error:
+	kfree(_data);
+	return ret;
+}
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+				  char *const _data, size_t len)
+{
+	u32 str_count, needed_count, lang_count;
+	struct usb_gadget_strings **stringtabs, *t;
+	const char *data = _data;
+	struct usb_string *s;
+
+	ENTER();
+
+	if (unlikely(len < 16 ||
+		     get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+		     get_unaligned_le32(data + 4) != len))
+		goto error;
+	str_count  = get_unaligned_le32(data + 8);
+	lang_count = get_unaligned_le32(data + 12);
+
+	/* if one is zero the other must be zero */
+	if (unlikely(!str_count != !lang_count))
+		goto error;
+
+	/* Do we have at least as many strings as descriptors need? */
+	needed_count = ffs->strings_count;
+	if (unlikely(str_count < needed_count))
+		goto error;
+
+	/*
+	 * If we don't need any strings just return and free all
+	 * memory.
+	 */
+	if (!needed_count) {
+		kfree(_data);
+		return 0;
+	}
+
+	/* Allocate everything in one chunk so there's less maintenance. */
+	{
+		unsigned i = 0;
+		vla_group(d);
+		vla_item(d, struct usb_gadget_strings *, stringtabs,
+			lang_count + 1);
+		vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
+		vla_item(d, struct usb_string, strings,
+			lang_count*(needed_count+1));
+
+		char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+
+		if (unlikely(!vlabuf)) {
+			kfree(_data);
+			return -ENOMEM;
+		}
+
+		/* Initialize the VLA pointers */
+		stringtabs = vla_ptr(vlabuf, d, stringtabs);
+		t = vla_ptr(vlabuf, d, stringtab);
+		i = lang_count;
+		do {
+			*stringtabs++ = t++;
+		} while (--i);
+		*stringtabs = NULL;
+
+		/* stringtabs = vlabuf = d_stringtabs for later kfree */
+		stringtabs = vla_ptr(vlabuf, d, stringtabs);
+		t = vla_ptr(vlabuf, d, stringtab);
+		s = vla_ptr(vlabuf, d, strings);
+	}
+
+	/* For each language */
+	data += 16;
+	len -= 16;
+
+	do { /* lang_count > 0 so we can use do-while */
+		unsigned needed = needed_count;
+
+		if (unlikely(len < 3))
+			goto error_free;
+		t->language = get_unaligned_le16(data);
+		t->strings  = s;
+		++t;
+
+		data += 2;
+		len -= 2;
+
+		/* For each string */
+		do { /* str_count > 0 so we can use do-while */
+			size_t length = strnlen(data, len);
+
+			if (unlikely(length == len))
+				goto error_free;
+
+			/*
+			 * User may provide more strings then we need,
+			 * if that's the case we simply ignore the
+			 * rest
+			 */
+			if (likely(needed)) {
+				/*
+				 * s->id will be set while adding
+				 * function to configuration so for
+				 * now just leave garbage here.
+				 */
+				s->s = data;
+				--needed;
+				++s;
+			}
+
+			data += length + 1;
+			len -= length + 1;
+		} while (--str_count);
+
+		s->id = 0;   /* terminator */
+		s->s = NULL;
+		++s;
+
+	} while (--lang_count);
+
+	/* Some garbage left? */
+	if (unlikely(len))
+		goto error_free;
+
+	/* Done! */
+	ffs->stringtabs = stringtabs;
+	ffs->raw_strings = _data;
+
+	return 0;
+
+error_free:
+	kfree(stringtabs);
+error:
+	kfree(_data);
+	return -EINVAL;
+}
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+			    enum usb_functionfs_event_type type)
+{
+	enum usb_functionfs_event_type rem_type1, rem_type2 = type;
+	int neg = 0;
+
+	/*
+	 * Abort any unhandled setup
+	 *
+	 * We do not need to worry about some cmpxchg() changing value
+	 * of ffs->setup_state without holding the lock because when
+	 * state is FFS_SETUP_PENDING cmpxchg() in several places in
+	 * the source does nothing.
+	 */
+	if (ffs->setup_state == FFS_SETUP_PENDING)
+		ffs->setup_state = FFS_SETUP_CANCELLED;
+
+	/*
+	 * Logic of this function guarantees that there are at most four pending
+	 * evens on ffs->ev.types queue.  This is important because the queue
+	 * has space for four elements only and __ffs_ep0_read_events function
+	 * depends on that limit as well.  If more event types are added, those
+	 * limits have to be revisited or guaranteed to still hold.
+	 */
+	switch (type) {
+	case FUNCTIONFS_RESUME:
+		rem_type2 = FUNCTIONFS_SUSPEND;
+		/* FALL THROUGH */
+	case FUNCTIONFS_SUSPEND:
+	case FUNCTIONFS_SETUP:
+		rem_type1 = type;
+		/* Discard all similar events */
+		break;
+
+	case FUNCTIONFS_BIND:
+	case FUNCTIONFS_UNBIND:
+	case FUNCTIONFS_DISABLE:
+	case FUNCTIONFS_ENABLE:
+		/* Discard everything other then power management. */
+		rem_type1 = FUNCTIONFS_SUSPEND;
+		rem_type2 = FUNCTIONFS_RESUME;
+		neg = 1;
+		break;
+
+	default:
+		WARN(1, "%d: unknown event, this should not happen\n", type);
+		return;
+	}
+
+	{
+		u8 *ev  = ffs->ev.types, *out = ev;
+		unsigned n = ffs->ev.count;
+		for (; n; --n, ++ev)
+			if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+				*out++ = *ev;
+			else
+				pr_vdebug("purging event %d\n", *ev);
+		ffs->ev.count = out - ffs->ev.types;
+	}
+
+	pr_vdebug("adding event %d\n", type);
+	ffs->ev.types[ffs->ev.count++] = type;
+	wake_up_locked(&ffs->ev.waitq);
+	if (ffs->ffs_eventfd)
+		eventfd_signal(ffs->ffs_eventfd, 1);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+			  enum usb_functionfs_event_type type)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	__ffs_event_add(ffs, type);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int ffs_ep_addr2idx(struct ffs_data *ffs, u8 endpoint_address)
+{
+	int i;
+
+	for (i = 1; i < ARRAY_SIZE(ffs->eps_addrmap); ++i)
+		if (ffs->eps_addrmap[i] == endpoint_address)
+			return i;
+	return -ENOENT;
+}
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+				    struct usb_descriptor_header *desc,
+				    void *priv)
+{
+	struct usb_endpoint_descriptor *ds = (void *)desc;
+	struct ffs_function *func = priv;
+	struct ffs_ep *ffs_ep;
+	unsigned ep_desc_id;
+	int idx;
+	static const char *speed_names[] = { "full", "high", "super" };
+
+	if (type != FFS_DESCRIPTOR)
+		return 0;
+
+	/*
+	 * If ss_descriptors is not NULL, we are reading super speed
+	 * descriptors; if hs_descriptors is not NULL, we are reading high
+	 * speed descriptors; otherwise, we are reading full speed
+	 * descriptors.
+	 */
+	if (func->function.ss_descriptors) {
+		ep_desc_id = 2;
+		func->function.ss_descriptors[(long)valuep] = desc;
+	} else if (func->function.hs_descriptors) {
+		ep_desc_id = 1;
+		func->function.hs_descriptors[(long)valuep] = desc;
+	} else {
+		ep_desc_id = 0;
+		func->function.fs_descriptors[(long)valuep]    = desc;
+	}
+
+	if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+		return 0;
+
+	idx = ffs_ep_addr2idx(func->ffs, ds->bEndpointAddress) - 1;
+	if (idx < 0)
+		return idx;
+
+	ffs_ep = func->eps + idx;
+
+	if (unlikely(ffs_ep->descs[ep_desc_id])) {
+		pr_err("two %sspeed descriptors for EP %d\n",
+			  speed_names[ep_desc_id],
+			  ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+		return -EINVAL;
+	}
+	ffs_ep->descs[ep_desc_id] = ds;
+
+	ffs_dump_mem(": Original  ep desc", ds, ds->bLength);
+	if (ffs_ep->ep) {
+		ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+		if (!ds->wMaxPacketSize)
+			ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+	} else {
+		struct usb_request *req;
+		struct usb_ep *ep;
+		u8 bEndpointAddress;
+
+		/*
+		 * We back up bEndpointAddress because autoconfig overwrites
+		 * it with physical endpoint address.
+		 */
+		bEndpointAddress = ds->bEndpointAddress;
+		pr_vdebug("autoconfig\n");
+		ep = usb_ep_autoconfig(func->gadget, ds);
+		if (unlikely(!ep))
+			return -ENOTSUPP;
+		ep->driver_data = func->eps + idx;
+
+		req = usb_ep_alloc_request(ep, GFP_KERNEL);
+		if (unlikely(!req))
+			return -ENOMEM;
+
+		ffs_ep->ep  = ep;
+		ffs_ep->req = req;
+		func->eps_revmap[ds->bEndpointAddress &
+				 USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+		/*
+		 * If we use virtual address mapping, we restore
+		 * original bEndpointAddress value.
+		 */
+		if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+			ds->bEndpointAddress = bEndpointAddress;
+	}
+	ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+	return 0;
+}
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+				   struct usb_descriptor_header *desc,
+				   void *priv)
+{
+	struct ffs_function *func = priv;
+	unsigned idx;
+	u8 newValue;
+
+	switch (type) {
+	default:
+	case FFS_DESCRIPTOR:
+		/* Handled in previous pass by __ffs_func_bind_do_descs() */
+		return 0;
+
+	case FFS_INTERFACE:
+		idx = *valuep;
+		if (func->interfaces_nums[idx] < 0) {
+			int id = usb_interface_id(func->conf, &func->function);
+			if (unlikely(id < 0))
+				return id;
+			func->interfaces_nums[idx] = id;
+		}
+		newValue = func->interfaces_nums[idx];
+		break;
+
+	case FFS_STRING:
+		/* String' IDs are allocated when fsf_data is bound to cdev */
+		newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+		break;
+
+	case FFS_ENDPOINT:
+		/*
+		 * USB_DT_ENDPOINT are handled in
+		 * __ffs_func_bind_do_descs().
+		 */
+		if (desc->bDescriptorType == USB_DT_ENDPOINT)
+			return 0;
+
+		idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+		if (unlikely(!func->eps[idx].ep))
+			return -EINVAL;
+
+		{
+			struct usb_endpoint_descriptor **descs;
+			descs = func->eps[idx].descs;
+			newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+		}
+		break;
+	}
+
+	pr_vdebug("%02x -> %02x\n", *valuep, newValue);
+	*valuep = newValue;
+	return 0;
+}
+
+static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
+				      struct usb_os_desc_header *h, void *data,
+				      unsigned len, void *priv)
+{
+	struct ffs_function *func = priv;
+	u8 length = 0;
+
+	switch (type) {
+	case FFS_OS_DESC_EXT_COMPAT: {
+		struct usb_ext_compat_desc *desc = data;
+		struct usb_os_desc_table *t;
+
+		t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
+		t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
+		memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
+		       ARRAY_SIZE(desc->CompatibleID) +
+		       ARRAY_SIZE(desc->SubCompatibleID));
+		length = sizeof(*desc);
+	}
+		break;
+	case FFS_OS_DESC_EXT_PROP: {
+		struct usb_ext_prop_desc *desc = data;
+		struct usb_os_desc_table *t;
+		struct usb_os_desc_ext_prop *ext_prop;
+		char *ext_prop_name;
+		char *ext_prop_data;
+
+		t = &func->function.os_desc_table[h->interface];
+		t->if_id = func->interfaces_nums[h->interface];
+
+		ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
+		func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
+
+		ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
+		ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
+		ext_prop->data_len = le32_to_cpu(*(__le32 *)
+			usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
+		length = ext_prop->name_len + ext_prop->data_len + 14;
+
+		ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
+		func->ffs->ms_os_descs_ext_prop_name_avail +=
+			ext_prop->name_len;
+
+		ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
+		func->ffs->ms_os_descs_ext_prop_data_avail +=
+			ext_prop->data_len;
+		memcpy(ext_prop_data,
+		       usb_ext_prop_data_ptr(data, ext_prop->name_len),
+		       ext_prop->data_len);
+		/* unicode data reported to the host as "WCHAR"s */
+		switch (ext_prop->type) {
+		case USB_EXT_PROP_UNICODE:
+		case USB_EXT_PROP_UNICODE_ENV:
+		case USB_EXT_PROP_UNICODE_LINK:
+		case USB_EXT_PROP_UNICODE_MULTI:
+			ext_prop->data_len *= 2;
+			break;
+		}
+		ext_prop->data = ext_prop_data;
+
+		memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
+		       ext_prop->name_len);
+		/* property name reported to the host as "WCHAR"s */
+		ext_prop->name_len *= 2;
+		ext_prop->name = ext_prop_name;
+
+		t->os_desc->ext_prop_len +=
+			ext_prop->name_len + ext_prop->data_len + 14;
+		++t->os_desc->ext_prop_count;
+		list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
+	}
+		break;
+	default:
+		pr_vdebug("unknown descriptor: %d\n", type);
+	}
+
+	return length;
+}
+
+static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
+						struct usb_configuration *c)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct f_fs_opts *ffs_opts =
+		container_of(f->fi, struct f_fs_opts, func_inst);
+	int ret;
+
+	ENTER();
+
+	/*
+	 * Legacy gadget triggers binding in functionfs_ready_callback,
+	 * which already uses locking; taking the same lock here would
+	 * cause a deadlock.
+	 *
+	 * Configfs-enabled gadgets however do need ffs_dev_lock.
+	 */
+	if (!ffs_opts->no_configfs)
+		ffs_dev_lock();
+	ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
+	func->ffs = ffs_opts->dev->ffs_data;
+	if (!ffs_opts->no_configfs)
+		ffs_dev_unlock();
+	if (ret)
+		return ERR_PTR(ret);
+
+	func->conf = c;
+	func->gadget = c->cdev->gadget;
+
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to ffs_opts->bound access
+	 */
+	if (!ffs_opts->refcnt) {
+		ret = functionfs_bind(func->ffs, c->cdev);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+	ffs_opts->refcnt++;
+	func->function.strings = func->ffs->stringtabs;
+
+	return ffs_opts;
+}
+
+static int _ffs_func_bind(struct usb_configuration *c,
+			  struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
+	const int full = !!func->ffs->fs_descs_count;
+	const int high = !!func->ffs->hs_descs_count;
+	const int super = !!func->ffs->ss_descs_count;
+
+	int fs_len, hs_len, ss_len, ret, i;
+	struct ffs_ep *eps_ptr;
+
+	/* Make it a single chunk, less management later on */
+	vla_group(d);
+	vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
+	vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
+		full ? ffs->fs_descs_count + 1 : 0);
+	vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
+		high ? ffs->hs_descs_count + 1 : 0);
+	vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
+		super ? ffs->ss_descs_count + 1 : 0);
+	vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+	vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
+			 c->cdev->use_os_string ? ffs->interfaces_count : 0);
+	vla_item_with_sz(d, char[16], ext_compat,
+			 c->cdev->use_os_string ? ffs->interfaces_count : 0);
+	vla_item_with_sz(d, struct usb_os_desc, os_desc,
+			 c->cdev->use_os_string ? ffs->interfaces_count : 0);
+	vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
+			 ffs->ms_os_descs_ext_prop_count);
+	vla_item_with_sz(d, char, ext_prop_name,
+			 ffs->ms_os_descs_ext_prop_name_len);
+	vla_item_with_sz(d, char, ext_prop_data,
+			 ffs->ms_os_descs_ext_prop_data_len);
+	vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
+	char *vlabuf;
+
+	ENTER();
+
+	/* Has descriptors only for speeds gadget does not support */
+	if (unlikely(!(full | high | super)))
+		return -ENOTSUPP;
+
+	/* Allocate a single chunk, less management later on */
+	vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
+	if (unlikely(!vlabuf))
+		return -ENOMEM;
+
+	ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+	ffs->ms_os_descs_ext_prop_name_avail =
+		vla_ptr(vlabuf, d, ext_prop_name);
+	ffs->ms_os_descs_ext_prop_data_avail =
+		vla_ptr(vlabuf, d, ext_prop_data);
+
+	/* Copy descriptors  */
+	memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
+	       ffs->raw_descs_length);
+
+	memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
+	eps_ptr = vla_ptr(vlabuf, d, eps);
+	for (i = 0; i < ffs->eps_count; i++)
+		eps_ptr[i].num = -1;
+
+	/* Save pointers
+	 * d_eps == vlabuf, func->eps used to kfree vlabuf later
+	*/
+	func->eps             = vla_ptr(vlabuf, d, eps);
+	func->interfaces_nums = vla_ptr(vlabuf, d, inums);
+
+	/*
+	 * Go through all the endpoint descriptors and allocate
+	 * endpoints first, so that later we can rewrite the endpoint
+	 * numbers without worrying that it may be described later on.
+	 */
+	if (likely(full)) {
+		func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
+		fs_len = ffs_do_descs(ffs->fs_descs_count,
+				      vla_ptr(vlabuf, d, raw_descs),
+				      d_raw_descs__sz,
+				      __ffs_func_bind_do_descs, func);
+		if (unlikely(fs_len < 0)) {
+			ret = fs_len;
+			goto error;
+		}
+	} else {
+		fs_len = 0;
+	}
+
+	if (likely(high)) {
+		func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
+		hs_len = ffs_do_descs(ffs->hs_descs_count,
+				      vla_ptr(vlabuf, d, raw_descs) + fs_len,
+				      d_raw_descs__sz - fs_len,
+				      __ffs_func_bind_do_descs, func);
+		if (unlikely(hs_len < 0)) {
+			ret = hs_len;
+			goto error;
+		}
+	} else {
+		hs_len = 0;
+	}
+
+	if (likely(super)) {
+		func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
+		ss_len = ffs_do_descs(ffs->ss_descs_count,
+				vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
+				d_raw_descs__sz - fs_len - hs_len,
+				__ffs_func_bind_do_descs, func);
+		if (unlikely(ss_len < 0)) {
+			ret = ss_len;
+			goto error;
+		}
+	} else {
+		ss_len = 0;
+	}
+
+	/*
+	 * Now handle interface numbers allocation and interface and
+	 * endpoint numbers rewriting.  We can do that in one go
+	 * now.
+	 */
+	ret = ffs_do_descs(ffs->fs_descs_count +
+			   (high ? ffs->hs_descs_count : 0) +
+			   (super ? ffs->ss_descs_count : 0),
+			   vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
+			   __ffs_func_bind_do_nums, func);
+	if (unlikely(ret < 0))
+		goto error;
+
+	func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
+	if (c->cdev->use_os_string) {
+		for (i = 0; i < ffs->interfaces_count; ++i) {
+			struct usb_os_desc *desc;
+
+			desc = func->function.os_desc_table[i].os_desc =
+				vla_ptr(vlabuf, d, os_desc) +
+				i * sizeof(struct usb_os_desc);
+			desc->ext_compat_id =
+				vla_ptr(vlabuf, d, ext_compat) + i * 16;
+			INIT_LIST_HEAD(&desc->ext_prop);
+		}
+		ret = ffs_do_os_descs(ffs->ms_os_descs_count,
+				      vla_ptr(vlabuf, d, raw_descs) +
+				      fs_len + hs_len + ss_len,
+				      d_raw_descs__sz - fs_len - hs_len -
+				      ss_len,
+				      __ffs_func_bind_do_os_desc, func);
+		if (unlikely(ret < 0))
+			goto error;
+	}
+	func->function.os_desc_n =
+		c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
+	/* And we're done */
+	ffs_event_add(ffs, FUNCTIONFS_BIND);
+	return 0;
+
+error:
+	/* XXX Do we need to release all claimed endpoints here? */
+	return ret;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+			 struct usb_function *f)
+{
+	struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
+	struct ffs_function *func = ffs_func_from_usb(f);
+	int ret;
+
+	if (IS_ERR(ffs_opts))
+		return PTR_ERR(ffs_opts);
+
+	ret = _ffs_func_bind(c, f);
+	if (ret && !--ffs_opts->refcnt)
+		functionfs_unbind(func->ffs);
+
+	return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_reset_work(struct work_struct *work)
+{
+	struct ffs_data *ffs = container_of(work,
+		struct ffs_data, reset_work);
+	ffs_data_reset(ffs);
+}
+
+static int ffs_func_set_alt(struct usb_function *f,
+			    unsigned interface, unsigned alt)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	int ret = 0, intf;
+
+	if (alt != (unsigned)-1) {
+		intf = ffs_func_revmap_intf(func, interface);
+		if (unlikely(intf < 0))
+			return intf;
+	}
+
+	if (ffs->func)
+		ffs_func_eps_disable(ffs->func);
+
+	if (ffs->state == FFS_DEACTIVATED) {
+		ffs->state = FFS_CLOSING;
+		INIT_WORK(&ffs->reset_work, ffs_reset_work);
+		schedule_work(&ffs->reset_work);
+		return -ENODEV;
+	}
+
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	if (alt == (unsigned)-1) {
+		ffs->func = NULL;
+		ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+		return 0;
+	}
+
+	ffs->func = func;
+	ret = ffs_func_eps_enable(func);
+	if (likely(ret >= 0))
+		ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+	return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+	ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+			  const struct usb_ctrlrequest *creq)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	unsigned long flags;
+	int ret;
+
+	ENTER();
+
+	pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
+	pr_vdebug("creq->bRequest     = %02x\n", creq->bRequest);
+	pr_vdebug("creq->wValue       = %04x\n", le16_to_cpu(creq->wValue));
+	pr_vdebug("creq->wIndex       = %04x\n", le16_to_cpu(creq->wIndex));
+	pr_vdebug("creq->wLength      = %04x\n", le16_to_cpu(creq->wLength));
+
+	/*
+	 * Most requests directed to interface go through here
+	 * (notable exceptions are set/get interface) so we need to
+	 * handle them.  All other either handled by composite or
+	 * passed to usb_configuration->setup() (if one is set).  No
+	 * matter, we will handle requests directed to endpoint here
+	 * as well (as it's straightforward).  Other request recipient
+	 * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
+	 * is being used.
+	 */
+	if (ffs->state != FFS_ACTIVE)
+		return -ENODEV;
+
+	switch (creq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+		if (unlikely(ret < 0))
+			return ret;
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
+		if (unlikely(ret < 0))
+			return ret;
+		if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+			ret = func->ffs->eps_addrmap[ret];
+		break;
+
+	default:
+		if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
+			ret = le16_to_cpu(creq->wIndex);
+		else
+			return -EOPNOTSUPP;
+	}
+
+	spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+	ffs->ev.setup = *creq;
+	ffs->ev.setup.wIndex = cpu_to_le16(ret);
+	__ffs_event_add(ffs, FUNCTIONFS_SETUP);
+	spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+	return creq->wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0;
+}
+
+static bool ffs_func_req_match(struct usb_function *f,
+			       const struct usb_ctrlrequest *creq,
+			       bool config0)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+
+	if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
+		return false;
+
+	switch (creq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		return (ffs_func_revmap_intf(func,
+					     le16_to_cpu(creq->wIndex)) >= 0);
+	case USB_RECIP_ENDPOINT:
+		return (ffs_func_revmap_ep(func,
+					   le16_to_cpu(creq->wIndex)) >= 0);
+	default:
+		return (bool) (func->ffs->user_flags &
+			       FUNCTIONFS_ALL_CTRL_RECIP);
+	}
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+	ENTER();
+	ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+/* Endpoint and interface numbers reverse mapping ***************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+	num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+	return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+	short *nums = func->interfaces_nums;
+	unsigned count = func->ffs->interfaces_count;
+
+	for (; count; --count, ++nums) {
+		if (*nums >= 0 && *nums == intf)
+			return nums - func->interfaces_nums;
+	}
+
+	return -EDOM;
+}
+
+
+/* Devices management *******************************************************/
+
+static LIST_HEAD(ffs_devices);
+
+static struct ffs_dev *_ffs_do_find_dev(const char *name)
+{
+	struct ffs_dev *dev;
+
+	if (!name)
+		return NULL;
+
+	list_for_each_entry(dev, &ffs_devices, entry) {
+		if (strcmp(dev->name, name) == 0)
+			return dev;
+	}
+
+	return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_get_single_dev(void)
+{
+	struct ffs_dev *dev;
+
+	if (list_is_singular(&ffs_devices)) {
+		dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
+		if (dev->single)
+			return dev;
+	}
+
+	return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_find_dev(const char *name)
+{
+	struct ffs_dev *dev;
+
+	dev = _ffs_get_single_dev();
+	if (dev)
+		return dev;
+
+	return _ffs_do_find_dev(name);
+}
+
+/* Configfs support *********************************************************/
+
+static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_fs_opts,
+			    func_inst.group);
+}
+
+static void ffs_attr_release(struct config_item *item)
+{
+	struct f_fs_opts *opts = to_ffs_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ffs_item_ops = {
+	.release	= ffs_attr_release,
+};
+
+static const struct config_item_type ffs_func_type = {
+	.ct_item_ops	= &ffs_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+
+/* Function registration interface ******************************************/
+
+static void ffs_free_inst(struct usb_function_instance *f)
+{
+	struct f_fs_opts *opts;
+
+	opts = to_f_fs_opts(f);
+	ffs_dev_lock();
+	_ffs_free_dev(opts->dev);
+	ffs_dev_unlock();
+	kfree(opts);
+}
+
+static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
+{
+	if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name))
+		return -ENAMETOOLONG;
+	return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
+}
+
+static struct usb_function_instance *ffs_alloc_inst(void)
+{
+	struct f_fs_opts *opts;
+	struct ffs_dev *dev;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.set_inst_name = ffs_set_inst_name;
+	opts->func_inst.free_func_inst = ffs_free_inst;
+	ffs_dev_lock();
+	dev = _ffs_alloc_dev();
+	ffs_dev_unlock();
+	if (IS_ERR(dev)) {
+		kfree(opts);
+		return ERR_CAST(dev);
+	}
+	opts->dev = dev;
+	dev->opts = opts;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &ffs_func_type);
+	return &opts->func_inst;
+}
+
+static void ffs_free(struct usb_function *f)
+{
+	kfree(ffs_func_from_usb(f));
+}
+
+static void ffs_func_unbind(struct usb_configuration *c,
+			    struct usb_function *f)
+{
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+	struct f_fs_opts *opts =
+		container_of(f->fi, struct f_fs_opts, func_inst);
+	struct ffs_ep *ep = func->eps;
+	unsigned count = ffs->eps_count;
+	unsigned long flags;
+
+	ENTER();
+	if (ffs->func == func) {
+		ffs_func_eps_disable(func);
+		ffs->func = NULL;
+	}
+
+	if (!--opts->refcnt)
+		functionfs_unbind(ffs);
+
+	/* cleanup after autoconfig */
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
+	while (count--) {
+		if (ep->ep && ep->req)
+			usb_ep_free_request(ep->ep, ep->req);
+		ep->req = NULL;
+		++ep;
+	}
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+	kfree(func->eps);
+	func->eps = NULL;
+	/*
+	 * eps, descriptors and interfaces_nums are allocated in the
+	 * same chunk so only one free is required.
+	 */
+	func->function.fs_descriptors = NULL;
+	func->function.hs_descriptors = NULL;
+	func->function.ss_descriptors = NULL;
+	func->interfaces_nums = NULL;
+
+	ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+}
+
+static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
+{
+	struct ffs_function *func;
+
+	ENTER();
+
+	func = kzalloc(sizeof(*func), GFP_KERNEL);
+	if (unlikely(!func))
+		return ERR_PTR(-ENOMEM);
+
+	func->function.name    = "Function FS Gadget";
+
+	func->function.bind    = ffs_func_bind;
+	func->function.unbind  = ffs_func_unbind;
+	func->function.set_alt = ffs_func_set_alt;
+	func->function.disable = ffs_func_disable;
+	func->function.setup   = ffs_func_setup;
+	func->function.req_match = ffs_func_req_match;
+	func->function.suspend = ffs_func_suspend;
+	func->function.resume  = ffs_func_resume;
+	func->function.free_func = ffs_free;
+
+	return &func->function;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_alloc_dev(void)
+{
+	struct ffs_dev *dev;
+	int ret;
+
+	if (_ffs_get_single_dev())
+			return ERR_PTR(-EBUSY);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	if (list_empty(&ffs_devices)) {
+		ret = functionfs_init();
+		if (ret) {
+			kfree(dev);
+			return ERR_PTR(ret);
+		}
+	}
+
+	list_add(&dev->entry, &ffs_devices);
+
+	return dev;
+}
+
+int ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+	struct ffs_dev *existing;
+	int ret = 0;
+
+	ffs_dev_lock();
+
+	existing = _ffs_do_find_dev(name);
+	if (!existing)
+		strlcpy(dev->name, name, ARRAY_SIZE(dev->name));
+	else if (existing != dev)
+		ret = -EBUSY;
+
+	ffs_dev_unlock();
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_name_dev);
+
+int ffs_single_dev(struct ffs_dev *dev)
+{
+	int ret;
+
+	ret = 0;
+	ffs_dev_lock();
+
+	if (!list_is_singular(&ffs_devices))
+		ret = -EBUSY;
+	else
+		dev->single = true;
+
+	ffs_dev_unlock();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_single_dev);
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static void _ffs_free_dev(struct ffs_dev *dev)
+{
+	list_del(&dev->entry);
+
+	/* Clear the private_data pointer to stop incorrect dev access */
+	if (dev->ffs_data)
+		dev->ffs_data->private_data = NULL;
+
+	kfree(dev);
+	if (list_empty(&ffs_devices))
+		functionfs_cleanup();
+}
+
+static void *ffs_acquire_dev(const char *dev_name)
+{
+	struct ffs_dev *ffs_dev;
+
+	ENTER();
+	ffs_dev_lock();
+
+	ffs_dev = _ffs_find_dev(dev_name);
+	if (!ffs_dev)
+		ffs_dev = ERR_PTR(-ENOENT);
+	else if (ffs_dev->mounted)
+		ffs_dev = ERR_PTR(-EBUSY);
+	else if (ffs_dev->ffs_acquire_dev_callback &&
+	    ffs_dev->ffs_acquire_dev_callback(ffs_dev))
+		ffs_dev = ERR_PTR(-ENOENT);
+	else
+		ffs_dev->mounted = true;
+
+	ffs_dev_unlock();
+	return ffs_dev;
+}
+
+static void ffs_release_dev(struct ffs_data *ffs_data)
+{
+	struct ffs_dev *ffs_dev;
+
+	ENTER();
+	ffs_dev_lock();
+
+	ffs_dev = ffs_data->private_data;
+	if (ffs_dev) {
+		ffs_dev->mounted = false;
+
+		if (ffs_dev->ffs_release_dev_callback)
+			ffs_dev->ffs_release_dev_callback(ffs_dev);
+	}
+
+	ffs_dev_unlock();
+}
+
+static int ffs_ready(struct ffs_data *ffs)
+{
+	struct ffs_dev *ffs_obj;
+	int ret = 0;
+
+	ENTER();
+	ffs_dev_lock();
+
+	ffs_obj = ffs->private_data;
+	if (!ffs_obj) {
+		ret = -EINVAL;
+		goto done;
+	}
+	if (WARN_ON(ffs_obj->desc_ready)) {
+		ret = -EBUSY;
+		goto done;
+	}
+
+	ffs_obj->desc_ready = true;
+	ffs_obj->ffs_data = ffs;
+
+	if (ffs_obj->ffs_ready_callback) {
+		ret = ffs_obj->ffs_ready_callback(ffs);
+		if (ret)
+			goto done;
+	}
+
+	set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+done:
+	ffs_dev_unlock();
+	return ret;
+}
+
+static void ffs_closed(struct ffs_data *ffs)
+{
+	struct ffs_dev *ffs_obj;
+	struct f_fs_opts *opts;
+	struct config_item *ci;
+
+	ENTER();
+	ffs_dev_lock();
+
+	ffs_obj = ffs->private_data;
+	if (!ffs_obj)
+		goto done;
+
+	ffs_obj->desc_ready = false;
+	ffs_obj->ffs_data = NULL;
+
+	if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
+	    ffs_obj->ffs_closed_callback)
+		ffs_obj->ffs_closed_callback(ffs);
+
+	if (ffs_obj->opts)
+		opts = ffs_obj->opts;
+	else
+		goto done;
+
+	if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
+	    || !kref_read(&opts->func_inst.group.cg_item.ci_kref))
+		goto done;
+
+	ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
+	ffs_dev_unlock();
+
+	if (test_bit(FFS_FL_BOUND, &ffs->flags))
+		unregister_gadget_item(ci);
+	return;
+done:
+	ffs_dev_unlock();
+}
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+	return nonblock
+		? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+		: mutex_lock_interruptible(mutex);
+}
+
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
+{
+	char *data;
+
+	if (unlikely(!len))
+		return NULL;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (unlikely(!data))
+		return ERR_PTR(-ENOMEM);
+
+	if (unlikely(copy_from_user(data, buf, len))) {
+		kfree(data);
+		return ERR_PTR(-EFAULT);
+	}
+
+	pr_vdebug("Buffer from user space:\n");
+	ffs_dump_mem("", data, len);
+
+	return data;
+}
+
+DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
new file mode 100644
index 0000000..54e859d
--- /dev/null
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_hid.c -- USB HID function driver
+ *
+ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+#include <linux/idr.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/usb/g_hid.h>
+
+#include "u_f.h"
+#include "u_hid.h"
+
+#define HIDG_MINORS	4
+
+static int major, minors;
+static struct class *hidg_class;
+static DEFINE_IDA(hidg_ida);
+static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
+
+/*-------------------------------------------------------------------------*/
+/*                            HID gadget struct                            */
+
+struct f_hidg_req_list {
+	struct usb_request	*req;
+	unsigned int		pos;
+	struct list_head 	list;
+};
+
+struct f_hidg {
+	/* configuration */
+	unsigned char			bInterfaceSubClass;
+	unsigned char			bInterfaceProtocol;
+	unsigned char			protocol;
+	unsigned short			report_desc_length;
+	char				*report_desc;
+	unsigned short			report_length;
+
+	/* recv report */
+	struct list_head		completed_out_req;
+	spinlock_t			read_spinlock;
+	wait_queue_head_t		read_queue;
+	unsigned int			qlen;
+
+	/* send report */
+	spinlock_t			write_spinlock;
+	bool				write_pending;
+	wait_queue_head_t		write_queue;
+	struct usb_request		*req;
+
+	int				minor;
+	struct cdev			cdev;
+	struct usb_function		func;
+
+	struct usb_ep			*in_ep;
+	struct usb_ep			*out_ep;
+};
+
+static inline struct f_hidg *func_to_hidg(struct usb_function *f)
+{
+	return container_of(f, struct f_hidg, func);
+}
+
+/*-------------------------------------------------------------------------*/
+/*                           Static descriptors                            */
+
+static struct usb_interface_descriptor hidg_interface_desc = {
+	.bLength		= sizeof hidg_interface_desc,
+	.bDescriptorType	= USB_DT_INTERFACE,
+	/* .bInterfaceNumber	= DYNAMIC */
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 2,
+	.bInterfaceClass	= USB_CLASS_HID,
+	/* .bInterfaceSubClass	= DYNAMIC */
+	/* .bInterfaceProtocol	= DYNAMIC */
+	/* .iInterface		= DYNAMIC */
+};
+
+static struct hid_descriptor hidg_desc = {
+	.bLength			= sizeof hidg_desc,
+	.bDescriptorType		= HID_DT_HID,
+	.bcdHID				= 0x0101,
+	.bCountryCode			= 0x00,
+	.bNumDescriptors		= 0x1,
+	/*.desc[0].bDescriptorType	= DYNAMIC */
+	/*.desc[0].wDescriptorLenght	= DYNAMIC */
+};
+
+/* Super-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 4, /* FIXME: Add this field in the
+				      * HID gadget configuration?
+				      * (struct hidg_func_descriptor)
+				      */
+};
+
+static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
+	.bLength                = sizeof(hidg_ss_in_comp_desc),
+	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+
+	/* .bMaxBurst           = 0, */
+	/* .bmAttributes        = 0, */
+	/* .wBytesPerInterval   = DYNAMIC */
+};
+
+static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 4, /* FIXME: Add this field in the
+				      * HID gadget configuration?
+				      * (struct hidg_func_descriptor)
+				      */
+};
+
+static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
+	.bLength                = sizeof(hidg_ss_out_comp_desc),
+	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+
+	/* .bMaxBurst           = 0, */
+	/* .bmAttributes        = 0, */
+	/* .wBytesPerInterval   = DYNAMIC */
+};
+
+static struct usb_descriptor_header *hidg_ss_descriptors[] = {
+	(struct usb_descriptor_header *)&hidg_interface_desc,
+	(struct usb_descriptor_header *)&hidg_desc,
+	(struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
+	(struct usb_descriptor_header *)&hidg_ss_in_comp_desc,
+	(struct usb_descriptor_header *)&hidg_ss_out_ep_desc,
+	(struct usb_descriptor_header *)&hidg_ss_out_comp_desc,
+	NULL,
+};
+
+/* High-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 4, /* FIXME: Add this field in the
+				      * HID gadget configuration?
+				      * (struct hidg_func_descriptor)
+				      */
+};
+
+static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 4, /* FIXME: Add this field in the
+				      * HID gadget configuration?
+				      * (struct hidg_func_descriptor)
+				      */
+};
+
+static struct usb_descriptor_header *hidg_hs_descriptors[] = {
+	(struct usb_descriptor_header *)&hidg_interface_desc,
+	(struct usb_descriptor_header *)&hidg_desc,
+	(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
+	(struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
+	NULL,
+};
+
+/* Full-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 10, /* FIXME: Add this field in the
+				       * HID gadget configuration?
+				       * (struct hidg_func_descriptor)
+				       */
+};
+
+static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	/*.wMaxPacketSize	= DYNAMIC */
+	.bInterval		= 10, /* FIXME: Add this field in the
+				       * HID gadget configuration?
+				       * (struct hidg_func_descriptor)
+				       */
+};
+
+static struct usb_descriptor_header *hidg_fs_descriptors[] = {
+	(struct usb_descriptor_header *)&hidg_interface_desc,
+	(struct usb_descriptor_header *)&hidg_desc,
+	(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
+	(struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+/*                                 Strings                                 */
+
+#define CT_FUNC_HID_IDX	0
+
+static struct usb_string ct_func_string_defs[] = {
+	[CT_FUNC_HID_IDX].s	= "HID Interface",
+	{},			/* end of list */
+};
+
+static struct usb_gadget_strings ct_func_string_table = {
+	.language	= 0x0409,	/* en-US */
+	.strings	= ct_func_string_defs,
+};
+
+static struct usb_gadget_strings *ct_func_strings[] = {
+	&ct_func_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+/*                              Char Device                                */
+
+static ssize_t f_hidg_read(struct file *file, char __user *buffer,
+			size_t count, loff_t *ptr)
+{
+	struct f_hidg *hidg = file->private_data;
+	struct f_hidg_req_list *list;
+	struct usb_request *req;
+	unsigned long flags;
+	int ret;
+
+	if (!count)
+		return 0;
+
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&hidg->read_spinlock, flags);
+
+#define READ_COND (!list_empty(&hidg->completed_out_req))
+
+	/* wait for at least one buffer to complete */
+	while (!READ_COND) {
+		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		if (wait_event_interruptible(hidg->read_queue, READ_COND))
+			return -ERESTARTSYS;
+
+		spin_lock_irqsave(&hidg->read_spinlock, flags);
+	}
+
+	/* pick the first one */
+	list = list_first_entry(&hidg->completed_out_req,
+				struct f_hidg_req_list, list);
+
+	/*
+	 * Remove this from list to protect it from beign free()
+	 * while host disables our function
+	 */
+	list_del(&list->list);
+
+	req = list->req;
+	count = min_t(unsigned int, count, req->actual - list->pos);
+	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+	/* copy to user outside spinlock */
+	count -= copy_to_user(buffer, req->buf + list->pos, count);
+	list->pos += count;
+
+	/*
+	 * if this request is completely handled and transfered to
+	 * userspace, remove its entry from the list and requeue it
+	 * again. Otherwise, we will revisit it again upon the next
+	 * call, taking into account its current read position.
+	 */
+	if (list->pos == req->actual) {
+		kfree(list);
+
+		req->length = hidg->report_length;
+		ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
+		if (ret < 0) {
+			free_ep_req(hidg->out_ep, req);
+			return ret;
+		}
+	} else {
+		spin_lock_irqsave(&hidg->read_spinlock, flags);
+		list_add(&list->list, &hidg->completed_out_req);
+		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+		wake_up(&hidg->read_queue);
+	}
+
+	return count;
+}
+
+static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
+	unsigned long flags;
+
+	if (req->status != 0) {
+		ERROR(hidg->func.config->cdev,
+			"End Point Request ERROR: %d\n", req->status);
+	}
+
+	spin_lock_irqsave(&hidg->write_spinlock, flags);
+	hidg->write_pending = 0;
+	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+	wake_up(&hidg->write_queue);
+}
+
+static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
+			    size_t count, loff_t *offp)
+{
+	struct f_hidg *hidg  = file->private_data;
+	struct usb_request *req;
+	unsigned long flags;
+	ssize_t status = -ENOMEM;
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&hidg->write_spinlock, flags);
+
+#define WRITE_COND (!hidg->write_pending)
+try_again:
+	/* write queue */
+	while (!WRITE_COND) {
+		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		if (wait_event_interruptible_exclusive(
+				hidg->write_queue, WRITE_COND))
+			return -ERESTARTSYS;
+
+		spin_lock_irqsave(&hidg->write_spinlock, flags);
+	}
+
+	hidg->write_pending = 1;
+	req = hidg->req;
+	count  = min_t(unsigned, count, hidg->report_length);
+
+	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+	status = copy_from_user(req->buf, buffer, count);
+
+	if (status != 0) {
+		ERROR(hidg->func.config->cdev,
+			"copy_from_user error\n");
+		status = -EINVAL;
+		goto release_write_pending;
+	}
+
+	spin_lock_irqsave(&hidg->write_spinlock, flags);
+
+	/* when our function has been disabled by host */
+	if (!hidg->req) {
+		free_ep_req(hidg->in_ep, req);
+		/*
+		 * TODO
+		 * Should we fail with error here?
+		 */
+		goto try_again;
+	}
+
+	req->status   = 0;
+	req->zero     = 0;
+	req->length   = count;
+	req->complete = f_hidg_req_complete;
+	req->context  = hidg;
+
+	status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
+	if (status < 0) {
+		ERROR(hidg->func.config->cdev,
+			"usb_ep_queue error on int endpoint %zd\n", status);
+		goto release_write_pending_unlocked;
+	} else {
+		status = count;
+	}
+	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+	return status;
+release_write_pending:
+	spin_lock_irqsave(&hidg->write_spinlock, flags);
+release_write_pending_unlocked:
+	hidg->write_pending = 0;
+	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+	wake_up(&hidg->write_queue);
+
+	return status;
+}
+
+static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
+{
+	struct f_hidg	*hidg  = file->private_data;
+	__poll_t	ret = 0;
+
+	poll_wait(file, &hidg->read_queue, wait);
+	poll_wait(file, &hidg->write_queue, wait);
+
+	if (WRITE_COND)
+		ret |= EPOLLOUT | EPOLLWRNORM;
+
+	if (READ_COND)
+		ret |= EPOLLIN | EPOLLRDNORM;
+
+	return ret;
+}
+
+#undef WRITE_COND
+#undef READ_COND
+
+static int f_hidg_release(struct inode *inode, struct file *fd)
+{
+	fd->private_data = NULL;
+	return 0;
+}
+
+static int f_hidg_open(struct inode *inode, struct file *fd)
+{
+	struct f_hidg *hidg =
+		container_of(inode->i_cdev, struct f_hidg, cdev);
+
+	fd->private_data = hidg;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/*                                usb_function                             */
+
+static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
+						    unsigned length)
+{
+	return alloc_ep_req(ep, length);
+}
+
+static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_hidg *hidg = (struct f_hidg *) req->context;
+	struct usb_composite_dev *cdev = hidg->func.config->cdev;
+	struct f_hidg_req_list *req_list;
+	unsigned long flags;
+
+	switch (req->status) {
+	case 0:
+		req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+		if (!req_list) {
+			ERROR(cdev, "Unable to allocate mem for req_list\n");
+			goto free_req;
+		}
+
+		req_list->req = req;
+
+		spin_lock_irqsave(&hidg->read_spinlock, flags);
+		list_add_tail(&req_list->list, &hidg->completed_out_req);
+		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+		wake_up(&hidg->read_queue);
+		break;
+	default:
+		ERROR(cdev, "Set report failed %d\n", req->status);
+		/* FALLTHROUGH */
+	case -ECONNABORTED:		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+free_req:
+		free_ep_req(ep, req);
+		return;
+	}
+}
+
+static int hidg_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct f_hidg			*hidg = func_to_hidg(f);
+	struct usb_composite_dev	*cdev = f->config->cdev;
+	struct usb_request		*req  = cdev->req;
+	int status = 0;
+	__u16 value, length;
+
+	value	= __le16_to_cpu(ctrl->wValue);
+	length	= __le16_to_cpu(ctrl->wLength);
+
+	VDBG(cdev,
+	     "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n",
+	     __func__, ctrl->bRequestType, ctrl->bRequest, value);
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+		  | HID_REQ_GET_REPORT):
+		VDBG(cdev, "get_report\n");
+
+		/* send an empty report */
+		length = min_t(unsigned, length, hidg->report_length);
+		memset(req->buf, 0x0, length);
+
+		goto respond;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+		  | HID_REQ_GET_PROTOCOL):
+		VDBG(cdev, "get_protocol\n");
+		length = min_t(unsigned int, length, 1);
+		((u8 *) req->buf)[0] = hidg->protocol;
+		goto respond;
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+		  | HID_REQ_SET_REPORT):
+		VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength);
+		goto stall;
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
+		  | HID_REQ_SET_PROTOCOL):
+		VDBG(cdev, "set_protocol\n");
+		if (value > HID_REPORT_PROTOCOL)
+			goto stall;
+		length = 0;
+		/*
+		 * We assume that programs implementing the Boot protocol
+		 * are also compatible with the Report Protocol
+		 */
+		if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
+			hidg->protocol = value;
+			goto respond;
+		}
+		goto stall;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
+		  | USB_REQ_GET_DESCRIPTOR):
+		switch (value >> 8) {
+		case HID_DT_HID:
+		{
+			struct hid_descriptor hidg_desc_copy = hidg_desc;
+
+			VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
+			hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
+			hidg_desc_copy.desc[0].wDescriptorLength =
+				cpu_to_le16(hidg->report_desc_length);
+
+			length = min_t(unsigned short, length,
+						   hidg_desc_copy.bLength);
+			memcpy(req->buf, &hidg_desc_copy, length);
+			goto respond;
+			break;
+		}
+		case HID_DT_REPORT:
+			VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
+			length = min_t(unsigned short, length,
+						   hidg->report_desc_length);
+			memcpy(req->buf, hidg->report_desc, length);
+			goto respond;
+			break;
+
+		default:
+			VDBG(cdev, "Unknown descriptor request 0x%x\n",
+				 value >> 8);
+			goto stall;
+			break;
+		}
+		break;
+
+	default:
+		VDBG(cdev, "Unknown request 0x%x\n",
+			 ctrl->bRequest);
+		goto stall;
+		break;
+	}
+
+stall:
+	return -EOPNOTSUPP;
+
+respond:
+	req->zero = 0;
+	req->length = length;
+	status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+	if (status < 0)
+		ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
+	return status;
+}
+
+static void hidg_disable(struct usb_function *f)
+{
+	struct f_hidg *hidg = func_to_hidg(f);
+	struct f_hidg_req_list *list, *next;
+	unsigned long flags;
+
+	usb_ep_disable(hidg->in_ep);
+	usb_ep_disable(hidg->out_ep);
+
+	spin_lock_irqsave(&hidg->read_spinlock, flags);
+	list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
+		free_ep_req(hidg->out_ep, list->req);
+		list_del(&list->list);
+		kfree(list);
+	}
+	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+	spin_lock_irqsave(&hidg->write_spinlock, flags);
+	if (!hidg->write_pending) {
+		free_ep_req(hidg->in_ep, hidg->req);
+		hidg->write_pending = 1;
+	}
+
+	hidg->req = NULL;
+	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+}
+
+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);
+	struct usb_request			*req_in = NULL;
+	unsigned long				flags;
+	int i, status = 0;
+
+	VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
+
+	if (hidg->in_ep != NULL) {
+		/* restart endpoint */
+		usb_ep_disable(hidg->in_ep);
+
+		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 IN endpoint FAILED!\n");
+			goto fail;
+		}
+		hidg->in_ep->driver_data = hidg;
+
+		req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
+		if (!req_in) {
+			status = -ENOMEM;
+			goto disable_ep_in;
+		}
+	}
+
+
+	if (hidg->out_ep != NULL) {
+		/* restart endpoint */
+		usb_ep_disable(hidg->out_ep);
+
+		status = config_ep_by_speed(f->config->cdev->gadget, f,
+					    hidg->out_ep);
+		if (status) {
+			ERROR(cdev, "config_ep_by_speed FAILED!\n");
+			goto free_req_in;
+		}
+		status = usb_ep_enable(hidg->out_ep);
+		if (status < 0) {
+			ERROR(cdev, "Enable OUT endpoint FAILED!\n");
+			goto free_req_in;
+		}
+		hidg->out_ep->driver_data = hidg;
+
+		/*
+		 * allocate a bunch of read buffers and queue them all at once.
+		 */
+		for (i = 0; i < hidg->qlen && status == 0; i++) {
+			struct usb_request *req =
+					hidg_alloc_ep_req(hidg->out_ep,
+							  hidg->report_length);
+			if (req) {
+				req->complete = hidg_set_report_complete;
+				req->context  = hidg;
+				status = usb_ep_queue(hidg->out_ep, req,
+						      GFP_ATOMIC);
+				if (status) {
+					ERROR(cdev, "%s queue req --> %d\n",
+						hidg->out_ep->name, status);
+					free_ep_req(hidg->out_ep, req);
+				}
+			} else {
+				status = -ENOMEM;
+				goto disable_out_ep;
+			}
+		}
+	}
+
+	if (hidg->in_ep != NULL) {
+		spin_lock_irqsave(&hidg->write_spinlock, flags);
+		hidg->req = req_in;
+		hidg->write_pending = 0;
+		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+		wake_up(&hidg->write_queue);
+	}
+	return 0;
+disable_out_ep:
+	usb_ep_disable(hidg->out_ep);
+free_req_in:
+	if (req_in)
+		free_ep_req(hidg->in_ep, req_in);
+
+disable_ep_in:
+	if (hidg->in_ep)
+		usb_ep_disable(hidg->in_ep);
+
+fail:
+	return status;
+}
+
+static const struct file_operations f_hidg_fops = {
+	.owner		= THIS_MODULE,
+	.open		= f_hidg_open,
+	.release	= f_hidg_release,
+	.write		= f_hidg_write,
+	.read		= f_hidg_read,
+	.poll		= f_hidg_poll,
+	.llseek		= noop_llseek,
+};
+
+static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_ep		*ep;
+	struct f_hidg		*hidg = func_to_hidg(f);
+	struct usb_string	*us;
+	struct device		*device;
+	int			status;
+	dev_t			dev;
+
+	/* maybe allocate device-global string IDs, and patch descriptors */
+	us = usb_gstrings_attach(c->cdev, ct_func_strings,
+				 ARRAY_SIZE(ct_func_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	hidg_interface_desc.bInterfaceNumber = status;
+
+	/* allocate instance-specific endpoints */
+	status = -ENODEV;
+	ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
+	if (!ep)
+		goto fail;
+	hidg->in_ep = ep;
+
+	ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
+	if (!ep)
+		goto fail;
+	hidg->out_ep = ep;
+
+	/* set descriptor dynamic values */
+	hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
+	hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
+	hidg->protocol = HID_REPORT_PROTOCOL;
+	hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	hidg_ss_in_comp_desc.wBytesPerInterval =
+				cpu_to_le16(hidg->report_length);
+	hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	hidg_ss_out_comp_desc.wBytesPerInterval =
+				cpu_to_le16(hidg->report_length);
+	hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+	/*
+	 * We can use hidg_desc struct here but we should not relay
+	 * that its content won't change after returning from this function.
+	 */
+	hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
+	hidg_desc.desc[0].wDescriptorLength =
+		cpu_to_le16(hidg->report_desc_length);
+
+	hidg_hs_in_ep_desc.bEndpointAddress =
+		hidg_fs_in_ep_desc.bEndpointAddress;
+	hidg_hs_out_ep_desc.bEndpointAddress =
+		hidg_fs_out_ep_desc.bEndpointAddress;
+
+	hidg_ss_in_ep_desc.bEndpointAddress =
+		hidg_fs_in_ep_desc.bEndpointAddress;
+	hidg_ss_out_ep_desc.bEndpointAddress =
+		hidg_fs_out_ep_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, hidg_fs_descriptors,
+			hidg_hs_descriptors, hidg_ss_descriptors, NULL);
+	if (status)
+		goto fail;
+
+	spin_lock_init(&hidg->write_spinlock);
+	hidg->write_pending = 1;
+	hidg->req = NULL;
+	spin_lock_init(&hidg->read_spinlock);
+	init_waitqueue_head(&hidg->write_queue);
+	init_waitqueue_head(&hidg->read_queue);
+	INIT_LIST_HEAD(&hidg->completed_out_req);
+
+	/* create char device */
+	cdev_init(&hidg->cdev, &f_hidg_fops);
+	dev = MKDEV(major, hidg->minor);
+	status = cdev_add(&hidg->cdev, dev, 1);
+	if (status)
+		goto fail_free_descs;
+
+	device = device_create(hidg_class, NULL, dev, NULL,
+			       "%s%d", "hidg", hidg->minor);
+	if (IS_ERR(device)) {
+		status = PTR_ERR(device);
+		goto del;
+	}
+
+	return 0;
+del:
+	cdev_del(&hidg->cdev);
+fail_free_descs:
+	usb_free_all_descriptors(f);
+fail:
+	ERROR(f->config->cdev, "hidg_bind FAILED\n");
+	if (hidg->req != NULL)
+		free_ep_req(hidg->in_ep, hidg->req);
+
+	return status;
+}
+
+static inline int hidg_get_minor(void)
+{
+	int ret;
+
+	ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
+	if (ret >= HIDG_MINORS) {
+		ida_simple_remove(&hidg_ida, ret);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_hid_opts,
+			    func_inst.group);
+}
+
+static void hid_attr_release(struct config_item *item)
+{
+	struct f_hid_opts *opts = to_f_hid_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations hidg_item_ops = {
+	.release	= hid_attr_release,
+};
+
+#define F_HID_OPT(name, prec, limit)					\
+static ssize_t f_hid_opts_##name##_show(struct config_item *item, char *page)\
+{									\
+	struct f_hid_opts *opts = to_f_hid_opts(item);			\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_hid_opts_##name##_store(struct config_item *item,	\
+					 const char *page, size_t len)	\
+{									\
+	struct f_hid_opts *opts = to_f_hid_opts(item);			\
+	int ret;							\
+	u##prec num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou##prec(page, 0, &num);				\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > limit) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_hid_opts_, name)
+
+F_HID_OPT(subclass, 8, 255);
+F_HID_OPT(protocol, 8, 255);
+F_HID_OPT(report_length, 16, 65535);
+
+static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
+{
+	struct f_hid_opts *opts = to_f_hid_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = opts->report_desc_length;
+	memcpy(page, opts->report_desc, opts->report_desc_length);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_hid_opts_report_desc_store(struct config_item *item,
+					    const char *page, size_t len)
+{
+	struct f_hid_opts *opts = to_f_hid_opts(item);
+	int ret = -EBUSY;
+	char *d;
+
+	mutex_lock(&opts->lock);
+
+	if (opts->refcnt)
+		goto end;
+	if (len > PAGE_SIZE) {
+		ret = -ENOSPC;
+		goto end;
+	}
+	d = kmemdup(page, len, GFP_KERNEL);
+	if (!d) {
+		ret = -ENOMEM;
+		goto end;
+	}
+	kfree(opts->report_desc);
+	opts->report_desc = d;
+	opts->report_desc_length = len;
+	opts->report_desc_alloc = true;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_hid_opts_, report_desc);
+
+static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
+{
+	struct f_hid_opts *opts = to_f_hid_opts(item);
+
+	return sprintf(page, "%d:%d\n", major, opts->minor);
+}
+
+CONFIGFS_ATTR_RO(f_hid_opts_, dev);
+
+static struct configfs_attribute *hid_attrs[] = {
+	&f_hid_opts_attr_subclass,
+	&f_hid_opts_attr_protocol,
+	&f_hid_opts_attr_report_length,
+	&f_hid_opts_attr_report_desc,
+	&f_hid_opts_attr_dev,
+	NULL,
+};
+
+static const struct config_item_type hid_func_type = {
+	.ct_item_ops	= &hidg_item_ops,
+	.ct_attrs	= hid_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static inline void hidg_put_minor(int minor)
+{
+	ida_simple_remove(&hidg_ida, minor);
+}
+
+static void hidg_free_inst(struct usb_function_instance *f)
+{
+	struct f_hid_opts *opts;
+
+	opts = container_of(f, struct f_hid_opts, func_inst);
+
+	mutex_lock(&hidg_ida_lock);
+
+	hidg_put_minor(opts->minor);
+	if (ida_is_empty(&hidg_ida))
+		ghid_cleanup();
+
+	mutex_unlock(&hidg_ida_lock);
+
+	if (opts->report_desc_alloc)
+		kfree(opts->report_desc);
+
+	kfree(opts);
+}
+
+static struct usb_function_instance *hidg_alloc_inst(void)
+{
+	struct f_hid_opts *opts;
+	struct usb_function_instance *ret;
+	int status = 0;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = hidg_free_inst;
+	ret = &opts->func_inst;
+
+	mutex_lock(&hidg_ida_lock);
+
+	if (ida_is_empty(&hidg_ida)) {
+		status = ghid_setup(NULL, HIDG_MINORS);
+		if (status)  {
+			ret = ERR_PTR(status);
+			kfree(opts);
+			goto unlock;
+		}
+	}
+
+	opts->minor = hidg_get_minor();
+	if (opts->minor < 0) {
+		ret = ERR_PTR(opts->minor);
+		kfree(opts);
+		if (ida_is_empty(&hidg_ida))
+			ghid_cleanup();
+		goto unlock;
+	}
+	config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type);
+
+unlock:
+	mutex_unlock(&hidg_ida_lock);
+	return ret;
+}
+
+static void hidg_free(struct usb_function *f)
+{
+	struct f_hidg *hidg;
+	struct f_hid_opts *opts;
+
+	hidg = func_to_hidg(f);
+	opts = container_of(f->fi, struct f_hid_opts, func_inst);
+	kfree(hidg->report_desc);
+	kfree(hidg);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_hidg *hidg = func_to_hidg(f);
+
+	device_destroy(hidg_class, MKDEV(major, hidg->minor));
+	cdev_del(&hidg->cdev);
+
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
+{
+	struct f_hidg *hidg;
+	struct f_hid_opts *opts;
+
+	/* allocate and initialize one new instance */
+	hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
+	if (!hidg)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_hid_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+
+	hidg->minor = opts->minor;
+	hidg->bInterfaceSubClass = opts->subclass;
+	hidg->bInterfaceProtocol = opts->protocol;
+	hidg->report_length = opts->report_length;
+	hidg->report_desc_length = opts->report_desc_length;
+	if (opts->report_desc) {
+		hidg->report_desc = kmemdup(opts->report_desc,
+					    opts->report_desc_length,
+					    GFP_KERNEL);
+		if (!hidg->report_desc) {
+			kfree(hidg);
+			mutex_unlock(&opts->lock);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+
+	mutex_unlock(&opts->lock);
+
+	hidg->func.name    = "hid";
+	hidg->func.bind    = hidg_bind;
+	hidg->func.unbind  = hidg_unbind;
+	hidg->func.set_alt = hidg_set_alt;
+	hidg->func.disable = hidg_disable;
+	hidg->func.setup   = hidg_setup;
+	hidg->func.free_func = hidg_free;
+
+	/* this could me made configurable at some point */
+	hidg->qlen	   = 4;
+
+	return &hidg->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Fabien Chouteau");
+
+int ghid_setup(struct usb_gadget *g, int count)
+{
+	int status;
+	dev_t dev;
+
+	hidg_class = class_create(THIS_MODULE, "hidg");
+	if (IS_ERR(hidg_class)) {
+		status = PTR_ERR(hidg_class);
+		hidg_class = NULL;
+		return status;
+	}
+
+	status = alloc_chrdev_region(&dev, 0, count, "hidg");
+	if (status) {
+		class_destroy(hidg_class);
+		hidg_class = NULL;
+		return status;
+	}
+
+	major = MAJOR(dev);
+	minors = count;
+
+	return 0;
+}
+
+void ghid_cleanup(void)
+{
+	if (major) {
+		unregister_chrdev_region(MKDEV(major, 0), minors);
+		major = minors = 0;
+	}
+
+	class_destroy(hidg_class);
+	hidg_class = NULL;
+}
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
new file mode 100644
index 0000000..1803646
--- /dev/null
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_loopback.c - USB peripheral loopback configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/usb/composite.h>
+
+#include "g_zero.h"
+#include "u_f.h"
+
+/*
+ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
+ *
+ * This takes messages of various sizes written OUT to a device, and loops
+ * them back so they can be read IN from it.  It has been used by certain
+ * test applications.  It supports limited testing of data queueing logic.
+ */
+struct f_loopback {
+	struct usb_function	function;
+
+	struct usb_ep		*in_ep;
+	struct usb_ep		*out_ep;
+
+	unsigned                qlen;
+	unsigned                buflen;
+};
+
+static inline struct f_loopback *func_to_loop(struct usb_function *f)
+{
+	return container_of(f, struct f_loopback, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor loopback_intf = {
+	.bLength =		sizeof(loopback_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_loop_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_loop_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_loopback_descs[] = {
+	(struct usb_descriptor_header *) &loopback_intf,
+	(struct usb_descriptor_header *) &fs_loop_sink_desc,
+	(struct usb_descriptor_header *) &fs_loop_source_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_loop_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_loop_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_loopback_descs[] = {
+	(struct usb_descriptor_header *) &loopback_intf,
+	(struct usb_descriptor_header *) &hs_loop_source_desc,
+	(struct usb_descriptor_header *) &hs_loop_sink_desc,
+	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),
+};
+
+static 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),
+};
+
+static 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[] = {
+	[0].s = "loop input to output",
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab_loop = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_loopback,
+};
+
+static struct usb_gadget_strings *loopback_strings[] = {
+	&stringtab_loop,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_loopback	*loop = func_to_loop(f);
+	int			id;
+	int ret;
+
+	/* allocate interface ID(s) */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	loopback_intf.bInterfaceNumber = id;
+
+	id = usb_string_id(cdev);
+	if (id < 0)
+		return id;
+	strings_loopback[0].id = id;
+	loopback_intf.iInterface = id;
+
+	/* allocate endpoints */
+
+	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
+	if (!loop->in_ep) {
+autoconf_fail:
+		ERROR(cdev, "%s: can't autoconfigure on %s\n",
+			f->name, cdev->gadget->name);
+		return -ENODEV;
+	}
+
+	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
+	if (!loop->out_ep)
+		goto autoconf_fail;
+
+	/* support high speed hardware */
+	hs_loop_source_desc.bEndpointAddress =
+		fs_loop_source_desc.bEndpointAddress;
+	hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
+
+	/* support super speed hardware */
+	ss_loop_source_desc.bEndpointAddress =
+		fs_loop_source_desc.bEndpointAddress;
+	ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
+			ss_loopback_descs, NULL);
+	if (ret)
+		return ret;
+
+	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+	    (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;
+}
+
+static void lb_free_func(struct usb_function *f)
+{
+	struct f_lb_opts *opts;
+
+	opts = container_of(f->fi, struct f_lb_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+
+	usb_free_all_descriptors(f);
+	kfree(func_to_loop(f));
+}
+
+static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_loopback	*loop = ep->driver_data;
+	struct usb_composite_dev *cdev = loop->function.config->cdev;
+	int			status = req->status;
+
+	switch (status) {
+	case 0:				/* normal completion? */
+		if (ep == loop->out_ep) {
+			/*
+			 * We received some data from the host so let's
+			 * queue it so host can read the from our in ep
+			 */
+			struct usb_request *in_req = req->context;
+
+			in_req->zero = (req->actual < req->length);
+			in_req->length = req->actual;
+			ep = loop->in_ep;
+			req = in_req;
+		} else {
+			/*
+			 * We have just looped back a bunch of data
+			 * to host. Now let's wait for some more data.
+			 */
+			req = req->context;
+			ep = loop->out_ep;
+		}
+
+		/* queue the buffer back to host or for next bunch of data */
+		status = usb_ep_queue(ep, req, GFP_ATOMIC);
+		if (status == 0) {
+			return;
+		} else {
+			ERROR(cdev, "Unable to loop back buffer to %s: %d\n",
+			      ep->name, status);
+			goto free_req;
+		}
+
+		/* "should never get here" */
+	default:
+		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+		/* FALLTHROUGH */
+
+	/* NOTE:  since this driver doesn't maintain an explicit record
+	 * of requests it submitted (just maintains qlen count), we
+	 * rely on the hardware driver to clean up on disconnect or
+	 * endpoint disable.
+	 */
+	case -ECONNABORTED:		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+free_req:
+		usb_ep_free_request(ep == loop->in_ep ?
+				    loop->out_ep : loop->in_ep,
+				    req->context);
+		free_ep_req(ep, req);
+		return;
+	}
+}
+
+static void disable_loopback(struct f_loopback *loop)
+{
+	struct usb_composite_dev	*cdev;
+
+	cdev = loop->function.config->cdev;
+	disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
+	VDBG(cdev, "%s disabled\n", loop->function.name);
+}
+
+static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
+{
+	return alloc_ep_req(ep, len);
+}
+
+static int alloc_requests(struct usb_composite_dev *cdev,
+			  struct f_loopback *loop)
+{
+	struct usb_request *in_req, *out_req;
+	int i;
+	int result = 0;
+
+	/*
+	 * allocate a bunch of read buffers and queue them all at once.
+	 * we buffer at most 'qlen' transfers; We allocate buffers only
+	 * for out transfer and reuse them in IN transfers to implement
+	 * our loopback functionality
+	 */
+	for (i = 0; i < loop->qlen && result == 0; i++) {
+		result = -ENOMEM;
+
+		in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
+		if (!in_req)
+			goto fail;
+
+		out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
+		if (!out_req)
+			goto fail_in;
+
+		in_req->complete = loopback_complete;
+		out_req->complete = loopback_complete;
+
+		in_req->buf = out_req->buf;
+		/* length will be set in complete routine */
+		in_req->context = out_req;
+		out_req->context = in_req;
+
+		result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);
+		if (result) {
+			ERROR(cdev, "%s queue req --> %d\n",
+					loop->out_ep->name, result);
+			goto fail_out;
+		}
+	}
+
+	return 0;
+
+fail_out:
+	free_ep_req(loop->out_ep, out_req);
+fail_in:
+	usb_ep_free_request(loop->in_ep, in_req);
+fail:
+	return result;
+}
+
+static int enable_endpoint(struct usb_composite_dev *cdev,
+			   struct f_loopback *loop, struct usb_ep *ep)
+{
+	int					result;
+
+	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
+	if (result)
+		goto out;
+
+	result = usb_ep_enable(ep);
+	if (result < 0)
+		goto out;
+	ep->driver_data = loop;
+	result = 0;
+
+out:
+	return result;
+}
+
+static int
+enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
+{
+	int					result = 0;
+
+	result = enable_endpoint(cdev, loop, loop->in_ep);
+	if (result)
+		goto out;
+
+	result = enable_endpoint(cdev, loop, loop->out_ep);
+	if (result)
+		goto disable_in;
+
+	result = alloc_requests(cdev, loop);
+	if (result)
+		goto disable_out;
+
+	DBG(cdev, "%s enabled\n", loop->function.name);
+	return 0;
+
+disable_out:
+	usb_ep_disable(loop->out_ep);
+disable_in:
+	usb_ep_disable(loop->in_ep);
+out:
+	return result;
+}
+
+static int loopback_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct f_loopback	*loop = func_to_loop(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* we know alt is zero */
+	disable_loopback(loop);
+	return enable_loopback(cdev, loop);
+}
+
+static void loopback_disable(struct usb_function *f)
+{
+	struct f_loopback	*loop = func_to_loop(f);
+
+	disable_loopback(loop);
+}
+
+static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
+{
+	struct f_loopback	*loop;
+	struct f_lb_opts	*lb_opts;
+
+	loop = kzalloc(sizeof *loop, GFP_KERNEL);
+	if (!loop)
+		return ERR_PTR(-ENOMEM);
+
+	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+
+	mutex_lock(&lb_opts->lock);
+	lb_opts->refcnt++;
+	mutex_unlock(&lb_opts->lock);
+
+	loop->buflen = lb_opts->bulk_buflen;
+	loop->qlen = lb_opts->qlen;
+	if (!loop->qlen)
+		loop->qlen = 32;
+
+	loop->function.name = "loopback";
+	loop->function.bind = loopback_bind;
+	loop->function.set_alt = loopback_set_alt;
+	loop->function.disable = loopback_disable;
+	loop->function.strings = loopback_strings;
+
+	loop->function.free_func = lb_free_func;
+
+	return &loop->function;
+}
+
+static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_lb_opts,
+			    func_inst.group);
+}
+
+static void lb_attr_release(struct config_item *item)
+{
+	struct f_lb_opts *lb_opts = to_f_lb_opts(item);
+
+	usb_put_function_instance(&lb_opts->func_inst);
+}
+
+static struct configfs_item_operations lb_item_ops = {
+	.release		= lb_attr_release,
+};
+
+static ssize_t f_lb_opts_qlen_show(struct config_item *item, char *page)
+{
+	struct f_lb_opts *opts = to_f_lb_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d\n", opts->qlen);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_lb_opts_qlen_store(struct config_item *item,
+				    const char *page, size_t len)
+{
+	struct f_lb_opts *opts = to_f_lb_opts(item);
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->qlen = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_lb_opts_, qlen);
+
+static ssize_t f_lb_opts_bulk_buflen_show(struct config_item *item, char *page)
+{
+	struct f_lb_opts *opts = to_f_lb_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d\n", opts->bulk_buflen);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_lb_opts_bulk_buflen_store(struct config_item *item,
+				    const char *page, size_t len)
+{
+	struct f_lb_opts *opts = to_f_lb_opts(item);
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->bulk_buflen = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_lb_opts_, bulk_buflen);
+
+static struct configfs_attribute *lb_attrs[] = {
+	&f_lb_opts_attr_qlen,
+	&f_lb_opts_attr_bulk_buflen,
+	NULL,
+};
+
+static const struct config_item_type lb_func_type = {
+	.ct_item_ops    = &lb_item_ops,
+	.ct_attrs	= lb_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static void lb_free_instance(struct usb_function_instance *fi)
+{
+	struct f_lb_opts *lb_opts;
+
+	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+	kfree(lb_opts);
+}
+
+static struct usb_function_instance *loopback_alloc_instance(void)
+{
+	struct f_lb_opts *lb_opts;
+
+	lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
+	if (!lb_opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&lb_opts->lock);
+	lb_opts->func_inst.free_func_inst = lb_free_instance;
+	lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+	lb_opts->qlen = GZERO_QLEN;
+
+	config_group_init_type_name(&lb_opts->func_inst.group, "",
+				    &lb_func_type);
+
+	return  &lb_opts->func_inst;
+}
+DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
+
+int __init lb_modinit(void)
+{
+	return usb_function_register(&Loopbackusb_func);
+}
+
+void __exit lb_modexit(void)
+{
+	usb_function_unregister(&Loopbackusb_func);
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
new file mode 100644
index 0000000..1074cb8
--- /dev/null
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -0,0 +1,3468 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ *                    Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The Mass Storage Function acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive.  In
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * For more information about MSF and in particular its module
+ * parameters and sysfs interface read the
+ * <Documentation/usb/mass-storage.txt> file.
+ */
+
+/*
+ * MSF is configured by specifying a fsg_config structure.  It has the
+ * following fields:
+ *
+ *	nluns		Number of LUNs function have (anywhere from 1
+ *				to FSG_MAX_LUNS).
+ *	luns		An array of LUN configuration values.  This
+ *				should be filled for each LUN that
+ *				function will include (ie. for "nluns"
+ *				LUNs).  Each element of the array has
+ *				the following fields:
+ *	->filename	The path to the backing file for the LUN.
+ *				Required if LUN is not marked as
+ *				removable.
+ *	->ro		Flag specifying access to the LUN shall be
+ *				read-only.  This is implied if CD-ROM
+ *				emulation is enabled as well as when
+ *				it was impossible to open "filename"
+ *				in R/W mode.
+ *	->removable	Flag specifying that LUN shall be indicated as
+ *				being removable.
+ *	->cdrom		Flag specifying that LUN shall be reported as
+ *				being a CD-ROM.
+ *	->nofua		Flag specifying that FUA flag in SCSI WRITE(10,12)
+ *				commands for this LUN shall be ignored.
+ *
+ *	vendor_name
+ *	product_name
+ *	release		Information used as a reply to INQUIRY
+ *				request.  To use default set to NULL,
+ *				NULL, 0xffff respectively.  The first
+ *				field should be 8 and the second 16
+ *				characters or less.
+ *
+ *	can_stall	Set to permit function to halt bulk endpoints.
+ *				Disabled on some USB devices known not
+ *				to work correctly.  You should set it
+ *				to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified.  If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error).  The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN.
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell.  The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ *				Driver Design
+ *
+ * The MSF is fairly straightforward.  There is a main kernel
+ * thread that handles most of the work.  Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls.  Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction.  It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file.  This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example.  To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind().  But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead.  As of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd).  In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering).  But it helps to think of the
+ * pipeline as being a long one.  Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol.  There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete.  Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows.  This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint.  However, under certain circumstances the
+ * Bulk-only specification requires a stall.  In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset.  Hopefully this
+ * will permit everything to work correctly.  Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests.  Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time.  During that delay the host might give up on
+ * the original ep0 request and issue a new one.  When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it.  So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change).  When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag.  Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long.  It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/sched/signal.h>
+#include <linux/limits.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include <linux/nospec.h>
+
+#include "configfs.h"
+
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC		"Mass Storage Function"
+#define FSG_DRIVER_VERSION	"2009/09/11"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+#include "storage_common.h"
+#include "f_mass_storage.h"
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string		fsg_strings[] = {
+	{FSG_STRING_INTERFACE,		fsg_string_interface},
+	{}
+};
+
+static struct usb_gadget_strings	fsg_stringtab = {
+	.language	= 0x0409,		/* en-us */
+	.strings	= fsg_strings,
+};
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+	&fsg_stringtab,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_dev;
+struct fsg_common;
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+	struct usb_gadget	*gadget;
+	struct usb_composite_dev *cdev;
+	struct fsg_dev		*fsg, *new_fsg;
+	wait_queue_head_t	io_wait;
+	wait_queue_head_t	fsg_wait;
+
+	/* filesem protects: backing files in use */
+	struct rw_semaphore	filesem;
+
+	/* lock protects: state and thread_task */
+	spinlock_t		lock;
+
+	struct usb_ep		*ep0;		/* Copy of gadget->ep0 */
+	struct usb_request	*ep0req;	/* Copy of cdev->req */
+	unsigned int		ep0_req_tag;
+
+	struct fsg_buffhd	*next_buffhd_to_fill;
+	struct fsg_buffhd	*next_buffhd_to_drain;
+	struct fsg_buffhd	*buffhds;
+	unsigned int		fsg_num_buffers;
+
+	int			cmnd_size;
+	u8			cmnd[MAX_COMMAND_SIZE];
+
+	unsigned int		lun;
+	struct fsg_lun		*luns[FSG_MAX_LUNS];
+	struct fsg_lun		*curlun;
+
+	unsigned int		bulk_out_maxpacket;
+	enum fsg_state		state;		/* For exception handling */
+	unsigned int		exception_req_tag;
+
+	enum data_direction	data_dir;
+	u32			data_size;
+	u32			data_size_from_cmnd;
+	u32			tag;
+	u32			residue;
+	u32			usb_amount_left;
+
+	unsigned int		can_stall:1;
+	unsigned int		free_storage_on_release:1;
+	unsigned int		phase_error:1;
+	unsigned int		short_packet_received:1;
+	unsigned int		bad_lun_okay:1;
+	unsigned int		running:1;
+	unsigned int		sysfs:1;
+
+	struct completion	thread_notifier;
+	struct task_struct	*thread_task;
+
+	/* Gadget's private data. */
+	void			*private_data;
+
+	char inquiry_string[INQUIRY_STRING_LEN];
+};
+
+struct fsg_dev {
+	struct usb_function	function;
+	struct usb_gadget	*gadget;	/* Copy of cdev->gadget */
+	struct fsg_common	*common;
+
+	u16			interface_number;
+
+	unsigned int		bulk_in_enabled:1;
+	unsigned int		bulk_out_enabled:1;
+
+	unsigned long		atomic_bitflags;
+#define IGNORE_BULK_OUT		0
+
+	struct usb_ep		*bulk_in;
+	struct usb_ep		*bulk_out;
+};
+
+static inline int __fsg_is_set(struct fsg_common *common,
+			       const char *func, unsigned line)
+{
+	if (common->fsg)
+		return 1;
+	ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+	WARN_ON(1);
+	return 0;
+}
+
+#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+
+static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
+{
+	return container_of(f, struct fsg_dev, function);
+}
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_common *common)
+{
+	return common->state > FSG_STATE_NORMAL;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+				    struct fsg_buffhd *bh, unsigned int length)
+{
+	unsigned int	rem;
+
+	bh->bulk_out_intended_length = length;
+	rem = length % common->bulk_out_maxpacket;
+	if (rem > 0)
+		length += common->bulk_out_maxpacket - rem;
+	bh->outreq->length = length;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+	const char	*name;
+
+	if (ep == fsg->bulk_in)
+		name = "bulk-in";
+	else if (ep == fsg->bulk_out)
+		name = "bulk-out";
+	else
+		name = ep->name;
+	DBG(fsg, "%s set halt\n", name);
+	return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
+{
+	unsigned long		flags;
+
+	/*
+	 * Do nothing if a higher-priority exception is already in progress.
+	 * If a lower-or-equal priority exception is in progress, preempt it
+	 * and notify the main thread by sending it a signal.
+	 */
+	spin_lock_irqsave(&common->lock, flags);
+	if (common->state <= new_state) {
+		common->exception_req_tag = common->ep0_req_tag;
+		common->state = new_state;
+		if (common->thread_task)
+			send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+				      common->thread_task);
+	}
+	spin_unlock_irqrestore(&common->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+	int	rc;
+
+	rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+	common->ep0->driver_data = common;
+	if (rc != 0 && rc != -ESHUTDOWN) {
+		/* We can't do much more than wait for a reset */
+		WARNING(common, "error in submission: %s --> %d\n",
+			common->ep0->name, rc);
+	}
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Completion handlers. These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_common	*common = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	if (req->status || req->actual != req->length)
+		DBG(common, "%s --> %d, %u/%u\n", __func__,
+		    req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		/* Request was cancelled */
+		usb_ep_fifo_flush(ep);
+
+	/* Synchronize with the smp_load_acquire() in sleep_thread() */
+	smp_store_release(&bh->state, BUF_STATE_EMPTY);
+	wake_up(&common->io_wait);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_common	*common = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	dump_msg(common, "bulk-out", req->buf, req->actual);
+	if (req->status || req->actual != bh->bulk_out_intended_length)
+		DBG(common, "%s --> %d, %u/%u\n", __func__,
+		    req->status, req->actual, bh->bulk_out_intended_length);
+	if (req->status == -ECONNRESET)		/* Request was cancelled */
+		usb_ep_fifo_flush(ep);
+
+	/* Synchronize with the smp_load_acquire() in sleep_thread() */
+	smp_store_release(&bh->state, BUF_STATE_FULL);
+	wake_up(&common->io_wait);
+}
+
+static int _fsg_common_get_max_lun(struct fsg_common *common)
+{
+	int i = ARRAY_SIZE(common->luns) - 1;
+
+	while (i >= 0 && !common->luns[i])
+		--i;
+
+	return i;
+}
+
+static int fsg_setup(struct usb_function *f,
+		     const struct usb_ctrlrequest *ctrl)
+{
+	struct fsg_dev		*fsg = fsg_from_func(f);
+	struct usb_request	*req = fsg->common->ep0req;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!fsg_is_set(fsg->common))
+		return -EOPNOTSUPP;
+
+	++fsg->common->ep0_req_tag;	/* Record arrival of a new request */
+	req->context = NULL;
+	req->length = 0;
+	dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+	switch (ctrl->bRequest) {
+
+	case US_BULK_RESET_REQUEST:
+		if (ctrl->bRequestType !=
+		    (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+			break;
+		if (w_index != fsg->interface_number || w_value != 0 ||
+				w_length != 0)
+			return -EDOM;
+
+		/*
+		 * Raise an exception to stop the current operation
+		 * and reinitialize our state.
+		 */
+		DBG(fsg, "bulk reset request\n");
+		raise_exception(fsg->common, FSG_STATE_PROTOCOL_RESET);
+		return USB_GADGET_DELAYED_STATUS;
+
+	case US_BULK_GET_MAX_LUN:
+		if (ctrl->bRequestType !=
+		    (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+			break;
+		if (w_index != fsg->interface_number || w_value != 0 ||
+				w_length != 1)
+			return -EDOM;
+		VDBG(fsg, "get max LUN\n");
+		*(u8 *)req->buf = _fsg_common_get_max_lun(fsg->common);
+
+		/* Respond with data/status */
+		req->length = min((u16)1, w_length);
+		return ep0_queue(fsg->common);
+	}
+
+	VDBG(fsg,
+	     "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n",
+	     ctrl->bRequestType, ctrl->bRequest,
+	     le16_to_cpu(ctrl->wValue), w_index, w_length);
+	return -EOPNOTSUPP;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+			   struct usb_request *req)
+{
+	int	rc;
+
+	if (ep == fsg->bulk_in)
+		dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+	rc = usb_ep_queue(ep, req, GFP_KERNEL);
+	if (rc) {
+
+		/* We can't do much more than wait for a reset */
+		req->status = rc;
+
+		/*
+		 * Note: currently the net2280 driver fails zero-length
+		 * submissions if DMA is enabled.
+		 */
+		if (rc != -ESHUTDOWN &&
+				!(rc == -EOPNOTSUPP && req->length == 0))
+			WARNING(fsg, "error in submission: %s --> %d\n",
+					ep->name, rc);
+	}
+	return rc;
+}
+
+static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	if (!fsg_is_set(common))
+		return false;
+	bh->state = BUF_STATE_SENDING;
+	if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq))
+		bh->state = BUF_STATE_EMPTY;
+	return true;
+}
+
+static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	if (!fsg_is_set(common))
+		return false;
+	bh->state = BUF_STATE_RECEIVING;
+	if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq))
+		bh->state = BUF_STATE_FULL;
+	return true;
+}
+
+static int sleep_thread(struct fsg_common *common, bool can_freeze,
+		struct fsg_buffhd *bh)
+{
+	int	rc;
+
+	/* Wait until a signal arrives or bh is no longer busy */
+	if (can_freeze)
+		/*
+		 * synchronize with the smp_store_release(&bh->state) in
+		 * bulk_in_complete() or bulk_out_complete()
+		 */
+		rc = wait_event_freezable(common->io_wait,
+				bh && smp_load_acquire(&bh->state) >=
+					BUF_STATE_EMPTY);
+	else
+		rc = wait_event_interruptible(common->io_wait,
+				bh && smp_load_acquire(&bh->state) >=
+					BUF_STATE_EMPTY);
+	return rc ? -EINTR : 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+	struct fsg_lun		*curlun = common->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			rc;
+	u32			amount_left;
+	loff_t			file_offset, file_offset_tmp;
+	unsigned int		amount;
+	ssize_t			nread;
+
+	/*
+	 * Get the starting Logical Block Address and check that it's
+	 * not too big.
+	 */
+	if (common->cmnd[0] == READ_6)
+		lba = get_unaligned_be24(&common->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&common->cmnd[2]);
+
+		/*
+		 * We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = don't read from the
+		 * cache), but we don't implement them.
+		 */
+		if ((common->cmnd[1] & ~0x18) != 0) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+	file_offset = ((loff_t) lba) << curlun->blkbits;
+
+	/* Carry out the file reads */
+	amount_left = common->data_size_from_cmnd;
+	if (unlikely(amount_left == 0))
+		return -EIO;		/* No default reply */
+
+	for (;;) {
+		/*
+		 * Figure out how much we need to read:
+		 * Try to read the remaining amount.
+		 * But don't read more than the buffer size.
+		 * And don't try to read past the end of the file.
+		 */
+		amount = min(amount_left, FSG_BUFLEN);
+		amount = min((loff_t)amount,
+			     curlun->file_length - file_offset);
+
+		/* Wait for the next buffer to become available */
+		bh = common->next_buffhd_to_fill;
+		rc = sleep_thread(common, false, bh);
+		if (rc)
+			return rc;
+
+		/*
+		 * If we were asked to read past the end of file,
+		 * end with an empty buffer.
+		 */
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info =
+					file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			bh->inreq->length = 0;
+			bh->state = BUF_STATE_FULL;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		nread = kernel_read(curlun->filp, bh->buf, amount,
+				&file_offset_tmp);
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+		      (unsigned long long)file_offset, (int)nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file read: %d\n", (int)nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file read: %d/%u\n",
+			     (int)nread, amount);
+			nread = round_down(nread, curlun->blksize);
+		}
+		file_offset  += nread;
+		amount_left  -= nread;
+		common->residue -= nread;
+
+		/*
+		 * Except at the end of the transfer, nread will be
+		 * equal to the buffer size, which is divisible by the
+		 * bulk-in maxpacket size.
+		 */
+		bh->inreq->length = nread;
+		bh->state = BUF_STATE_FULL;
+
+		/* If an error occurred, report it and its position */
+		if (nread < amount) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info =
+					file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		if (amount_left == 0)
+			break;		/* No more left to read */
+
+		/* Send this buffer and go read some more */
+		bh->inreq->zero = 0;
+		if (!start_in_transfer(common, bh))
+			/* Don't know what to do if common->fsg is NULL */
+			return -EIO;
+		common->next_buffhd_to_fill = bh->next;
+	}
+
+	return -EIO;		/* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+	struct fsg_lun		*curlun = common->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			get_some_more;
+	u32			amount_left_to_req, amount_left_to_write;
+	loff_t			usb_offset, file_offset, file_offset_tmp;
+	unsigned int		amount;
+	ssize_t			nwritten;
+	int			rc;
+
+	if (curlun->ro) {
+		curlun->sense_data = SS_WRITE_PROTECTED;
+		return -EINVAL;
+	}
+	spin_lock(&curlun->filp->f_lock);
+	curlun->filp->f_flags &= ~O_SYNC;	/* Default is not to wait */
+	spin_unlock(&curlun->filp->f_lock);
+
+	/*
+	 * Get the starting Logical Block Address and check that it's
+	 * not too big
+	 */
+	if (common->cmnd[0] == WRITE_6)
+		lba = get_unaligned_be24(&common->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&common->cmnd[2]);
+
+		/*
+		 * We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = write directly to the
+		 * medium).  We don't implement DPO; we implement FUA by
+		 * performing synchronous output.
+		 */
+		if (common->cmnd[1] & ~0x18) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+		if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
+			spin_lock(&curlun->filp->f_lock);
+			curlun->filp->f_flags |= O_SYNC;
+			spin_unlock(&curlun->filp->f_lock);
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/* Carry out the file writes */
+	get_some_more = 1;
+	file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
+	amount_left_to_req = common->data_size_from_cmnd;
+	amount_left_to_write = common->data_size_from_cmnd;
+
+	while (amount_left_to_write > 0) {
+
+		/* Queue a request for more data from the host */
+		bh = common->next_buffhd_to_fill;
+		if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+			/*
+			 * Figure out how much we want to get:
+			 * Try to get the remaining amount,
+			 * but not more than the buffer size.
+			 */
+			amount = min(amount_left_to_req, FSG_BUFLEN);
+
+			/* Beyond the end of the backing file? */
+			if (usb_offset >= curlun->file_length) {
+				get_some_more = 0;
+				curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+				curlun->sense_data_info =
+					usb_offset >> curlun->blkbits;
+				curlun->info_valid = 1;
+				continue;
+			}
+
+			/* Get the next buffer */
+			usb_offset += amount;
+			common->usb_amount_left -= amount;
+			amount_left_to_req -= amount;
+			if (amount_left_to_req == 0)
+				get_some_more = 0;
+
+			/*
+			 * Except at the end of the transfer, amount will be
+			 * equal to the buffer size, which is divisible by
+			 * the bulk-out maxpacket size.
+			 */
+			set_bulk_out_req_length(common, bh, amount);
+			if (!start_out_transfer(common, bh))
+				/* Dunno what to do if common->fsg is NULL */
+				return -EIO;
+			common->next_buffhd_to_fill = bh->next;
+			continue;
+		}
+
+		/* Write the received data to the backing file */
+		bh = common->next_buffhd_to_drain;
+		if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+			break;			/* We stopped early */
+
+		/* Wait for the data to be received */
+		rc = sleep_thread(common, false, bh);
+		if (rc)
+			return rc;
+
+		common->next_buffhd_to_drain = bh->next;
+		bh->state = BUF_STATE_EMPTY;
+
+		/* Did something go wrong with the transfer? */
+		if (bh->outreq->status != 0) {
+			curlun->sense_data = SS_COMMUNICATION_FAILURE;
+			curlun->sense_data_info =
+					file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		amount = bh->outreq->actual;
+		if (curlun->file_length - file_offset < amount) {
+			LERROR(curlun, "write %u @ %llu beyond end %llu\n",
+				       amount, (unsigned long long)file_offset,
+				       (unsigned long long)curlun->file_length);
+			amount = curlun->file_length - file_offset;
+		}
+
+		/*
+		 * Don't accept excess data.  The spec doesn't say
+		 * what to do in this case.  We'll ignore the error.
+		 */
+		amount = min(amount, bh->bulk_out_intended_length);
+
+		/* Don't write a partial block */
+		amount = round_down(amount, curlun->blksize);
+		if (amount == 0)
+			goto empty_write;
+
+		/* Perform the write */
+		file_offset_tmp = file_offset;
+		nwritten = kernel_write(curlun->filp, bh->buf, amount,
+				&file_offset_tmp);
+		VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+				(unsigned long long)file_offset, (int)nwritten);
+		if (signal_pending(current))
+			return -EINTR;		/* Interrupted! */
+
+		if (nwritten < 0) {
+			LDBG(curlun, "error in file write: %d\n",
+					(int) nwritten);
+			nwritten = 0;
+		} else if (nwritten < amount) {
+			LDBG(curlun, "partial file write: %d/%u\n",
+					(int) nwritten, amount);
+			nwritten = round_down(nwritten, curlun->blksize);
+		}
+		file_offset += nwritten;
+		amount_left_to_write -= nwritten;
+		common->residue -= nwritten;
+
+		/* If an error occurred, report it and its position */
+		if (nwritten < amount) {
+			curlun->sense_data = SS_WRITE_ERROR;
+			curlun->sense_data_info =
+					file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+ empty_write:
+		/* Did the host decide to stop early? */
+		if (bh->outreq->actual < bh->bulk_out_intended_length) {
+			common->short_packet_received = 1;
+			break;
+		}
+	}
+
+	return -EIO;		/* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		rc;
+
+	/* We ignore the requested LBA and write out all file's
+	 * dirty data buffers. */
+	rc = fsg_lun_fsync_sub(curlun);
+	if (rc)
+		curlun->sense_data = SS_WRITE_ERROR;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+	struct file	*filp = curlun->filp;
+	struct inode	*inode = file_inode(filp);
+	unsigned long	rc;
+
+	rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+	VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_common *common)
+{
+	struct fsg_lun		*curlun = common->curlun;
+	u32			lba;
+	u32			verification_length;
+	struct fsg_buffhd	*bh = common->next_buffhd_to_fill;
+	loff_t			file_offset, file_offset_tmp;
+	u32			amount_left;
+	unsigned int		amount;
+	ssize_t			nread;
+
+	/*
+	 * Get the starting Logical Block Address and check that it's
+	 * not too big.
+	 */
+	lba = get_unaligned_be32(&common->cmnd[2]);
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/*
+	 * We allow DPO (Disable Page Out = don't save data in the
+	 * cache) but we don't implement it.
+	 */
+	if (common->cmnd[1] & ~0x10) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	verification_length = get_unaligned_be16(&common->cmnd[7]);
+	if (unlikely(verification_length == 0))
+		return -EIO;		/* No default reply */
+
+	/* Prepare to carry out the file verify */
+	amount_left = verification_length << curlun->blkbits;
+	file_offset = ((loff_t) lba) << curlun->blkbits;
+
+	/* Write out all the dirty buffers before invalidating them */
+	fsg_lun_fsync_sub(curlun);
+	if (signal_pending(current))
+		return -EINTR;
+
+	invalidate_sub(curlun);
+	if (signal_pending(current))
+		return -EINTR;
+
+	/* Just try to read the requested blocks */
+	while (amount_left > 0) {
+		/*
+		 * Figure out how much we need to read:
+		 * Try to read the remaining amount, but not more than
+		 * the buffer size.
+		 * And don't try to read past the end of the file.
+		 */
+		amount = min(amount_left, FSG_BUFLEN);
+		amount = min((loff_t)amount,
+			     curlun->file_length - file_offset);
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info =
+				file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		nread = kernel_read(curlun->filp, bh->buf, amount,
+				&file_offset_tmp);
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+				(unsigned long long) file_offset,
+				(int) nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file verify: %d\n", (int)nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file verify: %d/%u\n",
+			     (int)nread, amount);
+			nread = round_down(nread, curlun->blksize);
+		}
+		if (nread == 0) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info =
+				file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+		file_offset += nread;
+		amount_left -= nread;
+	}
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun *curlun = common->curlun;
+	u8	*buf = (u8 *) bh->buf;
+
+	if (!curlun) {		/* Unsupported LUNs are okay */
+		common->bad_lun_okay = 1;
+		memset(buf, 0, 36);
+		buf[0] = TYPE_NO_LUN;	/* Unsupported, no device-type */
+		buf[4] = 31;		/* Additional length */
+		return 36;
+	}
+
+	buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK;
+	buf[1] = curlun->removable ? 0x80 : 0;
+	buf[2] = 2;		/* ANSI SCSI level 2 */
+	buf[3] = 2;		/* SCSI-2 INQUIRY data format */
+	buf[4] = 31;		/* Additional length */
+	buf[5] = 0;		/* No special options */
+	buf[6] = 0;
+	buf[7] = 0;
+	if (curlun->inquiry_string[0])
+		memcpy(buf + 8, curlun->inquiry_string,
+		       sizeof(curlun->inquiry_string));
+	else
+		memcpy(buf + 8, common->inquiry_string,
+		       sizeof(common->inquiry_string));
+	return 36;
+}
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	u8		*buf = (u8 *) bh->buf;
+	u32		sd, sdinfo;
+	int		valid;
+
+	/*
+	 * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+	 *
+	 * If a REQUEST SENSE command is received from an initiator
+	 * with a pending unit attention condition (before the target
+	 * generates the contingent allegiance condition), then the
+	 * target shall either:
+	 *   a) report any pending sense data and preserve the unit
+	 *	attention condition on the logical unit, or,
+	 *   b) report the unit attention condition, may discard any
+	 *	pending sense data, and clear the unit attention
+	 *	condition on the logical unit for that initiator.
+	 *
+	 * FSG normally uses option a); enable this code to use option b).
+	 */
+#if 0
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+	}
+#endif
+
+	if (!curlun) {		/* Unsupported LUNs are okay */
+		common->bad_lun_okay = 1;
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+		sdinfo = 0;
+		valid = 0;
+	} else {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+		valid = curlun->info_valid << 7;
+		curlun->sense_data = SS_NO_SENSE;
+		curlun->sense_data_info = 0;
+		curlun->info_valid = 0;
+	}
+
+	memset(buf, 0, 18);
+	buf[0] = valid | 0x70;			/* Valid, current error */
+	buf[2] = SK(sd);
+	put_unaligned_be32(sdinfo, &buf[3]);	/* Sense information */
+	buf[7] = 18 - 8;			/* Additional sense length */
+	buf[12] = ASC(sd);
+	buf[13] = ASCQ(sd);
+	return 18;
+}
+
+static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	u32		lba = get_unaligned_be32(&common->cmnd[2]);
+	int		pmi = common->cmnd[8];
+	u8		*buf = (u8 *)bh->buf;
+
+	/* Check the PMI and LBA fields */
+	if (pmi > 1 || (pmi == 0 && lba != 0)) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+						/* Max logical block */
+	put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+	return 8;
+}
+
+static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		msf = common->cmnd[1] & 0x02;
+	u32		lba = get_unaligned_be32(&common->cmnd[2]);
+	u8		*buf = (u8 *)bh->buf;
+
+	if (common->cmnd[1] & ~0x02) {		/* Mask away MSF */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 8);
+	buf[0] = 0x01;		/* 2048 bytes of user data, rest is EC */
+	store_cdrom_address(&buf[4], msf, lba);
+	return 8;
+}
+
+static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		msf = common->cmnd[1] & 0x02;
+	int		start_track = common->cmnd[6];
+	u8		*buf = (u8 *)bh->buf;
+
+	if ((common->cmnd[1] & ~0x02) != 0 ||	/* Mask away MSF */
+			start_track > 1) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 20);
+	buf[1] = (20-2);		/* TOC data length */
+	buf[2] = 1;			/* First track number */
+	buf[3] = 1;			/* Last track number */
+	buf[5] = 0x16;			/* Data track, copying allowed */
+	buf[6] = 0x01;			/* Only track is number 1 */
+	store_cdrom_address(&buf[8], msf, 0);
+
+	buf[13] = 0x16;			/* Lead-out track is data */
+	buf[14] = 0xAA;			/* Lead-out track number */
+	store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+	return 20;
+}
+
+static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		mscmnd = common->cmnd[0];
+	u8		*buf = (u8 *) bh->buf;
+	u8		*buf0 = buf;
+	int		pc, page_code;
+	int		changeable_values, all_pages;
+	int		valid_page = 0;
+	int		len, limit;
+
+	if ((common->cmnd[1] & ~0x08) != 0) {	/* Mask away DBD */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	pc = common->cmnd[2] >> 6;
+	page_code = common->cmnd[2] & 0x3f;
+	if (pc == 3) {
+		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+		return -EINVAL;
+	}
+	changeable_values = (pc == 1);
+	all_pages = (page_code == 0x3f);
+
+	/*
+	 * Write the mode parameter header.  Fixed values are: default
+	 * medium type, no cache control (DPOFUA), and no block descriptors.
+	 * The only variable value is the WriteProtect bit.  We will fill in
+	 * the mode data length later.
+	 */
+	memset(buf, 0, 8);
+	if (mscmnd == MODE_SENSE) {
+		buf[2] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */
+		buf += 4;
+		limit = 255;
+	} else {			/* MODE_SENSE_10 */
+		buf[3] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */
+		buf += 8;
+		limit = 65535;		/* Should really be FSG_BUFLEN */
+	}
+
+	/* No block descriptors */
+
+	/*
+	 * The mode pages, in numerical order.  The only page we support
+	 * is the Caching page.
+	 */
+	if (page_code == 0x08 || all_pages) {
+		valid_page = 1;
+		buf[0] = 0x08;		/* Page code */
+		buf[1] = 10;		/* Page length */
+		memset(buf+2, 0, 10);	/* None of the fields are changeable */
+
+		if (!changeable_values) {
+			buf[2] = 0x04;	/* Write cache enable, */
+					/* Read cache not disabled */
+					/* No cache retention priorities */
+			put_unaligned_be16(0xffff, &buf[4]);
+					/* Don't disable prefetch */
+					/* Minimum prefetch = 0 */
+			put_unaligned_be16(0xffff, &buf[8]);
+					/* Maximum prefetch */
+			put_unaligned_be16(0xffff, &buf[10]);
+					/* Maximum prefetch ceiling */
+		}
+		buf += 12;
+	}
+
+	/*
+	 * Check that a valid page was requested and the mode data length
+	 * isn't too long.
+	 */
+	len = buf - buf0;
+	if (!valid_page || len > limit) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	/*  Store the mode data length */
+	if (mscmnd == MODE_SENSE)
+		buf0[0] = len - 1;
+	else
+		put_unaligned_be16(len - 2, buf0);
+	return len;
+}
+
+static int do_start_stop(struct fsg_common *common)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		loej, start;
+
+	if (!curlun) {
+		return -EINVAL;
+	} else if (!curlun->removable) {
+		curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	} else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+		   (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	loej  = common->cmnd[4] & 0x02;
+	start = common->cmnd[4] & 0x01;
+
+	/*
+	 * Our emulation doesn't support mounting; the medium is
+	 * available for use as soon as it is loaded.
+	 */
+	if (start) {
+		if (!fsg_lun_is_open(curlun)) {
+			curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	/* Are we allowed to unload the media? */
+	if (curlun->prevent_medium_removal) {
+		LDBG(curlun, "unload attempt prevented\n");
+		curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+		return -EINVAL;
+	}
+
+	if (!loej)
+		return 0;
+
+	up_read(&common->filesem);
+	down_write(&common->filesem);
+	fsg_lun_close(curlun);
+	up_write(&common->filesem);
+	down_read(&common->filesem);
+
+	return 0;
+}
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	int		prevent;
+
+	if (!common->curlun) {
+		return -EINVAL;
+	} else if (!common->curlun->removable) {
+		common->curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	}
+
+	prevent = common->cmnd[4] & 0x01;
+	if ((common->cmnd[4] & ~0x01) != 0) {	/* Mask away Prevent */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	if (curlun->prevent_medium_removal && !prevent)
+		fsg_lun_fsync_sub(curlun);
+	curlun->prevent_medium_removal = prevent;
+	return 0;
+}
+
+static int do_read_format_capacities(struct fsg_common *common,
+			struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+	u8		*buf = (u8 *) bh->buf;
+
+	buf[0] = buf[1] = buf[2] = 0;
+	buf[3] = 8;	/* Only the Current/Maximum Capacity Descriptor */
+	buf += 4;
+
+	put_unaligned_be32(curlun->num_sectors, &buf[0]);
+						/* Number of blocks */
+	put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+	buf[4] = 0x02;				/* Current capacity */
+	return 12;
+}
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = common->curlun;
+
+	/* We don't support MODE SELECT */
+	if (curlun)
+		curlun->sense_data = SS_INVALID_COMMAND;
+	return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	rc = fsg_set_halt(fsg, fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint halt\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		rc = usb_ep_set_halt(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	DBG(fsg, "bulk-in set wedge\n");
+	rc = usb_ep_set_wedge(fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		rc = usb_ep_set_wedge(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+	struct fsg_buffhd	*bh, *bh2;
+	u32			amount;
+	int			rc;
+
+	for (bh = common->next_buffhd_to_drain;
+	     bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+	     bh = common->next_buffhd_to_drain) {
+
+		/* Try to submit another request if we need one */
+		bh2 = common->next_buffhd_to_fill;
+		if (bh2->state == BUF_STATE_EMPTY &&
+				common->usb_amount_left > 0) {
+			amount = min(common->usb_amount_left, FSG_BUFLEN);
+
+			/*
+			 * Except at the end of the transfer, amount will be
+			 * equal to the buffer size, which is divisible by
+			 * the bulk-out maxpacket size.
+			 */
+			set_bulk_out_req_length(common, bh2, amount);
+			if (!start_out_transfer(common, bh2))
+				/* Dunno what to do if common->fsg is NULL */
+				return -EIO;
+			common->next_buffhd_to_fill = bh2->next;
+			common->usb_amount_left -= amount;
+			continue;
+		}
+
+		/* Wait for the data to be received */
+		rc = sleep_thread(common, false, bh);
+		if (rc)
+			return rc;
+
+		/* Throw away the data in a filled buffer */
+		bh->state = BUF_STATE_EMPTY;
+		common->next_buffhd_to_drain = bh->next;
+
+		/* A short packet or an error ends everything */
+		if (bh->outreq->actual < bh->bulk_out_intended_length ||
+				bh->outreq->status != 0) {
+			raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+			return -EINTR;
+		}
+	}
+	return 0;
+}
+
+static int finish_reply(struct fsg_common *common)
+{
+	struct fsg_buffhd	*bh = common->next_buffhd_to_fill;
+	int			rc = 0;
+
+	switch (common->data_dir) {
+	case DATA_DIR_NONE:
+		break;			/* Nothing to send */
+
+	/*
+	 * If we don't know whether the host wants to read or write,
+	 * this must be CB or CBI with an unknown command.  We mustn't
+	 * try to send or receive any data.  So stall both bulk pipes
+	 * if we can and wait for a reset.
+	 */
+	case DATA_DIR_UNKNOWN:
+		if (!common->can_stall) {
+			/* Nothing */
+		} else if (fsg_is_set(common)) {
+			fsg_set_halt(common->fsg, common->fsg->bulk_out);
+			rc = halt_bulk_in_endpoint(common->fsg);
+		} else {
+			/* Don't know what to do if common->fsg is NULL */
+			rc = -EIO;
+		}
+		break;
+
+	/* All but the last buffer of data must have already been sent */
+	case DATA_DIR_TO_HOST:
+		if (common->data_size == 0) {
+			/* Nothing to send */
+
+		/* Don't know what to do if common->fsg is NULL */
+		} else if (!fsg_is_set(common)) {
+			rc = -EIO;
+
+		/* If there's no residue, simply send the last buffer */
+		} else if (common->residue == 0) {
+			bh->inreq->zero = 0;
+			if (!start_in_transfer(common, bh))
+				return -EIO;
+			common->next_buffhd_to_fill = bh->next;
+
+		/*
+		 * For Bulk-only, mark the end of the data with a short
+		 * packet.  If we are allowed to stall, halt the bulk-in
+		 * endpoint.  (Note: This violates the Bulk-Only Transport
+		 * specification, which requires us to pad the data if we
+		 * don't halt the endpoint.  Presumably nobody will mind.)
+		 */
+		} else {
+			bh->inreq->zero = 1;
+			if (!start_in_transfer(common, bh))
+				rc = -EIO;
+			common->next_buffhd_to_fill = bh->next;
+			if (common->can_stall)
+				rc = halt_bulk_in_endpoint(common->fsg);
+		}
+		break;
+
+	/*
+	 * We have processed all we want from the data the host has sent.
+	 * There may still be outstanding bulk-out requests.
+	 */
+	case DATA_DIR_FROM_HOST:
+		if (common->residue == 0) {
+			/* Nothing to receive */
+
+		/* Did the host stop sending unexpectedly early? */
+		} else if (common->short_packet_received) {
+			raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+
+		/*
+		 * We haven't processed all the incoming data.  Even though
+		 * we may be allowed to stall, doing so would cause a race.
+		 * The controller may already have ACK'ed all the remaining
+		 * bulk-out packets, in which case the host wouldn't see a
+		 * STALL.  Not realizing the endpoint was halted, it wouldn't
+		 * clear the halt -- leading to problems later on.
+		 */
+#if 0
+		} else if (common->can_stall) {
+			if (fsg_is_set(common))
+				fsg_set_halt(common->fsg,
+					     common->fsg->bulk_out);
+			raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+#endif
+
+		/*
+		 * We can't stall.  Read in the excess data and throw it
+		 * all away.
+		 */
+		} else {
+			rc = throw_away_data(common);
+		}
+		break;
+	}
+	return rc;
+}
+
+static void send_status(struct fsg_common *common)
+{
+	struct fsg_lun		*curlun = common->curlun;
+	struct fsg_buffhd	*bh;
+	struct bulk_cs_wrap	*csw;
+	int			rc;
+	u8			status = US_BULK_STAT_OK;
+	u32			sd, sdinfo = 0;
+
+	/* Wait for the next buffer to become available */
+	bh = common->next_buffhd_to_fill;
+	rc = sleep_thread(common, false, bh);
+	if (rc)
+		return;
+
+	if (curlun) {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+	} else if (common->bad_lun_okay)
+		sd = SS_NO_SENSE;
+	else
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+	if (common->phase_error) {
+		DBG(common, "sending phase-error status\n");
+		status = US_BULK_STAT_PHASE;
+		sd = SS_INVALID_COMMAND;
+	} else if (sd != SS_NO_SENSE) {
+		DBG(common, "sending command-failure status\n");
+		status = US_BULK_STAT_FAIL;
+		VDBG(common, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+				"  info x%x\n",
+				SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+	}
+
+	/* Store and send the Bulk-only CSW */
+	csw = (void *)bh->buf;
+
+	csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
+	csw->Tag = common->tag;
+	csw->Residue = cpu_to_le32(common->residue);
+	csw->Status = status;
+
+	bh->inreq->length = US_BULK_CS_WRAP_LEN;
+	bh->inreq->zero = 0;
+	if (!start_in_transfer(common, bh))
+		/* Don't know what to do if common->fsg is NULL */
+		return;
+
+	common->next_buffhd_to_fill = bh->next;
+	return;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have.
+ */
+static int check_command(struct fsg_common *common, int cmnd_size,
+			 enum data_direction data_dir, unsigned int mask,
+			 int needs_medium, const char *name)
+{
+	int			i;
+	unsigned int		lun = common->cmnd[1] >> 5;
+	static const char	dirletter[4] = {'u', 'o', 'i', 'n'};
+	char			hdlen[20];
+	struct fsg_lun		*curlun;
+
+	hdlen[0] = 0;
+	if (common->data_dir != DATA_DIR_UNKNOWN)
+		sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+			common->data_size);
+	VDBG(common, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
+	     name, cmnd_size, dirletter[(int) data_dir],
+	     common->data_size_from_cmnd, common->cmnd_size, hdlen);
+
+	/*
+	 * We can't reply at all until we know the correct data direction
+	 * and size.
+	 */
+	if (common->data_size_from_cmnd == 0)
+		data_dir = DATA_DIR_NONE;
+	if (common->data_size < common->data_size_from_cmnd) {
+		/*
+		 * Host data size < Device data size is a phase error.
+		 * Carry out the command, but only transfer as much as
+		 * we are allowed.
+		 */
+		common->data_size_from_cmnd = common->data_size;
+		common->phase_error = 1;
+	}
+	common->residue = common->data_size;
+	common->usb_amount_left = common->data_size;
+
+	/* Conflicting data directions is a phase error */
+	if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
+		common->phase_error = 1;
+		return -EINVAL;
+	}
+
+	/* Verify the length of the command itself */
+	if (cmnd_size != common->cmnd_size) {
+
+		/*
+		 * Special case workaround: There are plenty of buggy SCSI
+		 * implementations. Many have issues with cbw->Length
+		 * field passing a wrong command size. For those cases we
+		 * always try to work around the problem by using the length
+		 * sent by the host side provided it is at least as large
+		 * as the correct command length.
+		 * Examples of such cases would be MS-Windows, which issues
+		 * REQUEST SENSE with cbw->Length == 12 where it should
+		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+		 * REQUEST SENSE with cbw->Length == 10 where it should
+		 * be 6 as well.
+		 */
+		if (cmnd_size <= common->cmnd_size) {
+			DBG(common, "%s is buggy! Expected length %d "
+			    "but we got %d\n", name,
+			    cmnd_size, common->cmnd_size);
+			cmnd_size = common->cmnd_size;
+		} else {
+			common->phase_error = 1;
+			return -EINVAL;
+		}
+	}
+
+	/* Check that the LUN values are consistent */
+	if (common->lun != lun)
+		DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n",
+		    common->lun, lun);
+
+	/* Check the LUN */
+	curlun = common->curlun;
+	if (curlun) {
+		if (common->cmnd[0] != REQUEST_SENSE) {
+			curlun->sense_data = SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+	} else {
+		common->bad_lun_okay = 0;
+
+		/*
+		 * INQUIRY and REQUEST SENSE commands are explicitly allowed
+		 * to use unsupported LUNs; all others may not.
+		 */
+		if (common->cmnd[0] != INQUIRY &&
+		    common->cmnd[0] != REQUEST_SENSE) {
+			DBG(common, "unsupported LUN %u\n", common->lun);
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * If a unit attention condition exists, only INQUIRY and
+	 * REQUEST SENSE commands are allowed; anything else must fail.
+	 */
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+	    common->cmnd[0] != INQUIRY &&
+	    common->cmnd[0] != REQUEST_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+		return -EINVAL;
+	}
+
+	/* Check that only command bytes listed in the mask are non-zero */
+	common->cmnd[1] &= 0x1f;			/* Mask away the LUN */
+	for (i = 1; i < cmnd_size; ++i) {
+		if (common->cmnd[i] && !(mask & (1 << i))) {
+			if (curlun)
+				curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+
+	/* If the medium isn't mounted and the command needs to access
+	 * it, return an error. */
+	if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+		curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* wrapper of check_command for data size in blocks handling */
+static int check_command_size_in_blocks(struct fsg_common *common,
+		int cmnd_size, enum data_direction data_dir,
+		unsigned int mask, int needs_medium, const char *name)
+{
+	if (common->curlun)
+		common->data_size_from_cmnd <<= common->curlun->blkbits;
+	return check_command(common, cmnd_size, data_dir,
+			mask, needs_medium, name);
+}
+
+static int do_scsi_command(struct fsg_common *common)
+{
+	struct fsg_buffhd	*bh;
+	int			rc;
+	int			reply = -EINVAL;
+	int			i;
+	static char		unknown[16];
+
+	dump_cdb(common);
+
+	/* Wait for the next buffer to become available for data or status */
+	bh = common->next_buffhd_to_fill;
+	common->next_buffhd_to_drain = bh;
+	rc = sleep_thread(common, false, bh);
+	if (rc)
+		return rc;
+
+	common->phase_error = 0;
+	common->short_packet_received = 0;
+
+	down_read(&common->filesem);	/* We're using the backing file */
+	switch (common->cmnd[0]) {
+
+	case INQUIRY:
+		common->data_size_from_cmnd = common->cmnd[4];
+		reply = check_command(common, 6, DATA_DIR_TO_HOST,
+				      (1<<4), 0,
+				      "INQUIRY");
+		if (reply == 0)
+			reply = do_inquiry(common, bh);
+		break;
+
+	case MODE_SELECT:
+		common->data_size_from_cmnd = common->cmnd[4];
+		reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+				      (1<<1) | (1<<4), 0,
+				      "MODE SELECT(6)");
+		if (reply == 0)
+			reply = do_mode_select(common, bh);
+		break;
+
+	case MODE_SELECT_10:
+		common->data_size_from_cmnd =
+			get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+				      (1<<1) | (3<<7), 0,
+				      "MODE SELECT(10)");
+		if (reply == 0)
+			reply = do_mode_select(common, bh);
+		break;
+
+	case MODE_SENSE:
+		common->data_size_from_cmnd = common->cmnd[4];
+		reply = check_command(common, 6, DATA_DIR_TO_HOST,
+				      (1<<1) | (1<<2) | (1<<4), 0,
+				      "MODE SENSE(6)");
+		if (reply == 0)
+			reply = do_mode_sense(common, bh);
+		break;
+
+	case MODE_SENSE_10:
+		common->data_size_from_cmnd =
+			get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command(common, 10, DATA_DIR_TO_HOST,
+				      (1<<1) | (1<<2) | (3<<7), 0,
+				      "MODE SENSE(10)");
+		if (reply == 0)
+			reply = do_mode_sense(common, bh);
+		break;
+
+	case ALLOW_MEDIUM_REMOVAL:
+		common->data_size_from_cmnd = 0;
+		reply = check_command(common, 6, DATA_DIR_NONE,
+				      (1<<4), 0,
+				      "PREVENT-ALLOW MEDIUM REMOVAL");
+		if (reply == 0)
+			reply = do_prevent_allow(common);
+		break;
+
+	case READ_6:
+		i = common->cmnd[4];
+		common->data_size_from_cmnd = (i == 0) ? 256 : i;
+		reply = check_command_size_in_blocks(common, 6,
+				      DATA_DIR_TO_HOST,
+				      (7<<1) | (1<<4), 1,
+				      "READ(6)");
+		if (reply == 0)
+			reply = do_read(common);
+		break;
+
+	case READ_10:
+		common->data_size_from_cmnd =
+				get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command_size_in_blocks(common, 10,
+				      DATA_DIR_TO_HOST,
+				      (1<<1) | (0xf<<2) | (3<<7), 1,
+				      "READ(10)");
+		if (reply == 0)
+			reply = do_read(common);
+		break;
+
+	case READ_12:
+		common->data_size_from_cmnd =
+				get_unaligned_be32(&common->cmnd[6]);
+		reply = check_command_size_in_blocks(common, 12,
+				      DATA_DIR_TO_HOST,
+				      (1<<1) | (0xf<<2) | (0xf<<6), 1,
+				      "READ(12)");
+		if (reply == 0)
+			reply = do_read(common);
+		break;
+
+	case READ_CAPACITY:
+		common->data_size_from_cmnd = 8;
+		reply = check_command(common, 10, DATA_DIR_TO_HOST,
+				      (0xf<<2) | (1<<8), 1,
+				      "READ CAPACITY");
+		if (reply == 0)
+			reply = do_read_capacity(common, bh);
+		break;
+
+	case READ_HEADER:
+		if (!common->curlun || !common->curlun->cdrom)
+			goto unknown_cmnd;
+		common->data_size_from_cmnd =
+			get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command(common, 10, DATA_DIR_TO_HOST,
+				      (3<<7) | (0x1f<<1), 1,
+				      "READ HEADER");
+		if (reply == 0)
+			reply = do_read_header(common, bh);
+		break;
+
+	case READ_TOC:
+		if (!common->curlun || !common->curlun->cdrom)
+			goto unknown_cmnd;
+		common->data_size_from_cmnd =
+			get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command(common, 10, DATA_DIR_TO_HOST,
+				      (7<<6) | (1<<1), 1,
+				      "READ TOC");
+		if (reply == 0)
+			reply = do_read_toc(common, bh);
+		break;
+
+	case READ_FORMAT_CAPACITIES:
+		common->data_size_from_cmnd =
+			get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command(common, 10, DATA_DIR_TO_HOST,
+				      (3<<7), 1,
+				      "READ FORMAT CAPACITIES");
+		if (reply == 0)
+			reply = do_read_format_capacities(common, bh);
+		break;
+
+	case REQUEST_SENSE:
+		common->data_size_from_cmnd = common->cmnd[4];
+		reply = check_command(common, 6, DATA_DIR_TO_HOST,
+				      (1<<4), 0,
+				      "REQUEST SENSE");
+		if (reply == 0)
+			reply = do_request_sense(common, bh);
+		break;
+
+	case START_STOP:
+		common->data_size_from_cmnd = 0;
+		reply = check_command(common, 6, DATA_DIR_NONE,
+				      (1<<1) | (1<<4), 0,
+				      "START-STOP UNIT");
+		if (reply == 0)
+			reply = do_start_stop(common);
+		break;
+
+	case SYNCHRONIZE_CACHE:
+		common->data_size_from_cmnd = 0;
+		reply = check_command(common, 10, DATA_DIR_NONE,
+				      (0xf<<2) | (3<<7), 1,
+				      "SYNCHRONIZE CACHE");
+		if (reply == 0)
+			reply = do_synchronize_cache(common);
+		break;
+
+	case TEST_UNIT_READY:
+		common->data_size_from_cmnd = 0;
+		reply = check_command(common, 6, DATA_DIR_NONE,
+				0, 1,
+				"TEST UNIT READY");
+		break;
+
+	/*
+	 * Although optional, this command is used by MS-Windows.  We
+	 * support a minimal version: BytChk must be 0.
+	 */
+	case VERIFY:
+		common->data_size_from_cmnd = 0;
+		reply = check_command(common, 10, DATA_DIR_NONE,
+				      (1<<1) | (0xf<<2) | (3<<7), 1,
+				      "VERIFY");
+		if (reply == 0)
+			reply = do_verify(common);
+		break;
+
+	case WRITE_6:
+		i = common->cmnd[4];
+		common->data_size_from_cmnd = (i == 0) ? 256 : i;
+		reply = check_command_size_in_blocks(common, 6,
+				      DATA_DIR_FROM_HOST,
+				      (7<<1) | (1<<4), 1,
+				      "WRITE(6)");
+		if (reply == 0)
+			reply = do_write(common);
+		break;
+
+	case WRITE_10:
+		common->data_size_from_cmnd =
+				get_unaligned_be16(&common->cmnd[7]);
+		reply = check_command_size_in_blocks(common, 10,
+				      DATA_DIR_FROM_HOST,
+				      (1<<1) | (0xf<<2) | (3<<7), 1,
+				      "WRITE(10)");
+		if (reply == 0)
+			reply = do_write(common);
+		break;
+
+	case WRITE_12:
+		common->data_size_from_cmnd =
+				get_unaligned_be32(&common->cmnd[6]);
+		reply = check_command_size_in_blocks(common, 12,
+				      DATA_DIR_FROM_HOST,
+				      (1<<1) | (0xf<<2) | (0xf<<6), 1,
+				      "WRITE(12)");
+		if (reply == 0)
+			reply = do_write(common);
+		break;
+
+	/*
+	 * Some mandatory commands that we recognize but don't implement.
+	 * They don't mean much in this setting.  It's left as an exercise
+	 * for anyone interested to implement RESERVE and RELEASE in terms
+	 * of Posix locks.
+	 */
+	case FORMAT_UNIT:
+	case RELEASE:
+	case RESERVE:
+	case SEND_DIAGNOSTIC:
+		/* Fall through */
+
+	default:
+unknown_cmnd:
+		common->data_size_from_cmnd = 0;
+		sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+		reply = check_command(common, common->cmnd_size,
+				      DATA_DIR_UNKNOWN, ~0, 0, unknown);
+		if (reply == 0) {
+			common->curlun->sense_data = SS_INVALID_COMMAND;
+			reply = -EINVAL;
+		}
+		break;
+	}
+	up_read(&common->filesem);
+
+	if (reply == -EINTR || signal_pending(current))
+		return -EINTR;
+
+	/* Set up the single reply buffer for finish_reply() */
+	if (reply == -EINVAL)
+		reply = 0;		/* Error reply length */
+	if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
+		reply = min((u32)reply, common->data_size_from_cmnd);
+		bh->inreq->length = reply;
+		bh->state = BUF_STATE_FULL;
+		common->residue -= reply;
+	}				/* Otherwise it's already set */
+
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct usb_request	*req = bh->outreq;
+	struct bulk_cb_wrap	*cbw = req->buf;
+	struct fsg_common	*common = fsg->common;
+
+	/* Was this a real packet?  Should it be ignored? */
+	if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+		return -EINVAL;
+
+	/* Is the CBW valid? */
+	if (req->actual != US_BULK_CB_WRAP_LEN ||
+			cbw->Signature != cpu_to_le32(
+				US_BULK_CB_SIGN)) {
+		DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+				req->actual,
+				le32_to_cpu(cbw->Signature));
+
+		/*
+		 * The Bulk-only spec says we MUST stall the IN endpoint
+		 * (6.6.1), so it's unavoidable.  It also says we must
+		 * retain this state until the next reset, but there's
+		 * no way to tell the controller driver it should ignore
+		 * Clear-Feature(HALT) requests.
+		 *
+		 * We aren't required to halt the OUT endpoint; instead
+		 * we can simply accept and discard any data received
+		 * until the next reset.
+		 */
+		wedge_bulk_in_endpoint(fsg);
+		set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+		return -EINVAL;
+	}
+
+	/* Is the CBW meaningful? */
+	if (cbw->Lun >= ARRAY_SIZE(common->luns) ||
+	    cbw->Flags & ~US_BULK_FLAG_IN || cbw->Length <= 0 ||
+	    cbw->Length > MAX_COMMAND_SIZE) {
+		DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+				"cmdlen %u\n",
+				cbw->Lun, cbw->Flags, cbw->Length);
+
+		/*
+		 * We can do anything we want here, so let's stall the
+		 * bulk pipes if we are allowed to.
+		 */
+		if (common->can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			halt_bulk_in_endpoint(fsg);
+		}
+		return -EINVAL;
+	}
+
+	/* Save the command for later */
+	common->cmnd_size = cbw->Length;
+	memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+	if (cbw->Flags & US_BULK_FLAG_IN)
+		common->data_dir = DATA_DIR_TO_HOST;
+	else
+		common->data_dir = DATA_DIR_FROM_HOST;
+	common->data_size = le32_to_cpu(cbw->DataTransferLength);
+	if (common->data_size == 0)
+		common->data_dir = DATA_DIR_NONE;
+	common->lun = cbw->Lun;
+	if (common->lun < ARRAY_SIZE(common->luns))
+		common->curlun = common->luns[common->lun];
+	else
+		common->curlun = NULL;
+	common->tag = cbw->Tag;
+	return 0;
+}
+
+static int get_next_command(struct fsg_common *common)
+{
+	struct fsg_buffhd	*bh;
+	int			rc = 0;
+
+	/* Wait for the next buffer to become available */
+	bh = common->next_buffhd_to_fill;
+	rc = sleep_thread(common, true, bh);
+	if (rc)
+		return rc;
+
+	/* Queue a request to read a Bulk-only CBW */
+	set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
+	if (!start_out_transfer(common, bh))
+		/* Don't know what to do if common->fsg is NULL */
+		return -EIO;
+
+	/*
+	 * We will drain the buffer in software, which means we
+	 * can reuse it for the next filling.  No need to advance
+	 * next_buffhd_to_fill.
+	 */
+
+	/* Wait for the CBW to arrive */
+	rc = sleep_thread(common, true, bh);
+	if (rc)
+		return rc;
+
+	rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+	bh->state = BUF_STATE_EMPTY;
+
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+		struct usb_request **preq)
+{
+	*preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (*preq)
+		return 0;
+	ERROR(common, "can't allocate request for %s\n", ep->name);
+	return -ENOMEM;
+}
+
+/* Reset interface setting and re-init endpoint state (toggle etc). */
+static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
+{
+	struct fsg_dev *fsg;
+	int i, rc = 0;
+
+	if (common->running)
+		DBG(common, "reset interface\n");
+
+reset:
+	/* Deallocate the requests */
+	if (common->fsg) {
+		fsg = common->fsg;
+
+		for (i = 0; i < common->fsg_num_buffers; ++i) {
+			struct fsg_buffhd *bh = &common->buffhds[i];
+
+			if (bh->inreq) {
+				usb_ep_free_request(fsg->bulk_in, bh->inreq);
+				bh->inreq = NULL;
+			}
+			if (bh->outreq) {
+				usb_ep_free_request(fsg->bulk_out, bh->outreq);
+				bh->outreq = NULL;
+			}
+		}
+
+		/* Disable the endpoints */
+		if (fsg->bulk_in_enabled) {
+			usb_ep_disable(fsg->bulk_in);
+			fsg->bulk_in_enabled = 0;
+		}
+		if (fsg->bulk_out_enabled) {
+			usb_ep_disable(fsg->bulk_out);
+			fsg->bulk_out_enabled = 0;
+		}
+
+		common->fsg = NULL;
+		wake_up(&common->fsg_wait);
+	}
+
+	common->running = 0;
+	if (!new_fsg || rc)
+		return rc;
+
+	common->fsg = new_fsg;
+	fsg = common->fsg;
+
+	/* Enable the endpoints */
+	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;
+
+	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 = usb_endpoint_maxp(fsg->bulk_out->desc);
+	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+	/* Allocate the requests */
+	for (i = 0; i < common->fsg_num_buffers; ++i) {
+		struct fsg_buffhd	*bh = &common->buffhds[i];
+
+		rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+		if (rc)
+			goto reset;
+		rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+		if (rc)
+			goto reset;
+		bh->inreq->buf = bh->outreq->buf = bh->buf;
+		bh->inreq->context = bh->outreq->context = bh;
+		bh->inreq->complete = bulk_in_complete;
+		bh->outreq->complete = bulk_out_complete;
+	}
+
+	common->running = 1;
+	for (i = 0; i < ARRAY_SIZE(common->luns); ++i)
+		if (common->luns[i])
+			common->luns[i]->unit_attention_data =
+				SS_RESET_OCCURRED;
+	return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct fsg_dev *fsg = fsg_from_func(f);
+	fsg->common->new_fsg = fsg;
+	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+	return USB_GADGET_DELAYED_STATUS;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+	struct fsg_dev *fsg = fsg_from_func(f);
+	fsg->common->new_fsg = NULL;
+	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+	int			i;
+	struct fsg_buffhd	*bh;
+	enum fsg_state		old_state;
+	struct fsg_lun		*curlun;
+	unsigned int		exception_req_tag;
+
+	/*
+	 * Clear the existing signals.  Anything but SIGUSR1 is converted
+	 * into a high-priority EXIT exception.
+	 */
+	for (;;) {
+		int sig = kernel_dequeue_signal(NULL);
+		if (!sig)
+			break;
+		if (sig != SIGUSR1) {
+			spin_lock_irq(&common->lock);
+			if (common->state < FSG_STATE_EXIT)
+				DBG(common, "Main thread exiting on signal\n");
+			common->state = FSG_STATE_EXIT;
+			spin_unlock_irq(&common->lock);
+		}
+	}
+
+	/* Cancel all the pending transfers */
+	if (likely(common->fsg)) {
+		for (i = 0; i < common->fsg_num_buffers; ++i) {
+			bh = &common->buffhds[i];
+			if (bh->state == BUF_STATE_SENDING)
+				usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+			if (bh->state == BUF_STATE_RECEIVING)
+				usb_ep_dequeue(common->fsg->bulk_out,
+					       bh->outreq);
+
+			/* Wait for a transfer to become idle */
+			if (sleep_thread(common, false, bh))
+				return;
+		}
+
+		/* Clear out the controller's fifos */
+		if (common->fsg->bulk_in_enabled)
+			usb_ep_fifo_flush(common->fsg->bulk_in);
+		if (common->fsg->bulk_out_enabled)
+			usb_ep_fifo_flush(common->fsg->bulk_out);
+	}
+
+	/*
+	 * Reset the I/O buffer states and pointers, the SCSI
+	 * state, and the exception.  Then invoke the handler.
+	 */
+	spin_lock_irq(&common->lock);
+
+	for (i = 0; i < common->fsg_num_buffers; ++i) {
+		bh = &common->buffhds[i];
+		bh->state = BUF_STATE_EMPTY;
+	}
+	common->next_buffhd_to_fill = &common->buffhds[0];
+	common->next_buffhd_to_drain = &common->buffhds[0];
+	exception_req_tag = common->exception_req_tag;
+	old_state = common->state;
+	common->state = FSG_STATE_NORMAL;
+
+	if (old_state != FSG_STATE_ABORT_BULK_OUT) {
+		for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
+			curlun = common->luns[i];
+			if (!curlun)
+				continue;
+			curlun->prevent_medium_removal = 0;
+			curlun->sense_data = SS_NO_SENSE;
+			curlun->unit_attention_data = SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+	}
+	spin_unlock_irq(&common->lock);
+
+	/* Carry out any extra actions required for the exception */
+	switch (old_state) {
+	case FSG_STATE_NORMAL:
+		break;
+
+	case FSG_STATE_ABORT_BULK_OUT:
+		send_status(common);
+		break;
+
+	case FSG_STATE_PROTOCOL_RESET:
+		/*
+		 * In case we were forced against our will to halt a
+		 * bulk endpoint, clear the halt now.  (The SuperH UDC
+		 * requires this.)
+		 */
+		if (!fsg_is_set(common))
+			break;
+		if (test_and_clear_bit(IGNORE_BULK_OUT,
+				       &common->fsg->atomic_bitflags))
+			usb_ep_clear_halt(common->fsg->bulk_in);
+
+		if (common->ep0_req_tag == exception_req_tag)
+			ep0_queue(common);	/* Complete the status stage */
+
+		/*
+		 * Technically this should go here, but it would only be
+		 * a waste of time.  Ditto for the INTERFACE_CHANGE and
+		 * CONFIG_CHANGE cases.
+		 */
+		/* for (i = 0; i < common->ARRAY_SIZE(common->luns); ++i) */
+		/*	if (common->luns[i]) */
+		/*		common->luns[i]->unit_attention_data = */
+		/*			SS_RESET_OCCURRED;  */
+		break;
+
+	case FSG_STATE_CONFIG_CHANGE:
+		do_set_interface(common, common->new_fsg);
+		if (common->new_fsg)
+			usb_composite_setup_continue(common->cdev);
+		break;
+
+	case FSG_STATE_EXIT:
+		do_set_interface(common, NULL);		/* Free resources */
+		spin_lock_irq(&common->lock);
+		common->state = FSG_STATE_TERMINATED;	/* Stop the thread */
+		spin_unlock_irq(&common->lock);
+		break;
+
+	case FSG_STATE_TERMINATED:
+		break;
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_main_thread(void *common_)
+{
+	struct fsg_common	*common = common_;
+	int			i;
+
+	/*
+	 * Allow the thread to be killed by a signal, but set the signal mask
+	 * to block everything but INT, TERM, KILL, and USR1.
+	 */
+	allow_signal(SIGINT);
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+	allow_signal(SIGUSR1);
+
+	/* Allow the thread to be frozen */
+	set_freezable();
+
+	/* The main loop */
+	while (common->state != FSG_STATE_TERMINATED) {
+		if (exception_in_progress(common) || signal_pending(current)) {
+			handle_exception(common);
+			continue;
+		}
+
+		if (!common->running) {
+			sleep_thread(common, true, NULL);
+			continue;
+		}
+
+		if (get_next_command(common) || exception_in_progress(common))
+			continue;
+		if (do_scsi_command(common) || exception_in_progress(common))
+			continue;
+		if (finish_reply(common) || exception_in_progress(common))
+			continue;
+		send_status(common);
+	}
+
+	spin_lock_irq(&common->lock);
+	common->thread_task = NULL;
+	spin_unlock_irq(&common->lock);
+
+	/* Eject media from all LUNs */
+
+	down_write(&common->filesem);
+	for (i = 0; i < ARRAY_SIZE(common->luns); i++) {
+		struct fsg_lun *curlun = common->luns[i];
+
+		if (curlun && fsg_lun_is_open(curlun))
+			fsg_lun_close(curlun);
+	}
+	up_write(&common->filesem);
+
+	/* Let fsg_unbind() know the thread has exited */
+	complete_and_exit(&common->thread_notifier, 0);
+}
+
+
+/*************************** DEVICE ATTRIBUTES ***************************/
+
+static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+
+	return fsg_show_ro(curlun, buf);
+}
+
+static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+
+	return fsg_show_nofua(curlun, buf);
+}
+
+static ssize_t file_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
+
+	return fsg_show_file(curlun, filesem, buf);
+}
+
+static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
+
+	return fsg_store_ro(curlun, filesem, buf, count);
+}
+
+static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+
+	return fsg_store_nofua(curlun, buf, count);
+}
+
+static ssize_t file_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct fsg_lun		*curlun = fsg_lun_from_dev(dev);
+	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
+
+	return fsg_store_file(curlun, filesem, buf, count);
+}
+
+static DEVICE_ATTR_RW(nofua);
+/* mode wil be set in fsg_lun_attr_is_visible() */
+static DEVICE_ATTR(ro, 0, ro_show, ro_store);
+static DEVICE_ATTR(file, 0, file_show, file_store);
+
+/****************************** FSG COMMON ******************************/
+
+static void fsg_lun_release(struct device *dev)
+{
+	/* Nothing needs to be done */
+}
+
+static struct fsg_common *fsg_common_setup(struct fsg_common *common)
+{
+	if (!common) {
+		common = kzalloc(sizeof(*common), GFP_KERNEL);
+		if (!common)
+			return ERR_PTR(-ENOMEM);
+		common->free_storage_on_release = 1;
+	} else {
+		common->free_storage_on_release = 0;
+	}
+	init_rwsem(&common->filesem);
+	spin_lock_init(&common->lock);
+	init_completion(&common->thread_notifier);
+	init_waitqueue_head(&common->io_wait);
+	init_waitqueue_head(&common->fsg_wait);
+	common->state = FSG_STATE_TERMINATED;
+	memset(common->luns, 0, sizeof(common->luns));
+
+	return common;
+}
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs)
+{
+	common->sysfs = sysfs;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_sysfs);
+
+static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
+{
+	if (buffhds) {
+		struct fsg_buffhd *bh = buffhds;
+		while (n--) {
+			kfree(bh->buf);
+			++bh;
+		}
+		kfree(buffhds);
+	}
+}
+
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
+{
+	struct fsg_buffhd *bh, *buffhds;
+	int i;
+
+	buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
+	if (!buffhds)
+		return -ENOMEM;
+
+	/* Data buffers cyclic list */
+	bh = buffhds;
+	i = n;
+	goto buffhds_first_it;
+	do {
+		bh->next = bh + 1;
+		++bh;
+buffhds_first_it:
+		bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+		if (unlikely(!bh->buf))
+			goto error_release;
+	} while (--i);
+	bh->next = buffhds;
+
+	_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+	common->fsg_num_buffers = n;
+	common->buffhds = buffhds;
+
+	return 0;
+
+error_release:
+	/*
+	 * "buf"s pointed to by heads after n - i are NULL
+	 * so releasing them won't hurt
+	 */
+	_fsg_common_free_buffers(buffhds, n);
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
+
+void fsg_common_remove_lun(struct fsg_lun *lun)
+{
+	if (device_is_registered(&lun->dev))
+		device_unregister(&lun->dev);
+	fsg_lun_close(lun);
+	kfree(lun);
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_lun);
+
+static void _fsg_common_remove_luns(struct fsg_common *common, int n)
+{
+	int i;
+
+	for (i = 0; i < n; ++i)
+		if (common->luns[i]) {
+			fsg_common_remove_lun(common->luns[i]);
+			common->luns[i] = NULL;
+		}
+}
+
+void fsg_common_remove_luns(struct fsg_common *common)
+{
+	_fsg_common_remove_luns(common, ARRAY_SIZE(common->luns));
+}
+EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
+
+void fsg_common_free_buffers(struct fsg_common *common)
+{
+	_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+	common->buffhds = NULL;
+}
+EXPORT_SYMBOL_GPL(fsg_common_free_buffers);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+			 struct usb_composite_dev *cdev, bool can_stall)
+{
+	struct usb_string *us;
+
+	common->gadget = cdev->gadget;
+	common->ep0 = cdev->gadget->ep0;
+	common->ep0req = cdev->req;
+	common->cdev = cdev;
+
+	us = usb_gstrings_attach(cdev, fsg_strings_array,
+				 ARRAY_SIZE(fsg_strings));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+
+	fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id;
+
+	/*
+	 * Some peripheral controllers are known not to be able to
+	 * halt bulk endpoints correctly.  If one of them is present,
+	 * disable stalls.
+	 */
+	common->can_stall = can_stall &&
+			gadget_is_stall_supported(common->gadget);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
+
+static struct attribute *fsg_lun_dev_attrs[] = {
+	&dev_attr_ro.attr,
+	&dev_attr_file.attr,
+	&dev_attr_nofua.attr,
+	NULL
+};
+
+static umode_t fsg_lun_dev_is_visible(struct kobject *kobj,
+				      struct attribute *attr, int idx)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct fsg_lun *lun = fsg_lun_from_dev(dev);
+
+	if (attr == &dev_attr_ro.attr)
+		return lun->cdrom ? S_IRUGO : (S_IWUSR | S_IRUGO);
+	if (attr == &dev_attr_file.attr)
+		return lun->removable ? (S_IWUSR | S_IRUGO) : S_IRUGO;
+	return attr->mode;
+}
+
+static const struct attribute_group fsg_lun_dev_group = {
+	.attrs = fsg_lun_dev_attrs,
+	.is_visible = fsg_lun_dev_is_visible,
+};
+
+static const struct attribute_group *fsg_lun_dev_groups[] = {
+	&fsg_lun_dev_group,
+	NULL
+};
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+			  unsigned int id, const char *name,
+			  const char **name_pfx)
+{
+	struct fsg_lun *lun;
+	char *pathbuf, *p;
+	int rc = -ENOMEM;
+
+	if (id >= ARRAY_SIZE(common->luns))
+		return -ENODEV;
+
+	if (common->luns[id])
+		return -EBUSY;
+
+	if (!cfg->filename && !cfg->removable) {
+		pr_err("no file given for LUN%d\n", id);
+		return -EINVAL;
+	}
+
+	lun = kzalloc(sizeof(*lun), GFP_KERNEL);
+	if (!lun)
+		return -ENOMEM;
+
+	lun->name_pfx = name_pfx;
+
+	lun->cdrom = !!cfg->cdrom;
+	lun->ro = cfg->cdrom || cfg->ro;
+	lun->initially_ro = lun->ro;
+	lun->removable = !!cfg->removable;
+
+	if (!common->sysfs) {
+		/* we DON'T own the name!*/
+		lun->name = name;
+	} else {
+		lun->dev.release = fsg_lun_release;
+		lun->dev.parent = &common->gadget->dev;
+		lun->dev.groups = fsg_lun_dev_groups;
+		dev_set_drvdata(&lun->dev, &common->filesem);
+		dev_set_name(&lun->dev, "%s", name);
+		lun->name = dev_name(&lun->dev);
+
+		rc = device_register(&lun->dev);
+		if (rc) {
+			pr_info("failed to register LUN%d: %d\n", id, rc);
+			put_device(&lun->dev);
+			goto error_sysfs;
+		}
+	}
+
+	common->luns[id] = lun;
+
+	if (cfg->filename) {
+		rc = fsg_lun_open(lun, cfg->filename);
+		if (rc)
+			goto error_lun;
+	}
+
+	pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+	p = "(no medium)";
+	if (fsg_lun_is_open(lun)) {
+		p = "(error)";
+		if (pathbuf) {
+			p = file_path(lun->filp, pathbuf, PATH_MAX);
+			if (IS_ERR(p))
+				p = "(error)";
+		}
+	}
+	pr_info("LUN: %s%s%sfile: %s\n",
+	      lun->removable ? "removable " : "",
+	      lun->ro ? "read only " : "",
+	      lun->cdrom ? "CD-ROM " : "",
+	      p);
+	kfree(pathbuf);
+
+	return 0;
+
+error_lun:
+	if (device_is_registered(&lun->dev))
+		device_unregister(&lun->dev);
+	fsg_lun_close(lun);
+	common->luns[id] = NULL;
+error_sysfs:
+	kfree(lun);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_lun);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
+{
+	char buf[8]; /* enough for 100000000 different numbers, decimal */
+	int i, rc;
+
+	fsg_common_remove_luns(common);
+
+	for (i = 0; i < cfg->nluns; ++i) {
+		snprintf(buf, sizeof(buf), "lun%d", i);
+		rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL);
+		if (rc)
+			goto fail;
+	}
+
+	pr_info("Number of LUNs=%d\n", cfg->nluns);
+
+	return 0;
+
+fail:
+	_fsg_common_remove_luns(common, i);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_luns);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+				   const char *pn)
+{
+	int i;
+
+	/* Prepare inquiryString */
+	i = get_default_bcdDevice();
+	snprintf(common->inquiry_string, sizeof(common->inquiry_string),
+		 "%-8s%-16s%04x", vn ?: "Linux",
+		 /* Assume product name dependent on the first LUN */
+		 pn ?: ((*common->luns)->cdrom
+		     ? "File-CD Gadget"
+		     : "File-Stor Gadget"),
+		 i);
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
+
+static void fsg_common_release(struct fsg_common *common)
+{
+	int i;
+
+	/* If the thread isn't already dead, tell it to exit now */
+	if (common->state != FSG_STATE_TERMINATED) {
+		raise_exception(common, FSG_STATE_EXIT);
+		wait_for_completion(&common->thread_notifier);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
+		struct fsg_lun *lun = common->luns[i];
+		if (!lun)
+			continue;
+		fsg_lun_close(lun);
+		if (device_is_registered(&lun->dev))
+			device_unregister(&lun->dev);
+		kfree(lun);
+	}
+
+	_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
+	if (common->free_storage_on_release)
+		kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct fsg_dev		*fsg = fsg_from_func(f);
+	struct fsg_common	*common = fsg->common;
+	struct usb_gadget	*gadget = c->cdev->gadget;
+	int			i;
+	struct usb_ep		*ep;
+	unsigned		max_burst;
+	int			ret;
+	struct fsg_opts		*opts;
+
+	/* Don't allow to bind if we don't have at least one LUN */
+	ret = _fsg_common_get_max_lun(common);
+	if (ret < 0) {
+		pr_err("There should be at least one LUN.\n");
+		return -EINVAL;
+	}
+
+	opts = fsg_opts_from_func_inst(f->fi);
+	if (!opts->no_configfs) {
+		ret = fsg_common_set_cdev(fsg->common, c->cdev,
+					  fsg->common->can_stall);
+		if (ret)
+			return ret;
+		fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
+	}
+
+	if (!common->thread_task) {
+		common->state = FSG_STATE_NORMAL;
+		common->thread_task =
+			kthread_create(fsg_main_thread, common, "file-storage");
+		if (IS_ERR(common->thread_task)) {
+			ret = PTR_ERR(common->thread_task);
+			common->thread_task = NULL;
+			common->state = FSG_STATE_TERMINATED;
+			return ret;
+		}
+		DBG(common, "I/O thread pid: %d\n",
+		    task_pid_nr(common->thread_task));
+		wake_up_process(common->thread_task);
+	}
+
+	fsg->gadget = gadget;
+
+	/* New interface */
+	i = usb_interface_id(c, f);
+	if (i < 0)
+		goto fail;
+	fsg_intf_desc.bInterfaceNumber = i;
+	fsg->interface_number = i;
+
+	/* Find all the endpoints we will use */
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+	if (!ep)
+		goto autoconf_fail;
+	fsg->bulk_in = ep;
+
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+	if (!ep)
+		goto autoconf_fail;
+	fsg->bulk_out = ep;
+
+	/* Assume endpoint addresses are the same for both speeds */
+	fsg_hs_bulk_in_desc.bEndpointAddress =
+		fsg_fs_bulk_in_desc.bEndpointAddress;
+	fsg_hs_bulk_out_desc.bEndpointAddress =
+		fsg_fs_bulk_out_desc.bEndpointAddress;
+
+	/* Calculate bMaxBurst, we know packet size is 1024 */
+	max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+	fsg_ss_bulk_in_desc.bEndpointAddress =
+		fsg_fs_bulk_in_desc.bEndpointAddress;
+	fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+	fsg_ss_bulk_out_desc.bEndpointAddress =
+		fsg_fs_bulk_out_desc.bEndpointAddress;
+	fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+	ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
+			fsg_ss_function, fsg_ss_function);
+	if (ret)
+		goto autoconf_fail;
+
+	return 0;
+
+autoconf_fail:
+	ERROR(fsg, "unable to autoconfigure all endpoints\n");
+	i = -ENOTSUPP;
+fail:
+	/* terminate the thread */
+	if (fsg->common->state != FSG_STATE_TERMINATED) {
+		raise_exception(fsg->common, FSG_STATE_EXIT);
+		wait_for_completion(&fsg->common->thread_notifier);
+	}
+	return i;
+}
+
+/****************************** ALLOCATE FUNCTION *************************/
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct fsg_dev		*fsg = fsg_from_func(f);
+	struct fsg_common	*common = fsg->common;
+
+	DBG(fsg, "unbind\n");
+	if (fsg->common->fsg == fsg) {
+		fsg->common->new_fsg = NULL;
+		raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+		/* FIXME: make interruptible or killable somehow? */
+		wait_event(common->fsg_wait, common->fsg != fsg);
+	}
+
+	usb_free_all_descriptors(&fsg->function);
+}
+
+static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct fsg_lun_opts, group);
+}
+
+static inline struct fsg_opts *to_fsg_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct fsg_opts,
+			    func_inst.group);
+}
+
+static void fsg_lun_attr_release(struct config_item *item)
+{
+	struct fsg_lun_opts *lun_opts;
+
+	lun_opts = to_fsg_lun_opts(item);
+	kfree(lun_opts);
+}
+
+static struct configfs_item_operations fsg_lun_item_ops = {
+	.release		= fsg_lun_attr_release,
+};
+
+static ssize_t fsg_lun_opts_file_show(struct config_item *item, char *page)
+{
+	struct fsg_lun_opts *opts = to_fsg_lun_opts(item);
+	struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+	return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page);
+}
+
+static ssize_t fsg_lun_opts_file_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct fsg_lun_opts *opts = to_fsg_lun_opts(item);
+	struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+	return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, file);
+
+static ssize_t fsg_lun_opts_ro_show(struct config_item *item, char *page)
+{
+	return fsg_show_ro(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_ro_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct fsg_lun_opts *opts = to_fsg_lun_opts(item);
+	struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+	return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, ro);
+
+static ssize_t fsg_lun_opts_removable_show(struct config_item *item,
+					   char *page)
+{
+	return fsg_show_removable(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_removable_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	return fsg_store_removable(to_fsg_lun_opts(item)->lun, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, removable);
+
+static ssize_t fsg_lun_opts_cdrom_show(struct config_item *item, char *page)
+{
+	return fsg_show_cdrom(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_cdrom_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct fsg_lun_opts *opts = to_fsg_lun_opts(item);
+	struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+	return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page,
+			       len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, cdrom);
+
+static ssize_t fsg_lun_opts_nofua_show(struct config_item *item, char *page)
+{
+	return fsg_show_nofua(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_nofua_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, nofua);
+
+static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item,
+						char *page)
+{
+	return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item,
+						 const char *page, size_t len)
+{
+	return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string);
+
+static struct configfs_attribute *fsg_lun_attrs[] = {
+	&fsg_lun_opts_attr_file,
+	&fsg_lun_opts_attr_ro,
+	&fsg_lun_opts_attr_removable,
+	&fsg_lun_opts_attr_cdrom,
+	&fsg_lun_opts_attr_nofua,
+	&fsg_lun_opts_attr_inquiry_string,
+	NULL,
+};
+
+static const struct config_item_type fsg_lun_type = {
+	.ct_item_ops	= &fsg_lun_item_ops,
+	.ct_attrs	= fsg_lun_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *fsg_lun_make(struct config_group *group,
+					 const char *name)
+{
+	struct fsg_lun_opts *opts;
+	struct fsg_opts *fsg_opts;
+	struct fsg_lun_config config;
+	char *num_str;
+	u8 num;
+	int ret;
+
+	num_str = strchr(name, '.');
+	if (!num_str) {
+		pr_err("Unable to locate . in LUN.NUMBER\n");
+		return ERR_PTR(-EINVAL);
+	}
+	num_str++;
+
+	ret = kstrtou8(num_str, 0, &num);
+	if (ret)
+		return ERR_PTR(ret);
+
+	fsg_opts = to_fsg_opts(&group->cg_item);
+	if (num >= FSG_MAX_LUNS)
+		return ERR_PTR(-ERANGE);
+	num = array_index_nospec(num, FSG_MAX_LUNS);
+
+	mutex_lock(&fsg_opts->lock);
+	if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.removable = true;
+
+	ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
+				    (const char **)&group->cg_item.ci_name);
+	if (ret) {
+		kfree(opts);
+		goto out;
+	}
+	opts->lun = fsg_opts->common->luns[num];
+	opts->lun_id = num;
+	mutex_unlock(&fsg_opts->lock);
+
+	config_group_init_type_name(&opts->group, name, &fsg_lun_type);
+
+	return &opts->group;
+out:
+	mutex_unlock(&fsg_opts->lock);
+	return ERR_PTR(ret);
+}
+
+static void fsg_lun_drop(struct config_group *group, struct config_item *item)
+{
+	struct fsg_lun_opts *lun_opts;
+	struct fsg_opts *fsg_opts;
+
+	lun_opts = to_fsg_lun_opts(item);
+	fsg_opts = to_fsg_opts(&group->cg_item);
+
+	mutex_lock(&fsg_opts->lock);
+	if (fsg_opts->refcnt) {
+		struct config_item *gadget;
+
+		gadget = group->cg_item.ci_parent->ci_parent;
+		unregister_gadget_item(gadget);
+	}
+
+	fsg_common_remove_lun(lun_opts->lun);
+	fsg_opts->common->luns[lun_opts->lun_id] = NULL;
+	lun_opts->lun_id = 0;
+	mutex_unlock(&fsg_opts->lock);
+
+	config_item_put(item);
+}
+
+static void fsg_attr_release(struct config_item *item)
+{
+	struct fsg_opts *opts = to_fsg_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations fsg_item_ops = {
+	.release		= fsg_attr_release,
+};
+
+static ssize_t fsg_opts_stall_show(struct config_item *item, char *page)
+{
+	struct fsg_opts *opts = to_fsg_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", opts->common->can_stall);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t fsg_opts_stall_store(struct config_item *item, const char *page,
+				    size_t len)
+{
+	struct fsg_opts *opts = to_fsg_opts(item);
+	int ret;
+	bool stall;
+
+	mutex_lock(&opts->lock);
+
+	if (opts->refcnt) {
+		mutex_unlock(&opts->lock);
+		return -EBUSY;
+	}
+
+	ret = strtobool(page, &stall);
+	if (!ret) {
+		opts->common->can_stall = stall;
+		ret = len;
+	}
+
+	mutex_unlock(&opts->lock);
+
+	return ret;
+}
+
+CONFIGFS_ATTR(fsg_opts_, stall);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+static ssize_t fsg_opts_num_buffers_show(struct config_item *item, char *page)
+{
+	struct fsg_opts *opts = to_fsg_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d", opts->common->fsg_num_buffers);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t fsg_opts_num_buffers_store(struct config_item *item,
+					  const char *page, size_t len)
+{
+	struct fsg_opts *opts = to_fsg_opts(item);
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	ret = fsg_common_set_num_buffers(opts->common, num);
+	if (ret)
+		goto end;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(fsg_opts_, num_buffers);
+#endif
+
+static struct configfs_attribute *fsg_attrs[] = {
+	&fsg_opts_attr_stall,
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	&fsg_opts_attr_num_buffers,
+#endif
+	NULL,
+};
+
+static struct configfs_group_operations fsg_group_ops = {
+	.make_group	= fsg_lun_make,
+	.drop_item	= fsg_lun_drop,
+};
+
+static const struct config_item_type fsg_func_type = {
+	.ct_item_ops	= &fsg_item_ops,
+	.ct_group_ops	= &fsg_group_ops,
+	.ct_attrs	= fsg_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void fsg_free_inst(struct usb_function_instance *fi)
+{
+	struct fsg_opts *opts;
+
+	opts = fsg_opts_from_func_inst(fi);
+	fsg_common_release(opts->common);
+	kfree(opts);
+}
+
+static struct usb_function_instance *fsg_alloc_inst(void)
+{
+	struct fsg_opts *opts;
+	struct fsg_lun_config config;
+	int rc;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = fsg_free_inst;
+	opts->common = fsg_common_setup(opts->common);
+	if (IS_ERR(opts->common)) {
+		rc = PTR_ERR(opts->common);
+		goto release_opts;
+	}
+
+	rc = fsg_common_set_num_buffers(opts->common,
+					CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
+	if (rc)
+		goto release_common;
+
+	pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+
+	memset(&config, 0, sizeof(config));
+	config.removable = true;
+	rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
+			(const char **)&opts->func_inst.group.cg_item.ci_name);
+	if (rc)
+		goto release_buffers;
+
+	opts->lun0.lun = opts->common->luns[0];
+	opts->lun0.lun_id = 0;
+
+	config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
+
+	config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
+	configfs_add_default_group(&opts->lun0.group, &opts->func_inst.group);
+
+	return &opts->func_inst;
+
+release_buffers:
+	fsg_common_free_buffers(opts->common);
+release_common:
+	kfree(opts->common);
+release_opts:
+	kfree(opts);
+	return ERR_PTR(rc);
+}
+
+static void fsg_free(struct usb_function *f)
+{
+	struct fsg_dev *fsg;
+	struct fsg_opts *opts;
+
+	fsg = container_of(f, struct fsg_dev, function);
+	opts = container_of(f->fi, struct fsg_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+
+	kfree(fsg);
+}
+
+static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
+{
+	struct fsg_opts *opts = fsg_opts_from_func_inst(fi);
+	struct fsg_common *common = opts->common;
+	struct fsg_dev *fsg;
+
+	fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
+	if (unlikely(!fsg))
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+	mutex_unlock(&opts->lock);
+
+	fsg->function.name	= FSG_DRIVER_DESC;
+	fsg->function.bind	= fsg_bind;
+	fsg->function.unbind	= fsg_unbind;
+	fsg->function.setup	= fsg_setup;
+	fsg->function.set_alt	= fsg_set_alt;
+	fsg->function.disable	= fsg_disable;
+	fsg->function.free_func	= fsg_free;
+
+	fsg->common               = common;
+
+	return &fsg->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
+
+/************************* Module parameters *************************/
+
+
+void fsg_config_from_params(struct fsg_config *cfg,
+		       const struct fsg_module_parameters *params,
+		       unsigned int fsg_num_buffers)
+{
+	struct fsg_lun_config *lun;
+	unsigned i;
+
+	/* Configure LUNs */
+	cfg->nluns =
+		min(params->luns ?: (params->file_count ?: 1u),
+		    (unsigned)FSG_MAX_LUNS);
+	for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
+		lun->ro = !!params->ro[i];
+		lun->cdrom = !!params->cdrom[i];
+		lun->removable = !!params->removable[i];
+		lun->filename =
+			params->file_count > i && params->file[i][0]
+			? params->file[i]
+			: NULL;
+	}
+
+	/* Let MSF use defaults */
+	cfg->vendor_name = NULL;
+	cfg->product_name = NULL;
+
+	cfg->ops = NULL;
+	cfg->private_data = NULL;
+
+	/* Finalise */
+	cfg->can_stall = params->stall;
+	cfg->fsg_num_buffers = fsg_num_buffers;
+}
+EXPORT_SYMBOL_GPL(fsg_config_from_params);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
new file mode 100644
index 0000000..3b8c4ce
--- /dev/null
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef USB_F_MASS_STORAGE_H
+#define USB_F_MASS_STORAGE_H
+
+#include <linux/usb/composite.h>
+#include "storage_common.h"
+
+struct fsg_module_parameters {
+	char		*file[FSG_MAX_LUNS];
+	bool		ro[FSG_MAX_LUNS];
+	bool		removable[FSG_MAX_LUNS];
+	bool		cdrom[FSG_MAX_LUNS];
+	bool		nofua[FSG_MAX_LUNS];
+
+	unsigned int	file_count, ro_count, removable_count, cdrom_count;
+	unsigned int	nofua_count;
+	unsigned int	luns;	/* nluns */
+	bool		stall;	/* can_stall */
+};
+
+#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)	\
+	module_param_array_named(prefix ## name, params.name, type,	\
+				 &prefix ## params.name ## _count,	\
+				 S_IRUGO);				\
+	MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc)		\
+	module_param_named(prefix ## name, params.name, type,		\
+			   S_IRUGO);					\
+	MODULE_PARM_DESC(prefix ## name, desc)
+
+#define __FSG_MODULE_PARAMETERS(prefix, params)				\
+	_FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,		\
+				"names of backing files or devices");	\
+	_FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,		\
+				"true to force read-only");		\
+	_FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,	\
+				"true to simulate removable media");	\
+	_FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,		\
+				"true to simulate CD-ROM instead of disk"); \
+	_FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,		\
+				"true to ignore SCSI WRITE(10,12) FUA bit"); \
+	_FSG_MODULE_PARAM(prefix, params, luns, uint,			\
+			  "number of LUNs");				\
+	_FSG_MODULE_PARAM(prefix, params, stall, bool,			\
+			  "false to prevent bulk stalls")
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#define FSG_MODULE_PARAMETERS(prefix, params)				\
+	__FSG_MODULE_PARAMETERS(prefix, params);			\
+	module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
+	MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
+#else
+
+#define FSG_MODULE_PARAMETERS(prefix, params)				\
+	__FSG_MODULE_PARAMETERS(prefix, params)
+
+#endif
+
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_lun_opts {
+	struct config_group group;
+	struct fsg_lun *lun;
+	int lun_id;
+};
+
+struct fsg_opts {
+	struct fsg_common *common;
+	struct usb_function_instance func_inst;
+	struct fsg_lun_opts lun0;
+	struct config_group *default_groups[2];
+	bool no_configfs; /* for legacy gadgets */
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+struct fsg_lun_config {
+	const char *filename;
+	char ro;
+	char removable;
+	char cdrom;
+	char nofua;
+	char inquiry_string[INQUIRY_STRING_LEN];
+};
+
+struct fsg_config {
+	unsigned nluns;
+	struct fsg_lun_config luns[FSG_MAX_LUNS];
+
+	/* Callback functions. */
+	const struct fsg_operations	*ops;
+	/* Gadget's private data. */
+	void			*private_data;
+
+	const char *vendor_name;		/*  8 characters or less */
+	const char *product_name;		/* 16 characters or less */
+
+	char			can_stall;
+	unsigned int		fsg_num_buffers;
+};
+
+static inline struct fsg_opts *
+fsg_opts_from_func_inst(const struct usb_function_instance *fi)
+{
+	return container_of(fi, struct fsg_opts, func_inst);
+}
+
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
+
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
+
+void fsg_common_free_buffers(struct fsg_common *common);
+
+int fsg_common_set_cdev(struct fsg_common *common,
+			struct usb_composite_dev *cdev, bool can_stall);
+
+void fsg_common_remove_lun(struct fsg_lun *lun);
+
+void fsg_common_remove_luns(struct fsg_common *common);
+
+int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
+			  unsigned int id, const char *name,
+			  const char **name_pfx);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+				   const char *pn);
+
+void fsg_config_from_params(struct fsg_config *cfg,
+			    const struct fsg_module_parameters *params,
+			    unsigned int fsg_num_buffers);
+
+#endif /* USB_F_MASS_STORAGE_H */
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
new file mode 100644
index 0000000..46af0aa
--- /dev/null
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -0,0 +1,1352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi.c -- USB MIDI class function driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * Rewritten for the composite framework
+ *   Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
+ *
+ * Based on drivers/usb/gadget/f_audio.c,
+ *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ *   Copyright (C) 2008 Analog Devices, Inc
+ *
+ * and drivers/usb/gadget/midi.c,
+ *   Copyright (C) 2006 Thumtronics Pty Ltd.
+ *   Ben Williamson <ben.williamson@greyinnovation.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "u_f.h"
+#include "u_midi.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+static const char f_midi_shortname[] = "f_midi";
+static const char f_midi_longname[] = "MIDI Gadget";
+
+/*
+ * We can only handle 16 cables on one single endpoint, as cable numbers are
+ * stored in 4-bit fields. And as the interface currently only holds one
+ * single endpoint, this is the maximum number of ports we can allow.
+ */
+#define MAX_PORTS 16
+
+/* MIDI message states */
+enum {
+	STATE_INITIAL = 0,	/* pseudo state */
+	STATE_1PARAM,
+	STATE_2PARAM_1,
+	STATE_2PARAM_2,
+	STATE_SYSEX_0,
+	STATE_SYSEX_1,
+	STATE_SYSEX_2,
+	STATE_REAL_TIME,
+	STATE_FINISHED,		/* pseudo state */
+};
+
+/*
+ * This is a gadget, and the IN/OUT naming is from the host's perspective.
+ * USB -> OUT endpoint -> rawmidi
+ * USB <- IN endpoint  <- rawmidi
+ */
+struct gmidi_in_port {
+	struct snd_rawmidi_substream *substream;
+	int active;
+	uint8_t cable;
+	uint8_t state;
+	uint8_t data[2];
+};
+
+struct f_midi {
+	struct usb_function	func;
+	struct usb_gadget	*gadget;
+	struct usb_ep		*in_ep, *out_ep;
+	struct snd_card		*card;
+	struct snd_rawmidi	*rmidi;
+	u8			ms_id;
+
+	struct snd_rawmidi_substream *out_substream[MAX_PORTS];
+
+	unsigned long		out_triggered;
+	struct tasklet_struct	tasklet;
+	unsigned int in_ports;
+	unsigned int out_ports;
+	int index;
+	char *id;
+	unsigned int buflen, qlen;
+	/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
+	DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
+	spinlock_t transmit_lock;
+	unsigned int in_last_port;
+	unsigned char free_ref;
+
+	struct gmidi_in_port	in_ports_array[/* in_ports */];
+};
+
+static inline struct f_midi *func_to_midi(struct usb_function *f)
+{
+	return container_of(f, struct f_midi, func);
+}
+
+static void f_midi_transmit(struct f_midi *midi);
+static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
+static void f_midi_free_inst(struct usb_function_instance *f);
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber =	DYNAMIC */
+	/* .bNumEndpoints =	DYNAMIC */
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
+	/* .iInterface =	DYNAMIC */
+};
+
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
+	.bLength =		UAC_DT_AC_HEADER_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_HEADER,
+	.bcdADC =		cpu_to_le16(0x0100),
+	.wTotalLength =		cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
+	.bInCollection =	1,
+	/* .baInterfaceNr =	DYNAMIC */
+};
+
+/* B.4.1  Standard MS Interface Descriptor */
+static struct usb_interface_descriptor ms_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber =	DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_MIDISTREAMING,
+	/* .iInterface =	DYNAMIC */
+};
+
+/* B.4.2  Class-Specific MS Interface Descriptor */
+static struct usb_ms_header_descriptor ms_header_desc = {
+	.bLength =		USB_DT_MS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_HEADER,
+	.bcdMSC =		cpu_to_le16(0x0100),
+	/* .wTotalLength =	DYNAMIC */
+};
+
+/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor bulk_out_ss_comp_desc = {
+	.bLength                = sizeof(bulk_out_ss_comp_desc),
+	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+	/* .bMaxBurst           = 0, */
+	/* .bmAttributes        = 0, */
+};
+
+/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
+	/* .bLength =		DYNAMIC */
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	USB_MS_GENERAL,
+	/* .bNumEmbMIDIJack =	DYNAMIC */
+	/* .baAssocJackID =	DYNAMIC */
+};
+
+/* B.6.1  Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor bulk_in_ss_comp_desc = {
+	.bLength                = sizeof(bulk_in_ss_comp_desc),
+	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+	/* .bMaxBurst           = 0, */
+	/* .bmAttributes        = 0, */
+};
+
+/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
+static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
+	/* .bLength =		DYNAMIC */
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	USB_MS_GENERAL,
+	/* .bNumEmbMIDIJack =	DYNAMIC */
+	/* .baAssocJackID =	DYNAMIC */
+};
+
+/* string IDs are assigned dynamically */
+
+#define STRING_FUNC_IDX			0
+
+static struct usb_string midi_string_defs[] = {
+	[STRING_FUNC_IDX].s = "MIDI function",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings midi_stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= midi_string_defs,
+};
+
+static struct usb_gadget_strings *midi_strings[] = {
+	&midi_stringtab,
+	NULL,
+};
+
+static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
+						    unsigned length)
+{
+	return alloc_ep_req(ep, length);
+}
+
+static const uint8_t f_midi_cin_length[] = {
+	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void f_midi_read_data(struct usb_ep *ep, int cable,
+			     uint8_t *data, int length)
+{
+	struct f_midi *midi = ep->driver_data;
+	struct snd_rawmidi_substream *substream = midi->out_substream[cable];
+
+	if (!substream)
+		/* Nobody is listening - throw it on the floor. */
+		return;
+
+	if (!test_bit(cable, &midi->out_triggered))
+		return;
+
+	snd_rawmidi_receive(substream, data, length);
+}
+
+static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned int i;
+	u8 *buf = req->buf;
+
+	for (i = 0; i + 3 < req->actual; i += 4)
+		if (buf[i] != 0) {
+			int cable = buf[i] >> 4;
+			int length = f_midi_cin_length[buf[i] & 0x0f];
+			f_midi_read_data(ep, cable, &buf[i + 1], length);
+		}
+}
+
+static void
+f_midi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_midi *midi = ep->driver_data;
+	struct usb_composite_dev *cdev = midi->func.config->cdev;
+	int status = req->status;
+
+	switch (status) {
+	case 0:			 /* normal completion */
+		if (ep == midi->out_ep) {
+			/* We received stuff. req is queued again, below */
+			f_midi_handle_out_data(ep, req);
+		} else if (ep == midi->in_ep) {
+			/* Our transmit completed. See if there's more to go.
+			 * f_midi_transmit eats req, don't queue it again. */
+			req->length = 0;
+			f_midi_transmit(midi);
+			return;
+		}
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNABORTED:	/* hardware forced ep reset */
+	case -ECONNRESET:	/* request dequeued */
+	case -ESHUTDOWN:	/* disconnect from host */
+		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		if (ep == midi->out_ep) {
+			f_midi_handle_out_data(ep, req);
+			/* We don't need to free IN requests because it's handled
+			 * by the midi->in_req_fifo. */
+			free_ep_req(ep, req);
+		}
+		return;
+
+	case -EOVERFLOW:	/* buffer overrun on read means that
+				 * we didn't provide a big enough buffer.
+				 */
+	default:
+		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+		break;
+	case -EREMOTEIO:	/* short read */
+		break;
+	}
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt(ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static void f_midi_drop_out_substreams(struct f_midi *midi)
+{
+	unsigned int i;
+
+	for (i = 0; i < midi->in_ports; i++) {
+		struct gmidi_in_port *port = midi->in_ports_array + i;
+		struct snd_rawmidi_substream *substream = port->substream;
+
+		if (port->active && substream)
+			snd_rawmidi_drop_output(substream);
+	}
+}
+
+static int f_midi_start_ep(struct f_midi *midi,
+			   struct usb_function *f,
+			   struct usb_ep *ep)
+{
+	int err;
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	usb_ep_disable(ep);
+
+	err = config_ep_by_speed(midi->gadget, f, ep);
+	if (err) {
+		ERROR(cdev, "can't configure %s: %d\n", ep->name, err);
+		return err;
+	}
+
+	err = usb_ep_enable(ep);
+	if (err) {
+		ERROR(cdev, "can't start %s: %d\n", ep->name, err);
+		return err;
+	}
+
+	ep->driver_data = midi;
+
+	return 0;
+}
+
+static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_midi *midi = func_to_midi(f);
+	unsigned i;
+	int err;
+
+	/* we only set alt for MIDIStreaming interface */
+	if (intf != midi->ms_id)
+		return 0;
+
+	err = f_midi_start_ep(midi, f, midi->in_ep);
+	if (err)
+		return err;
+
+	err = f_midi_start_ep(midi, f, midi->out_ep);
+	if (err)
+		return err;
+
+	/* pre-allocate write usb requests to use on f_midi_transmit. */
+	while (kfifo_avail(&midi->in_req_fifo)) {
+		struct usb_request *req =
+			midi_alloc_ep_req(midi->in_ep, midi->buflen);
+
+		if (req == NULL)
+			return -ENOMEM;
+
+		req->length = 0;
+		req->complete = f_midi_complete;
+
+		kfifo_put(&midi->in_req_fifo, req);
+	}
+
+	/* allocate a bunch of read buffers and queue them all at once. */
+	for (i = 0; i < midi->qlen && err == 0; i++) {
+		struct usb_request *req =
+			midi_alloc_ep_req(midi->out_ep, midi->buflen);
+
+		if (req == NULL)
+			return -ENOMEM;
+
+		req->complete = f_midi_complete;
+		err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
+		if (err) {
+			ERROR(midi, "%s: couldn't enqueue request: %d\n",
+				    midi->out_ep->name, err);
+			if (req->buf != NULL)
+				free_ep_req(midi->out_ep, req);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void f_midi_disable(struct usb_function *f)
+{
+	struct f_midi *midi = func_to_midi(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request *req = NULL;
+
+	DBG(cdev, "disable\n");
+
+	/*
+	 * just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	usb_ep_disable(midi->in_ep);
+	usb_ep_disable(midi->out_ep);
+
+	/* release IN requests */
+	while (kfifo_get(&midi->in_req_fifo, &req))
+		free_ep_req(midi->in_ep, req);
+
+	f_midi_drop_out_substreams(midi);
+}
+
+static int f_midi_snd_free(struct snd_device *device)
+{
+	return 0;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void f_midi_transmit_byte(struct usb_request *req,
+				 struct gmidi_in_port *port, uint8_t b)
+{
+	uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
+	uint8_t next_state = STATE_INITIAL;
+
+	switch (b) {
+	case 0xf8 ... 0xff:
+		/* System Real-Time Messages */
+		p[0] |= 0x0f;
+		p[1] = b;
+		next_state = port->state;
+		port->state = STATE_REAL_TIME;
+		break;
+
+	case 0xf7:
+		/* End of SysEx */
+		switch (port->state) {
+		case STATE_SYSEX_0:
+			p[0] |= 0x05;
+			p[1] = 0xf7;
+			next_state = STATE_FINISHED;
+			break;
+		case STATE_SYSEX_1:
+			p[0] |= 0x06;
+			p[1] = port->data[0];
+			p[2] = 0xf7;
+			next_state = STATE_FINISHED;
+			break;
+		case STATE_SYSEX_2:
+			p[0] |= 0x07;
+			p[1] = port->data[0];
+			p[2] = port->data[1];
+			p[3] = 0xf7;
+			next_state = STATE_FINISHED;
+			break;
+		default:
+			/* Ignore byte */
+			next_state = port->state;
+			port->state = STATE_INITIAL;
+		}
+		break;
+
+	case 0xf0 ... 0xf6:
+		/* System Common Messages */
+		port->data[0] = port->data[1] = 0;
+		port->state = STATE_INITIAL;
+		switch (b) {
+		case 0xf0:
+			port->data[0] = b;
+			port->data[1] = 0;
+			next_state = STATE_SYSEX_1;
+			break;
+		case 0xf1:
+		case 0xf3:
+			port->data[0] = b;
+			next_state = STATE_1PARAM;
+			break;
+		case 0xf2:
+			port->data[0] = b;
+			next_state = STATE_2PARAM_1;
+			break;
+		case 0xf4:
+		case 0xf5:
+			next_state = STATE_INITIAL;
+			break;
+		case 0xf6:
+			p[0] |= 0x05;
+			p[1] = 0xf6;
+			next_state = STATE_FINISHED;
+			break;
+		}
+		break;
+
+	case 0x80 ... 0xef:
+		/*
+		 * Channel Voice Messages, Channel Mode Messages
+		 * and Control Change Messages.
+		 */
+		port->data[0] = b;
+		port->data[1] = 0;
+		port->state = STATE_INITIAL;
+		if (b >= 0xc0 && b <= 0xdf)
+			next_state = STATE_1PARAM;
+		else
+			next_state = STATE_2PARAM_1;
+		break;
+
+	case 0x00 ... 0x7f:
+		/* Message parameters */
+		switch (port->state) {
+		case STATE_1PARAM:
+			if (port->data[0] < 0xf0)
+				p[0] |= port->data[0] >> 4;
+			else
+				p[0] |= 0x02;
+
+			p[1] = port->data[0];
+			p[2] = b;
+			/* This is to allow Running State Messages */
+			next_state = STATE_1PARAM;
+			break;
+		case STATE_2PARAM_1:
+			port->data[1] = b;
+			next_state = STATE_2PARAM_2;
+			break;
+		case STATE_2PARAM_2:
+			if (port->data[0] < 0xf0)
+				p[0] |= port->data[0] >> 4;
+			else
+				p[0] |= 0x03;
+
+			p[1] = port->data[0];
+			p[2] = port->data[1];
+			p[3] = b;
+			/* This is to allow Running State Messages */
+			next_state = STATE_2PARAM_1;
+			break;
+		case STATE_SYSEX_0:
+			port->data[0] = b;
+			next_state = STATE_SYSEX_1;
+			break;
+		case STATE_SYSEX_1:
+			port->data[1] = b;
+			next_state = STATE_SYSEX_2;
+			break;
+		case STATE_SYSEX_2:
+			p[0] |= 0x04;
+			p[1] = port->data[0];
+			p[2] = port->data[1];
+			p[3] = b;
+			next_state = STATE_SYSEX_0;
+			break;
+		}
+		break;
+	}
+
+	/* States where we have to write into the USB request */
+	if (next_state == STATE_FINISHED ||
+	    port->state == STATE_SYSEX_2 ||
+	    port->state == STATE_1PARAM ||
+	    port->state == STATE_2PARAM_2 ||
+	    port->state == STATE_REAL_TIME) {
+
+		unsigned int length = req->length;
+		u8 *buf = (u8 *)req->buf + length;
+
+		memcpy(buf, p, sizeof(p));
+		req->length = length + sizeof(p);
+
+		if (next_state == STATE_FINISHED) {
+			next_state = STATE_INITIAL;
+			port->data[0] = port->data[1] = 0;
+		}
+	}
+
+	port->state = next_state;
+}
+
+static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
+{
+	struct usb_request *req = NULL;
+	unsigned int len, i;
+	bool active = false;
+	int err;
+
+	/*
+	 * We peek the request in order to reuse it if it fails to enqueue on
+	 * its endpoint
+	 */
+	len = kfifo_peek(&midi->in_req_fifo, &req);
+	if (len != 1) {
+		ERROR(midi, "%s: Couldn't get usb request\n", __func__);
+		return -1;
+	}
+
+	/*
+	 * If buffer overrun, then we ignore this transmission.
+	 * IMPORTANT: This will cause the user-space rawmidi device to block
+	 * until a) usb requests have been completed or b) snd_rawmidi_write()
+	 * times out.
+	 */
+	if (req->length > 0)
+		return 0;
+
+	for (i = midi->in_last_port; i < midi->in_ports; ++i) {
+		struct gmidi_in_port *port = midi->in_ports_array + i;
+		struct snd_rawmidi_substream *substream = port->substream;
+
+		if (!port->active || !substream)
+			continue;
+
+		while (req->length + 3 < midi->buflen) {
+			uint8_t b;
+
+			if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
+				port->active = 0;
+				break;
+			}
+			f_midi_transmit_byte(req, port, b);
+		}
+
+		active = !!port->active;
+		if (active)
+			break;
+	}
+	midi->in_last_port = active ? i : 0;
+
+	if (req->length <= 0)
+		goto done;
+
+	err = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (err < 0) {
+		ERROR(midi, "%s failed to queue req: %d\n",
+		      midi->in_ep->name, err);
+		req->length = 0; /* Re-use request next time. */
+	} else {
+		/* Upon success, put request at the back of the queue. */
+		kfifo_skip(&midi->in_req_fifo);
+		kfifo_put(&midi->in_req_fifo, req);
+	}
+
+done:
+	return active;
+}
+
+static void f_midi_transmit(struct f_midi *midi)
+{
+	struct usb_ep *ep = midi->in_ep;
+	int ret;
+	unsigned long flags;
+
+	/* We only care about USB requests if IN endpoint is enabled */
+	if (!ep || !ep->enabled)
+		goto drop_out;
+
+	spin_lock_irqsave(&midi->transmit_lock, flags);
+
+	do {
+		ret = f_midi_do_transmit(midi, ep);
+		if (ret < 0) {
+			spin_unlock_irqrestore(&midi->transmit_lock, flags);
+			goto drop_out;
+		}
+	} while (ret);
+
+	spin_unlock_irqrestore(&midi->transmit_lock, flags);
+
+	return;
+
+drop_out:
+	f_midi_drop_out_substreams(midi);
+}
+
+static void f_midi_in_tasklet(unsigned long data)
+{
+	struct f_midi *midi = (struct f_midi *) data;
+	f_midi_transmit(midi);
+}
+
+static int f_midi_in_open(struct snd_rawmidi_substream *substream)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+	struct gmidi_in_port *port;
+
+	if (substream->number >= midi->in_ports)
+		return -EINVAL;
+
+	VDBG(midi, "%s()\n", __func__);
+	port = midi->in_ports_array + substream->number;
+	port->substream = substream;
+	port->state = STATE_INITIAL;
+	return 0;
+}
+
+static int f_midi_in_close(struct snd_rawmidi_substream *substream)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+
+	VDBG(midi, "%s()\n", __func__);
+	return 0;
+}
+
+static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+
+	if (substream->number >= midi->in_ports)
+		return;
+
+	VDBG(midi, "%s() %d\n", __func__, up);
+	midi->in_ports_array[substream->number].active = up;
+	if (up)
+		tasklet_hi_schedule(&midi->tasklet);
+}
+
+static int f_midi_out_open(struct snd_rawmidi_substream *substream)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+
+	if (substream->number >= MAX_PORTS)
+		return -EINVAL;
+
+	VDBG(midi, "%s()\n", __func__);
+	midi->out_substream[substream->number] = substream;
+	return 0;
+}
+
+static int f_midi_out_close(struct snd_rawmidi_substream *substream)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+
+	VDBG(midi, "%s()\n", __func__);
+	return 0;
+}
+
+static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct f_midi *midi = substream->rmidi->private_data;
+
+	VDBG(midi, "%s()\n", __func__);
+
+	if (up)
+		set_bit(substream->number, &midi->out_triggered);
+	else
+		clear_bit(substream->number, &midi->out_triggered);
+}
+
+static const struct snd_rawmidi_ops gmidi_in_ops = {
+	.open = f_midi_in_open,
+	.close = f_midi_in_close,
+	.trigger = f_midi_in_trigger,
+};
+
+static const struct snd_rawmidi_ops gmidi_out_ops = {
+	.open = f_midi_out_open,
+	.close = f_midi_out_close,
+	.trigger = f_midi_out_trigger
+};
+
+static inline void f_midi_unregister_card(struct f_midi *midi)
+{
+	if (midi->card) {
+		snd_card_free(midi->card);
+		midi->card = NULL;
+	}
+}
+
+/* register as a sound "card" */
+static int f_midi_register_card(struct f_midi *midi)
+{
+	struct snd_card *card;
+	struct snd_rawmidi *rmidi;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free = f_midi_snd_free,
+	};
+
+	err = snd_card_new(&midi->gadget->dev, midi->index, midi->id,
+			   THIS_MODULE, 0, &card);
+	if (err < 0) {
+		ERROR(midi, "snd_card_new() failed\n");
+		goto fail;
+	}
+	midi->card = card;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops);
+	if (err < 0) {
+		ERROR(midi, "snd_device_new() failed: error %d\n", err);
+		goto fail;
+	}
+
+	strcpy(card->driver, f_midi_longname);
+	strcpy(card->longname, f_midi_longname);
+	strcpy(card->shortname, f_midi_shortname);
+
+	/* Set up rawmidi */
+	snd_component_add(card, "MIDI");
+	err = snd_rawmidi_new(card, card->longname, 0,
+			      midi->out_ports, midi->in_ports, &rmidi);
+	if (err < 0) {
+		ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err);
+		goto fail;
+	}
+	midi->rmidi = rmidi;
+	midi->in_last_port = 0;
+	strcpy(rmidi->name, card->shortname);
+	rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+			    SNDRV_RAWMIDI_INFO_INPUT |
+			    SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = midi;
+	rmidi->private_free = f_midi_rmidi_free;
+	midi->free_ref++;
+
+	/*
+	 * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+	 * It's an upside-down world being a gadget.
+	 */
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+	/* register it - we're ready to go */
+	err = snd_card_register(card);
+	if (err < 0) {
+		ERROR(midi, "snd_card_register() failed\n");
+		goto fail;
+	}
+
+	VDBG(midi, "%s() finished ok\n", __func__);
+	return 0;
+
+fail:
+	f_midi_unregister_card(midi);
+	return err;
+}
+
+/* MIDI function driver setup/binding */
+
+static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_descriptor_header **midi_function;
+	struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
+	struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS];
+	struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS];
+	struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_midi *midi = func_to_midi(f);
+	struct usb_string *us;
+	int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
+
+	midi->gadget = cdev->gadget;
+	tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+	status = f_midi_register_card(midi);
+	if (status < 0)
+		goto fail_register;
+
+	/* maybe allocate device-global string ID */
+	us = usb_gstrings_attach(c->cdev, midi_strings,
+				 ARRAY_SIZE(midi_string_defs));
+	if (IS_ERR(us)) {
+		status = PTR_ERR(us);
+		goto fail;
+	}
+	ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;
+
+	/* We have two interfaces, AudioControl and MIDIStreaming */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ac_interface_desc.bInterfaceNumber = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ms_interface_desc.bInterfaceNumber = status;
+	ac_header_desc.baInterfaceNr[0] = status;
+	midi->ms_id = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc);
+	if (!midi->in_ep)
+		goto fail;
+
+	midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc);
+	if (!midi->out_ep)
+		goto fail;
+
+	/* allocate temporary function list */
+	midi_function = kcalloc((MAX_PORTS * 4) + 11, sizeof(*midi_function),
+				GFP_KERNEL);
+	if (!midi_function) {
+		status = -ENOMEM;
+		goto fail;
+	}
+
+	/*
+	 * construct the function's descriptor set. As the number of
+	 * input and output MIDI ports is configurable, we have to do
+	 * it that way.
+	 */
+
+	/* add the headers - these are always the same */
+	midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc;
+	midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc;
+	midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc;
+
+	/* calculate the header's wTotalLength */
+	n = USB_DT_MS_HEADER_SIZE
+		+ (midi->in_ports + midi->out_ports) *
+			(USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+	ms_header_desc.wTotalLength = cpu_to_le16(n);
+
+	midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc;
+
+	/* configure the external IN jacks, each linked to an embedded OUT jack */
+	for (n = 0; n < midi->in_ports; n++) {
+		struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n];
+		struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n];
+
+		in_ext->bLength			= USB_DT_MIDI_IN_SIZE;
+		in_ext->bDescriptorType		= USB_DT_CS_INTERFACE;
+		in_ext->bDescriptorSubtype	= USB_MS_MIDI_IN_JACK;
+		in_ext->bJackType		= USB_MS_EXTERNAL;
+		in_ext->bJackID			= jack++;
+		in_ext->iJack			= 0;
+		midi_function[i++] = (struct usb_descriptor_header *) in_ext;
+
+		out_emb->bLength		= USB_DT_MIDI_OUT_SIZE(1);
+		out_emb->bDescriptorType	= USB_DT_CS_INTERFACE;
+		out_emb->bDescriptorSubtype	= USB_MS_MIDI_OUT_JACK;
+		out_emb->bJackType		= USB_MS_EMBEDDED;
+		out_emb->bJackID		= jack++;
+		out_emb->bNrInputPins		= 1;
+		out_emb->pins[0].baSourcePin	= 1;
+		out_emb->pins[0].baSourceID	= in_ext->bJackID;
+		out_emb->iJack			= 0;
+		midi_function[i++] = (struct usb_descriptor_header *) out_emb;
+
+		/* link it to the endpoint */
+		ms_in_desc.baAssocJackID[n] = out_emb->bJackID;
+	}
+
+	/* configure the external OUT jacks, each linked to an embedded IN jack */
+	for (n = 0; n < midi->out_ports; n++) {
+		struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n];
+		struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n];
+
+		in_emb->bLength			= USB_DT_MIDI_IN_SIZE;
+		in_emb->bDescriptorType		= USB_DT_CS_INTERFACE;
+		in_emb->bDescriptorSubtype	= USB_MS_MIDI_IN_JACK;
+		in_emb->bJackType		= USB_MS_EMBEDDED;
+		in_emb->bJackID			= jack++;
+		in_emb->iJack			= 0;
+		midi_function[i++] = (struct usb_descriptor_header *) in_emb;
+
+		out_ext->bLength =		USB_DT_MIDI_OUT_SIZE(1);
+		out_ext->bDescriptorType =	USB_DT_CS_INTERFACE;
+		out_ext->bDescriptorSubtype =	USB_MS_MIDI_OUT_JACK;
+		out_ext->bJackType =		USB_MS_EXTERNAL;
+		out_ext->bJackID =		jack++;
+		out_ext->bNrInputPins =		1;
+		out_ext->iJack =		0;
+		out_ext->pins[0].baSourceID =	in_emb->bJackID;
+		out_ext->pins[0].baSourcePin =	1;
+		midi_function[i++] = (struct usb_descriptor_header *) out_ext;
+
+		/* link it to the endpoint */
+		ms_out_desc.baAssocJackID[n] = in_emb->bJackID;
+	}
+
+	/* configure the endpoint descriptors ... */
+	ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports);
+	ms_out_desc.bNumEmbMIDIJack = midi->in_ports;
+
+	ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports);
+	ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
+
+	/* ... and add them to the list */
+	endpoint_descriptor_index = i;
+	midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc;
+	midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc;
+	midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc;
+	midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc;
+	midi_function[i++] = NULL;
+
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	/* copy descriptors, and track endpoint copies */
+	f->fs_descriptors = usb_copy_descriptors(midi_function);
+	if (!f->fs_descriptors)
+		goto fail_f_midi;
+
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
+		bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
+		f->hs_descriptors = usb_copy_descriptors(midi_function);
+		if (!f->hs_descriptors)
+			goto fail_f_midi;
+	}
+
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+		bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+		i = endpoint_descriptor_index;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &bulk_out_desc;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &bulk_out_ss_comp_desc;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &ms_out_desc;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &bulk_in_desc;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &bulk_in_ss_comp_desc;
+		midi_function[i++] = (struct usb_descriptor_header *)
+				     &ms_in_desc;
+		f->ss_descriptors = usb_copy_descriptors(midi_function);
+		if (!f->ss_descriptors)
+			goto fail_f_midi;
+	}
+
+	kfree(midi_function);
+
+	return 0;
+
+fail_f_midi:
+	kfree(midi_function);
+	usb_free_all_descriptors(f);
+fail:
+	f_midi_unregister_card(midi);
+fail_register:
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_midi_opts,
+			    func_inst.group);
+}
+
+static void midi_attr_release(struct config_item *item)
+{
+	struct f_midi_opts *opts = to_f_midi_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations midi_item_ops = {
+	.release	= midi_attr_release,
+};
+
+#define F_MIDI_OPT(name, test_limit, limit)				\
+static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
+{									\
+	struct f_midi_opts *opts = to_f_midi_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_midi_opts_##name##_store(struct config_item *item,	\
+					 const char *page, size_t len)	\
+{									\
+	struct f_midi_opts *opts = to_f_midi_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt > 1) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (test_limit && num > limit) {				\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_midi_opts_, name);
+
+F_MIDI_OPT(index, true, SNDRV_CARDS);
+F_MIDI_OPT(buflen, false, 0);
+F_MIDI_OPT(qlen, false, 0);
+F_MIDI_OPT(in_ports, true, MAX_PORTS);
+F_MIDI_OPT(out_ports, true, MAX_PORTS);
+
+static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
+{
+	struct f_midi_opts *opts = to_f_midi_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	if (opts->id) {
+		result = strlcpy(page, opts->id, PAGE_SIZE);
+	} else {
+		page[0] = 0;
+		result = 0;
+	}
+
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_midi_opts_id_store(struct config_item *item,
+				    const char *page, size_t len)
+{
+	struct f_midi_opts *opts = to_f_midi_opts(item);
+	int ret;
+	char *c;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt > 1) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	c = kstrndup(page, len, GFP_KERNEL);
+	if (!c) {
+		ret = -ENOMEM;
+		goto end;
+	}
+	if (opts->id_allocated)
+		kfree(opts->id);
+	opts->id = c;
+	opts->id_allocated = true;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_midi_opts_, id);
+
+static struct configfs_attribute *midi_attrs[] = {
+	&f_midi_opts_attr_index,
+	&f_midi_opts_attr_buflen,
+	&f_midi_opts_attr_qlen,
+	&f_midi_opts_attr_in_ports,
+	&f_midi_opts_attr_out_ports,
+	&f_midi_opts_attr_id,
+	NULL,
+};
+
+static const struct config_item_type midi_func_type = {
+	.ct_item_ops	= &midi_item_ops,
+	.ct_attrs	= midi_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void f_midi_free_inst(struct usb_function_instance *f)
+{
+	struct f_midi_opts *opts;
+	bool free = false;
+
+	opts = container_of(f, struct f_midi_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	if (!--opts->refcnt) {
+		free = true;
+	}
+	mutex_unlock(&opts->lock);
+
+	if (free) {
+		if (opts->id_allocated)
+			kfree(opts->id);
+		kfree(opts);
+	}
+}
+
+static struct usb_function_instance *f_midi_alloc_inst(void)
+{
+	struct f_midi_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = f_midi_free_inst;
+	opts->index = SNDRV_DEFAULT_IDX1;
+	opts->id = SNDRV_DEFAULT_STR1;
+	opts->buflen = 512;
+	opts->qlen = 32;
+	opts->in_ports = 1;
+	opts->out_ports = 1;
+	opts->refcnt = 1;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &midi_func_type);
+
+	return &opts->func_inst;
+}
+
+static void f_midi_free(struct usb_function *f)
+{
+	struct f_midi *midi;
+	struct f_midi_opts *opts;
+	bool free = false;
+
+	midi = func_to_midi(f);
+	opts = container_of(f->fi, struct f_midi_opts, func_inst);
+	mutex_lock(&opts->lock);
+	if (!--midi->free_ref) {
+		kfree(midi->id);
+		kfifo_free(&midi->in_req_fifo);
+		kfree(midi);
+		free = true;
+	}
+	mutex_unlock(&opts->lock);
+
+	if (free)
+		f_midi_free_inst(&opts->func_inst);
+}
+
+static void f_midi_rmidi_free(struct snd_rawmidi *rmidi)
+{
+	f_midi_free(rmidi->private_data);
+}
+
+static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct f_midi *midi = func_to_midi(f);
+	struct snd_card *card;
+
+	DBG(cdev, "unbind\n");
+
+	/* just to be sure */
+	f_midi_disable(f);
+
+	card = midi->card;
+	midi->card = NULL;
+	if (card)
+		snd_card_free_when_closed(card);
+
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
+{
+	struct f_midi *midi = NULL;
+	struct f_midi_opts *opts;
+	int status, i;
+
+	opts = container_of(fi, struct f_midi_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	/* sanity check */
+	if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
+		status = -EINVAL;
+		goto setup_fail;
+	}
+
+	/* allocate and initialize one new instance */
+	midi = kzalloc(struct_size(midi, in_ports_array, opts->in_ports),
+		       GFP_KERNEL);
+	if (!midi) {
+		status = -ENOMEM;
+		goto setup_fail;
+	}
+
+	for (i = 0; i < opts->in_ports; i++)
+		midi->in_ports_array[i].cable = i;
+
+	/* set up ALSA midi devices */
+	midi->id = kstrdup(opts->id, GFP_KERNEL);
+	if (opts->id && !midi->id) {
+		status = -ENOMEM;
+		goto setup_fail;
+	}
+	midi->in_ports = opts->in_ports;
+	midi->out_ports = opts->out_ports;
+	midi->index = opts->index;
+	midi->buflen = opts->buflen;
+	midi->qlen = opts->qlen;
+	midi->in_last_port = 0;
+	midi->free_ref = 1;
+
+	status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
+	if (status)
+		goto setup_fail;
+
+	spin_lock_init(&midi->transmit_lock);
+
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
+	midi->func.name		= "gmidi function";
+	midi->func.bind		= f_midi_bind;
+	midi->func.unbind	= f_midi_unbind;
+	midi->func.set_alt	= f_midi_set_alt;
+	midi->func.disable	= f_midi_disable;
+	midi->func.free_func	= f_midi_free;
+
+	return &midi->func;
+
+setup_fail:
+	mutex_unlock(&opts->lock);
+	kfree(midi);
+	return ERR_PTR(status);
+}
+
+DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
new file mode 100644
index 0000000..5780fba
--- /dev/null
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -0,0 +1,1669 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_ncm.c -- USB CDC Network (NCM) link function driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from f_ecm.c which is:
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+
+#include <linux/usb/cdc.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ncm.h"
+
+/*
+ * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
+ * NCM is intended to be used with high-speed network attachments.
+ *
+ * Note that NCM requires the use of "alternate settings" for its data
+ * interface.  This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+/* to trigger crc/non-crc ndp signature */
+
+#define NCM_NDP_HDR_CRC_MASK	0x01000000
+#define NCM_NDP_HDR_CRC		0x01000000
+#define NCM_NDP_HDR_NOCRC	0x00000000
+
+enum ncm_notify_state {
+	NCM_NOTIFY_NONE,		/* don't notify */
+	NCM_NOTIFY_CONNECT,		/* issue CONNECT next */
+	NCM_NOTIFY_SPEED,		/* issue SPEED_CHANGE next */
+};
+
+struct f_ncm {
+	struct gether			port;
+	u8				ctrl_id, data_id;
+
+	char				ethaddr[14];
+
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	u8				notify_state;
+	bool				is_open;
+
+	const struct ndp_parser_opts	*parser_opts;
+	bool				is_crc;
+	u32				ndp_sign;
+
+	/*
+	 * for notification, it is accessed from both
+	 * callback and ethernet open/close
+	 */
+	spinlock_t			lock;
+
+	struct net_device		*netdev;
+
+	/* For multi-frame NDP TX */
+	struct sk_buff			*skb_tx_data;
+	struct sk_buff			*skb_tx_ndp;
+	u16				ndp_dgram_count;
+	bool				timer_force_tx;
+	struct hrtimer			task_timer;
+	bool				timer_stopping;
+};
+
+static inline struct f_ncm *func_to_ncm(struct usb_function *f)
+{
+	return container_of(f, struct f_ncm, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned ncm_bitrate(struct usb_gadget *g)
+{
+	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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We cannot group frames so use just the minimal size which ok to put
+ * one max-size ethernet frame.
+ * If the host can group frames, allow it to do that, 16K is selected,
+ * because it's used by default by the current linux host driver
+ */
+#define NTB_DEFAULT_IN_SIZE	16384
+#define NTB_OUT_SIZE		16384
+
+/* Allocation for storing the NDP, 32 should suffice for a
+ * 16k packet. This allows a maximum of 32 * 507 Byte packets to
+ * be transmitted in a single 16kB skb, though when sending full size
+ * packets this limit will be plenty.
+ * Smaller packets are not likely to be trying to maximize the
+ * throughput and will be mstly sending smaller infrequent frames.
+ */
+#define TX_MAX_NUM_DPE		32
+
+/* Delay for the transmit to wait before sending an unfilled NTB frame. */
+#define TX_TIMEOUT_NSECS	300000
+
+#define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\
+				 USB_CDC_NCM_NTB32_SUPPORTED)
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+	.wLength = cpu_to_le16(sizeof(ntb_parameters)),
+	.bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+	.dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+	.wNdpInDivisor = cpu_to_le16(4),
+	.wNdpInPayloadRemainder = cpu_to_le16(0),
+	.wNdpInAlignment = cpu_to_le16(4),
+
+	.dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+	.wNdpOutDivisor = cpu_to_le16(4),
+	.wNdpOutPayloadRemainder = cpu_to_le16(0),
+	.wNdpOutAlignment = cpu_to_le16(4),
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define NCM_STATUS_INTERVAL_MS		32
+#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor ncm_iad_desc = {
+	.bLength =		sizeof ncm_iad_desc,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	USB_CLASS_COMM,
+	.bFunctionSubClass =	USB_CDC_SUBCLASS_NCM,
+	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
+	/* .iFunction =		DYNAMIC */
+};
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor ncm_control_intf = {
+	.bLength =		sizeof ncm_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_NCM,
+	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ncm_header_desc = {
+	.bLength =		sizeof ncm_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ncm_union_desc = {
+	.bLength =		sizeof(ncm_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_desc = {
+	.bLength =		sizeof ecm_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
+
+	/* this descriptor actually adds value, surprise! */
+	/* .iMACAddress = DYNAMIC */
+	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
+	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
+	.wNumberMCFilters =	cpu_to_le16(0),
+	.bNumberPowerFilters =	0,
+};
+
+#define NCAPS	(USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
+
+static struct usb_cdc_ncm_desc ncm_desc = {
+	.bLength =		sizeof ncm_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_NCM_TYPE,
+
+	.bcdNcmVersion =	cpu_to_le16(0x0100),
+	/* can process SetEthernetPacketFilter */
+	.bmNetworkCapabilities = NCAPS,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ncm_data_nop_intf = {
+	.bLength =		sizeof ncm_data_nop_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ncm_data_intf = {
+	.bLength =		sizeof ncm_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	USB_CDC_NCM_PROTO_NTB,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		NCM_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ncm_fs_function[] = {
+	(struct usb_descriptor_header *) &ncm_iad_desc,
+	/* CDC NCM control descriptors */
+	(struct usb_descriptor_header *) &ncm_control_intf,
+	(struct usb_descriptor_header *) &ncm_header_desc,
+	(struct usb_descriptor_header *) &ncm_union_desc,
+	(struct usb_descriptor_header *) &ecm_desc,
+	(struct usb_descriptor_header *) &ncm_desc,
+	(struct usb_descriptor_header *) &fs_ncm_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ncm_data_nop_intf,
+	(struct usb_descriptor_header *) &ncm_data_intf,
+	(struct usb_descriptor_header *) &fs_ncm_in_desc,
+	(struct usb_descriptor_header *) &fs_ncm_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
+};
+static struct usb_endpoint_descriptor hs_ncm_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ncm_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ncm_hs_function[] = {
+	(struct usb_descriptor_header *) &ncm_iad_desc,
+	/* CDC NCM control descriptors */
+	(struct usb_descriptor_header *) &ncm_control_intf,
+	(struct usb_descriptor_header *) &ncm_header_desc,
+	(struct usb_descriptor_header *) &ncm_union_desc,
+	(struct usb_descriptor_header *) &ecm_desc,
+	(struct usb_descriptor_header *) &ncm_desc,
+	(struct usb_descriptor_header *) &hs_ncm_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ncm_data_nop_intf,
+	(struct usb_descriptor_header *) &ncm_data_intf,
+	(struct usb_descriptor_header *) &hs_ncm_in_desc,
+	(struct usb_descriptor_header *) &hs_ncm_out_desc,
+	NULL,
+};
+
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
+	.bLength =		sizeof(ss_ncm_notify_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(NCM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ncm_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_ncm_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_ncm_bulk_comp_desc = {
+	.bLength =		sizeof(ss_ncm_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 *ncm_ss_function[] = {
+	(struct usb_descriptor_header *) &ncm_iad_desc,
+	/* CDC NCM control descriptors */
+	(struct usb_descriptor_header *) &ncm_control_intf,
+	(struct usb_descriptor_header *) &ncm_header_desc,
+	(struct usb_descriptor_header *) &ncm_union_desc,
+	(struct usb_descriptor_header *) &ecm_desc,
+	(struct usb_descriptor_header *) &ncm_desc,
+	(struct usb_descriptor_header *) &ss_ncm_notify_desc,
+	(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ncm_data_nop_intf,
+	(struct usb_descriptor_header *) &ncm_data_intf,
+	(struct usb_descriptor_header *) &ss_ncm_in_desc,
+	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+	(struct usb_descriptor_header *) &ss_ncm_out_desc,
+	(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX	0
+#define STRING_MAC_IDX	1
+#define STRING_DATA_IDX	2
+#define STRING_IAD_IDX	3
+
+static struct usb_string ncm_string_defs[] = {
+	[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
+	[STRING_MAC_IDX].s = "",
+	[STRING_DATA_IDX].s = "CDC Network Data",
+	[STRING_IAD_IDX].s = "CDC NCM",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings ncm_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		ncm_string_defs,
+};
+
+static struct usb_gadget_strings *ncm_strings[] = {
+	&ncm_string_table,
+	NULL,
+};
+
+/*
+ * Here are options for NCM Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+	u32		nth_sign;
+	u32		ndp_sign;
+	unsigned	nth_size;
+	unsigned	ndp_size;
+	unsigned	dpe_size;
+	unsigned	ndplen_align;
+	/* sizes in u16 units */
+	unsigned	dgram_item_len; /* index or length */
+	unsigned	block_length;
+	unsigned	ndp_index;
+	unsigned	reserved1;
+	unsigned	reserved2;
+	unsigned	next_ndp_index;
+};
+
+#define INIT_NDP16_OPTS {					\
+		.nth_sign = USB_CDC_NCM_NTH16_SIGN,		\
+		.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN,	\
+		.nth_size = sizeof(struct usb_cdc_ncm_nth16),	\
+		.ndp_size = sizeof(struct usb_cdc_ncm_ndp16),	\
+		.dpe_size = sizeof(struct usb_cdc_ncm_dpe16),	\
+		.ndplen_align = 4,				\
+		.dgram_item_len = 1,				\
+		.block_length = 1,				\
+		.ndp_index = 1,					\
+		.reserved1 = 0,					\
+		.reserved2 = 0,					\
+		.next_ndp_index = 1,				\
+	}
+
+
+#define INIT_NDP32_OPTS {					\
+		.nth_sign = USB_CDC_NCM_NTH32_SIGN,		\
+		.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN,	\
+		.nth_size = sizeof(struct usb_cdc_ncm_nth32),	\
+		.ndp_size = sizeof(struct usb_cdc_ncm_ndp32),	\
+		.dpe_size = sizeof(struct usb_cdc_ncm_dpe32),	\
+		.ndplen_align = 8,				\
+		.dgram_item_len = 2,				\
+		.block_length = 2,				\
+		.ndp_index = 2,					\
+		.reserved1 = 1,					\
+		.reserved2 = 2,					\
+		.next_ndp_index = 2,				\
+	}
+
+static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
+{
+	switch (size) {
+	case 1:
+		put_unaligned_le16((u16)val, *p);
+		break;
+	case 2:
+		put_unaligned_le32((u32)val, *p);
+
+		break;
+	default:
+		BUG();
+	}
+
+	*p += size;
+}
+
+static inline unsigned get_ncm(__le16 **p, unsigned size)
+{
+	unsigned tmp;
+
+	switch (size) {
+	case 1:
+		tmp = get_unaligned_le16(*p);
+		break;
+	case 2:
+		tmp = get_unaligned_le32(*p);
+		break;
+	default:
+		BUG();
+	}
+
+	*p += size;
+	return tmp;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void ncm_reset_values(struct f_ncm *ncm)
+{
+	ncm->parser_opts = &ndp16_opts;
+	ncm->is_crc = false;
+	ncm->port.cdc_filter = DEFAULT_FILTER;
+
+	/* doesn't make sense for ncm, fixed size used */
+	ncm->port.header_len = 0;
+
+	ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+	ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_do_notify(struct f_ncm *ncm)
+{
+	struct usb_request		*req = ncm->notify_req;
+	struct usb_cdc_notification	*event;
+	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
+	__le32				*data;
+	int				status;
+
+	/* notification already in flight? */
+	if (!req)
+		return;
+
+	event = req->buf;
+	switch (ncm->notify_state) {
+	case NCM_NOTIFY_NONE:
+		return;
+
+	case NCM_NOTIFY_CONNECT:
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		if (ncm->is_open)
+			event->wValue = cpu_to_le16(1);
+		else
+			event->wValue = cpu_to_le16(0);
+		event->wLength = 0;
+		req->length = sizeof *event;
+
+		DBG(cdev, "notify connect %s\n",
+				ncm->is_open ? "true" : "false");
+		ncm->notify_state = NCM_NOTIFY_NONE;
+		break;
+
+	case NCM_NOTIFY_SPEED:
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+		req->length = NCM_STATUS_BYTECOUNT;
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof *event;
+		data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget));
+		ncm->notify_state = NCM_NOTIFY_CONNECT;
+		break;
+	}
+	event->bmRequestType = 0xA1;
+	event->wIndex = cpu_to_le16(ncm->ctrl_id);
+
+	ncm->notify_req = NULL;
+	/*
+	 * In double buffering if there is a space in FIFO,
+	 * completion callback can be called right after the call,
+	 * so unlocking
+	 */
+	spin_unlock(&ncm->lock);
+	status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
+	spin_lock(&ncm->lock);
+	if (status < 0) {
+		ncm->notify_req = req;
+		DBG(cdev, "notify --> %d\n", status);
+	}
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_notify(struct f_ncm *ncm)
+{
+	/*
+	 * NOTE on most versions of Linux, host side cdc-ethernet
+	 * won't listen for notifications until its netdevice opens.
+	 * The first notification then sits in the FIFO for a long
+	 * time, and the second one is queued.
+	 *
+	 * If ncm_notify() is called before the second (CONNECT)
+	 * notification is sent, then it will reset to send the SPEED
+	 * notificaion again (and again, and again), but it's not a problem
+	 */
+	ncm->notify_state = NCM_NOTIFY_SPEED;
+	ncm_do_notify(ncm);
+}
+
+static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ncm			*ncm = req->context;
+	struct usb_composite_dev	*cdev = ncm->port.func.config->cdev;
+	struct usb_cdc_notification	*event = req->buf;
+
+	spin_lock(&ncm->lock);
+	switch (req->status) {
+	case 0:
+		VDBG(cdev, "Notification %02x sent\n",
+		     event->bNotificationType);
+		break;
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		ncm->notify_state = NCM_NOTIFY_NONE;
+		break;
+	default:
+		DBG(cdev, "event %02x --> %d\n",
+			event->bNotificationType, req->status);
+		break;
+	}
+	ncm->notify_req = req;
+	ncm_do_notify(ncm);
+	spin_unlock(&ncm->lock);
+}
+
+static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* now for SET_NTB_INPUT_SIZE only */
+	unsigned		in_size;
+	struct usb_function	*f = req->context;
+	struct f_ncm		*ncm = func_to_ncm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	req->context = NULL;
+	if (req->status || req->actual != req->length) {
+		DBG(cdev, "Bad control-OUT transfer\n");
+		goto invalid;
+	}
+
+	in_size = get_unaligned_le32(req->buf);
+	if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+	    in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+		DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
+		goto invalid;
+	}
+
+	ncm->port.fixed_in_len = in_size;
+	VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
+	return;
+
+invalid:
+	usb_ep_set_halt(ep);
+	return;
+}
+
+static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_ncm		*ncm = func_to_ncm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/*
+	 * composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
+		/*
+		 * see 6.2.30: no data, wIndex = interface,
+		 * wValue = packet filter bitmap
+		 */
+		if (w_length != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		DBG(cdev, "packet filter %02x\n", w_value);
+		/*
+		 * REVISIT locking of cdc_filter.  This assumes the UDC
+		 * driver won't have a concurrent packet TX irq running on
+		 * another CPU; or that if it does, this write is atomic...
+		 */
+		ncm->port.cdc_filter = w_value;
+		value = 0;
+		break;
+	/*
+	 * and optionally:
+	 * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+	 * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+	 * case USB_CDC_GET_ETHERNET_STATISTIC:
+	 */
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_PARAMETERS:
+
+		if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		value = w_length > sizeof ntb_parameters ?
+			sizeof ntb_parameters : w_length;
+		memcpy(req->buf, &ntb_parameters, value);
+		VDBG(cdev, "Host asked NTB parameters\n");
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_INPUT_SIZE:
+
+		if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
+		value = 4;
+		VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
+		     ncm->port.fixed_in_len);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_INPUT_SIZE:
+	{
+		if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		req->complete = ncm_ep0out_complete;
+		req->length = w_length;
+		req->context = f;
+
+		value = req->length;
+		break;
+	}
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_FORMAT:
+	{
+		uint16_t format;
+
+		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+		put_unaligned_le16(format, req->buf);
+		value = 2;
+		VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
+		break;
+	}
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_FORMAT:
+	{
+		if (w_length != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		switch (w_value) {
+		case 0x0000:
+			ncm->parser_opts = &ndp16_opts;
+			DBG(cdev, "NCM16 selected\n");
+			break;
+		case 0x0001:
+			ncm->parser_opts = &ndp32_opts;
+			DBG(cdev, "NCM32 selected\n");
+			break;
+		default:
+			goto invalid;
+		}
+		value = 0;
+		break;
+	}
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_CRC_MODE:
+	{
+		uint16_t is_crc;
+
+		if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		is_crc = ncm->is_crc ? 0x0001 : 0x0000;
+		put_unaligned_le16(is_crc, req->buf);
+		value = 2;
+		VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
+		break;
+	}
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_CRC_MODE:
+	{
+		int ndp_hdr_crc = 0;
+
+		if (w_length != 0 || w_index != ncm->ctrl_id)
+			goto invalid;
+		switch (w_value) {
+		case 0x0000:
+			ncm->is_crc = false;
+			ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
+			DBG(cdev, "non-CRC mode selected\n");
+			break;
+		case 0x0001:
+			ncm->is_crc = true;
+			ndp_hdr_crc = NCM_NDP_HDR_CRC;
+			DBG(cdev, "CRC mode selected\n");
+			break;
+		default:
+			goto invalid;
+		}
+		ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
+		value = 0;
+		break;
+	}
+
+	/* and disabled in ncm descriptor: */
+	/* case USB_CDC_GET_NET_ADDRESS: */
+	/* case USB_CDC_SET_NET_ADDRESS: */
+	/* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+	/* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+	default:
+invalid:
+		DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "ncm req %02x.%02x response err %d\n",
+					ctrl->bRequestType, ctrl->bRequest,
+					value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+
+static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_ncm		*ncm = func_to_ncm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* Control interface has only altsetting 0 */
+	if (intf == ncm->ctrl_id) {
+		if (alt != 0)
+			goto fail;
+
+		DBG(cdev, "reset ncm control %d\n", intf);
+		usb_ep_disable(ncm->notify);
+
+		if (!(ncm->notify->desc)) {
+			DBG(cdev, "init ncm ctrl %d\n", intf);
+			if (config_ep_by_speed(cdev->gadget, f, ncm->notify))
+				goto fail;
+		}
+		usb_ep_enable(ncm->notify);
+
+	/* Data interface has two altsettings, 0 and 1 */
+	} else if (intf == ncm->data_id) {
+		if (alt > 1)
+			goto fail;
+
+		if (ncm->port.in_ep->enabled) {
+			DBG(cdev, "reset ncm\n");
+			ncm->timer_stopping = true;
+			ncm->netdev = NULL;
+			gether_disconnect(&ncm->port);
+			ncm_reset_values(ncm);
+		}
+
+		/*
+		 * CDC Network only sends data in non-default altsettings.
+		 * Changing altsettings resets filters, statistics, etc.
+		 */
+		if (alt == 1) {
+			struct net_device	*net;
+
+			if (!ncm->port.in_ep->desc ||
+			    !ncm->port.out_ep->desc) {
+				DBG(cdev, "init ncm\n");
+				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 */
+			/* Enable zlps by default for NCM conformance;
+			 * override for musb_hdrc (avoids txdma ovhead)
+			 */
+			ncm->port.is_zlp_ok =
+				gadget_is_zlp_supported(cdev->gadget);
+			ncm->port.cdc_filter = DEFAULT_FILTER;
+			DBG(cdev, "activate ncm\n");
+			net = gether_connect(&ncm->port);
+			if (IS_ERR(net))
+				return PTR_ERR(net);
+			ncm->netdev = net;
+			ncm->timer_stopping = false;
+		}
+
+		spin_lock(&ncm->lock);
+		ncm_notify(ncm);
+		spin_unlock(&ncm->lock);
+	} else
+		goto fail;
+
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this NCM function *MUST* implement a get_alt() method.
+ */
+static int ncm_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_ncm		*ncm = func_to_ncm(f);
+
+	if (intf == ncm->ctrl_id)
+		return 0;
+	return ncm->port.in_ep->enabled ? 1 : 0;
+}
+
+static struct sk_buff *package_for_tx(struct f_ncm *ncm)
+{
+	__le16		*ntb_iter;
+	struct sk_buff	*skb2 = NULL;
+	unsigned	ndp_pad;
+	unsigned	ndp_index;
+	unsigned	new_len;
+
+	const struct ndp_parser_opts *opts = ncm->parser_opts;
+	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+	/* Stop the timer */
+	hrtimer_try_to_cancel(&ncm->task_timer);
+
+	ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
+			ncm->skb_tx_data->len;
+	ndp_index = ncm->skb_tx_data->len + ndp_pad;
+	new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
+
+	/* Set the final BlockLength and wNdpIndex */
+	ntb_iter = (void *) ncm->skb_tx_data->data;
+	/* Increment pointer to BlockLength */
+	ntb_iter += 2 + 1 + 1;
+	put_ncm(&ntb_iter, opts->block_length, new_len);
+	put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
+
+	/* Set the final NDP wLength */
+	new_len = opts->ndp_size +
+			(ncm->ndp_dgram_count * dgram_idx_len);
+	ncm->ndp_dgram_count = 0;
+	/* Increment from start to wLength */
+	ntb_iter = (void *) ncm->skb_tx_ndp->data;
+	ntb_iter += 2;
+	put_unaligned_le16(new_len, ntb_iter);
+
+	/* Merge the skbs */
+	swap(skb2, ncm->skb_tx_data);
+	if (ncm->skb_tx_data) {
+		dev_consume_skb_any(ncm->skb_tx_data);
+		ncm->skb_tx_data = NULL;
+	}
+
+	/* Insert NDP alignment. */
+	skb_put_zero(skb2, ndp_pad);
+
+	/* Copy NTB across. */
+	skb_put_data(skb2, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
+	dev_consume_skb_any(ncm->skb_tx_ndp);
+	ncm->skb_tx_ndp = NULL;
+
+	/* Insert zero'd datagram. */
+	skb_put_zero(skb2, dgram_idx_len);
+
+	return skb2;
+}
+
+static struct sk_buff *ncm_wrap_ntb(struct gether *port,
+				    struct sk_buff *skb)
+{
+	struct f_ncm	*ncm = func_to_ncm(&port->func);
+	struct sk_buff	*skb2 = NULL;
+	int		ncb_len = 0;
+	__le16		*ntb_data;
+	__le16		*ntb_ndp;
+	int		dgram_pad;
+
+	unsigned	max_size = ncm->port.fixed_in_len;
+	const struct ndp_parser_opts *opts = ncm->parser_opts;
+	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+	const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+	const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
+	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+	if (!skb && !ncm->skb_tx_data)
+		return NULL;
+
+	if (skb) {
+		/* Add the CRC if required up front */
+		if (ncm->is_crc) {
+			uint32_t	crc;
+			__le16		*crc_pos;
+
+			crc = ~crc32_le(~0,
+					skb->data,
+					skb->len);
+			crc_pos = skb_put(skb, sizeof(uint32_t));
+			put_unaligned_le32(crc, crc_pos);
+		}
+
+		/* If the new skb is too big for the current NCM NTB then
+		 * set the current stored skb to be sent now and clear it
+		 * ready for new data.
+		 * NOTE: Assume maximum align for speed of calculation.
+		 */
+		if (ncm->skb_tx_data
+		    && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
+		    || (ncm->skb_tx_data->len +
+		    div + rem + skb->len +
+		    ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
+		    > max_size)) {
+			skb2 = package_for_tx(ncm);
+			if (!skb2)
+				goto err;
+		}
+
+		if (!ncm->skb_tx_data) {
+			ncb_len = opts->nth_size;
+			dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+			ncb_len += dgram_pad;
+
+			/* Create a new skb for the NTH and datagrams. */
+			ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
+			if (!ncm->skb_tx_data)
+				goto err;
+
+			ncm->skb_tx_data->dev = ncm->netdev;
+			ntb_data = skb_put_zero(ncm->skb_tx_data, ncb_len);
+			/* dwSignature */
+			put_unaligned_le32(opts->nth_sign, ntb_data);
+			ntb_data += 2;
+			/* wHeaderLength */
+			put_unaligned_le16(opts->nth_size, ntb_data++);
+
+			/* Allocate an skb for storing the NDP,
+			 * TX_MAX_NUM_DPE should easily suffice for a
+			 * 16k packet.
+			 */
+			ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
+						    + opts->dpe_size
+						    * TX_MAX_NUM_DPE),
+						    GFP_ATOMIC);
+			if (!ncm->skb_tx_ndp)
+				goto err;
+
+			ncm->skb_tx_ndp->dev = ncm->netdev;
+			ntb_ndp = skb_put(ncm->skb_tx_ndp, opts->ndp_size);
+			memset(ntb_ndp, 0, ncb_len);
+			/* dwSignature */
+			put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
+			ntb_ndp += 2;
+
+			/* There is always a zeroed entry */
+			ncm->ndp_dgram_count = 1;
+
+			/* Note: we skip opts->next_ndp_index */
+		}
+
+		/* Delay the timer. */
+		hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
+			      HRTIMER_MODE_REL_SOFT);
+
+		/* Add the datagram position entries */
+		ntb_ndp = skb_put_zero(ncm->skb_tx_ndp, dgram_idx_len);
+
+		ncb_len = ncm->skb_tx_data->len;
+		dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+		ncb_len += dgram_pad;
+
+		/* (d)wDatagramIndex */
+		put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
+		/* (d)wDatagramLength */
+		put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
+		ncm->ndp_dgram_count++;
+
+		/* Add the new data to the skb */
+		skb_put_zero(ncm->skb_tx_data, dgram_pad);
+		skb_put_data(ncm->skb_tx_data, skb->data, skb->len);
+		dev_consume_skb_any(skb);
+		skb = NULL;
+
+	} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
+		/* If the tx was requested because of a timeout then send */
+		skb2 = package_for_tx(ncm);
+		if (!skb2)
+			goto err;
+	}
+
+	return skb2;
+
+err:
+	ncm->netdev->stats.tx_dropped++;
+
+	if (skb)
+		dev_kfree_skb_any(skb);
+	if (ncm->skb_tx_data)
+		dev_kfree_skb_any(ncm->skb_tx_data);
+	if (ncm->skb_tx_ndp)
+		dev_kfree_skb_any(ncm->skb_tx_ndp);
+
+	return NULL;
+}
+
+/*
+ * The transmit should only be run if no skb data has been sent
+ * for a certain duration.
+ */
+static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
+{
+	struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
+
+	/* Only send if data is available. */
+	if (!ncm->timer_stopping && ncm->skb_tx_data) {
+		ncm->timer_force_tx = true;
+
+		/* XXX This allowance of a NULL skb argument to ndo_start_xmit
+		 * XXX is not sane.  The gadget layer should be redesigned so
+		 * XXX that the dev->wrap() invocations to build SKBs is transparent
+		 * XXX and performed in some way outside of the ndo_start_xmit
+		 * XXX interface.
+		 */
+		ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
+
+		ncm->timer_force_tx = false;
+	}
+	return HRTIMER_NORESTART;
+}
+
+static int ncm_unwrap_ntb(struct gether *port,
+			  struct sk_buff *skb,
+			  struct sk_buff_head *list)
+{
+	struct f_ncm	*ncm = func_to_ncm(&port->func);
+	__le16		*tmp = (void *) skb->data;
+	unsigned	index, index2;
+	int		ndp_index;
+	unsigned	dg_len, dg_len2;
+	unsigned	ndp_len;
+	struct sk_buff	*skb2;
+	int		ret = -EINVAL;
+	unsigned	max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+	const struct ndp_parser_opts *opts = ncm->parser_opts;
+	unsigned	crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
+	int		dgram_counter;
+
+	/* dwSignature */
+	if (get_unaligned_le32(tmp) != opts->nth_sign) {
+		INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
+			skb->len);
+		print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
+			       skb->data, 32, false);
+
+		goto err;
+	}
+	tmp += 2;
+	/* wHeaderLength */
+	if (get_unaligned_le16(tmp++) != opts->nth_size) {
+		INFO(port->func.config->cdev, "Wrong NTB headersize\n");
+		goto err;
+	}
+	tmp++; /* skip wSequence */
+
+	/* (d)wBlockLength */
+	if (get_ncm(&tmp, opts->block_length) > max_size) {
+		INFO(port->func.config->cdev, "OUT size exceeded\n");
+		goto err;
+	}
+
+	ndp_index = get_ncm(&tmp, opts->ndp_index);
+
+	/* Run through all the NDP's in the NTB */
+	do {
+		/* NCM 3.2 */
+		if (((ndp_index % 4) != 0) &&
+				(ndp_index < opts->nth_size)) {
+			INFO(port->func.config->cdev, "Bad index: %#X\n",
+			     ndp_index);
+			goto err;
+		}
+
+		/* walk through NDP */
+		tmp = (void *)(skb->data + ndp_index);
+		if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
+			INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
+			goto err;
+		}
+		tmp += 2;
+
+		ndp_len = get_unaligned_le16(tmp++);
+		/*
+		 * NCM 3.3.1
+		 * entry is 2 items
+		 * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
+		 * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
+		 * Each entry is a dgram index and a dgram length.
+		 */
+		if ((ndp_len < opts->ndp_size
+				+ 2 * 2 * (opts->dgram_item_len * 2))
+				|| (ndp_len % opts->ndplen_align != 0)) {
+			INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
+			     ndp_len);
+			goto err;
+		}
+		tmp += opts->reserved1;
+		/* Check for another NDP (d)wNextNdpIndex */
+		ndp_index = get_ncm(&tmp, opts->next_ndp_index);
+		tmp += opts->reserved2;
+
+		ndp_len -= opts->ndp_size;
+		index2 = get_ncm(&tmp, opts->dgram_item_len);
+		dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+		dgram_counter = 0;
+
+		do {
+			index = index2;
+			dg_len = dg_len2;
+			if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
+				INFO(port->func.config->cdev,
+				     "Bad dgram length: %#X\n", dg_len);
+				goto err;
+			}
+			if (ncm->is_crc) {
+				uint32_t crc, crc2;
+
+				crc = get_unaligned_le32(skb->data +
+							 index + dg_len -
+							 crc_len);
+				crc2 = ~crc32_le(~0,
+						 skb->data + index,
+						 dg_len - crc_len);
+				if (crc != crc2) {
+					INFO(port->func.config->cdev,
+					     "Bad CRC\n");
+					goto err;
+				}
+			}
+
+			index2 = get_ncm(&tmp, opts->dgram_item_len);
+			dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+
+			/*
+			 * Copy the data into a new skb.
+			 * This ensures the truesize is correct
+			 */
+			skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
+							 dg_len - crc_len);
+			if (skb2 == NULL)
+				goto err;
+			skb_put_data(skb2, skb->data + index,
+				     dg_len - crc_len);
+
+			skb_queue_tail(list, skb2);
+
+			ndp_len -= 2 * (opts->dgram_item_len * 2);
+
+			dgram_counter++;
+
+			if (index2 == 0 || dg_len2 == 0)
+				break;
+		} while (ndp_len > 2 * (opts->dgram_item_len * 2));
+	} while (ndp_index);
+
+	dev_consume_skb_any(skb);
+
+	VDBG(port->func.config->cdev,
+	     "Parsed NTB with %d frames\n", dgram_counter);
+	return 0;
+err:
+	skb_queue_purge(list);
+	dev_kfree_skb_any(skb);
+	return ret;
+}
+
+static void ncm_disable(struct usb_function *f)
+{
+	struct f_ncm		*ncm = func_to_ncm(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	DBG(cdev, "ncm deactivated\n");
+
+	if (ncm->port.in_ep->enabled) {
+		ncm->timer_stopping = true;
+		ncm->netdev = NULL;
+		gether_disconnect(&ncm->port);
+	}
+
+	if (ncm->notify->enabled) {
+		usb_ep_disable(ncm->notify);
+		ncm->notify->desc = NULL;
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Callbacks let us notify the host about connect/disconnect when the
+ * net device is opened or closed.
+ *
+ * For testing, note that link states on this side include both opened
+ * and closed variants of:
+ *
+ *   - disconnected/unconfigured
+ *   - configured but inactive (data alt 0)
+ *   - configured and active (data alt 1)
+ *
+ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
+ * SET_INTERFACE (altsetting).  Remember also that "configured" doesn't
+ * imply the host is actually polling the notification endpoint, and
+ * likewise that "active" doesn't imply it's actually using the data
+ * endpoints for traffic.
+ */
+
+static void ncm_open(struct gether *geth)
+{
+	struct f_ncm		*ncm = func_to_ncm(&geth->func);
+
+	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+	spin_lock(&ncm->lock);
+	ncm->is_open = true;
+	ncm_notify(ncm);
+	spin_unlock(&ncm->lock);
+}
+
+static void ncm_close(struct gether *geth)
+{
+	struct f_ncm		*ncm = func_to_ncm(&geth->func);
+
+	DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+	spin_lock(&ncm->lock);
+	ncm->is_open = false;
+	ncm_notify(ncm);
+	spin_unlock(&ncm->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_ncm		*ncm = func_to_ncm(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+	struct f_ncm_opts	*ncm_opts;
+
+	if (!can_support_ecm(cdev->gadget))
+		return -EINVAL;
+
+	ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to ncm_opts->bound access
+	 */
+	if (!ncm_opts->bound) {
+		mutex_lock(&ncm_opts->lock);
+		gether_set_gadget(ncm_opts->net, cdev->gadget);
+		status = gether_register_netdev(ncm_opts->net);
+		mutex_unlock(&ncm_opts->lock);
+		if (status)
+			return status;
+		ncm_opts->bound = true;
+	}
+	us = usb_gstrings_attach(cdev, ncm_strings,
+				 ARRAY_SIZE(ncm_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
+	ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
+	ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
+	ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
+	ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ncm->ctrl_id = status;
+	ncm_iad_desc.bFirstInterface = status;
+
+	ncm_control_intf.bInterfaceNumber = status;
+	ncm_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ncm->data_id = status;
+
+	ncm_data_nop_intf.bInterfaceNumber = status;
+	ncm_data_intf.bInterfaceNumber = status;
+	ncm_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
+	if (!ep)
+		goto fail;
+	ncm->port.in_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
+	if (!ep)
+		goto fail;
+	ncm->port.out_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
+	if (!ep)
+		goto fail;
+	ncm->notify = ep;
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!ncm->notify_req)
+		goto fail;
+	ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
+	if (!ncm->notify_req->buf)
+		goto fail;
+	ncm->notify_req->context = ncm;
+	ncm->notify_req->complete = ncm_notify_complete;
+
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
+	hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
+	hs_ncm_notify_desc.bEndpointAddress =
+		fs_ncm_notify_desc.bEndpointAddress;
+
+	ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
+	ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
+	ss_ncm_notify_desc.bEndpointAddress =
+		fs_ncm_notify_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
+			ncm_ss_function, NULL);
+	if (status)
+		goto fail;
+
+	/*
+	 * NOTE:  all that is done without knowing or caring about
+	 * the network link ... which is unavailable to this code
+	 * until we're activated via set_alt().
+	 */
+
+	ncm->port.open = ncm_open;
+	ncm->port.close = ncm_close;
+
+	hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+	ncm->task_timer.function = ncm_tx_timeout;
+
+	DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+			gadget_is_superspeed(c->cdev->gadget) ? "super" :
+			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+			ncm->port.in_ep->name, ncm->port.out_ep->name,
+			ncm->notify->name);
+	return 0;
+
+fail:
+	if (ncm->notify_req) {
+		kfree(ncm->notify_req->buf);
+		usb_ep_free_request(ncm->notify, ncm->notify_req);
+	}
+
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_ncm_opts,
+			    func_inst.group);
+}
+
+/* f_ncm_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(ncm);
+
+/* f_ncm_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+
+/* f_ncm_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+
+/* f_ncm_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+
+/* f_ncm_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+
+static struct configfs_attribute *ncm_attrs[] = {
+	&ncm_opts_attr_dev_addr,
+	&ncm_opts_attr_host_addr,
+	&ncm_opts_attr_qmult,
+	&ncm_opts_attr_ifname,
+	NULL,
+};
+
+static const struct config_item_type ncm_func_type = {
+	.ct_item_ops	= &ncm_item_ops,
+	.ct_attrs	= ncm_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void ncm_free_inst(struct usb_function_instance *f)
+{
+	struct f_ncm_opts *opts;
+
+	opts = container_of(f, struct f_ncm_opts, func_inst);
+	if (opts->bound)
+		gether_cleanup(netdev_priv(opts->net));
+	else
+		free_netdev(opts->net);
+	kfree(opts);
+}
+
+static struct usb_function_instance *ncm_alloc_inst(void)
+{
+	struct f_ncm_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = ncm_free_inst;
+	opts->net = gether_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+
+	config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
+
+	return &opts->func_inst;
+}
+
+static void ncm_free(struct usb_function *f)
+{
+	struct f_ncm *ncm;
+	struct f_ncm_opts *opts;
+
+	ncm = func_to_ncm(f);
+	opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+	kfree(ncm);
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+}
+
+static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_ncm *ncm = func_to_ncm(f);
+
+	DBG(c->cdev, "ncm unbind\n");
+
+	hrtimer_cancel(&ncm->task_timer);
+
+	ncm_string_defs[0].id = 0;
+	usb_free_all_descriptors(f);
+
+	kfree(ncm->notify_req->buf);
+	usb_ep_free_request(ncm->notify, ncm->notify_req);
+}
+
+static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
+{
+	struct f_ncm		*ncm;
+	struct f_ncm_opts	*opts;
+	int status;
+
+	/* allocate and initialize one new instance */
+	ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
+	if (!ncm)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_ncm_opts, func_inst);
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+
+	/* export host's Ethernet address in CDC format */
+	status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
+				      sizeof(ncm->ethaddr));
+	if (status < 12) { /* strlen("01234567890a") */
+		kfree(ncm);
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-EINVAL);
+	}
+	ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
+
+	spin_lock_init(&ncm->lock);
+	ncm_reset_values(ncm);
+	ncm->port.ioport = netdev_priv(opts->net);
+	mutex_unlock(&opts->lock);
+	ncm->port.is_fixed = true;
+	ncm->port.supports_multi_frame = true;
+
+	ncm->port.func.name = "cdc_network";
+	/* descriptors are per-instance copies */
+	ncm->port.func.bind = ncm_bind;
+	ncm->port.func.unbind = ncm_unbind;
+	ncm->port.func.set_alt = ncm_set_alt;
+	ncm->port.func.get_alt = ncm_get_alt;
+	ncm->port.func.setup = ncm_setup;
+	ncm->port.func.disable = ncm_disable;
+	ncm->port.func.free_func = ncm_free;
+
+	ncm->port.wrap = ncm_wrap_ntb;
+	ncm->port.unwrap = ncm_unwrap_ntb;
+
+	return &ncm->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
new file mode 100644
index 0000000..55b7f57
--- /dev/null
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_obex.c -- USB CDC OBEX function driver
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Based on f_acm.c by Al Borchers and David Brownell.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This CDC OBEX function support just packages a TTY-ish byte stream.
+ * A user mode server will put it into "raw" mode and handle all the
+ * relevant protocol details ... this is just a kernel passthrough.
+ * When possible, we prevent gadget enumeration until that server is
+ * ready to handle the commands.
+ */
+
+struct f_obex {
+	struct gserial			port;
+	u8				ctrl_id;
+	u8				data_id;
+	u8				cur_alt;
+	u8				port_num;
+};
+
+static inline struct f_obex *func_to_obex(struct usb_function *f)
+{
+	return container_of(f, struct f_obex, port.func);
+}
+
+static inline struct f_obex *port_to_obex(struct gserial *p)
+{
+	return container_of(p, struct f_obex, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OBEX_CTRL_IDX	0
+#define OBEX_DATA_IDX	1
+
+static struct usb_string obex_string_defs[] = {
+	[OBEX_CTRL_IDX].s	= "CDC Object Exchange (OBEX)",
+	[OBEX_DATA_IDX].s	= "CDC OBEX Data",
+	{  },	/* end of list */
+};
+
+static struct usb_gadget_strings obex_string_table = {
+	.language		= 0x0409,	/* en-US */
+	.strings		= obex_string_defs,
+};
+
+static struct usb_gadget_strings *obex_strings[] = {
+	&obex_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor obex_control_intf = {
+	.bLength		= sizeof(obex_control_intf),
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= 0,
+
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 0,
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= USB_CDC_SUBCLASS_OBEX,
+};
+
+static struct usb_interface_descriptor obex_data_nop_intf = {
+	.bLength		= sizeof(obex_data_nop_intf),
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= 1,
+
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 0,
+	.bInterfaceClass	= USB_CLASS_CDC_DATA,
+};
+
+static struct usb_interface_descriptor obex_data_intf = {
+	.bLength		= sizeof(obex_data_intf),
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= 2,
+
+	.bAlternateSetting	= 1,
+	.bNumEndpoints		= 2,
+	.bInterfaceClass	= USB_CLASS_CDC_DATA,
+};
+
+static struct usb_cdc_header_desc obex_cdc_header_desc = {
+	.bLength		= sizeof(obex_cdc_header_desc),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= USB_CDC_HEADER_TYPE,
+	.bcdCDC			= cpu_to_le16(0x0120),
+};
+
+static struct usb_cdc_union_desc obex_cdc_union_desc = {
+	.bLength		= sizeof(obex_cdc_union_desc),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= USB_CDC_UNION_TYPE,
+	.bMasterInterface0	= 1,
+	.bSlaveInterface0	= 2,
+};
+
+static struct usb_cdc_obex_desc obex_desc = {
+	.bLength		= sizeof(obex_desc),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= USB_CDC_OBEX_TYPE,
+	.bcdVersion		= cpu_to_le16(0x0100),
+};
+
+/* High-Speed Support */
+
+static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_function[] = {
+	(struct usb_descriptor_header *) &obex_control_intf,
+	(struct usb_descriptor_header *) &obex_cdc_header_desc,
+	(struct usb_descriptor_header *) &obex_desc,
+	(struct usb_descriptor_header *) &obex_cdc_union_desc,
+
+	(struct usb_descriptor_header *) &obex_data_nop_intf,
+	(struct usb_descriptor_header *) &obex_data_intf,
+	(struct usb_descriptor_header *) &obex_hs_ep_in_desc,
+	(struct usb_descriptor_header *) &obex_hs_ep_out_desc,
+	NULL,
+};
+
+/* Full-Speed Support */
+
+static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_function[] = {
+	(struct usb_descriptor_header *) &obex_control_intf,
+	(struct usb_descriptor_header *) &obex_cdc_header_desc,
+	(struct usb_descriptor_header *) &obex_desc,
+	(struct usb_descriptor_header *) &obex_cdc_union_desc,
+
+	(struct usb_descriptor_header *) &obex_data_nop_intf,
+	(struct usb_descriptor_header *) &obex_data_intf,
+	(struct usb_descriptor_header *) &obex_fs_ep_in_desc,
+	(struct usb_descriptor_header *) &obex_fs_ep_out_desc,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_obex		*obex = func_to_obex(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if (intf == obex->ctrl_id) {
+		if (alt != 0)
+			goto fail;
+		/* NOP */
+		dev_dbg(&cdev->gadget->dev,
+			"reset obex ttyGS%d control\n", obex->port_num);
+
+	} else if (intf == obex->data_id) {
+		if (alt > 1)
+			goto fail;
+
+		if (obex->port.in->enabled) {
+			dev_dbg(&cdev->gadget->dev,
+				"reset obex ttyGS%d\n", obex->port_num);
+			gserial_disconnect(&obex->port);
+		}
+
+		if (!obex->port.in->desc || !obex->port.out->desc) {
+			dev_dbg(&cdev->gadget->dev,
+				"init obex ttyGS%d\n", obex->port_num);
+			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) {
+			dev_dbg(&cdev->gadget->dev,
+				"activate obex ttyGS%d\n", obex->port_num);
+			gserial_connect(&obex->port, obex->port_num);
+		}
+
+	} else
+		goto fail;
+
+	obex->cur_alt = alt;
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int obex_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_obex		*obex = func_to_obex(f);
+
+	return obex->cur_alt;
+}
+
+static void obex_disable(struct usb_function *f)
+{
+	struct f_obex	*obex = func_to_obex(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
+	gserial_disconnect(&obex->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void obex_connect(struct gserial *g)
+{
+	struct f_obex		*obex = port_to_obex(g);
+	struct usb_composite_dev *cdev = g->func.config->cdev;
+	int			status;
+
+	status = usb_function_activate(&g->func);
+	if (status)
+		dev_dbg(&cdev->gadget->dev,
+			"obex ttyGS%d function activate --> %d\n",
+			obex->port_num, status);
+}
+
+static void obex_disconnect(struct gserial *g)
+{
+	struct f_obex		*obex = port_to_obex(g);
+	struct usb_composite_dev *cdev = g->func.config->cdev;
+	int			status;
+
+	status = usb_function_deactivate(&g->func);
+	if (status)
+		dev_dbg(&cdev->gadget->dev,
+			"obex ttyGS%d function deactivate --> %d\n",
+			obex->port_num, status);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Some controllers can't support CDC OBEX ... */
+static inline bool can_support_obex(struct usb_configuration *c)
+{
+	/* Since the first interface is a NOP, we can ignore the
+	 * issue of multi-interface support on most controllers.
+	 *
+	 * Altsettings are mandatory, however...
+	 */
+	if (!gadget_is_altset_supported(c->cdev->gadget))
+		return false;
+
+	/* everything else is *probably* fine ... */
+	return true;
+}
+
+static int obex_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_obex		*obex = func_to_obex(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	if (!can_support_obex(c))
+		return -EINVAL;
+
+	us = usb_gstrings_attach(cdev, obex_strings,
+				 ARRAY_SIZE(obex_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
+	obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
+	obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	obex->ctrl_id = status;
+
+	obex_control_intf.bInterfaceNumber = status;
+	obex_cdc_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	obex->data_id = status;
+
+	obex_data_nop_intf.bInterfaceNumber = status;
+	obex_data_intf.bInterfaceNumber = status;
+	obex_cdc_union_desc.bSlaveInterface0 = status;
+
+	/* allocate instance-specific endpoints */
+
+	status = -ENODEV;
+	ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
+	if (!ep)
+		goto fail;
+	obex->port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
+	if (!ep)
+		goto fail;
+	obex->port.out = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+
+	obex_hs_ep_in_desc.bEndpointAddress =
+		obex_fs_ep_in_desc.bEndpointAddress;
+	obex_hs_ep_out_desc.bEndpointAddress =
+		obex_fs_ep_out_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, fs_function, hs_function, NULL,
+					NULL);
+	if (status)
+		goto fail;
+
+	dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
+		obex->port_num,
+		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+		obex->port.in->name, obex->port.out->name);
+
+	return 0;
+
+fail:
+	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+	return status;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_serial_opts,
+			    func_inst.group);
+}
+
+static void obex_attr_release(struct config_item *item)
+{
+	struct f_serial_opts *opts = to_f_serial_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations obex_item_ops = {
+	.release	= obex_attr_release,
+};
+
+static ssize_t f_obex_port_num_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
+}
+
+CONFIGFS_ATTR_RO(f_obex_, port_num);
+
+static struct configfs_attribute *acm_attrs[] = {
+	&f_obex_attr_port_num,
+	NULL,
+};
+
+static const struct config_item_type obex_func_type = {
+	.ct_item_ops	= &obex_item_ops,
+	.ct_attrs	= acm_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void obex_free_inst(struct usb_function_instance *f)
+{
+	struct f_serial_opts *opts;
+
+	opts = container_of(f, struct f_serial_opts, func_inst);
+	gserial_free_line(opts->port_num);
+	kfree(opts);
+}
+
+static struct usb_function_instance *obex_alloc_inst(void)
+{
+	struct f_serial_opts *opts;
+	int ret;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.free_func_inst = obex_free_inst;
+	ret = gserial_alloc_line(&opts->port_num);
+	if (ret) {
+		kfree(opts);
+		return ERR_PTR(ret);
+	}
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &obex_func_type);
+
+	return &opts->func_inst;
+}
+
+static void obex_free(struct usb_function *f)
+{
+	struct f_obex *obex;
+
+	obex = func_to_obex(f);
+	kfree(obex);
+}
+
+static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *obex_alloc(struct usb_function_instance *fi)
+{
+	struct f_obex	*obex;
+	struct f_serial_opts *opts;
+
+	/* allocate and initialize one new instance */
+	obex = kzalloc(sizeof(*obex), GFP_KERNEL);
+	if (!obex)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_serial_opts, func_inst);
+
+	obex->port_num = opts->port_num;
+
+	obex->port.connect = obex_connect;
+	obex->port.disconnect = obex_disconnect;
+
+	obex->port.func.name = "obex";
+	/* descriptors are per-instance copies */
+	obex->port.func.bind = obex_bind;
+	obex->port.func.unbind = obex_unbind;
+	obex->port.func.set_alt = obex_set_alt;
+	obex->port.func.get_alt = obex_get_alt;
+	obex->port.func.disable = obex_disable;
+	obex->port.func.free_func = obex_free;
+	obex->port.func.bind_deactivated = true;
+
+	return &obex->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
new file mode 100644
index 0000000..8b72b19
--- /dev/null
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -0,0 +1,731 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * f_phonet.c -- USB CDC Phonet function
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ *
+ * Author: Rémi Denis-Courmont
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_phonet.h>
+#include <linux/if_arp.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+
+#include "u_phonet.h"
+#include "u_ether.h"
+
+#define PN_MEDIA_USB	0x1B
+#define MAXPACKET	512
+#if (PAGE_SIZE % MAXPACKET)
+#error MAXPACKET must divide PAGE_SIZE!
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+struct phonet_port {
+	struct f_phonet			*usb;
+	spinlock_t			lock;
+};
+
+struct f_phonet {
+	struct usb_function		function;
+	struct {
+		struct sk_buff		*skb;
+		spinlock_t		lock;
+	} rx;
+	struct net_device		*dev;
+	struct usb_ep			*in_ep, *out_ep;
+
+	struct usb_request		*in_req;
+	struct usb_request		*out_reqv[0];
+};
+
+static int phonet_rxq_size = 17;
+
+static inline struct f_phonet *func_to_pn(struct usb_function *f)
+{
+	return container_of(f, struct f_phonet, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define USB_CDC_SUBCLASS_PHONET	0xfe
+#define USB_CDC_PHONET_TYPE	0xab
+
+static struct usb_interface_descriptor
+pn_control_intf_desc = {
+	.bLength =		sizeof pn_control_intf_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber =	DYNAMIC, */
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_PHONET,
+};
+
+static const struct usb_cdc_header_desc
+pn_header_desc = {
+	.bLength =		sizeof pn_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static const struct usb_cdc_header_desc
+pn_phonet_desc = {
+	.bLength =		sizeof pn_phonet_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_PHONET_TYPE,
+	.bcdCDC =		cpu_to_le16(0x1505), /* ??? */
+};
+
+static struct usb_cdc_union_desc
+pn_union_desc = {
+	.bLength =		sizeof pn_union_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+
+	/* .bMasterInterface0 =	DYNAMIC, */
+	/* .bSlaveInterface0 =	DYNAMIC, */
+};
+
+static struct usb_interface_descriptor
+pn_data_nop_intf_desc = {
+	.bLength =		sizeof pn_data_nop_intf_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber =	DYNAMIC, */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+};
+
+static struct usb_interface_descriptor
+pn_data_intf_desc = {
+	.bLength =		sizeof pn_data_intf_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber =	DYNAMIC, */
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(MAXPACKET),
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *fs_pn_function[] = {
+	(struct usb_descriptor_header *) &pn_control_intf_desc,
+	(struct usb_descriptor_header *) &pn_header_desc,
+	(struct usb_descriptor_header *) &pn_phonet_desc,
+	(struct usb_descriptor_header *) &pn_union_desc,
+	(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+	(struct usb_descriptor_header *) &pn_data_intf_desc,
+	(struct usb_descriptor_header *) &pn_fs_sink_desc,
+	(struct usb_descriptor_header *) &pn_fs_source_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *hs_pn_function[] = {
+	(struct usb_descriptor_header *) &pn_control_intf_desc,
+	(struct usb_descriptor_header *) &pn_header_desc,
+	(struct usb_descriptor_header *) &pn_phonet_desc,
+	(struct usb_descriptor_header *) &pn_union_desc,
+	(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+	(struct usb_descriptor_header *) &pn_data_intf_desc,
+	(struct usb_descriptor_header *) &pn_hs_sink_desc,
+	(struct usb_descriptor_header *) &pn_hs_source_desc,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int pn_net_open(struct net_device *dev)
+{
+	netif_wake_queue(dev);
+	return 0;
+}
+
+static int pn_net_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_phonet *fp = ep->driver_data;
+	struct net_device *dev = fp->dev;
+	struct sk_buff *skb = req->context;
+
+	switch (req->status) {
+	case 0:
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+		break;
+
+	case -ESHUTDOWN: /* disconnected */
+	case -ECONNRESET: /* disabled */
+		dev->stats.tx_aborted_errors++;
+		/* fall through */
+	default:
+		dev->stats.tx_errors++;
+	}
+
+	dev_kfree_skb_any(skb);
+	netif_wake_queue(dev);
+}
+
+static netdev_tx_t pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct phonet_port *port = netdev_priv(dev);
+	struct f_phonet *fp;
+	struct usb_request *req;
+	unsigned long flags;
+
+	if (skb->protocol != htons(ETH_P_PHONET))
+		goto out;
+
+	spin_lock_irqsave(&port->lock, flags);
+	fp = port->usb;
+	if (unlikely(!fp)) /* race with carrier loss */
+		goto out_unlock;
+
+	req = fp->in_req;
+	req->buf = skb->data;
+	req->length = skb->len;
+	req->complete = pn_tx_complete;
+	req->zero = 1;
+	req->context = skb;
+
+	if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
+		goto out_unlock;
+
+	netif_stop_queue(dev);
+	skb = NULL;
+
+out_unlock:
+	spin_unlock_irqrestore(&port->lock, flags);
+out:
+	if (unlikely(skb)) {
+		dev_kfree_skb(skb);
+		dev->stats.tx_dropped++;
+	}
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops pn_netdev_ops = {
+	.ndo_open	= pn_net_open,
+	.ndo_stop	= pn_net_close,
+	.ndo_start_xmit	= pn_net_xmit,
+};
+
+static void pn_net_setup(struct net_device *dev)
+{
+	dev->features		= 0;
+	dev->type		= ARPHRD_PHONET;
+	dev->flags		= IFF_POINTOPOINT | IFF_NOARP;
+	dev->mtu		= PHONET_DEV_MTU;
+	dev->min_mtu		= PHONET_MIN_MTU;
+	dev->max_mtu		= PHONET_MAX_MTU;
+	dev->hard_header_len	= 1;
+	dev->dev_addr[0]	= PN_MEDIA_USB;
+	dev->addr_len		= 1;
+	dev->tx_queue_len	= 1;
+
+	dev->netdev_ops		= &pn_netdev_ops;
+	dev->needs_free_netdev	= true;
+	dev->header_ops		= &phonet_header_ops;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Queue buffer for data from the host
+ */
+static int
+pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
+{
+	struct page *page;
+	int err;
+
+	page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC);
+	if (!page)
+		return -ENOMEM;
+
+	req->buf = page_address(page);
+	req->length = PAGE_SIZE;
+	req->context = page;
+
+	err = usb_ep_queue(fp->out_ep, req, gfp_flags);
+	if (unlikely(err))
+		put_page(page);
+	return err;
+}
+
+static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_phonet *fp = ep->driver_data;
+	struct net_device *dev = fp->dev;
+	struct page *page = req->context;
+	struct sk_buff *skb;
+	unsigned long flags;
+	int status = req->status;
+
+	switch (status) {
+	case 0:
+		spin_lock_irqsave(&fp->rx.lock, flags);
+		skb = fp->rx.skb;
+		if (!skb)
+			skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
+		if (req->actual < req->length) /* Last fragment */
+			fp->rx.skb = NULL;
+		spin_unlock_irqrestore(&fp->rx.lock, flags);
+
+		if (unlikely(!skb))
+			break;
+
+		if (skb->len == 0) { /* First fragment */
+			skb->protocol = htons(ETH_P_PHONET);
+			skb_reset_mac_header(skb);
+			/* Can't use pskb_pull() on page in IRQ */
+			skb_put_data(skb, page_address(page), 1);
+		}
+
+		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
+				skb->len <= 1, req->actual, PAGE_SIZE);
+		page = NULL;
+
+		if (req->actual < req->length) { /* Last fragment */
+			skb->dev = dev;
+			dev->stats.rx_packets++;
+			dev->stats.rx_bytes += skb->len;
+
+			netif_rx(skb);
+		}
+		break;
+
+	/* Do not resubmit in these cases: */
+	case -ESHUTDOWN: /* disconnect */
+	case -ECONNABORTED: /* hw reset */
+	case -ECONNRESET: /* dequeued (unlink or netif down) */
+		req = NULL;
+		break;
+
+	/* Do resubmit in these cases: */
+	case -EOVERFLOW: /* request buffer overflow */
+		dev->stats.rx_over_errors++;
+		/* fall through */
+	default:
+		dev->stats.rx_errors++;
+		break;
+	}
+
+	if (page)
+		put_page(page);
+	if (req)
+		pn_rx_submit(fp, req, GFP_ATOMIC);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __pn_reset(struct usb_function *f)
+{
+	struct f_phonet *fp = func_to_pn(f);
+	struct net_device *dev = fp->dev;
+	struct phonet_port *port = netdev_priv(dev);
+
+	netif_carrier_off(dev);
+	port->usb = NULL;
+
+	usb_ep_disable(fp->out_ep);
+	usb_ep_disable(fp->in_ep);
+	if (fp->rx.skb) {
+		dev_kfree_skb_irq(fp->rx.skb);
+		fp->rx.skb = NULL;
+	}
+}
+
+static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_phonet *fp = func_to_pn(f);
+	struct usb_gadget *gadget = fp->function.config->cdev->gadget;
+
+	if (intf == pn_control_intf_desc.bInterfaceNumber)
+		/* control interface, no altsetting */
+		return (alt > 0) ? -EINVAL : 0;
+
+	if (intf == pn_data_intf_desc.bInterfaceNumber) {
+		struct net_device *dev = fp->dev;
+		struct phonet_port *port = netdev_priv(dev);
+
+		/* data intf (0: inactive, 1: active) */
+		if (alt > 1)
+			return -EINVAL;
+
+		spin_lock(&port->lock);
+
+		if (fp->in_ep->enabled)
+			__pn_reset(f);
+
+		if (alt == 1) {
+			int i;
+
+			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;
+				spin_unlock(&port->lock);
+				return -EINVAL;
+			}
+			usb_ep_enable(fp->out_ep);
+			usb_ep_enable(fp->in_ep);
+
+			port->usb = fp;
+			fp->out_ep->driver_data = fp;
+			fp->in_ep->driver_data = fp;
+
+			netif_carrier_on(dev);
+			for (i = 0; i < phonet_rxq_size; i++)
+				pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC);
+		}
+		spin_unlock(&port->lock);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int pn_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_phonet *fp = func_to_pn(f);
+
+	if (intf == pn_control_intf_desc.bInterfaceNumber)
+		return 0;
+
+	if (intf == pn_data_intf_desc.bInterfaceNumber) {
+		struct phonet_port *port = netdev_priv(fp->dev);
+		u8 alt;
+
+		spin_lock(&port->lock);
+		alt = port->usb != NULL;
+		spin_unlock(&port->lock);
+		return alt;
+	}
+
+	return -EINVAL;
+}
+
+static void pn_disconnect(struct usb_function *f)
+{
+	struct f_phonet *fp = func_to_pn(f);
+	struct phonet_port *port = netdev_priv(fp->dev);
+	unsigned long flags;
+
+	/* remain disabled until set_alt */
+	spin_lock_irqsave(&port->lock, flags);
+	__pn_reset(f);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pn_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct f_phonet *fp = func_to_pn(f);
+	struct usb_ep *ep;
+	int status, i;
+
+	struct f_phonet_opts *phonet_opts;
+
+	phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
+
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to phonet_opts->bound access
+	 */
+	if (!phonet_opts->bound) {
+		gphonet_set_gadget(phonet_opts->net, gadget);
+		status = gphonet_register_netdev(phonet_opts->net);
+		if (status)
+			return status;
+		phonet_opts->bound = true;
+	}
+
+	/* Reserve interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto err;
+	pn_control_intf_desc.bInterfaceNumber = status;
+	pn_union_desc.bMasterInterface0 = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto err;
+	pn_data_nop_intf_desc.bInterfaceNumber = status;
+	pn_data_intf_desc.bInterfaceNumber = status;
+	pn_union_desc.bSlaveInterface0 = status;
+
+	/* Reserve endpoints */
+	status = -ENODEV;
+	ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
+	if (!ep)
+		goto err;
+	fp->out_ep = ep;
+
+	ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
+	if (!ep)
+		goto err;
+	fp->in_ep = ep;
+
+	pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
+	pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
+
+	/* Do not try to bind Phonet twice... */
+	status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
+			NULL, NULL);
+	if (status)
+		goto err;
+
+	/* Incoming USB requests */
+	status = -ENOMEM;
+	for (i = 0; i < phonet_rxq_size; i++) {
+		struct usb_request *req;
+
+		req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
+		if (!req)
+			goto err_req;
+
+		req->complete = pn_rx_complete;
+		fp->out_reqv[i] = req;
+	}
+
+	/* Outgoing USB requests */
+	fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
+	if (!fp->in_req)
+		goto err_req;
+
+	INFO(cdev, "USB CDC Phonet function\n");
+	INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
+		fp->out_ep->name, fp->in_ep->name);
+	return 0;
+
+err_req:
+	for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
+		usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
+	usb_free_all_descriptors(f);
+err:
+	ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
+	return status;
+}
+
+static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_phonet_opts,
+			func_inst.group);
+}
+
+static void phonet_attr_release(struct config_item *item)
+{
+	struct f_phonet_opts *opts = to_f_phonet_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations phonet_item_ops = {
+	.release		= phonet_attr_release,
+};
+
+static ssize_t f_phonet_ifname_show(struct config_item *item, char *page)
+{
+	return gether_get_ifname(to_f_phonet_opts(item)->net, page, PAGE_SIZE);
+}
+
+CONFIGFS_ATTR_RO(f_phonet_, ifname);
+
+static struct configfs_attribute *phonet_attrs[] = {
+	&f_phonet_attr_ifname,
+	NULL,
+};
+
+static const struct config_item_type phonet_func_type = {
+	.ct_item_ops	= &phonet_item_ops,
+	.ct_attrs	= phonet_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void phonet_free_inst(struct usb_function_instance *f)
+{
+	struct f_phonet_opts *opts;
+
+	opts = container_of(f, struct f_phonet_opts, func_inst);
+	if (opts->bound)
+		gphonet_cleanup(opts->net);
+	else
+		free_netdev(opts->net);
+	kfree(opts);
+}
+
+static struct usb_function_instance *phonet_alloc_inst(void)
+{
+	struct f_phonet_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.free_func_inst = phonet_free_inst;
+	opts->net = gphonet_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+			&phonet_func_type);
+
+	return &opts->func_inst;
+}
+
+static void phonet_free(struct usb_function *f)
+{
+	struct f_phonet *phonet;
+
+	phonet = func_to_pn(f);
+	kfree(phonet);
+}
+
+static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_phonet *fp = func_to_pn(f);
+	int i;
+
+	/* We are already disconnected */
+	if (fp->in_req)
+		usb_ep_free_request(fp->in_ep, fp->in_req);
+	for (i = 0; i < phonet_rxq_size; i++)
+		if (fp->out_reqv[i])
+			usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
+
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
+{
+	struct f_phonet *fp;
+	struct f_phonet_opts *opts;
+	int size;
+
+	size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
+	fp = kzalloc(size, GFP_KERNEL);
+	if (!fp)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_phonet_opts, func_inst);
+
+	fp->dev = opts->net;
+	fp->function.name = "phonet";
+	fp->function.bind = pn_bind;
+	fp->function.unbind = pn_unbind;
+	fp->function.set_alt = pn_set_alt;
+	fp->function.get_alt = pn_get_alt;
+	fp->function.disable = pn_disconnect;
+	fp->function.free_func = phonet_free;
+	spin_lock_init(&fp->rx.lock);
+
+	return &fp->function;
+}
+
+struct net_device *gphonet_setup_default(void)
+{
+	struct net_device *dev;
+	struct phonet_port *port;
+
+	/* Create net device */
+	dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN,
+			   pn_net_setup);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	port = netdev_priv(dev);
+	spin_lock_init(&port->lock);
+	netif_carrier_off(dev);
+
+	return dev;
+}
+
+void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
+{
+	SET_NETDEV_DEV(net, &g->dev);
+}
+
+int gphonet_register_netdev(struct net_device *net)
+{
+	int status;
+
+	status = register_netdev(net);
+	if (status)
+		free_netdev(net);
+
+	return status;
+}
+
+void gphonet_cleanup(struct net_device *dev)
+{
+	unregister_netdev(dev);
+}
+
+DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
+MODULE_AUTHOR("Rémi Denis-Courmont");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
new file mode 100644
index 0000000..9c7ed25
--- /dev/null
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -0,0 +1,1498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_printer.c - USB printer function driver
+ *
+ * Copied from drivers/usb/gadget/legacy/printer.c,
+ * which was:
+ *
+ * printer.c -- Printer gadget driver
+ *
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/cdev.h>
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+#include "u_printer.h"
+
+#define PRINTER_MINORS		4
+#define GET_DEVICE_ID		0
+#define GET_PORT_STATUS		1
+#define SOFT_RESET		2
+
+static int major, minors;
+static struct class *usb_gadget_class;
+static DEFINE_IDA(printer_ida);
+static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
+
+/*-------------------------------------------------------------------------*/
+
+struct printer_dev {
+	spinlock_t		lock;		/* lock this structure */
+	/* lock buffer lists during read/write calls */
+	struct mutex		lock_printer_io;
+	struct usb_gadget	*gadget;
+	s8			interface;
+	struct usb_ep		*in_ep, *out_ep;
+
+	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 */
+	/* wait until there is data to be read. */
+	wait_queue_head_t	rx_wait;
+	struct list_head	tx_reqs;	/* List of free TX structs */
+	struct list_head	tx_reqs_active; /* List of Active TX xfers */
+	/* Wait until there are write buffers available to use. */
+	wait_queue_head_t	tx_wait;
+	/* Wait until all write buffers have been sent. */
+	wait_queue_head_t	tx_flush_wait;
+	struct usb_request	*current_rx_req;
+	size_t			current_rx_bytes;
+	u8			*current_rx_buf;
+	u8			printer_status;
+	u8			reset_printer;
+	int			minor;
+	struct cdev		printer_cdev;
+	u8			printer_cdev_open;
+	wait_queue_head_t	wait;
+	unsigned		q_len;
+	char			*pnp_string;	/* We don't own memory! */
+	struct usb_function	function;
+};
+
+static inline struct printer_dev *func_to_printer(struct usb_function *f)
+{
+	return container_of(f, struct printer_dev, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+/* holds our biggest descriptor */
+#define USB_DESC_BUFSIZE		256
+#define USB_BUFSIZE			8192
+
+static struct usb_interface_descriptor intf_desc = {
+	.bLength =		sizeof(intf_desc),
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_PRINTER,
+	.bInterfaceSubClass =	1,	/* Printer Sub-Class */
+	.bInterfaceProtocol =	2,	/* Bi-Directional */
+	.iInterface =		0
+};
+
+static struct usb_endpoint_descriptor fs_ep_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_endpoint_descriptor fs_ep_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_descriptor_header *fs_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &fs_ep_in_desc,
+	(struct usb_descriptor_header *) &fs_ep_out_desc,
+	NULL
+};
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+static struct usb_endpoint_descriptor hs_ep_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512)
+};
+
+static struct usb_endpoint_descriptor hs_ep_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512)
+};
+
+static struct usb_descriptor_header *hs_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &hs_ep_in_desc,
+	(struct usb_descriptor_header *) &hs_ep_out_desc,
+	NULL
+};
+
+/*
+ * Added endpoint descriptors for 3.0 devices
+ */
+
+static struct usb_endpoint_descriptor ss_ep_in_desc = {
+	.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_ep_in_comp_desc = {
+	.bLength =              sizeof(ss_ep_in_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor ss_ep_out_desc = {
+	.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_ep_out_comp_desc = {
+	.bLength =              sizeof(ss_ep_out_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *ss_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &ss_ep_in_desc,
+	(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
+	(struct usb_descriptor_header *) &ss_ep_out_desc,
+	(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
+	NULL
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
+					struct usb_endpoint_descriptor *fs,
+					struct usb_endpoint_descriptor *hs,
+					struct usb_endpoint_descriptor *ss)
+{
+	switch (gadget->speed) {
+	case USB_SPEED_SUPER:
+		return ss;
+	case USB_SPEED_HIGH:
+		return hs;
+	default:
+		return fs;
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request(ep, gfp_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, gfp_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			return NULL;
+		}
+	}
+
+	return req;
+}
+
+static void
+printer_req_free(struct usb_ep *ep, struct usb_request *req)
+{
+	if (ep != NULL && req != NULL) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct printer_dev	*dev = ep->driver_data;
+	int			status = req->status;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	list_del_init(&req->list);	/* Remode from Active List */
+
+	switch (status) {
+
+	/* normal completion */
+	case 0:
+		if (req->actual > 0) {
+			list_add_tail(&req->list, &dev->rx_buffers);
+			DBG(dev, "G_Printer : rx length %d\n", req->actual);
+		} else {
+			list_add(&req->list, &dev->rx_reqs);
+		}
+		break;
+
+	/* software-driven interface shutdown */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		VDBG(dev, "rx shutdown, code %d\n", status);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+
+	/* for hardware automagic (such as pxa) */
+	case -ECONNABORTED:		/* endpoint reset */
+		DBG(dev, "rx %s reset\n", ep->name);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+
+	/* data overrun */
+	case -EOVERFLOW:
+		/* FALLTHROUGH */
+
+	default:
+		DBG(dev, "rx status %d\n", status);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+	}
+
+	wake_up_interruptible(&dev->rx_wait);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct printer_dev	*dev = ep->driver_data;
+
+	switch (req->status) {
+	default:
+		VDBG(dev, "tx err %d\n", req->status);
+		/* FALLTHROUGH */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		break;
+	case 0:
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	/* Take the request struct off the active list and put it on the
+	 * free list.
+	 */
+	list_del_init(&req->list);
+	list_add(&req->list, &dev->tx_reqs);
+	wake_up_interruptible(&dev->tx_wait);
+	if (likely(list_empty(&dev->tx_reqs_active)))
+		wake_up_interruptible(&dev->tx_flush_wait);
+
+	spin_unlock(&dev->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+printer_open(struct inode *inode, struct file *fd)
+{
+	struct printer_dev	*dev;
+	unsigned long		flags;
+	int			ret = -EBUSY;
+
+	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!dev->printer_cdev_open) {
+		dev->printer_cdev_open = 1;
+		fd->private_data = dev;
+		ret = 0;
+		/* Change the printer status to show that it's on-line. */
+		dev->printer_status |= PRINTER_SELECTED;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	DBG(dev, "printer_open returned %x\n", ret);
+	return ret;
+}
+
+static int
+printer_close(struct inode *inode, struct file *fd)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->printer_cdev_open = 0;
+	fd->private_data = NULL;
+	/* Change printer status to show that the printer is off-line. */
+	dev->printer_status &= ~PRINTER_SELECTED;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	DBG(dev, "printer_close\n");
+
+	return 0;
+}
+
+/* This function must be called with interrupts turned off. */
+static void
+setup_rx_reqs(struct printer_dev *dev)
+{
+	struct usb_request              *req;
+
+	while (likely(!list_empty(&dev->rx_reqs))) {
+		int error;
+
+		req = container_of(dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del_init(&req->list);
+
+		/* The USB Host sends us whatever amount of data it wants to
+		 * so we always set the length field to the full USB_BUFSIZE.
+		 * If the amount of data is more than the read() caller asked
+		 * for it will be stored in the request buffer until it is
+		 * asked for by read().
+		 */
+		req->length = USB_BUFSIZE;
+		req->complete = rx_complete;
+
+		/* here, we unlock, and only unlock, to avoid deadlock. */
+		spin_unlock(&dev->lock);
+		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (error) {
+			DBG(dev, "rx submit --> %d\n", error);
+			list_add(&req->list, &dev->rx_reqs);
+			break;
+		}
+		/* if the req is empty, then add it into dev->rx_reqs_active. */
+		else if (list_empty(&req->list))
+			list_add(&req->list, &dev->rx_reqs_active);
+	}
+}
+
+static ssize_t
+printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
+{
+	struct printer_dev		*dev = fd->private_data;
+	unsigned long			flags;
+	size_t				size;
+	size_t				bytes_copied;
+	struct usb_request		*req;
+	/* This is a pointer to the current USB rx request. */
+	struct usb_request		*current_rx_req;
+	/* This is the number of bytes in the current rx buffer. */
+	size_t				current_rx_bytes;
+	/* This is a pointer to the current rx buffer. */
+	u8				*current_rx_buf;
+
+	if (len == 0)
+		return -EINVAL;
+
+	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* We will use this flag later to check if a printer reset happened
+	 * after we turn interrupts back on.
+	 */
+	dev->reset_printer = 0;
+
+	setup_rx_reqs(dev);
+
+	bytes_copied = 0;
+	current_rx_req = dev->current_rx_req;
+	current_rx_bytes = dev->current_rx_bytes;
+	current_rx_buf = dev->current_rx_buf;
+	dev->current_rx_req = NULL;
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+
+	/* Check if there is any data in the read buffers. Please note that
+	 * current_rx_bytes is the number of bytes in the current rx buffer.
+	 * If it is zero then check if there are any other rx_buffers that
+	 * are on the completed list. We are only out of data if all rx
+	 * buffers are empty.
+	 */
+	if ((current_rx_bytes == 0) &&
+			(likely(list_empty(&dev->rx_buffers)))) {
+		/* Turn interrupts back on before sleeping. */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		/*
+		 * If no data is available check if this is a NON-Blocking
+		 * call or not.
+		 */
+		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* Sleep until data is available */
+		wait_event_interruptible(dev->rx_wait,
+				(likely(!list_empty(&dev->rx_buffers))));
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	/* We have data to return then copy it to the caller's buffer.*/
+	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
+			&& len) {
+		if (current_rx_bytes == 0) {
+			req = container_of(dev->rx_buffers.next,
+					struct usb_request, list);
+			list_del_init(&req->list);
+
+			if (req->actual && req->buf) {
+				current_rx_req = req;
+				current_rx_bytes = req->actual;
+				current_rx_buf = req->buf;
+			} else {
+				list_add(&req->list, &dev->rx_reqs);
+				continue;
+			}
+		}
+
+		/* Don't leave irqs off while doing memory copies */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (len > current_rx_bytes)
+			size = current_rx_bytes;
+		else
+			size = len;
+
+		size -= copy_to_user(buf, current_rx_buf, size);
+		bytes_copied += size;
+		len -= size;
+		buf += size;
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		/* We've disconnected or reset so return. */
+		if (dev->reset_printer) {
+			list_add(&current_rx_req->list, &dev->rx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* If we not returning all the data left in this RX request
+		 * buffer then adjust the amount of data left in the buffer.
+		 * Othewise if we are done with this RX request buffer then
+		 * requeue it to get any incoming data from the USB host.
+		 */
+		if (size < current_rx_bytes) {
+			current_rx_bytes -= size;
+			current_rx_buf += size;
+		} else {
+			list_add(&current_rx_req->list, &dev->rx_reqs);
+			current_rx_bytes = 0;
+			current_rx_buf = NULL;
+			current_rx_req = NULL;
+		}
+	}
+
+	dev->current_rx_req = current_rx_req;
+	dev->current_rx_bytes = current_rx_bytes;
+	dev->current_rx_buf = current_rx_buf;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
+
+	if (bytes_copied)
+		return bytes_copied;
+	else
+		return -EAGAIN;
+}
+
+static ssize_t
+printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	size_t			size;	/* Amount of data in a TX request. */
+	size_t			bytes_copied = 0;
+	struct usb_request	*req;
+	int			value;
+
+	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
+
+	if (len == 0)
+		return -EINVAL;
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* Check if a printer reset happens while we have interrupts on */
+	dev->reset_printer = 0;
+
+	/* Check if there is any available write buffers */
+	if (likely(list_empty(&dev->tx_reqs))) {
+		/* Turn interrupts back on before sleeping. */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		/*
+		 * If write buffers are available check if this is
+		 * a NON-Blocking call or not.
+		 */
+		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* Sleep until a write buffer is available */
+		wait_event_interruptible(dev->tx_wait,
+				(likely(!list_empty(&dev->tx_reqs))));
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	while (likely(!list_empty(&dev->tx_reqs)) && len) {
+
+		if (len > USB_BUFSIZE)
+			size = USB_BUFSIZE;
+		else
+			size = len;
+
+		req = container_of(dev->tx_reqs.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+
+		req->complete = tx_complete;
+		req->length = size;
+
+		/* Check if we need to send a zero length packet. */
+		if (len > size)
+			/* They will be more TX requests so no yet. */
+			req->zero = 0;
+		else
+			/* If the data amount is not a multiple of the
+			 * maxpacket size then send a zero length packet.
+			 */
+			req->zero = ((len % dev->in_ep->maxpacket) == 0);
+
+		/* Don't leave irqs off while doing memory copies */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (copy_from_user(req->buf, buf, size)) {
+			list_add(&req->list, &dev->tx_reqs);
+			mutex_unlock(&dev->lock_printer_io);
+			return bytes_copied;
+		}
+
+		bytes_copied += size;
+		len -= size;
+		buf += size;
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		/* We've disconnected or reset so free the req and buffer */
+		if (dev->reset_printer) {
+			list_add(&req->list, &dev->tx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		list_add(&req->list, &dev->tx_reqs_active);
+
+		/* here, we unlock, and only unlock, to avoid deadlock. */
+		spin_unlock(&dev->lock);
+		value = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (value) {
+			list_del(&req->list);
+			list_add(&req->list, &dev->tx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
+
+	if (bytes_copied)
+		return bytes_copied;
+	else
+		return -EAGAIN;
+}
+
+static int
+printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
+{
+	struct printer_dev	*dev = fd->private_data;
+	struct inode *inode = file_inode(fd);
+	unsigned long		flags;
+	int			tx_list_empty;
+
+	inode_lock(inode);
+	spin_lock_irqsave(&dev->lock, flags);
+	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (!tx_list_empty) {
+		/* Sleep until all data has been sent */
+		wait_event_interruptible(dev->tx_flush_wait,
+				(likely(list_empty(&dev->tx_reqs_active))));
+	}
+	inode_unlock(inode);
+
+	return 0;
+}
+
+static __poll_t
+printer_poll(struct file *fd, poll_table *wait)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	__poll_t		status = 0;
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+	setup_rx_reqs(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	poll_wait(fd, &dev->rx_wait, wait);
+	poll_wait(fd, &dev->tx_wait, wait);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (likely(!list_empty(&dev->tx_reqs)))
+		status |= EPOLLOUT | EPOLLWRNORM;
+
+	if (likely(dev->current_rx_bytes) ||
+			likely(!list_empty(&dev->rx_buffers)))
+		status |= EPOLLIN | EPOLLRDNORM;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return status;
+}
+
+static long
+printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	int			status = 0;
+
+	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
+
+	/* handle ioctls */
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	switch (code) {
+	case GADGET_GET_PRINTER_STATUS:
+		status = (int)dev->printer_status;
+		break;
+	case GADGET_SET_PRINTER_STATUS:
+		dev->printer_status = (u8)arg;
+		break;
+	default:
+		/* could not handle ioctl */
+		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
+				code);
+		status = -ENOTTY;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return status;
+}
+
+/* used after endpoint configuration */
+static const struct file_operations printer_io_operations = {
+	.owner =	THIS_MODULE,
+	.open =		printer_open,
+	.read =		printer_read,
+	.write =	printer_write,
+	.fsync =	printer_fsync,
+	.poll =		printer_poll,
+	.unlocked_ioctl = printer_ioctl,
+	.release =	printer_close,
+	.llseek =	noop_llseek,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+set_printer_interface(struct printer_dev *dev)
+{
+	int			result = 0;
+
+	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
+				&ss_ep_in_desc);
+	dev->in_ep->driver_data = dev;
+
+	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
+				    &hs_ep_out_desc, &ss_ep_out_desc);
+	dev->out_ep->driver_data = dev;
+
+	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);
+	if (result != 0) {
+		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
+		goto done;
+	}
+
+done:
+	/* on error, disable any endpoints  */
+	if (result != 0) {
+		(void) usb_ep_disable(dev->in_ep);
+		(void) usb_ep_disable(dev->out_ep);
+		dev->in_ep->desc = NULL;
+		dev->out_ep->desc = NULL;
+	}
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+static void printer_reset_interface(struct printer_dev *dev)
+{
+	unsigned long	flags;
+
+	if (dev->interface < 0)
+		return;
+
+	DBG(dev, "%s\n", __func__);
+
+	if (dev->in_ep->desc)
+		usb_ep_disable(dev->in_ep);
+
+	if (dev->out_ep->desc)
+		usb_ep_disable(dev->out_ep);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->in_ep->desc = NULL;
+	dev->out_ep->desc = NULL;
+	dev->interface = -1;
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* Change our operational Interface. */
+static int set_interface(struct printer_dev *dev, unsigned number)
+{
+	int			result = 0;
+
+	/* Free the current interface */
+	printer_reset_interface(dev);
+
+	result = set_printer_interface(dev);
+	if (result)
+		printer_reset_interface(dev);
+	else
+		dev->interface = number;
+
+	if (!result)
+		INFO(dev, "Using interface %x\n", number);
+
+	return result;
+}
+
+static void printer_soft_reset(struct printer_dev *dev)
+{
+	struct usb_request	*req;
+
+	INFO(dev, "Received Printer Reset Request\n");
+
+	if (usb_ep_disable(dev->in_ep))
+		DBG(dev, "Failed to disable USB in_ep\n");
+	if (usb_ep_disable(dev->out_ep))
+		DBG(dev, "Failed to disable USB out_ep\n");
+
+	if (dev->current_rx_req != NULL) {
+		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
+		dev->current_rx_req = NULL;
+	}
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+	dev->reset_printer = 1;
+
+	while (likely(!(list_empty(&dev->rx_buffers)))) {
+		req = container_of(dev->rx_buffers.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
+		req = container_of(dev->rx_buffers.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
+		req = container_of(dev->tx_reqs_active.next,
+				struct usb_request, list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->tx_reqs);
+	}
+
+	if (usb_ep_enable(dev->in_ep))
+		DBG(dev, "Failed to enable USB in_ep\n");
+	if (usb_ep_enable(dev->out_ep))
+		DBG(dev, "Failed to enable USB out_ep\n");
+
+	wake_up_interruptible(&dev->rx_wait);
+	wake_up_interruptible(&dev->tx_wait);
+	wake_up_interruptible(&dev->tx_flush_wait);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static bool gprinter_req_match(struct usb_function *f,
+			       const struct usb_ctrlrequest *ctrl,
+			       bool config0)
+{
+	struct printer_dev	*dev = func_to_printer(f);
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	if (config0)
+		return false;
+
+	if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
+	    (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+		return false;
+
+	switch (ctrl->bRequest) {
+	case GET_DEVICE_ID:
+		w_index >>= 8;
+		if (USB_DIR_IN & ctrl->bRequestType)
+			break;
+		return false;
+	case GET_PORT_STATUS:
+		if (!w_value && w_length == 1 &&
+		    (USB_DIR_IN & ctrl->bRequestType))
+			break;
+		return false;
+	case SOFT_RESET:
+		if (!w_value && !w_length &&
+		   !(USB_DIR_IN & ctrl->bRequestType))
+			break;
+		/* fall through */
+	default:
+		return false;
+	}
+	return w_index == dev->interface;
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down.
+ */
+static int printer_func_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	u8			*buf = req->buf;
+	int			value = -EOPNOTSUPP;
+	u16			wIndex = le16_to_cpu(ctrl->wIndex);
+	u16			wValue = le16_to_cpu(ctrl->wValue);
+	u16			wLength = le16_to_cpu(ctrl->wLength);
+
+	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
+
+	switch (ctrl->bRequestType&USB_TYPE_MASK) {
+	case USB_TYPE_CLASS:
+		switch (ctrl->bRequest) {
+		case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */
+			/* Only one printer interface is supported. */
+			if ((wIndex>>8) != dev->interface)
+				break;
+
+			if (!dev->pnp_string) {
+				value = 0;
+				break;
+			}
+			value = strlen(dev->pnp_string);
+			buf[0] = (value >> 8) & 0xFF;
+			buf[1] = value & 0xFF;
+			memcpy(buf + 2, dev->pnp_string, value);
+			DBG(dev, "1284 PNP String: %x %s\n", value,
+			    dev->pnp_string);
+			break;
+
+		case GET_PORT_STATUS: /* Get Port Status */
+			/* Only one printer interface is supported. */
+			if (wIndex != dev->interface)
+				break;
+
+			buf[0] = dev->printer_status;
+			value = min_t(u16, wLength, 1);
+			break;
+
+		case SOFT_RESET: /* Soft Reset */
+			/* Only one printer interface is supported. */
+			if (wIndex != dev->interface)
+				break;
+
+			printer_soft_reset(dev);
+
+			value = 0;
+			break;
+
+		default:
+			goto unknown;
+		}
+		break;
+
+	default:
+unknown:
+		VDBG(dev,
+			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
+		break;
+	}
+	/* host either stalls (value < 0) or reports success */
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < wLength;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
+			req->status = 0;
+		}
+	}
+	return value;
+}
+
+static int printer_func_bind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct usb_gadget *gadget = c->cdev->gadget;
+	struct printer_dev *dev = func_to_printer(f);
+	struct device *pdev;
+	struct usb_composite_dev *cdev = c->cdev;
+	struct usb_ep *in_ep;
+	struct usb_ep *out_ep = NULL;
+	struct usb_request *req;
+	dev_t devt;
+	int id;
+	int ret;
+	u32 i;
+
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	intf_desc.bInterfaceNumber = id;
+
+	/* finish hookup to lower layer ... */
+	dev->gadget = gadget;
+
+	/* all we really need is bulk IN/OUT */
+	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
+	if (!in_ep) {
+autoconf_fail:
+		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
+			cdev->gadget->name);
+		return -ENODEV;
+	}
+
+	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
+	if (!out_ep)
+		goto autoconf_fail;
+
+	/* 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;
+	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, fs_printer_function,
+			hs_printer_function, ss_printer_function, NULL);
+	if (ret)
+		return ret;
+
+	dev->in_ep = in_ep;
+	dev->out_ep = out_ep;
+
+	ret = -ENOMEM;
+	for (i = 0; i < dev->q_len; i++) {
+		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
+		if (!req)
+			goto fail_tx_reqs;
+		list_add(&req->list, &dev->tx_reqs);
+	}
+
+	for (i = 0; i < dev->q_len; i++) {
+		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
+		if (!req)
+			goto fail_rx_reqs;
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	/* Setup the sysfs files for the printer gadget. */
+	devt = MKDEV(major, dev->minor);
+	pdev = device_create(usb_gadget_class, NULL, devt,
+				  NULL, "g_printer%d", dev->minor);
+	if (IS_ERR(pdev)) {
+		ERROR(dev, "Failed to create device: g_printer\n");
+		ret = PTR_ERR(pdev);
+		goto fail_rx_reqs;
+	}
+
+	/*
+	 * Register a character device as an interface to a user mode
+	 * program that handles the printer specific functionality.
+	 */
+	cdev_init(&dev->printer_cdev, &printer_io_operations);
+	dev->printer_cdev.owner = THIS_MODULE;
+	ret = cdev_add(&dev->printer_cdev, devt, 1);
+	if (ret) {
+		ERROR(dev, "Failed to open char device\n");
+		goto fail_cdev_add;
+	}
+
+	return 0;
+
+fail_cdev_add:
+	device_destroy(usb_gadget_class, devt);
+
+fail_rx_reqs:
+	while (!list_empty(&dev->rx_reqs)) {
+		req = container_of(dev->rx_reqs.next, struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+
+fail_tx_reqs:
+	while (!list_empty(&dev->tx_reqs)) {
+		req = container_of(dev->tx_reqs.next, struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->in_ep, req);
+	}
+
+	return ret;
+
+}
+
+static int printer_func_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	int ret = -ENOTSUPP;
+
+	if (!alt)
+		ret = set_interface(dev, intf);
+
+	return ret;
+}
+
+static void printer_func_disable(struct usb_function *f)
+{
+	struct printer_dev *dev = func_to_printer(f);
+
+	DBG(dev, "%s\n", __func__);
+
+	printer_reset_interface(dev);
+}
+
+static inline struct f_printer_opts
+*to_f_printer_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_printer_opts,
+			    func_inst.group);
+}
+
+static void printer_attr_release(struct config_item *item)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations printer_item_ops = {
+	.release	= printer_attr_release,
+};
+
+static ssize_t f_printer_opts_pnp_string_show(struct config_item *item,
+					      char *page)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+	int result = 0;
+
+	mutex_lock(&opts->lock);
+	if (!opts->pnp_string)
+		goto unlock;
+
+	result = strlcpy(page, opts->pnp_string, PAGE_SIZE);
+	if (result >= PAGE_SIZE) {
+		result = PAGE_SIZE;
+	} else if (page[result - 1] != '\n' && result + 1 < PAGE_SIZE) {
+		page[result++] = '\n';
+		page[result] = '\0';
+	}
+
+unlock:
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_printer_opts_pnp_string_store(struct config_item *item,
+					       const char *page, size_t len)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+	char *new_pnp;
+	int result;
+
+	mutex_lock(&opts->lock);
+
+	new_pnp = kstrndup(page, len, GFP_KERNEL);
+	if (!new_pnp) {
+		result = -ENOMEM;
+		goto unlock;
+	}
+
+	if (opts->pnp_string_allocated)
+		kfree(opts->pnp_string);
+
+	opts->pnp_string_allocated = true;
+	opts->pnp_string = new_pnp;
+	result = len;
+unlock:
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+CONFIGFS_ATTR(f_printer_opts_, pnp_string);
+
+static ssize_t f_printer_opts_q_len_show(struct config_item *item,
+					 char *page)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d\n", opts->q_len);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_printer_opts_q_len_store(struct config_item *item,
+					  const char *page, size_t len)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+	int ret;
+	u16 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou16(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->q_len = (unsigned)num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_printer_opts_, q_len);
+
+static struct configfs_attribute *printer_attrs[] = {
+	&f_printer_opts_attr_pnp_string,
+	&f_printer_opts_attr_q_len,
+	NULL,
+};
+
+static const struct config_item_type printer_func_type = {
+	.ct_item_ops	= &printer_item_ops,
+	.ct_attrs	= printer_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static inline int gprinter_get_minor(void)
+{
+	int ret;
+
+	ret = ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
+	if (ret >= PRINTER_MINORS) {
+		ida_simple_remove(&printer_ida, ret);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static inline void gprinter_put_minor(int minor)
+{
+	ida_simple_remove(&printer_ida, minor);
+}
+
+static int gprinter_setup(int);
+static void gprinter_cleanup(void);
+
+static void gprinter_free_inst(struct usb_function_instance *f)
+{
+	struct f_printer_opts *opts;
+
+	opts = container_of(f, struct f_printer_opts, func_inst);
+
+	mutex_lock(&printer_ida_lock);
+
+	gprinter_put_minor(opts->minor);
+	if (ida_is_empty(&printer_ida))
+		gprinter_cleanup();
+
+	mutex_unlock(&printer_ida_lock);
+
+	if (opts->pnp_string_allocated)
+		kfree(opts->pnp_string);
+	kfree(opts);
+}
+
+static struct usb_function_instance *gprinter_alloc_inst(void)
+{
+	struct f_printer_opts *opts;
+	struct usb_function_instance *ret;
+	int status = 0;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = gprinter_free_inst;
+	ret = &opts->func_inst;
+
+	mutex_lock(&printer_ida_lock);
+
+	if (ida_is_empty(&printer_ida)) {
+		status = gprinter_setup(PRINTER_MINORS);
+		if (status) {
+			ret = ERR_PTR(status);
+			kfree(opts);
+			goto unlock;
+		}
+	}
+
+	opts->minor = gprinter_get_minor();
+	if (opts->minor < 0) {
+		ret = ERR_PTR(opts->minor);
+		kfree(opts);
+		if (ida_is_empty(&printer_ida))
+			gprinter_cleanup();
+		goto unlock;
+	}
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &printer_func_type);
+
+unlock:
+	mutex_unlock(&printer_ida_lock);
+	return ret;
+}
+
+static void gprinter_free(struct usb_function *f)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	struct f_printer_opts *opts;
+
+	opts = container_of(f->fi, struct f_printer_opts, func_inst);
+	kfree(dev);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void printer_func_unbind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct printer_dev	*dev;
+	struct usb_request	*req;
+
+	dev = func_to_printer(f);
+
+	device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
+
+	/* Remove Character Device */
+	cdev_del(&dev->printer_cdev);
+
+	/* we must already have been disconnected ... no i/o may be active */
+	WARN_ON(!list_empty(&dev->tx_reqs_active));
+	WARN_ON(!list_empty(&dev->rx_reqs_active));
+
+	/* Free all memory for this driver. */
+	while (!list_empty(&dev->tx_reqs)) {
+		req = container_of(dev->tx_reqs.next, struct usb_request,
+				list);
+		list_del(&req->list);
+		printer_req_free(dev->in_ep, req);
+	}
+
+	if (dev->current_rx_req != NULL)
+		printer_req_free(dev->out_ep, dev->current_rx_req);
+
+	while (!list_empty(&dev->rx_reqs)) {
+		req = container_of(dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+
+	while (!list_empty(&dev->rx_buffers)) {
+		req = container_of(dev->rx_buffers.next,
+				struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
+{
+	struct printer_dev	*dev;
+	struct f_printer_opts	*opts;
+
+	opts = container_of(fi, struct f_printer_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	if (opts->minor >= minors) {
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-ENOENT);
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	++opts->refcnt;
+	dev->minor = opts->minor;
+	dev->pnp_string = opts->pnp_string;
+	dev->q_len = opts->q_len;
+	mutex_unlock(&opts->lock);
+
+	dev->function.name = "printer";
+	dev->function.bind = printer_func_bind;
+	dev->function.setup = printer_func_setup;
+	dev->function.unbind = printer_func_unbind;
+	dev->function.set_alt = printer_func_set_alt;
+	dev->function.disable = printer_func_disable;
+	dev->function.req_match = gprinter_req_match;
+	dev->function.free_func = gprinter_free;
+
+	INIT_LIST_HEAD(&dev->tx_reqs);
+	INIT_LIST_HEAD(&dev->rx_reqs);
+	INIT_LIST_HEAD(&dev->rx_buffers);
+	INIT_LIST_HEAD(&dev->tx_reqs_active);
+	INIT_LIST_HEAD(&dev->rx_reqs_active);
+
+	spin_lock_init(&dev->lock);
+	mutex_init(&dev->lock_printer_io);
+	init_waitqueue_head(&dev->rx_wait);
+	init_waitqueue_head(&dev->tx_wait);
+	init_waitqueue_head(&dev->tx_flush_wait);
+
+	dev->interface = -1;
+	dev->printer_cdev_open = 0;
+	dev->printer_status = PRINTER_NOT_ERROR;
+	dev->current_rx_req = NULL;
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+
+	return &dev->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Craig Nadler");
+
+static int gprinter_setup(int count)
+{
+	int status;
+	dev_t devt;
+
+	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
+	if (IS_ERR(usb_gadget_class)) {
+		status = PTR_ERR(usb_gadget_class);
+		usb_gadget_class = NULL;
+		pr_err("unable to create usb_gadget class %d\n", status);
+		return status;
+	}
+
+	status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
+	if (status) {
+		pr_err("alloc_chrdev_region %d\n", status);
+		class_destroy(usb_gadget_class);
+		usb_gadget_class = NULL;
+		return status;
+	}
+
+	major = MAJOR(devt);
+	minors = count;
+
+	return status;
+}
+
+static void gprinter_cleanup(void)
+{
+	if (major) {
+		unregister_chrdev_region(MKDEV(major, 0), minors);
+		major = minors = 0;
+	}
+	class_destroy(usb_gadget_class);
+	usb_gadget_class = NULL;
+}
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
new file mode 100644
index 0000000..d48df36
--- /dev/null
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -0,0 +1,1029 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_rndis.c -- RNDIS link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ *                    Author: Michal Nazarewicz (mina86@mina86.com)
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include <linux/atomic.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_rndis.h"
+#include "rndis.h"
+#include "configfs.h"
+
+/*
+ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
+ * been promoted instead of the standard CDC Ethernet.  The published RNDIS
+ * spec is ambiguous, incomplete, and needlessly complex.  Variants such as
+ * ActiveSync have even worse status in terms of specification.
+ *
+ * In short:  it's a protocol controlled by (and for) Microsoft, not for an
+ * Open ecosystem or markets.  Linux supports it *only* because Microsoft
+ * doesn't support the CDC Ethernet standard.
+ *
+ * The RNDIS data transfer model is complex, with multiple Ethernet packets
+ * per USB message, and out of band data.  The control model is built around
+ * what's essentially an "RNDIS RPC" protocol.  It's all wrapped in a CDC ACM
+ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
+ * useless (they're ignored).  RNDIS expects to be the only function in its
+ * configuration, so it's no real help if you need composite devices; and
+ * it expects to be the first configuration too.
+ *
+ * There is a single technical advantage of RNDIS over CDC Ethernet, if you
+ * discount the fluff that its RPC can be made to deliver: it doesn't need
+ * a NOP altsetting for the data interface.  That lets it work on some of the
+ * "so smart it's stupid" hardware which takes over configuration changes
+ * from the software, and adds restrictions like "no altsettings".
+ *
+ * Unfortunately MSFT's RNDIS drivers are buggy.  They hang or oops, and
+ * have all sorts of contrary-to-specification oddities that can prevent
+ * them from working sanely.  Since bugfixes (or accurate specs, letting
+ * Linux work around those bugs) are unlikely to ever come from MSFT, you
+ * may want to avoid using RNDIS on purely operational grounds.
+ *
+ * Omissions from the RNDIS 1.0 specification include:
+ *
+ *   - Power management ... references data that's scattered around lots
+ *     of other documentation, which is incorrect/incomplete there too.
+ *
+ *   - There are various undocumented protocol requirements, like the need
+ *     to send garbage in some control-OUT messages.
+ *
+ *   - MS-Windows drivers sometimes emit undocumented requests.
+ */
+
+struct f_rndis {
+	struct gether			port;
+	u8				ctrl_id, data_id;
+	u8				ethaddr[ETH_ALEN];
+	u32				vendorID;
+	const char			*manufacturer;
+	struct rndis_params		*params;
+
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	atomic_t			notify_count;
+};
+
+static inline struct f_rndis *func_to_rndis(struct usb_function *f)
+{
+	return container_of(f, struct f_rndis, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static unsigned int bitrate(struct usb_gadget *g)
+{
+	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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ */
+
+#define RNDIS_STATUS_INTERVAL_MS	32
+#define STATUS_BYTECOUNT		8	/* 8 bytes data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor rndis_control_intf = {
+	.bLength =		sizeof rndis_control_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	/* status endpoint is optional; this could be patched later */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
+	.bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc header_desc = {
+	.bLength =		sizeof header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+	.bLength =		sizeof call_mgmt_descriptor,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+
+	.bmCapabilities =	0x00,
+	.bDataInterface =	0x01,
+};
+
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
+	.bLength =		sizeof rndis_acm_descriptor,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+
+	.bmCapabilities =	0x00,
+};
+
+static struct usb_cdc_union_desc rndis_union_desc = {
+	.bLength =		sizeof(rndis_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+/* the data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor rndis_data_intf = {
+	.bLength =		sizeof rndis_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+
+static struct usb_interface_assoc_descriptor
+rndis_iad_descriptor = {
+	.bLength =		sizeof rndis_iad_descriptor,
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	.bFirstInterface =	0, /* XXX, hardcoded */
+	.bInterfaceCount = 	2,	// control + data
+	.bFunctionClass =	USB_CLASS_COMM,
+	.bFunctionSubClass =	USB_CDC_SUBCLASS_ETHERNET,
+	.bFunctionProtocol =	USB_CDC_PROTO_NONE,
+	/* .iFunction = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_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 =		RNDIS_STATUS_INTERVAL_MS,
+};
+
+static struct usb_endpoint_descriptor fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+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,
+	(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 *) &fs_notify_desc,
+
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_data_intf,
+	(struct usb_descriptor_header *) &fs_in_desc,
+	(struct usb_descriptor_header *) &fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_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 =		USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
+};
+
+static struct usb_endpoint_descriptor hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+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,
+	(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 *) &hs_notify_desc,
+
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_data_intf,
+	(struct usb_descriptor_header *) &hs_in_desc,
+	(struct usb_descriptor_header *) &hs_out_desc,
+	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 =		USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
+};
+
+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[] = {
+	[0].s = "RNDIS Communications Control",
+	[1].s = "RNDIS Ethernet Data",
+	[2].s = "RNDIS",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rndis_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rndis_string_defs,
+};
+
+static struct usb_gadget_strings *rndis_strings[] = {
+	&rndis_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct sk_buff *rndis_add_header(struct gether *port,
+					struct sk_buff *skb)
+{
+	struct sk_buff *skb2;
+
+	if (!skb)
+		return NULL;
+
+	skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
+	rndis_add_hdr(skb2);
+
+	dev_kfree_skb(skb);
+	return skb2;
+}
+
+static void rndis_response_available(void *_rndis)
+{
+	struct f_rndis			*rndis = _rndis;
+	struct usb_request		*req = rndis->notify_req;
+	struct usb_composite_dev	*cdev = rndis->port.func.config->cdev;
+	__le32				*data = req->buf;
+	int				status;
+
+	if (atomic_inc_return(&rndis->notify_count) != 1)
+		return;
+
+	/* Send RNDIS RESPONSE_AVAILABLE notification; a
+	 * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
+	 *
+	 * This is the only notification defined by RNDIS.
+	 */
+	data[0] = cpu_to_le32(1);
+	data[1] = cpu_to_le32(0);
+
+	status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+	if (status) {
+		atomic_dec(&rndis->notify_count);
+		DBG(cdev, "notify/0 --> %d\n", status);
+	}
+}
+
+static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rndis			*rndis = req->context;
+	struct usb_composite_dev	*cdev = rndis->port.func.config->cdev;
+	int				status = req->status;
+
+	/* after TX:
+	 *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
+	 *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
+	 */
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		atomic_set(&rndis->notify_count, 0);
+		break;
+	default:
+		DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
+			ep->name, status,
+			req->actual, req->length);
+		/* FALLTHROUGH */
+	case 0:
+		if (ep != rndis->notify)
+			break;
+
+		/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
+		 * notifications by resending until we're done
+		 */
+		if (atomic_dec_and_test(&rndis->notify_count))
+			break;
+		status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+		if (status) {
+			atomic_dec(&rndis->notify_count);
+			DBG(cdev, "notify/1 --> %d\n", status);
+		}
+		break;
+	}
+}
+
+static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_rndis			*rndis = req->context;
+	int				status;
+
+	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+//	spin_lock(&dev->lock);
+	status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
+	if (status < 0)
+		pr_err("RNDIS command error %d, %d/%d\n",
+			status, req->actual, req->length);
+//	spin_unlock(&dev->lock);
+}
+
+static int
+rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_rndis		*rndis = func_to_rndis(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	/* RNDIS uses the CDC command encapsulation mechanism to implement
+	 * an RPC scheme, with much getting/setting of attributes by OID.
+	 */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		if (w_value || w_index != rndis->ctrl_id)
+			goto invalid;
+		/* read the request; process it later */
+		value = w_length;
+		req->complete = rndis_command_complete;
+		req->context = rndis;
+		/* later, rndis_response_available() sends a notification */
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		if (w_value || w_index != rndis->ctrl_id)
+			goto invalid;
+		else {
+			u8 *buf;
+			u32 n;
+
+			/* return the result */
+			buf = rndis_get_next_response(rndis->params, &n);
+			if (buf) {
+				memcpy(req->buf, buf, n);
+				req->complete = rndis_response_complete;
+				req->context = rndis;
+				rndis_free_response(rndis->params, buf);
+				value = n;
+			}
+			/* else stalls ... spec says to avoid that */
+		}
+		break;
+
+	default:
+invalid:
+		VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (value < w_length);
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "rndis response on err %d\n", value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+
+static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_rndis		*rndis = func_to_rndis(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* we know alt == 0 */
+
+	if (intf == rndis->ctrl_id) {
+		VDBG(cdev, "reset rndis control %d\n", intf);
+		usb_ep_disable(rndis->notify);
+
+		if (!rndis->notify->desc) {
+			VDBG(cdev, "init rndis ctrl %d\n", intf);
+			if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
+				goto fail;
+		}
+		usb_ep_enable(rndis->notify);
+
+	} else if (intf == rndis->data_id) {
+		struct net_device	*net;
+
+		if (rndis->port.in_ep->enabled) {
+			DBG(cdev, "reset rndis\n");
+			gether_disconnect(&rndis->port);
+		}
+
+		if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+			DBG(cdev, "init rndis\n");
+			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. */
+		rndis->port.is_zlp_ok = false;
+
+		/* RNDIS should be in the "RNDIS uninitialized" state,
+		 * either never activated or after rndis_uninit().
+		 *
+		 * We don't want data to flow here until a nonzero packet
+		 * filter is set, at which point it enters "RNDIS data
+		 * initialized" state ... but we do want the endpoints
+		 * to be activated.  It's a strange little state.
+		 *
+		 * REVISIT the RNDIS gadget code has done this wrong for a
+		 * very long time.  We need another call to the link layer
+		 * code -- gether_updown(...bool) maybe -- to do it right.
+		 */
+		rndis->port.cdc_filter = 0;
+
+		DBG(cdev, "RNDIS RX/TX early activation ... \n");
+		net = gether_connect(&rndis->port);
+		if (IS_ERR(net))
+			return PTR_ERR(net);
+
+		rndis_set_param_dev(rndis->params, net,
+				&rndis->port.cdc_filter);
+	} else
+		goto fail;
+
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+static void rndis_disable(struct usb_function *f)
+{
+	struct f_rndis		*rndis = func_to_rndis(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if (!rndis->notify->enabled)
+		return;
+
+	DBG(cdev, "rndis deactivated\n");
+
+	rndis_uninit(rndis->params);
+	gether_disconnect(&rndis->port);
+
+	usb_ep_disable(rndis->notify);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This isn't quite the same mechanism as CDC Ethernet, since the
+ * notification scheme passes less data, but the same set of link
+ * states must be tested.  A key difference is that altsettings are
+ * not used to tell whether the link should send packets or not.
+ */
+
+static void rndis_open(struct gether *geth)
+{
+	struct f_rndis		*rndis = func_to_rndis(&geth->func);
+	struct usb_composite_dev *cdev = geth->func.config->cdev;
+
+	DBG(cdev, "%s\n", __func__);
+
+	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
+				bitrate(cdev->gadget) / 100);
+	rndis_signal_connect(rndis->params);
+}
+
+static void rndis_close(struct gether *geth)
+{
+	struct f_rndis		*rndis = func_to_rndis(&geth->func);
+
+	DBG(geth->func.config->cdev, "%s\n", __func__);
+
+	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
+	rndis_signal_disconnect(rndis->params);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Some controllers can't support RNDIS ... */
+static inline bool can_support_rndis(struct usb_configuration *c)
+{
+	/* everything else is *presumably* fine */
+	return true;
+}
+
+/* ethernet function driver setup/binding */
+
+static int
+rndis_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_rndis		*rndis = func_to_rndis(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	struct f_rndis_opts *rndis_opts;
+
+	if (!can_support_rndis(c))
+		return -EINVAL;
+
+	rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+
+	if (cdev->use_os_string) {
+		f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
+					   GFP_KERNEL);
+		if (!f->os_desc_table)
+			return -ENOMEM;
+		f->os_desc_n = 1;
+		f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
+	}
+
+	rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
+	rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
+	rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
+
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to rndis_opts->bound access
+	 */
+	if (!rndis_opts->bound) {
+		gether_set_gadget(rndis_opts->net, cdev->gadget);
+		status = gether_register_netdev(rndis_opts->net);
+		if (status)
+			goto fail;
+		rndis_opts->bound = true;
+	}
+
+	us = usb_gstrings_attach(cdev, rndis_strings,
+				 ARRAY_SIZE(rndis_string_defs));
+	if (IS_ERR(us)) {
+		status = PTR_ERR(us);
+		goto fail;
+	}
+	rndis_control_intf.iInterface = us[0].id;
+	rndis_data_intf.iInterface = us[1].id;
+	rndis_iad_descriptor.iFunction = us[2].id;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	rndis->ctrl_id = status;
+	rndis_iad_descriptor.bFirstInterface = status;
+
+	rndis_control_intf.bInterfaceNumber = status;
+	rndis_union_desc.bMasterInterface0 = status;
+
+	if (cdev->use_os_string)
+		f->os_desc_table[0].if_id =
+			rndis_iad_descriptor.bFirstInterface;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	rndis->data_id = status;
+
+	rndis_data_intf.bInterfaceNumber = status;
+	rndis_union_desc.bSlaveInterface0 = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
+	if (!ep)
+		goto fail;
+	rndis->port.in_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
+	if (!ep)
+		goto fail;
+	rndis->port.out_ep = ep;
+
+	/* NOTE:  a status/notification endpoint is, strictly speaking,
+	 * optional.  We don't treat it that way though!  It's simpler,
+	 * and some newer profiles don't treat it as optional.
+	 */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
+	if (!ep)
+		goto fail;
+	rndis->notify = ep;
+
+	status = -ENOMEM;
+
+	/* allocate notification request and buffer */
+	rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!rndis->notify_req)
+		goto fail;
+	rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+	if (!rndis->notify_req->buf)
+		goto fail;
+	rndis->notify_req->length = STATUS_BYTECOUNT;
+	rndis->notify_req->context = rndis;
+	rndis->notify_req->complete = rndis_response_complete;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
+	hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
+	hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+
+	ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
+	ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
+	ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
+			eth_ss_function, NULL);
+	if (status)
+		goto fail;
+
+	rndis->port.open = rndis_open;
+	rndis->port.close = rndis_close;
+
+	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
+	rndis_set_host_mac(rndis->params, rndis->ethaddr);
+
+	if (rndis->manufacturer && rndis->vendorID &&
+			rndis_set_param_vendor(rndis->params, rndis->vendorID,
+					       rndis->manufacturer)) {
+		status = -EINVAL;
+		goto fail_free_descs;
+	}
+
+	/* NOTE:  all that is done without knowing or caring about
+	 * the network link ... which is unavailable to this code
+	 * until we're activated via set_alt().
+	 */
+
+	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_free_descs:
+	usb_free_all_descriptors(f);
+fail:
+	kfree(f->os_desc_table);
+	f->os_desc_n = 0;
+
+	if (rndis->notify_req) {
+		kfree(rndis->notify_req->buf);
+		usb_ep_free_request(rndis->notify, rndis->notify_req);
+	}
+
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
+{
+	struct f_rndis_opts *opts;
+
+	opts = container_of(f, struct f_rndis_opts, func_inst);
+	if (opts->bound)
+		gether_cleanup(netdev_priv(opts->net));
+	else
+		free_netdev(opts->net);
+	opts->borrowed_net = opts->bound = true;
+	opts->net = net;
+}
+EXPORT_SYMBOL_GPL(rndis_borrow_net);
+
+static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_rndis_opts,
+			    func_inst.group);
+}
+
+/* f_rndis_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(rndis);
+
+/* f_rndis_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis);
+
+/* f_rndis_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis);
+
+/* f_rndis_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
+
+/* f_rndis_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
+
+/* f_rndis_opts_class */
+USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, class);
+
+/* f_rndis_opts_subclass */
+USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, subclass);
+
+/* f_rndis_opts_protocol */
+USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol);
+
+static struct configfs_attribute *rndis_attrs[] = {
+	&rndis_opts_attr_dev_addr,
+	&rndis_opts_attr_host_addr,
+	&rndis_opts_attr_qmult,
+	&rndis_opts_attr_ifname,
+	&rndis_opts_attr_class,
+	&rndis_opts_attr_subclass,
+	&rndis_opts_attr_protocol,
+	NULL,
+};
+
+static const struct config_item_type rndis_func_type = {
+	.ct_item_ops	= &rndis_item_ops,
+	.ct_attrs	= rndis_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void rndis_free_inst(struct usb_function_instance *f)
+{
+	struct f_rndis_opts *opts;
+
+	opts = container_of(f, struct f_rndis_opts, func_inst);
+	if (!opts->borrowed_net) {
+		if (opts->bound)
+			gether_cleanup(netdev_priv(opts->net));
+		else
+			free_netdev(opts->net);
+	}
+
+	kfree(opts->rndis_interf_group);	/* single VLA chunk */
+	kfree(opts);
+}
+
+static struct usb_function_instance *rndis_alloc_inst(void)
+{
+	struct f_rndis_opts *opts;
+	struct usb_os_desc *descs[1];
+	char *names[1];
+	struct config_group *rndis_interf_group;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = rndis_free_inst;
+	opts->net = gether_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+	INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
+
+	opts->class = rndis_iad_descriptor.bFunctionClass;
+	opts->subclass = rndis_iad_descriptor.bFunctionSubClass;
+	opts->protocol = rndis_iad_descriptor.bFunctionProtocol;
+
+	descs[0] = &opts->rndis_os_desc;
+	names[0] = "rndis";
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &rndis_func_type);
+	rndis_interf_group =
+		usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+					       names, THIS_MODULE);
+	if (IS_ERR(rndis_interf_group)) {
+		rndis_free_inst(&opts->func_inst);
+		return ERR_CAST(rndis_interf_group);
+	}
+	opts->rndis_interf_group = rndis_interf_group;
+
+	return &opts->func_inst;
+}
+
+static void rndis_free(struct usb_function *f)
+{
+	struct f_rndis *rndis;
+	struct f_rndis_opts *opts;
+
+	rndis = func_to_rndis(f);
+	rndis_deregister(rndis->params);
+	opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+	kfree(rndis);
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+}
+
+static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_rndis		*rndis = func_to_rndis(f);
+
+	kfree(f->os_desc_table);
+	f->os_desc_n = 0;
+	usb_free_all_descriptors(f);
+
+	kfree(rndis->notify_req->buf);
+	usb_ep_free_request(rndis->notify, rndis->notify_req);
+}
+
+static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
+{
+	struct f_rndis	*rndis;
+	struct f_rndis_opts *opts;
+	struct rndis_params *params;
+
+	/* allocate and initialize one new instance */
+	rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
+	if (!rndis)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_rndis_opts, func_inst);
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+
+	gether_get_host_addr_u8(opts->net, rndis->ethaddr);
+	rndis->vendorID = opts->vendor_id;
+	rndis->manufacturer = opts->manufacturer;
+
+	rndis->port.ioport = netdev_priv(opts->net);
+	mutex_unlock(&opts->lock);
+	/* RNDIS activates when the host changes this filter */
+	rndis->port.cdc_filter = 0;
+
+	/* RNDIS has special (and complex) framing */
+	rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
+	rndis->port.wrap = rndis_add_header;
+	rndis->port.unwrap = rndis_rm_hdr;
+
+	rndis->port.func.name = "rndis";
+	/* descriptors are per-instance copies */
+	rndis->port.func.bind = rndis_bind;
+	rndis->port.func.unbind = rndis_unbind;
+	rndis->port.func.set_alt = rndis_set_alt;
+	rndis->port.func.setup = rndis_setup;
+	rndis->port.func.disable = rndis_disable;
+	rndis->port.func.free_func = rndis_free;
+
+	params = rndis_register(rndis_response_available, rndis);
+	if (IS_ERR(params)) {
+		kfree(rndis);
+		return ERR_CAST(params);
+	}
+	rndis->params = params;
+
+	return &rndis->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
new file mode 100644
index 0000000..c860f30
--- /dev/null
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_serial.c - generic USB serial function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This function packages a simple "generic serial" port with no real
+ * control mechanisms, just raw data transfer over two bulk endpoints.
+ *
+ * Because it's not standardized, this isn't as interoperable as the
+ * CDC ACM driver.  However, for many purposes it's just as functional
+ * if you can arrange appropriate host side drivers.
+ */
+
+struct f_gser {
+	struct gserial			port;
+	u8				data_id;
+	u8				port_num;
+};
+
+static inline struct f_gser *func_to_gser(struct usb_function *f)
+{
+	return container_of(f, struct f_gser, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor gser_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor gser_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor gser_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *gser_fs_function[] = {
+	(struct usb_descriptor_header *) &gser_interface_desc,
+	(struct usb_descriptor_header *) &gser_fs_in_desc,
+	(struct usb_descriptor_header *) &gser_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor gser_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor gser_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *gser_hs_function[] = {
+	(struct usb_descriptor_header *) &gser_interface_desc,
+	(struct usb_descriptor_header *) &gser_hs_in_desc,
+	(struct usb_descriptor_header *) &gser_hs_out_desc,
+	NULL,
+};
+
+static struct usb_endpoint_descriptor gser_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor gser_ss_out_desc = {
+	.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 gser_ss_bulk_comp_desc = {
+	.bLength =              sizeof gser_ss_bulk_comp_desc,
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *gser_ss_function[] = {
+	(struct usb_descriptor_header *) &gser_interface_desc,
+	(struct usb_descriptor_header *) &gser_ss_in_desc,
+	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *) &gser_ss_out_desc,
+	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string gser_string_defs[] = {
+	[0].s = "Generic Serial",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings gser_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		gser_string_defs,
+};
+
+static struct usb_gadget_strings *gser_strings[] = {
+	&gser_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_gser		*gser = func_to_gser(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	/* we know alt == 0, so this is an activation or a reset */
+
+	if (gser->port.in->enabled) {
+		dev_dbg(&cdev->gadget->dev,
+			"reset generic ttyGS%d\n", gser->port_num);
+		gserial_disconnect(&gser->port);
+	}
+	if (!gser->port.in->desc || !gser->port.out->desc) {
+		dev_dbg(&cdev->gadget->dev,
+			"activate generic ttyGS%d\n", gser->port_num);
+		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;
+}
+
+static void gser_disable(struct usb_function *f)
+{
+	struct f_gser	*gser = func_to_gser(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	dev_dbg(&cdev->gadget->dev,
+		"generic ttyGS%d deactivated\n", gser->port_num);
+	gserial_disconnect(&gser->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int gser_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_gser		*gser = func_to_gser(f);
+	int			status;
+	struct usb_ep		*ep;
+
+	/* REVISIT might want instance-specific strings to help
+	 * distinguish instances ...
+	 */
+
+	/* maybe allocate device-global string ID */
+	if (gser_string_defs[0].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		gser_string_defs[0].id = status;
+	}
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	gser->data_id = status;
+	gser_interface_desc.bInterfaceNumber = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
+	if (!ep)
+		goto fail;
+	gser->port.in = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
+	if (!ep)
+		goto fail;
+	gser->port.out = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
+	gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+
+	gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
+	gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
+			gser_ss_function, NULL);
+	if (status)
+		goto fail;
+	dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+		gser->port_num,
+		gadget_is_superspeed(c->cdev->gadget) ? "super" :
+		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+		gser->port.in->name, gser->port.out->name);
+	return 0;
+
+fail:
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_serial_opts,
+			    func_inst.group);
+}
+
+static void serial_attr_release(struct config_item *item)
+{
+	struct f_serial_opts *opts = to_f_serial_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations serial_item_ops = {
+	.release	= serial_attr_release,
+};
+
+static ssize_t f_serial_port_num_show(struct config_item *item, char *page)
+{
+	return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
+}
+
+CONFIGFS_ATTR_RO(f_serial_, port_num);
+
+static struct configfs_attribute *acm_attrs[] = {
+	&f_serial_attr_port_num,
+	NULL,
+};
+
+static const struct config_item_type serial_func_type = {
+	.ct_item_ops	= &serial_item_ops,
+	.ct_attrs	= acm_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void gser_free_inst(struct usb_function_instance *f)
+{
+	struct f_serial_opts *opts;
+
+	opts = container_of(f, struct f_serial_opts, func_inst);
+	gserial_free_line(opts->port_num);
+	kfree(opts);
+}
+
+static struct usb_function_instance *gser_alloc_inst(void)
+{
+	struct f_serial_opts *opts;
+	int ret;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.free_func_inst = gser_free_inst;
+	ret = gserial_alloc_line(&opts->port_num);
+	if (ret) {
+		kfree(opts);
+		return ERR_PTR(ret);
+	}
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &serial_func_type);
+
+	return &opts->func_inst;
+}
+
+static void gser_free(struct usb_function *f)
+{
+	struct f_gser *serial;
+
+	serial = func_to_gser(f);
+	kfree(serial);
+}
+
+static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gser_alloc(struct usb_function_instance *fi)
+{
+	struct f_gser	*gser;
+	struct f_serial_opts *opts;
+
+	/* allocate and initialize one new instance */
+	gser = kzalloc(sizeof(*gser), GFP_KERNEL);
+	if (!gser)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_serial_opts, func_inst);
+
+	gser->port_num = opts->port_num;
+
+	gser->port.func.name = "gser";
+	gser->port.func.strings = gser_strings;
+	gser->port.func.bind = gser_bind;
+	gser->port.func.unbind = gser_unbind;
+	gser->port.func.set_alt = gser_set_alt;
+	gser->port.func.disable = gser_disable;
+	gser->port.func.free_func = gser_free;
+
+	return &gser->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Al Borchers");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
new file mode 100644
index 0000000..9cdef10
--- /dev/null
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -0,0 +1,1286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_sourcesink.c - USB peripheral source/sink configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include <linux/err.h>
+
+#include "g_zero.h"
+#include "u_f.h"
+
+/*
+ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
+ * controller drivers.
+ *
+ * This just sinks bulk packets OUT to the peripheral and sources them IN
+ * to the host, optionally with specific data patterns for integrity tests.
+ * As such it supports basic functionality and load tests.
+ *
+ * In terms of control messaging, this supports all the standard requests
+ * plus two that support control-OUT tests.  If the optional "autoresume"
+ * mode is enabled, it provides good functional coverage for the "USBCV"
+ * test harness from USB-IF.
+ */
+struct f_sourcesink {
+	struct usb_function	function;
+
+	struct usb_ep		*in_ep;
+	struct usb_ep		*out_ep;
+	struct usb_ep		*iso_in_ep;
+	struct usb_ep		*iso_out_ep;
+	int			cur_alt;
+
+	unsigned pattern;
+	unsigned isoc_interval;
+	unsigned isoc_maxpacket;
+	unsigned isoc_mult;
+	unsigned isoc_maxburst;
+	unsigned buflen;
+	unsigned bulk_qlen;
+	unsigned iso_qlen;
+};
+
+static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
+{
+	return container_of(f, struct f_sourcesink, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor source_sink_intf_alt0 = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface		= DYNAMIC */
+};
+
+static struct usb_interface_descriptor source_sink_intf_alt1 = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	4,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface		= DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1023),
+	.bInterval =		4,
+};
+
+static struct usb_endpoint_descriptor fs_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1023),
+	.bInterval =		4,
+};
+
+static struct usb_descriptor_header *fs_source_sink_descs[] = {
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
+	(struct usb_descriptor_header *) &fs_sink_desc,
+	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define FS_ALT_IFC_1_OFFSET	3
+	(struct usb_descriptor_header *) &fs_sink_desc,
+	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &fs_iso_sink_desc,
+	(struct usb_descriptor_header *) &fs_iso_source_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+static struct usb_endpoint_descriptor hs_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+static struct usb_descriptor_header *hs_source_sink_descs[] = {
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
+	(struct usb_descriptor_header *) &hs_source_desc,
+	(struct usb_descriptor_header *) &hs_sink_desc,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define HS_ALT_IFC_1_OFFSET	3
+	(struct usb_descriptor_header *) &hs_source_desc,
+	(struct usb_descriptor_header *) &hs_sink_desc,
+	(struct usb_descriptor_header *) &hs_iso_source_desc,
+	(struct usb_descriptor_header *) &hs_iso_sink_desc,
+	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),
+};
+
+static 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),
+};
+
+static 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_endpoint_descriptor ss_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
+	.bLength =		USB_DT_SS_EP_COMP_SIZE,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	.bMaxBurst =		0,
+	.bmAttributes =		0,
+	.wBytesPerInterval =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
+	.bLength =		USB_DT_SS_EP_COMP_SIZE,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	.bMaxBurst =		0,
+	.bmAttributes =		0,
+	.wBytesPerInterval =	cpu_to_le16(1024),
+};
+
+static struct usb_descriptor_header *ss_source_sink_descs[] = {
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
+	(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,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define SS_ALT_IFC_1_OFFSET	5
+	(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,
+	(struct usb_descriptor_header *) &ss_iso_source_desc,
+	(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
+	(struct usb_descriptor_header *) &ss_iso_sink_desc,
+	(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
+	NULL,
+};
+
+/* function-specific strings: */
+
+static struct usb_string strings_sourcesink[] = {
+	[0].s = "source and sink data",
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sourcesink = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_sourcesink,
+};
+
+static struct usb_gadget_strings *sourcesink_strings[] = {
+	&stringtab_sourcesink,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
+{
+	return alloc_ep_req(ep, len);
+}
+
+static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
+{
+	int			value;
+
+	value = usb_ep_disable(ep);
+	if (value < 0)
+		DBG(cdev, "disable %s --> %d\n", ep->name, value);
+}
+
+void disable_endpoints(struct usb_composite_dev *cdev,
+		struct usb_ep *in, struct usb_ep *out,
+		struct usb_ep *iso_in, struct usb_ep *iso_out)
+{
+	disable_ep(cdev, in);
+	disable_ep(cdev, out);
+	if (iso_in)
+		disable_ep(cdev, iso_in);
+	if (iso_out)
+		disable_ep(cdev, iso_out);
+}
+
+static int
+sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_sourcesink	*ss = func_to_ss(f);
+	int	id;
+	int ret;
+
+	/* allocate interface ID(s) */
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	source_sink_intf_alt0.bInterfaceNumber = id;
+	source_sink_intf_alt1.bInterfaceNumber = id;
+
+	/* allocate bulk endpoints */
+	ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
+	if (!ss->in_ep) {
+autoconf_fail:
+		ERROR(cdev, "%s: can't autoconfigure on %s\n",
+			f->name, cdev->gadget->name);
+		return -ENODEV;
+	}
+
+	ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
+	if (!ss->out_ep)
+		goto autoconf_fail;
+
+	/* sanity check the isoc module parameters */
+	if (ss->isoc_interval < 1)
+		ss->isoc_interval = 1;
+	if (ss->isoc_interval > 16)
+		ss->isoc_interval = 16;
+	if (ss->isoc_mult > 2)
+		ss->isoc_mult = 2;
+	if (ss->isoc_maxburst > 15)
+		ss->isoc_maxburst = 15;
+
+	/* fill in the FS isoc descriptors from the module parameters */
+	fs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
+						1023 : ss->isoc_maxpacket;
+	fs_iso_source_desc.bInterval = ss->isoc_interval;
+	fs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
+						1023 : ss->isoc_maxpacket;
+	fs_iso_sink_desc.bInterval = ss->isoc_interval;
+
+	/* allocate iso endpoints */
+	ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
+	if (!ss->iso_in_ep)
+		goto no_iso;
+
+	ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
+	if (!ss->iso_out_ep) {
+		usb_ep_autoconfig_release(ss->iso_in_ep);
+		ss->iso_in_ep = NULL;
+no_iso:
+		/*
+		 * We still want to work even if the UDC doesn't have isoc
+		 * endpoints, so null out the alt interface that contains
+		 * them and continue.
+		 */
+		fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
+		hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
+		ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
+	}
+
+	if (ss->isoc_maxpacket > 1024)
+		ss->isoc_maxpacket = 1024;
+
+	/* support high speed hardware */
+	hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+	hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+
+	/*
+	 * Fill in the HS isoc descriptors from the module parameters.
+	 * We assume that the user knows what they are doing and won't
+	 * give parameters that their UDC doesn't support.
+	 */
+	hs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
+	hs_iso_source_desc.wMaxPacketSize |= ss->isoc_mult << 11;
+	hs_iso_source_desc.bInterval = ss->isoc_interval;
+	hs_iso_source_desc.bEndpointAddress =
+		fs_iso_source_desc.bEndpointAddress;
+
+	hs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
+	hs_iso_sink_desc.wMaxPacketSize |= ss->isoc_mult << 11;
+	hs_iso_sink_desc.bInterval = ss->isoc_interval;
+	hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
+
+	/* support super speed hardware */
+	ss_source_desc.bEndpointAddress =
+		fs_source_desc.bEndpointAddress;
+	ss_sink_desc.bEndpointAddress =
+		fs_sink_desc.bEndpointAddress;
+
+	/*
+	 * Fill in the SS isoc descriptors from the module parameters.
+	 * We assume that the user knows what they are doing and won't
+	 * give parameters that their UDC doesn't support.
+	 */
+	ss_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
+	ss_iso_source_desc.bInterval = ss->isoc_interval;
+	ss_iso_source_comp_desc.bmAttributes = ss->isoc_mult;
+	ss_iso_source_comp_desc.bMaxBurst = ss->isoc_maxburst;
+	ss_iso_source_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
+		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
+	ss_iso_source_desc.bEndpointAddress =
+		fs_iso_source_desc.bEndpointAddress;
+
+	ss_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket;
+	ss_iso_sink_desc.bInterval = ss->isoc_interval;
+	ss_iso_sink_comp_desc.bmAttributes = ss->isoc_mult;
+	ss_iso_sink_comp_desc.bMaxBurst = ss->isoc_maxburst;
+	ss_iso_sink_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
+		(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
+	ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, fs_source_sink_descs,
+			hs_source_sink_descs, ss_source_sink_descs, NULL);
+	if (ret)
+		return ret;
+
+	DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
+	    (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,
+			ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
+			ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
+	return 0;
+}
+
+static void
+sourcesink_free_func(struct usb_function *f)
+{
+	struct f_ss_opts *opts;
+
+	opts = container_of(f->fi, struct f_ss_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	opts->refcnt--;
+	mutex_unlock(&opts->lock);
+
+	usb_free_all_descriptors(f);
+	kfree(func_to_ss(f));
+}
+
+/* optionally require specific source/sink data patterns  */
+static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
+{
+	unsigned		i;
+	u8			*buf = req->buf;
+	struct usb_composite_dev *cdev = ss->function.config->cdev;
+	int max_packet_size = le16_to_cpu(ss->out_ep->desc->wMaxPacketSize);
+
+	if (ss->pattern == 2)
+		return 0;
+
+	for (i = 0; i < req->actual; i++, buf++) {
+		switch (ss->pattern) {
+
+		/* all-zeroes has no synchronization issues */
+		case 0:
+			if (*buf == 0)
+				continue;
+			break;
+
+		/* "mod63" stays in sync with short-terminated transfers,
+		 * OR otherwise when host and gadget agree on how large
+		 * each usb transfer request should be.  Resync is done
+		 * with set_interface or set_config.  (We *WANT* it to
+		 * get quickly out of sync if controllers or their drivers
+		 * stutter for any reason, including buffer duplication...)
+		 */
+		case 1:
+			if (*buf == (u8)((i % max_packet_size) % 63))
+				continue;
+			break;
+		}
+		ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
+		usb_ep_set_halt(ss->out_ep);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned	i;
+	u8		*buf = req->buf;
+	int max_packet_size = le16_to_cpu(ep->desc->wMaxPacketSize);
+	struct f_sourcesink *ss = ep->driver_data;
+
+	switch (ss->pattern) {
+	case 0:
+		memset(req->buf, 0, req->length);
+		break;
+	case 1:
+		for  (i = 0; i < req->length; i++)
+			*buf++ = (u8) ((i % max_packet_size) % 63);
+		break;
+	case 2:
+		break;
+	}
+}
+
+static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usb_composite_dev	*cdev;
+	struct f_sourcesink		*ss = ep->driver_data;
+	int				status = req->status;
+
+	/* driver_data will be null if ep has been disabled */
+	if (!ss)
+		return;
+
+	cdev = ss->function.config->cdev;
+
+	switch (status) {
+
+	case 0:				/* normal completion? */
+		if (ep == ss->out_ep) {
+			check_read_data(ss, req);
+			if (ss->pattern != 2)
+				memset(req->buf, 0x55, req->length);
+		}
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNABORTED:		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		if (ep == ss->out_ep)
+			check_read_data(ss, req);
+		free_ep_req(ep, req);
+		return;
+
+	case -EOVERFLOW:		/* buffer overrun on read means that
+					 * we didn't provide a big enough
+					 * buffer.
+					 */
+	default:
+#if 1
+		DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+#endif
+	case -EREMOTEIO:		/* short read */
+		break;
+	}
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(cdev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt(ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
+		bool is_iso, int speed)
+{
+	struct usb_ep		*ep;
+	struct usb_request	*req;
+	int			i, size, qlen, status = 0;
+
+	if (is_iso) {
+		switch (speed) {
+		case USB_SPEED_SUPER:
+			size = ss->isoc_maxpacket *
+					(ss->isoc_mult + 1) *
+					(ss->isoc_maxburst + 1);
+			break;
+		case USB_SPEED_HIGH:
+			size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
+			break;
+		default:
+			size = ss->isoc_maxpacket > 1023 ?
+					1023 : ss->isoc_maxpacket;
+			break;
+		}
+		ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
+		qlen = ss->iso_qlen;
+	} else {
+		ep = is_in ? ss->in_ep : ss->out_ep;
+		qlen = ss->bulk_qlen;
+		size = ss->buflen;
+	}
+
+	for (i = 0; i < qlen; i++) {
+		req = ss_alloc_ep_req(ep, size);
+		if (!req)
+			return -ENOMEM;
+
+		req->complete = source_sink_complete;
+		if (is_in)
+			reinit_write_data(ep, req);
+		else if (ss->pattern != 2)
+			memset(req->buf, 0x55, req->length);
+
+		status = usb_ep_queue(ep, req, GFP_ATOMIC);
+		if (status) {
+			struct usb_composite_dev	*cdev;
+
+			cdev = ss->function.config->cdev;
+			ERROR(cdev, "start %s%s %s --> %d\n",
+			      is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+			      ep->name, status);
+			free_ep_req(ep, req);
+			return status;
+		}
+	}
+
+	return status;
+}
+
+static void disable_source_sink(struct f_sourcesink *ss)
+{
+	struct usb_composite_dev	*cdev;
+
+	cdev = ss->function.config->cdev;
+	disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
+			ss->iso_out_ep);
+	VDBG(cdev, "%s disabled\n", ss->function.name);
+}
+
+static int
+enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
+		int alt)
+{
+	int					result = 0;
+	int					speed = cdev->gadget->speed;
+	struct usb_ep				*ep;
+
+	/* one bulk endpoint writes (sources) zeroes IN (to the host) */
+	ep = ss->in_ep;
+	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;
+
+	result = source_sink_start_ep(ss, true, false, speed);
+	if (result < 0) {
+fail:
+		ep = ss->in_ep;
+		usb_ep_disable(ep);
+		return result;
+	}
+
+	/* one bulk endpoint reads (sinks) anything OUT (from the host) */
+	ep = ss->out_ep;
+	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;
+
+	result = source_sink_start_ep(ss, false, false, speed);
+	if (result < 0) {
+fail2:
+		ep = ss->out_ep;
+		usb_ep_disable(ep);
+		goto fail;
+	}
+
+	if (alt == 0)
+		goto out;
+
+	/* one iso endpoint writes (sources) zeroes IN (to the host) */
+	ep = ss->iso_in_ep;
+	if (ep) {
+		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+		if (result)
+			goto fail2;
+		result = usb_ep_enable(ep);
+		if (result < 0)
+			goto fail2;
+		ep->driver_data = ss;
+
+		result = source_sink_start_ep(ss, true, true, speed);
+		if (result < 0) {
+fail3:
+			ep = ss->iso_in_ep;
+			if (ep)
+				usb_ep_disable(ep);
+			goto fail2;
+		}
+	}
+
+	/* one iso endpoint reads (sinks) anything OUT (from the host) */
+	ep = ss->iso_out_ep;
+	if (ep) {
+		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+		if (result)
+			goto fail3;
+		result = usb_ep_enable(ep);
+		if (result < 0)
+			goto fail3;
+		ep->driver_data = ss;
+
+		result = source_sink_start_ep(ss, false, true, speed);
+		if (result < 0) {
+			usb_ep_disable(ep);
+			goto fail3;
+		}
+	}
+out:
+	ss->cur_alt = alt;
+
+	DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
+	return result;
+}
+
+static int sourcesink_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct f_sourcesink		*ss = func_to_ss(f);
+	struct usb_composite_dev	*cdev = f->config->cdev;
+
+	disable_source_sink(ss);
+	return enable_source_sink(cdev, ss, alt);
+}
+
+static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_sourcesink		*ss = func_to_ss(f);
+
+	return ss->cur_alt;
+}
+
+static void sourcesink_disable(struct usb_function *f)
+{
+	struct f_sourcesink	*ss = func_to_ss(f);
+
+	disable_source_sink(ss);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sourcesink_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_configuration        *c = f->config;
+	struct usb_request	*req = c->cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	req->length = USB_COMP_EP0_BUFSIZ;
+
+	/* composite driver infrastructure handles everything except
+	 * the two control test requests.
+	 */
+	switch (ctrl->bRequest) {
+
+	/*
+	 * These are the same vendor-specific requests supported by
+	 * Intel's USB 2.0 compliance test devices.  We exceed that
+	 * device spec by allowing multiple-packet requests.
+	 *
+	 * NOTE:  the Control-OUT data stays in req->buf ... better
+	 * would be copying it into a scratch buffer, so that other
+	 * requests may safely intervene.
+	 */
+	case 0x5b:	/* control WRITE test -- fill the buffer */
+		if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
+			goto unknown;
+		if (w_value || w_index)
+			break;
+		/* just read that many bytes into the buffer */
+		if (w_length > req->length)
+			break;
+		value = w_length;
+		break;
+	case 0x5c:	/* control READ test -- return the buffer */
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
+			goto unknown;
+		if (w_value || w_index)
+			break;
+		/* expect those bytes are still in the buffer; send back */
+		if (w_length > req->length)
+			break;
+		value = w_length;
+		break;
+
+	default:
+unknown:
+		VDBG(c->cdev,
+			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(c->cdev, "source/sink response, err %d\n",
+					value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static struct usb_function *source_sink_alloc_func(
+		struct usb_function_instance *fi)
+{
+	struct f_sourcesink     *ss;
+	struct f_ss_opts	*ss_opts;
+
+	ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+	if (!ss)
+		return NULL;
+
+	ss_opts =  container_of(fi, struct f_ss_opts, func_inst);
+
+	mutex_lock(&ss_opts->lock);
+	ss_opts->refcnt++;
+	mutex_unlock(&ss_opts->lock);
+
+	ss->pattern = ss_opts->pattern;
+	ss->isoc_interval = ss_opts->isoc_interval;
+	ss->isoc_maxpacket = ss_opts->isoc_maxpacket;
+	ss->isoc_mult = ss_opts->isoc_mult;
+	ss->isoc_maxburst = ss_opts->isoc_maxburst;
+	ss->buflen = ss_opts->bulk_buflen;
+	ss->bulk_qlen = ss_opts->bulk_qlen;
+	ss->iso_qlen = ss_opts->iso_qlen;
+
+	ss->function.name = "source/sink";
+	ss->function.bind = sourcesink_bind;
+	ss->function.set_alt = sourcesink_set_alt;
+	ss->function.get_alt = sourcesink_get_alt;
+	ss->function.disable = sourcesink_disable;
+	ss->function.setup = sourcesink_setup;
+	ss->function.strings = sourcesink_strings;
+
+	ss->function.free_func = sourcesink_free_func;
+
+	return &ss->function;
+}
+
+static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_ss_opts,
+			    func_inst.group);
+}
+
+static void ss_attr_release(struct config_item *item)
+{
+	struct f_ss_opts *ss_opts = to_f_ss_opts(item);
+
+	usb_put_function_instance(&ss_opts->func_inst);
+}
+
+static struct configfs_item_operations ss_item_ops = {
+	.release		= ss_attr_release,
+};
+
+static ssize_t f_ss_opts_pattern_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->pattern);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_pattern_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num != 0 && num != 1 && num != 2) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	opts->pattern = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, pattern);
+
+static ssize_t f_ss_opts_isoc_interval_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->isoc_interval);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_isoc_interval_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 16) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	opts->isoc_interval = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, isoc_interval);
+
+static ssize_t f_ss_opts_isoc_maxpacket_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->isoc_maxpacket);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxpacket_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u16 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou16(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 1024) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	opts->isoc_maxpacket = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, isoc_maxpacket);
+
+static ssize_t f_ss_opts_isoc_mult_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->isoc_mult);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_isoc_mult_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 2) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	opts->isoc_mult = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, isoc_mult);
+
+static ssize_t f_ss_opts_isoc_maxburst_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->isoc_maxburst);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxburst_store(struct config_item *item,
+				       const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u8 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou8(page, 0, &num);
+	if (ret)
+		goto end;
+
+	if (num > 15) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	opts->isoc_maxburst = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
+
+static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->bulk_buflen);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_bulk_buflen_store(struct config_item *item,
+					   const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->bulk_buflen = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
+
+static ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->bulk_qlen);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
+					   const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->bulk_qlen = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
+
+static ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%u\n", opts->iso_qlen);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
+					   const char *page, size_t len)
+{
+	struct f_ss_opts *opts = to_f_ss_opts(item);
+	int ret;
+	u32 num;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = kstrtou32(page, 0, &num);
+	if (ret)
+		goto end;
+
+	opts->iso_qlen = num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, iso_qlen);
+
+static struct configfs_attribute *ss_attrs[] = {
+	&f_ss_opts_attr_pattern,
+	&f_ss_opts_attr_isoc_interval,
+	&f_ss_opts_attr_isoc_maxpacket,
+	&f_ss_opts_attr_isoc_mult,
+	&f_ss_opts_attr_isoc_maxburst,
+	&f_ss_opts_attr_bulk_buflen,
+	&f_ss_opts_attr_bulk_qlen,
+	&f_ss_opts_attr_iso_qlen,
+	NULL,
+};
+
+static const struct config_item_type ss_func_type = {
+	.ct_item_ops    = &ss_item_ops,
+	.ct_attrs	= ss_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static void source_sink_free_instance(struct usb_function_instance *fi)
+{
+	struct f_ss_opts *ss_opts;
+
+	ss_opts = container_of(fi, struct f_ss_opts, func_inst);
+	kfree(ss_opts);
+}
+
+static struct usb_function_instance *source_sink_alloc_inst(void)
+{
+	struct f_ss_opts *ss_opts;
+
+	ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
+	if (!ss_opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&ss_opts->lock);
+	ss_opts->func_inst.free_func_inst = source_sink_free_instance;
+	ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
+	ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
+	ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+	ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
+	ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
+
+	config_group_init_type_name(&ss_opts->func_inst.group, "",
+				    &ss_func_type);
+
+	return &ss_opts->func_inst;
+}
+DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
+		source_sink_alloc_func);
+
+static int __init sslb_modinit(void)
+{
+	int ret;
+
+	ret = usb_function_register(&SourceSinkusb_func);
+	if (ret)
+		return ret;
+	ret = lb_modinit();
+	if (ret)
+		usb_function_unregister(&SourceSinkusb_func);
+	return ret;
+}
+static void __exit sslb_modexit(void)
+{
+	usb_function_unregister(&SourceSinkusb_func);
+	lb_modexit();
+}
+module_init(sslb_modinit);
+module_exit(sslb_modexit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
new file mode 100644
index 0000000..4d94525
--- /dev/null
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_subset.c -- "CDC Subset" Ethernet link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_gether.h"
+
+/*
+ * This function packages a simple "CDC Subset" Ethernet port with no real
+ * control mechanisms; just raw data transfer over two bulk endpoints.
+ * The data transfer model is exactly that of CDC Ethernet, which is
+ * why we call it the "CDC Subset".
+ *
+ * Because it's not standardized, this has some interoperability issues.
+ * They mostly relate to driver binding, since the data transfer model is
+ * so simple (CDC Ethernet).  The original versions of this protocol used
+ * specific product/vendor IDs:  byteswapped IDs for Digital Equipment's
+ * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
+ * daughtercards with USB peripheral connectors.  (It was used more often
+ * with other boards, using the Itsy identifiers.)  Linux hosts recognized
+ * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
+ * and one interface.
+ *
+ * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
+ * "SAFE", which happens to have a mode which is identical to the "CDC
+ * Subset" in terms of data transfer and lack of control model.  This was
+ * adopted by later Sharp Zaurus models, and by some other software which
+ * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
+ *
+ * Because Microsoft's RNDIS drivers are far from robust, we added a few
+ * descriptors to the CDC Subset code, making this code look like a SAFE
+ * implementation.  This lets you use MCCI's host side MS-Windows drivers
+ * if you get fed up with RNDIS.  It also makes it easier for composite
+ * drivers to work, since they can use class based binding instead of
+ * caring about specific product and vendor IDs.
+ */
+
+struct f_gether {
+	struct gether			port;
+
+	char				ethaddr[14];
+};
+
+static inline struct f_gether *func_to_geth(struct usb_function *f)
+{
+	return container_of(f, struct f_gether, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Simple" CDC-subset option is a simple vendor-neutral model that most
+ * full speed controllers can handle:  one interface, two bulk endpoints.
+ * To assist host side drivers, we fancy it up a bit, and add descriptors so
+ * some host side drivers will understand it as a "SAFE" variant.
+ *
+ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
+ * Data endpoints live in the control interface, there's no data interface.
+ * And it's not used to talk to a cell phone radio.
+ */
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor subset_data_intf = {
+	.bLength =		sizeof subset_data_intf,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =      USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_MDLM,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mdlm_header_desc = {
+	.bLength =		sizeof mdlm_header_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_mdlm_desc mdlm_desc = {
+	.bLength =		sizeof mdlm_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MDLM_TYPE,
+
+	.bcdVersion =		cpu_to_le16(0x0100),
+	.bGUID = {
+		0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+		0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+	},
+};
+
+/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
+ * can't really use its struct.  All we do here is say that we're using
+ * the submode of "SAFE" which directly matches the CDC Subset.
+ */
+static u8 mdlm_detail_desc[] = {
+	6,
+	USB_DT_CS_INTERFACE,
+	USB_CDC_MDLM_DETAIL_TYPE,
+
+	0,	/* "SAFE" */
+	0,	/* network control capabilities (none) */
+	0,	/* network data capabilities ("raw" encapsulation) */
+};
+
+static struct usb_cdc_ether_desc ether_desc = {
+	.bLength =		sizeof ether_desc,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
+
+	/* this descriptor actually adds value, surprise! */
+	/* .iMACAddress = DYNAMIC */
+	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
+	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
+	.wNumberMCFilters =	cpu_to_le16(0),
+	.bNumberPowerFilters =	0,
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_subset_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_subset_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_eth_function[] = {
+	(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 *) &fs_subset_in_desc,
+	(struct usb_descriptor_header *) &fs_subset_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_subset_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_subset_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_eth_function[] = {
+	(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 *) &hs_subset_in_desc,
+	(struct usb_descriptor_header *) &hs_subset_out_desc,
+	NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_subset_in_desc = {
+	.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 = {
+	.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 = {
+	.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[] = {
+	(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[] = {
+	[0].s = "CDC Ethernet Subset/SAFE",
+	[1].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings geth_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		geth_string_defs,
+};
+
+static struct usb_gadget_strings *geth_strings[] = {
+	&geth_string_table,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_gether		*geth = func_to_geth(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct net_device	*net;
+
+	/* we know alt == 0, so this is an activation or a reset */
+
+	if (geth->port.in_ep->enabled) {
+		DBG(cdev, "reset cdc subset\n");
+		gether_disconnect(&geth->port);
+	}
+
+	DBG(cdev, "init + activate cdc subset\n");
+	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 PTR_ERR_OR_ZERO(net);
+}
+
+static void geth_disable(struct usb_function *f)
+{
+	struct f_gether	*geth = func_to_geth(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	DBG(cdev, "net deactivated\n");
+	gether_disconnect(&geth->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int
+geth_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_gether		*geth = func_to_geth(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep;
+
+	struct f_gether_opts	*gether_opts;
+
+	gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
+
+	/*
+	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+	 * configurations are bound in sequence with list_for_each_entry,
+	 * in each configuration its functions are bound in sequence
+	 * with list_for_each_entry, so we assume no race condition
+	 * with regard to gether_opts->bound access
+	 */
+	if (!gether_opts->bound) {
+		mutex_lock(&gether_opts->lock);
+		gether_set_gadget(gether_opts->net, cdev->gadget);
+		status = gether_register_netdev(gether_opts->net);
+		mutex_unlock(&gether_opts->lock);
+		if (status)
+			return status;
+		gether_opts->bound = true;
+	}
+
+	us = usb_gstrings_attach(cdev, geth_strings,
+				 ARRAY_SIZE(geth_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+
+	subset_data_intf.iInterface = us[0].id;
+	ether_desc.iMACAddress = us[1].id;
+
+	/* allocate instance-specific interface IDs */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	subset_data_intf.bInterfaceNumber = status;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
+	if (!ep)
+		goto fail;
+	geth->port.in_ep = ep;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
+	if (!ep)
+		goto fail;
+	geth->port.out_ep = ep;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
+	hs_subset_out_desc.bEndpointAddress =
+		fs_subset_out_desc.bEndpointAddress;
+
+	ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
+	ss_subset_out_desc.bEndpointAddress =
+		fs_subset_out_desc.bEndpointAddress;
+
+	status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
+			ss_eth_function, NULL);
+	if (status)
+		goto fail;
+
+	/* NOTE:  all that is done without knowing or caring about
+	 * the network link ... which is unavailable to this code
+	 * until we're activated via set_alt().
+	 */
+
+	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:
+	ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+	return status;
+}
+
+static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_gether_opts,
+			    func_inst.group);
+}
+
+/* f_gether_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(gether);
+
+/* f_gether_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
+
+/* f_gether_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
+
+/* f_gether_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
+
+/* f_gether_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
+
+static struct configfs_attribute *gether_attrs[] = {
+	&gether_opts_attr_dev_addr,
+	&gether_opts_attr_host_addr,
+	&gether_opts_attr_qmult,
+	&gether_opts_attr_ifname,
+	NULL,
+};
+
+static const struct config_item_type gether_func_type = {
+	.ct_item_ops	= &gether_item_ops,
+	.ct_attrs	= gether_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void geth_free_inst(struct usb_function_instance *f)
+{
+	struct f_gether_opts *opts;
+
+	opts = container_of(f, struct f_gether_opts, func_inst);
+	if (opts->bound)
+		gether_cleanup(netdev_priv(opts->net));
+	else
+		free_netdev(opts->net);
+	kfree(opts);
+}
+
+static struct usb_function_instance *geth_alloc_inst(void)
+{
+	struct f_gether_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = geth_free_inst;
+	opts->net = gether_setup_default();
+	if (IS_ERR(opts->net)) {
+		struct net_device *net = opts->net;
+		kfree(opts);
+		return ERR_CAST(net);
+	}
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &gether_func_type);
+
+	return &opts->func_inst;
+}
+
+static void geth_free(struct usb_function *f)
+{
+	struct f_gether *eth;
+
+	eth = func_to_geth(f);
+	kfree(eth);
+}
+
+static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	geth_string_defs[0].id = 0;
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *geth_alloc(struct usb_function_instance *fi)
+{
+	struct f_gether	*geth;
+	struct f_gether_opts *opts;
+	int status;
+
+	/* allocate and initialize one new instance */
+	geth = kzalloc(sizeof(*geth), GFP_KERNEL);
+	if (!geth)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_gether_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	opts->refcnt++;
+	/* export host's Ethernet address in CDC format */
+	status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
+					  sizeof(geth->ethaddr));
+	if (status < 12) {
+		kfree(geth);
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-EINVAL);
+	}
+	geth_string_defs[1].s = geth->ethaddr;
+
+	geth->port.ioport = netdev_priv(opts->net);
+	mutex_unlock(&opts->lock);
+	geth->port.cdc_filter = DEFAULT_FILTER;
+
+	geth->port.func.name = "cdc_subset";
+	geth->port.func.bind = geth_bind;
+	geth->port.func.unbind = geth_unbind;
+	geth->port.func.set_alt = geth_set_alt;
+	geth->port.func.disable = geth_disable;
+	geth->port.func.free_func = geth_free;
+
+	return &geth->port.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
new file mode 100644
index 0000000..106988a
--- /dev/null
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -0,0 +1,2353 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Target based USB-Gadget
+ *
+ * UAS protocol handling, target callbacks, configfs handling,
+ * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <asm/unaligned.h>
+
+#include "tcm.h"
+#include "u_tcm.h"
+#include "configfs.h"
+
+#define TPG_INSTANCES		1
+
+struct tpg_instance {
+	struct usb_function_instance	*func_inst;
+	struct usbg_tpg			*tpg;
+};
+
+static struct tpg_instance tpg_instances[TPG_INSTANCES];
+
+static DEFINE_MUTEX(tpg_instances_lock);
+
+static inline struct f_uas *to_f_uas(struct usb_function *f)
+{
+	return container_of(f, struct f_uas, function);
+}
+
+/* Start bot.c code */
+
+static int bot_enqueue_cmd_cbw(struct f_uas *fu)
+{
+	int ret;
+
+	if (fu->flags & USBG_BOT_CMD_PEND)
+		return 0;
+
+	ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
+	if (!ret)
+		fu->flags |= USBG_BOT_CMD_PEND;
+	return ret;
+}
+
+static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbg_cmd *cmd = req->context;
+	struct f_uas *fu = cmd->fu;
+
+	transport_generic_free_cmd(&cmd->se_cmd, 0);
+	if (req->status < 0) {
+		pr_err("ERR %s(%d)\n", __func__, __LINE__);
+		return;
+	}
+
+	/* CSW completed, wait for next CBW */
+	bot_enqueue_cmd_cbw(fu);
+}
+
+static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd)
+{
+	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+	int ret;
+	unsigned int csw_stat;
+
+	csw_stat = cmd->csw_code;
+	csw->Tag = cmd->bot_tag;
+	csw->Status = csw_stat;
+	fu->bot_status.req->context = cmd;
+	ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC);
+	if (ret)
+		pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+}
+
+static void bot_err_compl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbg_cmd *cmd = req->context;
+	struct f_uas *fu = cmd->fu;
+
+	if (req->status < 0)
+		pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+	if (cmd->data_len) {
+		if (cmd->data_len > ep->maxpacket) {
+			req->length = ep->maxpacket;
+			cmd->data_len -= ep->maxpacket;
+		} else {
+			req->length = cmd->data_len;
+			cmd->data_len = 0;
+		}
+
+		usb_ep_queue(ep, req, GFP_ATOMIC);
+		return;
+	}
+	bot_enqueue_sense_code(fu, cmd);
+}
+
+static void bot_send_bad_status(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+	struct usb_request *req;
+	struct usb_ep *ep;
+
+	csw->Residue = cpu_to_le32(cmd->data_len);
+
+	if (cmd->data_len) {
+		if (cmd->is_read) {
+			ep = fu->ep_in;
+			req = fu->bot_req_in;
+		} else {
+			ep = fu->ep_out;
+			req = fu->bot_req_out;
+		}
+
+		if (cmd->data_len > fu->ep_in->maxpacket) {
+			req->length = ep->maxpacket;
+			cmd->data_len -= ep->maxpacket;
+		} else {
+			req->length = cmd->data_len;
+			cmd->data_len = 0;
+		}
+		req->complete = bot_err_compl;
+		req->context = cmd;
+		req->buf = fu->cmd.buf;
+		usb_ep_queue(ep, req, GFP_KERNEL);
+	} else {
+		bot_enqueue_sense_code(fu, cmd);
+	}
+}
+
+static int bot_send_status(struct usbg_cmd *cmd, bool moved_data)
+{
+	struct f_uas *fu = cmd->fu;
+	struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+	int ret;
+
+	if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) {
+		if (!moved_data && cmd->data_len) {
+			/*
+			 * the host wants to move data, we don't. Fill / empty
+			 * the pipe and then send the csw with reside set.
+			 */
+			cmd->csw_code = US_BULK_STAT_OK;
+			bot_send_bad_status(cmd);
+			return 0;
+		}
+
+		csw->Tag = cmd->bot_tag;
+		csw->Residue = cpu_to_le32(0);
+		csw->Status = US_BULK_STAT_OK;
+		fu->bot_status.req->context = cmd;
+
+		ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_KERNEL);
+		if (ret)
+			pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+	} else {
+		cmd->csw_code = US_BULK_STAT_FAIL;
+		bot_send_bad_status(cmd);
+	}
+	return 0;
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+static int bot_send_status_response(struct usbg_cmd *cmd)
+{
+	bool moved_data = false;
+
+	if (!cmd->is_read)
+		moved_data = true;
+	return bot_send_status(cmd, moved_data);
+}
+
+/* Read request completed, now we have to send the CSW */
+static void bot_read_compl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbg_cmd *cmd = req->context;
+
+	if (req->status < 0)
+		pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+	bot_send_status(cmd, true);
+}
+
+static int bot_send_read_response(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct usb_gadget *gadget = fuas_to_gadget(fu);
+	int ret;
+
+	if (!cmd->data_len) {
+		cmd->csw_code = US_BULK_STAT_PHASE;
+		bot_send_bad_status(cmd);
+		return 0;
+	}
+
+	if (!gadget->sg_supported) {
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		sg_copy_to_buffer(se_cmd->t_data_sg,
+				se_cmd->t_data_nents,
+				cmd->data_buf,
+				se_cmd->data_length);
+
+		fu->bot_req_in->buf = cmd->data_buf;
+	} else {
+		fu->bot_req_in->buf = NULL;
+		fu->bot_req_in->num_sgs = se_cmd->t_data_nents;
+		fu->bot_req_in->sg = se_cmd->t_data_sg;
+	}
+
+	fu->bot_req_in->complete = bot_read_compl;
+	fu->bot_req_in->length = se_cmd->data_length;
+	fu->bot_req_in->context = cmd;
+	ret = usb_ep_queue(fu->ep_in, fu->bot_req_in, GFP_ATOMIC);
+	if (ret)
+		pr_err("%s(%d)\n", __func__, __LINE__);
+	return 0;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *, struct usb_request *);
+static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *);
+
+static int bot_send_write_request(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct usb_gadget *gadget = fuas_to_gadget(fu);
+	int ret;
+
+	init_completion(&cmd->write_complete);
+	cmd->fu = fu;
+
+	if (!cmd->data_len) {
+		cmd->csw_code = US_BULK_STAT_PHASE;
+		return -EINVAL;
+	}
+
+	if (!gadget->sg_supported) {
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		fu->bot_req_out->buf = cmd->data_buf;
+	} else {
+		fu->bot_req_out->buf = NULL;
+		fu->bot_req_out->num_sgs = se_cmd->t_data_nents;
+		fu->bot_req_out->sg = se_cmd->t_data_sg;
+	}
+
+	fu->bot_req_out->complete = usbg_data_write_cmpl;
+	fu->bot_req_out->length = se_cmd->data_length;
+	fu->bot_req_out->context = cmd;
+
+	ret = usbg_prepare_w_request(cmd, fu->bot_req_out);
+	if (ret)
+		goto cleanup;
+	ret = usb_ep_queue(fu->ep_out, fu->bot_req_out, GFP_KERNEL);
+	if (ret)
+		pr_err("%s(%d)\n", __func__, __LINE__);
+
+	wait_for_completion(&cmd->write_complete);
+	target_execute_cmd(se_cmd);
+cleanup:
+	return ret;
+}
+
+static int bot_submit_command(struct f_uas *, void *, unsigned int);
+
+static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_uas *fu = req->context;
+	int ret;
+
+	fu->flags &= ~USBG_BOT_CMD_PEND;
+
+	if (req->status < 0)
+		return;
+
+	ret = bot_submit_command(fu, req->buf, req->actual);
+	if (ret)
+		pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
+}
+
+static int bot_prepare_reqs(struct f_uas *fu)
+{
+	int ret;
+
+	fu->bot_req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+	if (!fu->bot_req_in)
+		goto err;
+
+	fu->bot_req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+	if (!fu->bot_req_out)
+		goto err_out;
+
+	fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+	if (!fu->cmd.req)
+		goto err_cmd;
+
+	fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+	if (!fu->bot_status.req)
+		goto err_sts;
+
+	fu->bot_status.req->buf = &fu->bot_status.csw;
+	fu->bot_status.req->length = US_BULK_CS_WRAP_LEN;
+	fu->bot_status.req->complete = bot_status_complete;
+	fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
+
+	fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
+	if (!fu->cmd.buf)
+		goto err_buf;
+
+	fu->cmd.req->complete = bot_cmd_complete;
+	fu->cmd.req->buf = fu->cmd.buf;
+	fu->cmd.req->length = fu->ep_out->maxpacket;
+	fu->cmd.req->context = fu;
+
+	ret = bot_enqueue_cmd_cbw(fu);
+	if (ret)
+		goto err_queue;
+	return 0;
+err_queue:
+	kfree(fu->cmd.buf);
+	fu->cmd.buf = NULL;
+err_buf:
+	usb_ep_free_request(fu->ep_in, fu->bot_status.req);
+err_sts:
+	usb_ep_free_request(fu->ep_out, fu->cmd.req);
+	fu->cmd.req = NULL;
+err_cmd:
+	usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+	fu->bot_req_out = NULL;
+err_out:
+	usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+	fu->bot_req_in = NULL;
+err:
+	pr_err("BOT: endpoint setup failed\n");
+	return -ENOMEM;
+}
+
+static void bot_cleanup_old_alt(struct f_uas *fu)
+{
+	if (!(fu->flags & USBG_ENABLED))
+		return;
+
+	usb_ep_disable(fu->ep_in);
+	usb_ep_disable(fu->ep_out);
+
+	if (!fu->bot_req_in)
+		return;
+
+	usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+	usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+	usb_ep_free_request(fu->ep_out, fu->cmd.req);
+	usb_ep_free_request(fu->ep_in, fu->bot_status.req);
+
+	kfree(fu->cmd.buf);
+
+	fu->bot_req_in = NULL;
+	fu->bot_req_out = NULL;
+	fu->cmd.req = NULL;
+	fu->bot_status.req = NULL;
+	fu->cmd.buf = NULL;
+}
+
+static void bot_set_alt(struct f_uas *fu)
+{
+	struct usb_function *f = &fu->function;
+	struct usb_gadget *gadget = f->config->cdev->gadget;
+	int ret;
+
+	fu->flags = USBG_IS_BOT;
+
+	config_ep_by_speed(gadget, f, fu->ep_in);
+	ret = usb_ep_enable(fu->ep_in);
+	if (ret)
+		goto err_b_in;
+
+	config_ep_by_speed(gadget, f, fu->ep_out);
+	ret = usb_ep_enable(fu->ep_out);
+	if (ret)
+		goto err_b_out;
+
+	ret = bot_prepare_reqs(fu);
+	if (ret)
+		goto err_wq;
+	fu->flags |= USBG_ENABLED;
+	pr_info("Using the BOT protocol\n");
+	return;
+err_wq:
+	usb_ep_disable(fu->ep_out);
+err_b_out:
+	usb_ep_disable(fu->ep_in);
+err_b_in:
+	fu->flags = USBG_IS_BOT;
+}
+
+static int usbg_bot_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct f_uas *fu = to_f_uas(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+	int luns;
+	u8 *ret_lun;
+
+	switch (ctrl->bRequest) {
+	case US_BULK_GET_MAX_LUN:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS |
+					USB_RECIP_INTERFACE))
+			return -ENOTSUPP;
+
+		if (w_length < 1)
+			return -EINVAL;
+		if (w_value != 0)
+			return -EINVAL;
+		luns = atomic_read(&fu->tpg->tpg_port_count);
+		if (!luns) {
+			pr_err("No LUNs configured?\n");
+			return -EINVAL;
+		}
+		/*
+		 * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be
+		 * accessed. The upper limit is 0xf
+		 */
+		luns--;
+		if (luns > 0xf) {
+			pr_info_once("Limiting the number of luns to 16\n");
+			luns = 0xf;
+		}
+		ret_lun = cdev->req->buf;
+		*ret_lun = luns;
+		cdev->req->length = 1;
+		return usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+
+	case US_BULK_RESET_REQUEST:
+		/* XXX maybe we should remove previous requests for IN + OUT */
+		bot_enqueue_cmd_cbw(fu);
+		return 0;
+	}
+	return -ENOTSUPP;
+}
+
+/* Start uas.c code */
+
+static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
+{
+	/* We have either all three allocated or none */
+	if (!stream->req_in)
+		return;
+
+	usb_ep_free_request(fu->ep_in, stream->req_in);
+	usb_ep_free_request(fu->ep_out, stream->req_out);
+	usb_ep_free_request(fu->ep_status, stream->req_status);
+
+	stream->req_in = NULL;
+	stream->req_out = NULL;
+	stream->req_status = NULL;
+}
+
+static void uasp_free_cmdreq(struct f_uas *fu)
+{
+	usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+	kfree(fu->cmd.buf);
+	fu->cmd.req = NULL;
+	fu->cmd.buf = NULL;
+}
+
+static void uasp_cleanup_old_alt(struct f_uas *fu)
+{
+	int i;
+
+	if (!(fu->flags & USBG_ENABLED))
+		return;
+
+	usb_ep_disable(fu->ep_in);
+	usb_ep_disable(fu->ep_out);
+	usb_ep_disable(fu->ep_status);
+	usb_ep_disable(fu->ep_cmd);
+
+	for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
+		uasp_cleanup_one_stream(fu, &fu->stream[i]);
+	uasp_free_cmdreq(fu);
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
+
+static int uasp_prepare_r_request(struct usbg_cmd *cmd)
+{
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct f_uas *fu = cmd->fu;
+	struct usb_gadget *gadget = fuas_to_gadget(fu);
+	struct uas_stream *stream = cmd->stream;
+
+	if (!gadget->sg_supported) {
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		sg_copy_to_buffer(se_cmd->t_data_sg,
+				se_cmd->t_data_nents,
+				cmd->data_buf,
+				se_cmd->data_length);
+
+		stream->req_in->buf = cmd->data_buf;
+	} else {
+		stream->req_in->buf = NULL;
+		stream->req_in->num_sgs = se_cmd->t_data_nents;
+		stream->req_in->sg = se_cmd->t_data_sg;
+	}
+
+	stream->req_in->complete = uasp_status_data_cmpl;
+	stream->req_in->length = se_cmd->data_length;
+	stream->req_in->context = cmd;
+
+	cmd->state = UASP_SEND_STATUS;
+	return 0;
+}
+
+static void uasp_prepare_status(struct usbg_cmd *cmd)
+{
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct sense_iu *iu = &cmd->sense_iu;
+	struct uas_stream *stream = cmd->stream;
+
+	cmd->state = UASP_QUEUE_COMMAND;
+	iu->iu_id = IU_ID_STATUS;
+	iu->tag = cpu_to_be16(cmd->tag);
+
+	/*
+	 * iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where R U?);
+	 */
+	iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
+	iu->status = se_cmd->scsi_status;
+	stream->req_status->context = cmd;
+	stream->req_status->length = se_cmd->scsi_sense_length + 16;
+	stream->req_status->buf = iu;
+	stream->req_status->complete = uasp_status_data_cmpl;
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbg_cmd *cmd = req->context;
+	struct uas_stream *stream = cmd->stream;
+	struct f_uas *fu = cmd->fu;
+	int ret;
+
+	if (req->status < 0)
+		goto cleanup;
+
+	switch (cmd->state) {
+	case UASP_SEND_DATA:
+		ret = uasp_prepare_r_request(cmd);
+		if (ret)
+			goto cleanup;
+		ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+		break;
+
+	case UASP_RECEIVE_DATA:
+		ret = usbg_prepare_w_request(cmd, stream->req_out);
+		if (ret)
+			goto cleanup;
+		ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+		break;
+
+	case UASP_SEND_STATUS:
+		uasp_prepare_status(cmd);
+		ret = usb_ep_queue(fu->ep_status, stream->req_status,
+				GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+		break;
+
+	case UASP_QUEUE_COMMAND:
+		transport_generic_free_cmd(&cmd->se_cmd, 0);
+		usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+		break;
+
+	default:
+		BUG();
+	}
+	return;
+
+cleanup:
+	transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static int uasp_send_status_response(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct uas_stream *stream = cmd->stream;
+	struct sense_iu *iu = &cmd->sense_iu;
+
+	iu->tag = cpu_to_be16(cmd->tag);
+	stream->req_status->complete = uasp_status_data_cmpl;
+	stream->req_status->context = cmd;
+	cmd->fu = fu;
+	uasp_prepare_status(cmd);
+	return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
+}
+
+static int uasp_send_read_response(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct uas_stream *stream = cmd->stream;
+	struct sense_iu *iu = &cmd->sense_iu;
+	int ret;
+
+	cmd->fu = fu;
+
+	iu->tag = cpu_to_be16(cmd->tag);
+	if (fu->flags & USBG_USE_STREAMS) {
+
+		ret = uasp_prepare_r_request(cmd);
+		if (ret)
+			goto out;
+		ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+		if (ret) {
+			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+			kfree(cmd->data_buf);
+			cmd->data_buf = NULL;
+		}
+
+	} else {
+
+		iu->iu_id = IU_ID_READ_READY;
+		iu->tag = cpu_to_be16(cmd->tag);
+
+		stream->req_status->complete = uasp_status_data_cmpl;
+		stream->req_status->context = cmd;
+
+		cmd->state = UASP_SEND_DATA;
+		stream->req_status->buf = iu;
+		stream->req_status->length = sizeof(struct iu);
+
+		ret = usb_ep_queue(fu->ep_status, stream->req_status,
+				GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+	}
+out:
+	return ret;
+}
+
+static int uasp_send_write_request(struct usbg_cmd *cmd)
+{
+	struct f_uas *fu = cmd->fu;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct uas_stream *stream = cmd->stream;
+	struct sense_iu *iu = &cmd->sense_iu;
+	int ret;
+
+	init_completion(&cmd->write_complete);
+	cmd->fu = fu;
+
+	iu->tag = cpu_to_be16(cmd->tag);
+
+	if (fu->flags & USBG_USE_STREAMS) {
+
+		ret = usbg_prepare_w_request(cmd, stream->req_out);
+		if (ret)
+			goto cleanup;
+		ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d)\n", __func__, __LINE__);
+
+	} else {
+
+		iu->iu_id = IU_ID_WRITE_READY;
+		iu->tag = cpu_to_be16(cmd->tag);
+
+		stream->req_status->complete = uasp_status_data_cmpl;
+		stream->req_status->context = cmd;
+
+		cmd->state = UASP_RECEIVE_DATA;
+		stream->req_status->buf = iu;
+		stream->req_status->length = sizeof(struct iu);
+
+		ret = usb_ep_queue(fu->ep_status, stream->req_status,
+				GFP_ATOMIC);
+		if (ret)
+			pr_err("%s(%d)\n", __func__, __LINE__);
+	}
+
+	wait_for_completion(&cmd->write_complete);
+	target_execute_cmd(se_cmd);
+cleanup:
+	return ret;
+}
+
+static int usbg_submit_command(struct f_uas *, void *, unsigned int);
+
+static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_uas *fu = req->context;
+	int ret;
+
+	if (req->status < 0)
+		return;
+
+	ret = usbg_submit_command(fu, req->buf, req->actual);
+	/*
+	 * Once we tune for performance enqueue the command req here again so
+	 * we can receive a second command while we processing this one. Pay
+	 * attention to properly sync STAUS endpoint with DATA IN + OUT so you
+	 * don't break HS.
+	 */
+	if (!ret)
+		return;
+	usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+}
+
+static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
+{
+	stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+	if (!stream->req_in)
+		goto out;
+
+	stream->req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+	if (!stream->req_out)
+		goto err_out;
+
+	stream->req_status = usb_ep_alloc_request(fu->ep_status, GFP_KERNEL);
+	if (!stream->req_status)
+		goto err_sts;
+
+	return 0;
+err_sts:
+	usb_ep_free_request(fu->ep_status, stream->req_status);
+	stream->req_status = NULL;
+err_out:
+	usb_ep_free_request(fu->ep_out, stream->req_out);
+	stream->req_out = NULL;
+out:
+	return -ENOMEM;
+}
+
+static int uasp_alloc_cmd(struct f_uas *fu)
+{
+	fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
+	if (!fu->cmd.req)
+		goto err;
+
+	fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
+	if (!fu->cmd.buf)
+		goto err_buf;
+
+	fu->cmd.req->complete = uasp_cmd_complete;
+	fu->cmd.req->buf = fu->cmd.buf;
+	fu->cmd.req->length = fu->ep_cmd->maxpacket;
+	fu->cmd.req->context = fu;
+	return 0;
+
+err_buf:
+	usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+err:
+	return -ENOMEM;
+}
+
+static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
+{
+	int i;
+
+	for (i = 0; i < max_streams; i++) {
+		struct uas_stream *s = &fu->stream[i];
+
+		s->req_in->stream_id = i + 1;
+		s->req_out->stream_id = i + 1;
+		s->req_status->stream_id = i + 1;
+	}
+}
+
+static int uasp_prepare_reqs(struct f_uas *fu)
+{
+	int ret;
+	int i;
+	int max_streams;
+
+	if (fu->flags & USBG_USE_STREAMS)
+		max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
+	else
+		max_streams = 1;
+
+	for (i = 0; i < max_streams; i++) {
+		ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
+		if (ret)
+			goto err_cleanup;
+	}
+
+	ret = uasp_alloc_cmd(fu);
+	if (ret)
+		goto err_free_stream;
+	uasp_setup_stream_res(fu, max_streams);
+
+	ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+	if (ret)
+		goto err_free_stream;
+
+	return 0;
+
+err_free_stream:
+	uasp_free_cmdreq(fu);
+
+err_cleanup:
+	if (i) {
+		do {
+			uasp_cleanup_one_stream(fu, &fu->stream[i - 1]);
+			i--;
+		} while (i);
+	}
+	pr_err("UASP: endpoint setup failed\n");
+	return ret;
+}
+
+static void uasp_set_alt(struct f_uas *fu)
+{
+	struct usb_function *f = &fu->function;
+	struct usb_gadget *gadget = f->config->cdev->gadget;
+	int ret;
+
+	fu->flags = USBG_IS_UAS;
+
+	if (gadget->speed == USB_SPEED_SUPER)
+		fu->flags |= USBG_USE_STREAMS;
+
+	config_ep_by_speed(gadget, f, fu->ep_in);
+	ret = usb_ep_enable(fu->ep_in);
+	if (ret)
+		goto err_b_in;
+
+	config_ep_by_speed(gadget, f, fu->ep_out);
+	ret = usb_ep_enable(fu->ep_out);
+	if (ret)
+		goto err_b_out;
+
+	config_ep_by_speed(gadget, f, fu->ep_cmd);
+	ret = usb_ep_enable(fu->ep_cmd);
+	if (ret)
+		goto err_cmd;
+	config_ep_by_speed(gadget, f, fu->ep_status);
+	ret = usb_ep_enable(fu->ep_status);
+	if (ret)
+		goto err_status;
+
+	ret = uasp_prepare_reqs(fu);
+	if (ret)
+		goto err_wq;
+	fu->flags |= USBG_ENABLED;
+
+	pr_info("Using the UAS protocol\n");
+	return;
+err_wq:
+	usb_ep_disable(fu->ep_status);
+err_status:
+	usb_ep_disable(fu->ep_cmd);
+err_cmd:
+	usb_ep_disable(fu->ep_out);
+err_b_out:
+	usb_ep_disable(fu->ep_in);
+err_b_in:
+	fu->flags = 0;
+}
+
+static int get_cmd_dir(const unsigned char *cdb)
+{
+	int ret;
+
+	switch (cdb[0]) {
+	case READ_6:
+	case READ_10:
+	case READ_12:
+	case READ_16:
+	case INQUIRY:
+	case MODE_SENSE:
+	case MODE_SENSE_10:
+	case SERVICE_ACTION_IN_16:
+	case MAINTENANCE_IN:
+	case PERSISTENT_RESERVE_IN:
+	case SECURITY_PROTOCOL_IN:
+	case ACCESS_CONTROL_IN:
+	case REPORT_LUNS:
+	case READ_BLOCK_LIMITS:
+	case READ_POSITION:
+	case READ_CAPACITY:
+	case READ_TOC:
+	case READ_FORMAT_CAPACITIES:
+	case REQUEST_SENSE:
+		ret = DMA_FROM_DEVICE;
+		break;
+
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_12:
+	case WRITE_16:
+	case MODE_SELECT:
+	case MODE_SELECT_10:
+	case WRITE_VERIFY:
+	case WRITE_VERIFY_12:
+	case PERSISTENT_RESERVE_OUT:
+	case MAINTENANCE_OUT:
+	case SECURITY_PROTOCOL_OUT:
+	case ACCESS_CONTROL_OUT:
+		ret = DMA_TO_DEVICE;
+		break;
+	case ALLOW_MEDIUM_REMOVAL:
+	case TEST_UNIT_READY:
+	case SYNCHRONIZE_CACHE:
+	case START_STOP:
+	case ERASE:
+	case REZERO_UNIT:
+	case SEEK_10:
+	case SPACE:
+	case VERIFY:
+	case WRITE_FILEMARKS:
+		ret = DMA_NONE;
+		break;
+	default:
+#define CMD_DIR_MSG "target: Unknown data direction for SCSI Opcode 0x%02x\n"
+		pr_warn(CMD_DIR_MSG, cdb[0]);
+#undef CMD_DIR_MSG
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbg_cmd *cmd = req->context;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+
+	if (req->status < 0) {
+		pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
+		goto cleanup;
+	}
+
+	if (req->num_sgs == 0) {
+		sg_copy_from_buffer(se_cmd->t_data_sg,
+				se_cmd->t_data_nents,
+				cmd->data_buf,
+				se_cmd->data_length);
+	}
+
+	complete(&cmd->write_complete);
+	return;
+
+cleanup:
+	transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
+{
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+	struct f_uas *fu = cmd->fu;
+	struct usb_gadget *gadget = fuas_to_gadget(fu);
+
+	if (!gadget->sg_supported) {
+		cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+		if (!cmd->data_buf)
+			return -ENOMEM;
+
+		req->buf = cmd->data_buf;
+	} else {
+		req->buf = NULL;
+		req->num_sgs = se_cmd->t_data_nents;
+		req->sg = se_cmd->t_data_sg;
+	}
+
+	req->complete = usbg_data_write_cmpl;
+	req->length = se_cmd->data_length;
+	req->context = cmd;
+	return 0;
+}
+
+static int usbg_send_status_response(struct se_cmd *se_cmd)
+{
+	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+			se_cmd);
+	struct f_uas *fu = cmd->fu;
+
+	if (fu->flags & USBG_IS_BOT)
+		return bot_send_status_response(cmd);
+	else
+		return uasp_send_status_response(cmd);
+}
+
+static int usbg_send_write_request(struct se_cmd *se_cmd)
+{
+	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+			se_cmd);
+	struct f_uas *fu = cmd->fu;
+
+	if (fu->flags & USBG_IS_BOT)
+		return bot_send_write_request(cmd);
+	else
+		return uasp_send_write_request(cmd);
+}
+
+static int usbg_send_read_response(struct se_cmd *se_cmd)
+{
+	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+			se_cmd);
+	struct f_uas *fu = cmd->fu;
+
+	if (fu->flags & USBG_IS_BOT)
+		return bot_send_read_response(cmd);
+	else
+		return uasp_send_read_response(cmd);
+}
+
+static void usbg_cmd_work(struct work_struct *work)
+{
+	struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+	struct se_cmd *se_cmd;
+	struct tcm_usbg_nexus *tv_nexus;
+	struct usbg_tpg *tpg;
+	int dir, flags = (TARGET_SCF_UNKNOWN_SIZE | TARGET_SCF_ACK_KREF);
+
+	se_cmd = &cmd->se_cmd;
+	tpg = cmd->fu->tpg;
+	tv_nexus = tpg->tpg_nexus;
+	dir = get_cmd_dir(cmd->cmd_buf);
+	if (dir < 0) {
+		transport_init_se_cmd(se_cmd,
+				tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+				tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+				cmd->prio_attr, cmd->sense_iu.sense);
+		goto out;
+	}
+
+	if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
+			      cmd->sense_iu.sense, cmd->unpacked_lun, 0,
+			      cmd->prio_attr, dir, flags) < 0)
+		goto out;
+
+	return;
+
+out:
+	transport_send_check_condition_and_sense(se_cmd,
+			TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+	transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
+		struct tcm_usbg_nexus *tv_nexus, u32 scsi_tag)
+{
+	struct se_session *se_sess = tv_nexus->tvn_se_sess;
+	struct usbg_cmd *cmd;
+	int tag, cpu;
+
+	tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu);
+	if (tag < 0)
+		return ERR_PTR(-ENOMEM);
+
+	cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[tag];
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->se_cmd.map_tag = tag;
+	cmd->se_cmd.map_cpu = cpu;
+	cmd->se_cmd.tag = cmd->tag = scsi_tag;
+	cmd->fu = fu;
+
+	return cmd;
+}
+
+static void usbg_release_cmd(struct se_cmd *);
+
+static int usbg_submit_command(struct f_uas *fu,
+		void *cmdbuf, unsigned int len)
+{
+	struct command_iu *cmd_iu = cmdbuf;
+	struct usbg_cmd *cmd;
+	struct usbg_tpg *tpg = fu->tpg;
+	struct tcm_usbg_nexus *tv_nexus;
+	u32 cmd_len;
+	u16 scsi_tag;
+
+	if (cmd_iu->iu_id != IU_ID_COMMAND) {
+		pr_err("Unsupported type %d\n", cmd_iu->iu_id);
+		return -EINVAL;
+	}
+
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus) {
+		pr_err("Missing nexus, ignoring command\n");
+		return -EINVAL;
+	}
+
+	cmd_len = (cmd_iu->len & ~0x3) + 16;
+	if (cmd_len > USBG_MAX_CMD)
+		return -EINVAL;
+
+	scsi_tag = be16_to_cpup(&cmd_iu->tag);
+	cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag);
+	if (IS_ERR(cmd)) {
+		pr_err("usbg_get_cmd failed\n");
+		return -ENOMEM;
+	}
+	memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
+
+	if (fu->flags & USBG_USE_STREAMS) {
+		if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS)
+			goto err;
+		if (!cmd->tag)
+			cmd->stream = &fu->stream[0];
+		else
+			cmd->stream = &fu->stream[cmd->tag - 1];
+	} else {
+		cmd->stream = &fu->stream[0];
+	}
+
+	switch (cmd_iu->prio_attr & 0x7) {
+	case UAS_HEAD_TAG:
+		cmd->prio_attr = TCM_HEAD_TAG;
+		break;
+	case UAS_ORDERED_TAG:
+		cmd->prio_attr = TCM_ORDERED_TAG;
+		break;
+	case UAS_ACA:
+		cmd->prio_attr = TCM_ACA_TAG;
+		break;
+	default:
+		pr_debug_once("Unsupported prio_attr: %02x.\n",
+				cmd_iu->prio_attr);
+		/* fall through */
+	case UAS_SIMPLE_TAG:
+		cmd->prio_attr = TCM_SIMPLE_TAG;
+		break;
+	}
+
+	cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
+
+	INIT_WORK(&cmd->work, usbg_cmd_work);
+	queue_work(tpg->workqueue, &cmd->work);
+
+	return 0;
+err:
+	usbg_release_cmd(&cmd->se_cmd);
+	return -EINVAL;
+}
+
+static void bot_cmd_work(struct work_struct *work)
+{
+	struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+	struct se_cmd *se_cmd;
+	struct tcm_usbg_nexus *tv_nexus;
+	struct usbg_tpg *tpg;
+	int dir;
+
+	se_cmd = &cmd->se_cmd;
+	tpg = cmd->fu->tpg;
+	tv_nexus = tpg->tpg_nexus;
+	dir = get_cmd_dir(cmd->cmd_buf);
+	if (dir < 0) {
+		transport_init_se_cmd(se_cmd,
+				tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+				tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+				cmd->prio_attr, cmd->sense_iu.sense);
+		goto out;
+	}
+
+	if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
+			cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
+			cmd->data_len, cmd->prio_attr, dir, 0) < 0)
+		goto out;
+
+	return;
+
+out:
+	transport_send_check_condition_and_sense(se_cmd,
+				TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+	transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static int bot_submit_command(struct f_uas *fu,
+		void *cmdbuf, unsigned int len)
+{
+	struct bulk_cb_wrap *cbw = cmdbuf;
+	struct usbg_cmd *cmd;
+	struct usbg_tpg *tpg = fu->tpg;
+	struct tcm_usbg_nexus *tv_nexus;
+	u32 cmd_len;
+
+	if (cbw->Signature != cpu_to_le32(US_BULK_CB_SIGN)) {
+		pr_err("Wrong signature on CBW\n");
+		return -EINVAL;
+	}
+	if (len != 31) {
+		pr_err("Wrong length for CBW\n");
+		return -EINVAL;
+	}
+
+	cmd_len = cbw->Length;
+	if (cmd_len < 1 || cmd_len > 16)
+		return -EINVAL;
+
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus) {
+		pr_err("Missing nexus, ignoring command\n");
+		return -ENODEV;
+	}
+
+	cmd = usbg_get_cmd(fu, tv_nexus, cbw->Tag);
+	if (IS_ERR(cmd)) {
+		pr_err("usbg_get_cmd failed\n");
+		return -ENOMEM;
+	}
+	memcpy(cmd->cmd_buf, cbw->CDB, cmd_len);
+
+	cmd->bot_tag = cbw->Tag;
+	cmd->prio_attr = TCM_SIMPLE_TAG;
+	cmd->unpacked_lun = cbw->Lun;
+	cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0;
+	cmd->data_len = le32_to_cpu(cbw->DataTransferLength);
+	cmd->se_cmd.tag = le32_to_cpu(cmd->bot_tag);
+
+	INIT_WORK(&cmd->work, bot_cmd_work);
+	queue_work(tpg->workqueue, &cmd->work);
+
+	return 0;
+}
+
+/* Start fabric.c code */
+
+static int usbg_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static int usbg_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+static char *usbg_get_fabric_name(void)
+{
+	return "usb_gadget";
+}
+
+static char *usbg_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg,
+				struct usbg_tpg, se_tpg);
+	struct usbg_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+static u16 usbg_get_tag(struct se_portal_group *se_tpg)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg,
+				struct usbg_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+static u32 usbg_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static void usbg_release_cmd(struct se_cmd *se_cmd)
+{
+	struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+			se_cmd);
+	struct se_session *se_sess = se_cmd->se_sess;
+
+	kfree(cmd->data_buf);
+	target_free_tag(se_sess, se_cmd);
+}
+
+static u32 usbg_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+/*
+ * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be
+ */
+static int usbg_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void usbg_set_default_node_attrs(struct se_node_acl *nacl)
+{
+}
+
+static int usbg_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+}
+
+static void usbg_aborted_task(struct se_cmd *se_cmd)
+{
+}
+
+static const char *usbg_check_wwn(const char *name)
+{
+	const char *n;
+	unsigned int len;
+
+	n = strstr(name, "naa.");
+	if (!n)
+		return NULL;
+	n += 4;
+	len = strlen(n);
+	if (len == 0 || len > USBG_NAMELEN - 1)
+		return NULL;
+	return n;
+}
+
+static int usbg_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
+{
+	if (!usbg_check_wwn(name))
+		return -EINVAL;
+	return 0;
+}
+
+static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
+					     const char *name)
+{
+	struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
+			tport_wwn);
+	struct usbg_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+	struct f_tcm_opts *opts;
+	unsigned i;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+	ret = -ENODEV;
+	mutex_lock(&tpg_instances_lock);
+	for (i = 0; i < TPG_INSTANCES; ++i)
+		if (tpg_instances[i].func_inst && !tpg_instances[i].tpg)
+			break;
+	if (i == TPG_INSTANCES)
+		goto unlock_inst;
+
+	opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
+		func_inst);
+	mutex_lock(&opts->dep_lock);
+	if (!opts->ready)
+		goto unlock_dep;
+
+	if (opts->has_dep) {
+		if (!try_module_get(opts->dependent))
+			goto unlock_dep;
+	} else {
+		ret = configfs_depend_item_unlocked(
+			wwn->wwn_group.cg_subsys,
+			&opts->func_inst.group.cg_item);
+		if (ret)
+			goto unlock_dep;
+	}
+
+	tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
+	ret = -ENOMEM;
+	if (!tpg)
+		goto unref_dep;
+	mutex_init(&tpg->tpg_mutex);
+	atomic_set(&tpg->tpg_port_count, 0);
+	tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
+	if (!tpg->workqueue)
+		goto free_tpg;
+
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+
+	/*
+	 * SPC doesn't assign a protocol identifier for USB-SCSI, so we
+	 * pretend to be SAS..
+	 */
+	ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SAS);
+	if (ret < 0)
+		goto free_workqueue;
+
+	tpg_instances[i].tpg = tpg;
+	tpg->fi = tpg_instances[i].func_inst;
+	mutex_unlock(&opts->dep_lock);
+	mutex_unlock(&tpg_instances_lock);
+	return &tpg->se_tpg;
+
+free_workqueue:
+	destroy_workqueue(tpg->workqueue);
+free_tpg:
+	kfree(tpg);
+unref_dep:
+	if (opts->has_dep)
+		module_put(opts->dependent);
+	else
+		configfs_undepend_item_unlocked(&opts->func_inst.group.cg_item);
+unlock_dep:
+	mutex_unlock(&opts->dep_lock);
+unlock_inst:
+	mutex_unlock(&tpg_instances_lock);
+
+	return ERR_PTR(ret);
+}
+
+static int tcm_usbg_drop_nexus(struct usbg_tpg *);
+
+static void usbg_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg,
+				struct usbg_tpg, se_tpg);
+	unsigned i;
+	struct f_tcm_opts *opts;
+
+	tcm_usbg_drop_nexus(tpg);
+	core_tpg_deregister(se_tpg);
+	destroy_workqueue(tpg->workqueue);
+
+	mutex_lock(&tpg_instances_lock);
+	for (i = 0; i < TPG_INSTANCES; ++i)
+		if (tpg_instances[i].tpg == tpg)
+			break;
+	if (i < TPG_INSTANCES) {
+		tpg_instances[i].tpg = NULL;
+		opts = container_of(tpg_instances[i].func_inst,
+			struct f_tcm_opts, func_inst);
+		mutex_lock(&opts->dep_lock);
+		if (opts->has_dep)
+			module_put(opts->dependent);
+		else
+			configfs_undepend_item_unlocked(
+				&opts->func_inst.group.cg_item);
+		mutex_unlock(&opts->dep_lock);
+	}
+	mutex_unlock(&tpg_instances_lock);
+
+	kfree(tpg);
+}
+
+static struct se_wwn *usbg_make_tport(
+	struct target_fabric_configfs *tf,
+	struct config_group *group,
+	const char *name)
+{
+	struct usbg_tport *tport;
+	const char *wnn_name;
+	u64 wwpn = 0;
+
+	wnn_name = usbg_check_wwn(name);
+	if (!wnn_name)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
+	if (!(tport))
+		return ERR_PTR(-ENOMEM);
+
+	tport->tport_wwpn = wwpn;
+	snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
+	return &tport->tport_wwn;
+}
+
+static void usbg_drop_tport(struct se_wwn *wwn)
+{
+	struct usbg_tport *tport = container_of(wwn,
+				struct usbg_tport, tport_wwn);
+	kfree(tport);
+}
+
+/*
+ * If somebody feels like dropping the version property, go ahead.
+ */
+static ssize_t usbg_wwn_version_show(struct config_item *item,  char *page)
+{
+	return sprintf(page, "usb-gadget fabric module\n");
+}
+
+CONFIGFS_ATTR_RO(usbg_wwn_, version);
+
+static struct configfs_attribute *usbg_wwn_attrs[] = {
+	&usbg_wwn_attr_version,
+	NULL,
+};
+
+static ssize_t tcm_usbg_tpg_enable_show(struct config_item *item, char *page)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct usbg_tpg  *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+	return snprintf(page, PAGE_SIZE, "%u\n", tpg->gadget_connect);
+}
+
+static int usbg_attach(struct usbg_tpg *);
+static void usbg_detach(struct usbg_tpg *);
+
+static ssize_t tcm_usbg_tpg_enable_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct usbg_tpg  *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+	bool op;
+	ssize_t ret;
+
+	ret = strtobool(page, &op);
+	if (ret)
+		return ret;
+
+	if ((op && tpg->gadget_connect) || (!op && !tpg->gadget_connect))
+		return -EINVAL;
+
+	if (op)
+		ret = usbg_attach(tpg);
+	else
+		usbg_detach(tpg);
+	if (ret)
+		return ret;
+
+	tpg->gadget_connect = op;
+
+	return count;
+}
+
+static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+	struct tcm_usbg_nexus *tv_nexus;
+	ssize_t ret;
+
+	mutex_lock(&tpg->tpg_mutex);
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus) {
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = snprintf(page, PAGE_SIZE, "%s\n",
+			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+out:
+	mutex_unlock(&tpg->tpg_mutex);
+	return ret;
+}
+
+static int usbg_alloc_sess_cb(struct se_portal_group *se_tpg,
+			      struct se_session *se_sess, void *p)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg,
+				struct usbg_tpg, se_tpg);
+
+	tpg->tpg_nexus = p;
+	return 0;
+}
+
+static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
+{
+	struct tcm_usbg_nexus *tv_nexus;
+	int ret = 0;
+
+	mutex_lock(&tpg->tpg_mutex);
+	if (tpg->tpg_nexus) {
+		ret = -EEXIST;
+		pr_debug("tpg->tpg_nexus already exists\n");
+		goto out_unlock;
+	}
+
+	tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
+	if (!tv_nexus) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	tv_nexus->tvn_se_sess = target_setup_session(&tpg->se_tpg,
+						     USB_G_DEFAULT_SESSION_TAGS,
+						     sizeof(struct usbg_cmd),
+						     TARGET_PROT_NORMAL, name,
+						     tv_nexus, usbg_alloc_sess_cb);
+	if (IS_ERR(tv_nexus->tvn_se_sess)) {
+#define MAKE_NEXUS_MSG "core_tpg_check_initiator_node_acl() failed for %s\n"
+		pr_debug(MAKE_NEXUS_MSG, name);
+#undef MAKE_NEXUS_MSG
+		ret = PTR_ERR(tv_nexus->tvn_se_sess);
+		kfree(tv_nexus);
+	}
+
+out_unlock:
+	mutex_unlock(&tpg->tpg_mutex);
+	return ret;
+}
+
+static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
+{
+	struct se_session *se_sess;
+	struct tcm_usbg_nexus *tv_nexus;
+	int ret = -ENODEV;
+
+	mutex_lock(&tpg->tpg_mutex);
+	tv_nexus = tpg->tpg_nexus;
+	if (!tv_nexus)
+		goto out;
+
+	se_sess = tv_nexus->tvn_se_sess;
+	if (!se_sess)
+		goto out;
+
+	if (atomic_read(&tpg->tpg_port_count)) {
+		ret = -EPERM;
+#define MSG "Unable to remove Host I_T Nexus with active TPG port count: %d\n"
+		pr_err(MSG, atomic_read(&tpg->tpg_port_count));
+#undef MSG
+		goto out;
+	}
+
+	pr_debug("Removing I_T Nexus to Initiator Port: %s\n",
+			tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+	/*
+	 * Release the SCSI I_T Nexus to the emulated vHost Target Port
+	 */
+	target_remove_session(se_sess);
+	tpg->tpg_nexus = NULL;
+
+	kfree(tv_nexus);
+	ret = 0;
+out:
+	mutex_unlock(&tpg->tpg_mutex);
+	return ret;
+}
+
+static ssize_t tcm_usbg_tpg_nexus_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+	unsigned char i_port[USBG_NAMELEN], *ptr;
+	int ret;
+
+	if (!strncmp(page, "NULL", 4)) {
+		ret = tcm_usbg_drop_nexus(tpg);
+		return (!ret) ? count : ret;
+	}
+	if (strlen(page) >= USBG_NAMELEN) {
+
+#define NEXUS_STORE_MSG "Emulated NAA Sas Address: %s, exceeds max: %d\n"
+		pr_err(NEXUS_STORE_MSG, page, USBG_NAMELEN);
+#undef NEXUS_STORE_MSG
+		return -EINVAL;
+	}
+	snprintf(i_port, USBG_NAMELEN, "%s", page);
+
+	ptr = strstr(i_port, "naa.");
+	if (!ptr) {
+		pr_err("Missing 'naa.' prefix\n");
+		return -EINVAL;
+	}
+
+	if (i_port[strlen(i_port) - 1] == '\n')
+		i_port[strlen(i_port) - 1] = '\0';
+
+	ret = tcm_usbg_make_nexus(tpg, &i_port[0]);
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
+CONFIGFS_ATTR(tcm_usbg_tpg_, enable);
+CONFIGFS_ATTR(tcm_usbg_tpg_, nexus);
+
+static struct configfs_attribute *usbg_base_attrs[] = {
+	&tcm_usbg_tpg_attr_enable,
+	&tcm_usbg_tpg_attr_nexus,
+	NULL,
+};
+
+static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+	atomic_inc(&tpg->tpg_port_count);
+	smp_mb__after_atomic();
+	return 0;
+}
+
+static void usbg_port_unlink(struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+	atomic_dec(&tpg->tpg_port_count);
+	smp_mb__after_atomic();
+}
+
+static int usbg_check_stop_free(struct se_cmd *se_cmd)
+{
+	return target_put_sess_cmd(se_cmd);
+}
+
+static const struct target_core_fabric_ops usbg_ops = {
+	.module				= THIS_MODULE,
+	.name				= "usb_gadget",
+	.get_fabric_name		= usbg_get_fabric_name,
+	.tpg_get_wwn			= usbg_get_fabric_wwn,
+	.tpg_get_tag			= usbg_get_tag,
+	.tpg_check_demo_mode		= usbg_check_true,
+	.tpg_check_demo_mode_cache	= usbg_check_false,
+	.tpg_check_demo_mode_write_protect = usbg_check_false,
+	.tpg_check_prod_mode_write_protect = usbg_check_false,
+	.tpg_get_inst_index		= usbg_tpg_get_inst_index,
+	.release_cmd			= usbg_release_cmd,
+	.sess_get_index			= usbg_sess_get_index,
+	.sess_get_initiator_sid		= NULL,
+	.write_pending			= usbg_send_write_request,
+	.write_pending_status		= usbg_write_pending_status,
+	.set_default_node_attributes	= usbg_set_default_node_attrs,
+	.get_cmd_state			= usbg_get_cmd_state,
+	.queue_data_in			= usbg_send_read_response,
+	.queue_status			= usbg_send_status_response,
+	.queue_tm_rsp			= usbg_queue_tm_rsp,
+	.aborted_task			= usbg_aborted_task,
+	.check_stop_free		= usbg_check_stop_free,
+
+	.fabric_make_wwn		= usbg_make_tport,
+	.fabric_drop_wwn		= usbg_drop_tport,
+	.fabric_make_tpg		= usbg_make_tpg,
+	.fabric_drop_tpg		= usbg_drop_tpg,
+	.fabric_post_link		= usbg_port_link,
+	.fabric_pre_unlink		= usbg_port_unlink,
+	.fabric_init_nodeacl		= usbg_init_nodeacl,
+
+	.tfc_wwn_attrs			= usbg_wwn_attrs,
+	.tfc_tpg_base_attrs		= usbg_base_attrs,
+};
+
+/* Start gadget.c code */
+
+static struct usb_interface_descriptor bot_intf_desc = {
+	.bLength =              sizeof(bot_intf_desc),
+	.bDescriptorType =      USB_DT_INTERFACE,
+	.bNumEndpoints =        2,
+	.bAlternateSetting =	USB_G_ALT_INT_BBB,
+	.bInterfaceClass =      USB_CLASS_MASS_STORAGE,
+	.bInterfaceSubClass =   USB_SC_SCSI,
+	.bInterfaceProtocol =   USB_PR_BULK,
+};
+
+static struct usb_interface_descriptor uasp_intf_desc = {
+	.bLength =		sizeof(uasp_intf_desc),
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	4,
+	.bAlternateSetting =	USB_G_ALT_INT_UAS,
+	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
+	.bInterfaceSubClass =	USB_SC_SCSI,
+	.bInterfaceProtocol =	USB_PR_UAS,
+};
+
+static struct usb_endpoint_descriptor uasp_bi_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_bi_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = {
+	.bLength =		sizeof(uasp_bi_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		DATA_IN_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bi_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_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
+	.bLength =		sizeof(uasp_bi_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst =		0,
+	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
+	.wBytesPerInterval =	0,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = {
+	.bLength =		sizeof(bot_bi_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst =		0,
+};
+
+static struct usb_endpoint_descriptor uasp_bo_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_bo_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = {
+	.bLength =		sizeof(uasp_bo_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		DATA_OUT_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(0x400),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = {
+	.bLength =		sizeof(uasp_bo_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = {
+	.bLength =		sizeof(bot_bo_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor uasp_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_status_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = {
+	.bLength =		sizeof(uasp_status_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		STATUS_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_status_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_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = {
+	.bLength =		sizeof(uasp_status_in_ep_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+	.bmAttributes =		UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_endpoint_descriptor uasp_cmd_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_cmd_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = {
+	.bLength =		sizeof(uasp_cmd_pipe_desc),
+	.bDescriptorType =	USB_DT_PIPE_USAGE,
+	.bPipeID =		CMD_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_cmd_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 uasp_cmd_comp_desc = {
+	.bLength =		sizeof(uasp_cmd_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *uasp_fs_function_desc[] = {
+	(struct usb_descriptor_header *) &bot_intf_desc,
+	(struct usb_descriptor_header *) &uasp_fs_bi_desc,
+	(struct usb_descriptor_header *) &uasp_fs_bo_desc,
+
+	(struct usb_descriptor_header *) &uasp_intf_desc,
+	(struct usb_descriptor_header *) &uasp_fs_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_fs_bo_desc,
+	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_fs_status_desc,
+	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_fs_cmd_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *uasp_hs_function_desc[] = {
+	(struct usb_descriptor_header *) &bot_intf_desc,
+	(struct usb_descriptor_header *) &uasp_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bo_desc,
+
+	(struct usb_descriptor_header *) &uasp_intf_desc,
+	(struct usb_descriptor_header *) &uasp_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_bo_desc,
+	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_status_desc,
+	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *uasp_ss_function_desc[] = {
+	(struct usb_descriptor_header *) &bot_intf_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bi_desc,
+	(struct usb_descriptor_header *) &bot_bi_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bo_desc,
+	(struct usb_descriptor_header *) &bot_bo_ep_comp_desc,
+
+	(struct usb_descriptor_header *) &uasp_intf_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bi_desc,
+	(struct usb_descriptor_header *) &uasp_bi_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_bo_desc,
+	(struct usb_descriptor_header *) &uasp_bo_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_status_desc,
+	(struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
+	(struct usb_descriptor_header *) &uasp_status_pipe_desc,
+	(struct usb_descriptor_header *) &uasp_ss_cmd_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_comp_desc,
+	(struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+	NULL,
+};
+
+static struct usb_string	tcm_us_strings[] = {
+	[USB_G_STR_INT_UAS].s		= "USB Attached SCSI",
+	[USB_G_STR_INT_BBB].s		= "Bulk Only Transport",
+	{ },
+};
+
+static struct usb_gadget_strings tcm_stringtab = {
+	.language = 0x0409,
+	.strings = tcm_us_strings,
+};
+
+static struct usb_gadget_strings *tcm_strings[] = {
+	&tcm_stringtab,
+	NULL,
+};
+
+static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_uas		*fu = to_f_uas(f);
+	struct usb_string	*us;
+	struct usb_gadget	*gadget = c->cdev->gadget;
+	struct usb_ep		*ep;
+	struct f_tcm_opts	*opts;
+	int			iface;
+	int			ret;
+
+	opts = container_of(f->fi, struct f_tcm_opts, func_inst);
+
+	mutex_lock(&opts->dep_lock);
+	if (!opts->can_attach) {
+		mutex_unlock(&opts->dep_lock);
+		return -ENODEV;
+	}
+	mutex_unlock(&opts->dep_lock);
+	us = usb_gstrings_attach(c->cdev, tcm_strings,
+		ARRAY_SIZE(tcm_us_strings));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	bot_intf_desc.iInterface = us[USB_G_STR_INT_BBB].id;
+	uasp_intf_desc.iInterface = us[USB_G_STR_INT_UAS].id;
+
+	iface = usb_interface_id(c, f);
+	if (iface < 0)
+		return iface;
+
+	bot_intf_desc.bInterfaceNumber = iface;
+	uasp_intf_desc.bInterfaceNumber = iface;
+	fu->iface = iface;
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc,
+			&uasp_bi_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+
+	fu->ep_in = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc,
+			&uasp_bo_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	fu->ep_out = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc,
+			&uasp_status_in_ep_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	fu->ep_status = ep;
+
+	ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc,
+			&uasp_cmd_comp_desc);
+	if (!ep)
+		goto ep_fail;
+	fu->ep_cmd = ep;
+
+	/* Assume endpoint addresses are the same for both speeds */
+	uasp_bi_desc.bEndpointAddress =	uasp_ss_bi_desc.bEndpointAddress;
+	uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+	uasp_status_desc.bEndpointAddress =
+		uasp_ss_status_desc.bEndpointAddress;
+	uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+	uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
+	uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+	uasp_fs_status_desc.bEndpointAddress =
+		uasp_ss_status_desc.bEndpointAddress;
+	uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, uasp_fs_function_desc,
+			uasp_hs_function_desc, uasp_ss_function_desc, NULL);
+	if (ret)
+		goto ep_fail;
+
+	return 0;
+ep_fail:
+	pr_err("Can't claim all required eps\n");
+
+	return -ENOTSUPP;
+}
+
+struct guas_setup_wq {
+	struct work_struct work;
+	struct f_uas *fu;
+	unsigned int alt;
+};
+
+static void tcm_delayed_set_alt(struct work_struct *wq)
+{
+	struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
+			work);
+	struct f_uas *fu = work->fu;
+	int alt = work->alt;
+
+	kfree(work);
+
+	if (fu->flags & USBG_IS_BOT)
+		bot_cleanup_old_alt(fu);
+	if (fu->flags & USBG_IS_UAS)
+		uasp_cleanup_old_alt(fu);
+
+	if (alt == USB_G_ALT_INT_BBB)
+		bot_set_alt(fu);
+	else if (alt == USB_G_ALT_INT_UAS)
+		uasp_set_alt(fu);
+	usb_composite_setup_continue(fu->function.config->cdev);
+}
+
+static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_uas *fu = to_f_uas(f);
+
+	if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
+		struct guas_setup_wq *work;
+
+		work = kmalloc(sizeof(*work), GFP_ATOMIC);
+		if (!work)
+			return -ENOMEM;
+		INIT_WORK(&work->work, tcm_delayed_set_alt);
+		work->fu = fu;
+		work->alt = alt;
+		schedule_work(&work->work);
+		return USB_GADGET_DELAYED_STATUS;
+	}
+	return -EOPNOTSUPP;
+}
+
+static void tcm_disable(struct usb_function *f)
+{
+	struct f_uas *fu = to_f_uas(f);
+
+	if (fu->flags & USBG_IS_UAS)
+		uasp_cleanup_old_alt(fu);
+	else if (fu->flags & USBG_IS_BOT)
+		bot_cleanup_old_alt(fu);
+	fu->flags = 0;
+}
+
+static int tcm_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct f_uas *fu = to_f_uas(f);
+
+	if (!(fu->flags & USBG_IS_BOT))
+		return -EOPNOTSUPP;
+
+	return usbg_bot_setup(f, ctrl);
+}
+
+static inline struct f_tcm_opts *to_f_tcm_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_tcm_opts,
+		func_inst.group);
+}
+
+static void tcm_attr_release(struct config_item *item)
+{
+	struct f_tcm_opts *opts = to_f_tcm_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations tcm_item_ops = {
+	.release		= tcm_attr_release,
+};
+
+static const struct config_item_type tcm_func_type = {
+	.ct_item_ops	= &tcm_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void tcm_free_inst(struct usb_function_instance *f)
+{
+	struct f_tcm_opts *opts;
+	unsigned i;
+
+	opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	mutex_lock(&tpg_instances_lock);
+	for (i = 0; i < TPG_INSTANCES; ++i)
+		if (tpg_instances[i].func_inst == f)
+			break;
+	if (i < TPG_INSTANCES)
+		tpg_instances[i].func_inst = NULL;
+	mutex_unlock(&tpg_instances_lock);
+
+	kfree(opts);
+}
+
+static int tcm_register_callback(struct usb_function_instance *f)
+{
+	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	mutex_lock(&opts->dep_lock);
+	opts->can_attach = true;
+	mutex_unlock(&opts->dep_lock);
+
+	return 0;
+}
+
+static void tcm_unregister_callback(struct usb_function_instance *f)
+{
+	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	mutex_lock(&opts->dep_lock);
+	unregister_gadget_item(opts->
+		func_inst.group.cg_item.ci_parent->ci_parent);
+	opts->can_attach = false;
+	mutex_unlock(&opts->dep_lock);
+}
+
+static int usbg_attach(struct usbg_tpg *tpg)
+{
+	struct usb_function_instance *f = tpg->fi;
+	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	if (opts->tcm_register_callback)
+		return opts->tcm_register_callback(f);
+
+	return 0;
+}
+
+static void usbg_detach(struct usbg_tpg *tpg)
+{
+	struct usb_function_instance *f = tpg->fi;
+	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	if (opts->tcm_unregister_callback)
+		opts->tcm_unregister_callback(f);
+}
+
+static int tcm_set_name(struct usb_function_instance *f, const char *name)
+{
+	struct f_tcm_opts *opts = container_of(f, struct f_tcm_opts, func_inst);
+
+	pr_debug("tcm: Activating %s\n", name);
+
+	mutex_lock(&opts->dep_lock);
+	opts->ready = true;
+	mutex_unlock(&opts->dep_lock);
+
+	return 0;
+}
+
+static struct usb_function_instance *tcm_alloc_inst(void)
+{
+	struct f_tcm_opts *opts;
+	int i;
+
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&tpg_instances_lock);
+	for (i = 0; i < TPG_INSTANCES; ++i)
+		if (!tpg_instances[i].func_inst)
+			break;
+
+	if (i == TPG_INSTANCES) {
+		mutex_unlock(&tpg_instances_lock);
+		kfree(opts);
+		return ERR_PTR(-EBUSY);
+	}
+	tpg_instances[i].func_inst = &opts->func_inst;
+	mutex_unlock(&tpg_instances_lock);
+
+	mutex_init(&opts->dep_lock);
+	opts->func_inst.set_inst_name = tcm_set_name;
+	opts->func_inst.free_func_inst = tcm_free_inst;
+	opts->tcm_register_callback = tcm_register_callback;
+	opts->tcm_unregister_callback = tcm_unregister_callback;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+			&tcm_func_type);
+
+	return &opts->func_inst;
+}
+
+static void tcm_free(struct usb_function *f)
+{
+	struct f_uas *tcm = to_f_uas(f);
+
+	kfree(tcm);
+}
+
+static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
+{
+	struct f_uas *fu;
+	unsigned i;
+
+	mutex_lock(&tpg_instances_lock);
+	for (i = 0; i < TPG_INSTANCES; ++i)
+		if (tpg_instances[i].func_inst == fi)
+			break;
+	if (i == TPG_INSTANCES) {
+		mutex_unlock(&tpg_instances_lock);
+		return ERR_PTR(-ENODEV);
+	}
+
+	fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+	if (!fu) {
+		mutex_unlock(&tpg_instances_lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fu->function.name = "Target Function";
+	fu->function.bind = tcm_bind;
+	fu->function.unbind = tcm_unbind;
+	fu->function.set_alt = tcm_set_alt;
+	fu->function.setup = tcm_setup;
+	fu->function.disable = tcm_disable;
+	fu->function.free_func = tcm_free;
+	fu->tpg = tpg_instances[i].tpg;
+	mutex_unlock(&tpg_instances_lock);
+
+	return &fu->function;
+}
+
+DECLARE_USB_FUNCTION(tcm, tcm_alloc_inst, tcm_alloc);
+
+static int tcm_init(void)
+{
+	int ret;
+
+	ret = usb_function_register(&tcmusb_func);
+	if (ret)
+		return ret;
+
+	ret = target_register_template(&usbg_ops);
+	if (ret)
+		usb_function_unregister(&tcmusb_func);
+
+	return ret;
+}
+module_init(tcm_init);
+
+static void tcm_exit(void)
+{
+	target_unregister_template(&usbg_ops);
+	usb_function_unregister(&tcmusb_func);
+}
+module_exit(tcm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sebastian Andrzej Siewior");
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
new file mode 100644
index 0000000..2746a92
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
+ *
+ * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
+ * This driver doesn't expect any real Audio codec to be present
+ * on the device - the audio streams are simply sinked to and
+ * sourced from a virtual ALSA sound card created.
+ *
+ * This file is based on f_uac1.c which is
+ *   Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ *   Copyright (C) 2008 Analog Devices, Inc
+ */
+
+#include <linux/usb/audio.h>
+#include <linux/module.h>
+
+#include "u_audio.h"
+#include "u_uac1.h"
+
+struct f_uac1 {
+	struct g_audio g_audio;
+	u8 ac_intf, as_in_intf, as_out_intf;
+	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
+};
+
+static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
+{
+	return container_of(f, struct f_uac1, g_audio.func);
+}
+
+/*
+ * DESCRIPTORS ... most are static, but strings and full
+ * configuration descriptors are built on demand.
+ */
+
+/*
+ * We have three interfaces - one AudioControl and two AudioStreaming
+ *
+ * The driver implements a simple UAC_1 topology.
+ * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
+ * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
+ */
+#define F_AUDIO_AC_INTERFACE		0
+#define F_AUDIO_AS_OUT_INTERFACE	1
+#define F_AUDIO_AS_IN_INTERFACE		2
+/* Number of streaming interfaces */
+#define F_AUDIO_NUM_INTERFACES		2
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
+};
+
+/*
+ * The number of AudioStreaming and MIDIStreaming interfaces
+ * in the Audio Interface Collection
+ */
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+
+#define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+/* 2 input terminals and 2 output terminals */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+	+ 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE)
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+	.bLength =		UAC_DT_AC_HEADER_LENGTH,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_HEADER,
+	.bcdADC =		cpu_to_le16(0x0100),
+	.wTotalLength =		cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+	.bInCollection =	F_AUDIO_NUM_INTERFACES,
+	.baInterfaceNr = {
+	/* Interface number of the AudioStream interfaces */
+		[0] =		1,
+		[1] =		2,
+	}
+};
+
+#define USB_OUT_IT_ID	1
+static struct uac_input_terminal_descriptor usb_out_it_desc = {
+	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
+	.bTerminalID =		USB_OUT_IT_ID,
+	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal =	0,
+	.wChannelConfig =	cpu_to_le16(0x3),
+};
+
+#define IO_OUT_OT_ID	2
+static struct uac1_output_terminal_descriptor io_out_ot_desc = {
+	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
+	.bTerminalID		= IO_OUT_OT_ID,
+	.wTerminalType		= cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
+	.bAssocTerminal		= 0,
+	.bSourceID		= USB_OUT_IT_ID,
+};
+
+#define IO_IN_IT_ID	3
+static struct uac_input_terminal_descriptor io_in_it_desc = {
+	.bLength		= UAC_DT_INPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_INPUT_TERMINAL,
+	.bTerminalID		= IO_IN_IT_ID,
+	.wTerminalType		= cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
+	.bAssocTerminal		= 0,
+	.wChannelConfig		= cpu_to_le16(0x3),
+};
+
+#define USB_IN_OT_ID	4
+static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
+	.bLength =		UAC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_OUTPUT_TERMINAL,
+	.bTerminalID =		USB_IN_OT_ID,
+	.wTerminalType =	cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal =	0,
+	.bSourceID =		IO_IN_IT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_out_header_desc = {
+	.bLength =		UAC_DT_AS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_AS_GENERAL,
+	.bTerminalLink =	USB_OUT_IT_ID,
+	.bDelay =		1,
+	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
+};
+
+static struct uac1_as_header_descriptor as_in_header_desc = {
+	.bLength =		UAC_DT_AS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_AS_GENERAL,
+	.bTerminalLink =	USB_IN_OT_ID,
+	.bDelay =		1,
+	.wFormatTag =		cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
+};
+
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
+	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
+	.bFormatType =		UAC_FORMAT_TYPE_I,
+	.bSubframeSize =	2,
+	.bBitResolution =	16,
+	.bSamFreqType =		1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_out_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+	.bInterval =		4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
+	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	UAC_EP_GENERAL,
+	.bmAttributes =		1,
+	.bLockDelayUnits =	1,
+	.wLockDelay =		cpu_to_le16(1),
+};
+
+static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
+	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
+	.bFormatType =		UAC_FORMAT_TYPE_I,
+	.bSubframeSize =	2,
+	.bBitResolution =	16,
+	.bSamFreqType =		1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_in_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_SYNC_ASYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+	.bInterval =		4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	UAC_EP_GENERAL,
+	.bmAttributes =		1,
+	.bLockDelayUnits =	0,
+	.wLockDelay =		0,
+};
+
+static struct usb_descriptor_header *f_audio_desc[] = {
+	(struct usb_descriptor_header *)&ac_interface_desc,
+	(struct usb_descriptor_header *)&ac_header_desc,
+
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+
+	(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_out_header_desc,
+
+	(struct usb_descriptor_header *)&as_out_type_i_desc,
+
+	(struct usb_descriptor_header *)&as_out_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_in_header_desc,
+
+	(struct usb_descriptor_header *)&as_in_type_i_desc,
+
+	(struct usb_descriptor_header *)&as_in_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+enum {
+	STR_AC_IF,
+	STR_USB_OUT_IT,
+	STR_USB_OUT_IT_CH_NAMES,
+	STR_IO_OUT_OT,
+	STR_IO_IN_IT,
+	STR_IO_IN_IT_CH_NAMES,
+	STR_USB_IN_OT,
+	STR_AS_OUT_IF_ALT0,
+	STR_AS_OUT_IF_ALT1,
+	STR_AS_IN_IF_ALT0,
+	STR_AS_IN_IF_ALT1,
+};
+
+static struct usb_string strings_uac1[] = {
+	[STR_AC_IF].s = "AC Interface",
+	[STR_USB_OUT_IT].s = "Playback Input terminal",
+	[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
+	[STR_IO_OUT_OT].s = "Playback Output terminal",
+	[STR_IO_IN_IT].s = "Capture Input terminal",
+	[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
+	[STR_USB_IN_OT].s = "Capture Output terminal",
+	[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
+	[STR_AS_OUT_IF_ALT1].s = "Playback Active",
+	[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
+	[STR_AS_IN_IF_ALT1].s = "Capture Active",
+	{ },
+};
+
+static struct usb_gadget_strings str_uac1 = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings_uac1,
+};
+
+static struct usb_gadget_strings *uac1_strings[] = {
+	&str_uac1,
+	NULL,
+};
+
+/*
+ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
+ */
+
+static int audio_set_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int			value = -EOPNOTSUPP;
+	u16			ep = le16_to_cpu(ctrl->wIndex);
+	u16			len = le16_to_cpu(ctrl->wLength);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	switch (ctrl->bRequest) {
+	case UAC_SET_CUR:
+		value = len;
+		break;
+
+	case UAC_SET_MIN:
+		break;
+
+	case UAC_SET_MAX:
+		break;
+
+	case UAC_SET_RES:
+		break;
+
+	case UAC_SET_MEM:
+		break;
+
+	default:
+		break;
+	}
+
+	return value;
+}
+
+static int audio_get_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int value = -EOPNOTSUPP;
+	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+	u16 len = le16_to_cpu(ctrl->wLength);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	switch (ctrl->bRequest) {
+	case UAC_GET_CUR:
+	case UAC_GET_MIN:
+	case UAC_GET_MAX:
+	case UAC_GET_RES:
+		value = len;
+		break;
+	case UAC_GET_MEM:
+		break;
+	default:
+		break;
+	}
+
+	return value;
+}
+
+static int
+f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything; interface
+	 * activation uses set_alt().
+	 */
+	switch (ctrl->bRequestType) {
+	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_set_endpoint_req(f, ctrl);
+		break;
+
+	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_get_endpoint_req(f, ctrl);
+		break;
+
+	default:
+		ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "audio response on err %d\n", value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac1 *uac1 = func_to_uac1(f);
+	int ret = 0;
+
+	/* No i/f has more than 2 alt settings */
+	if (alt > 1) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	if (intf == uac1->ac_intf) {
+		/* Control I/f has only 1 AltSetting - 0 */
+		if (alt) {
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	if (intf == uac1->as_out_intf) {
+		uac1->as_out_alt = alt;
+
+		if (alt)
+			ret = u_audio_start_capture(&uac1->g_audio);
+		else
+			u_audio_stop_capture(&uac1->g_audio);
+	} else if (intf == uac1->as_in_intf) {
+		uac1->as_in_alt = alt;
+
+			if (alt)
+				ret = u_audio_start_playback(&uac1->g_audio);
+			else
+				u_audio_stop_playback(&uac1->g_audio);
+	} else {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int f_audio_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac1 *uac1 = func_to_uac1(f);
+
+	if (intf == uac1->ac_intf)
+		return uac1->ac_alt;
+	else if (intf == uac1->as_out_intf)
+		return uac1->as_out_alt;
+	else if (intf == uac1->as_in_intf)
+		return uac1->as_in_alt;
+	else
+		dev_err(dev, "%s:%d Invalid Interface %d!\n",
+			__func__, __LINE__, intf);
+
+	return -EINVAL;
+}
+
+
+static void f_audio_disable(struct usb_function *f)
+{
+	struct f_uac1 *uac1 = func_to_uac1(f);
+
+	uac1->as_out_alt = 0;
+	uac1->as_in_alt = 0;
+
+	u_audio_stop_capture(&uac1->g_audio);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* audio function driver setup/binding */
+static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev	*cdev = c->cdev;
+	struct usb_gadget		*gadget = cdev->gadget;
+	struct f_uac1			*uac1 = func_to_uac1(f);
+	struct g_audio			*audio = func_to_g_audio(f);
+	struct f_uac1_opts		*audio_opts;
+	struct usb_ep			*ep = NULL;
+	struct usb_string		*us;
+	u8				*sam_freq;
+	int				rate;
+	int				status;
+
+	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+
+	us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	ac_interface_desc.iInterface = us[STR_AC_IF].id;
+	usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
+	usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
+	io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id;
+	as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id;
+	as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id;
+	io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id;
+	io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id;
+	usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id;
+	as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
+	as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
+
+	/* Set channel numbers */
+	usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
+	as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
+	as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize;
+	as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
+	io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask);
+	io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask);
+	as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask);
+	as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
+	as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
+
+	/* Set sample rates */
+	rate = audio_opts->c_srate;
+	sam_freq = as_out_type_i_desc.tSamFreq[0];
+	memcpy(sam_freq, &rate, 3);
+	rate = audio_opts->p_srate;
+	sam_freq = as_in_type_i_desc.tSamFreq[0];
+	memcpy(sam_freq, &rate, 3);
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ac_interface_desc.bInterfaceNumber = status;
+	uac1->ac_intf = status;
+	uac1->ac_alt = 0;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	as_out_interface_alt_0_desc.bInterfaceNumber = status;
+	as_out_interface_alt_1_desc.bInterfaceNumber = status;
+	uac1->as_out_intf = status;
+	uac1->as_out_alt = 0;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	as_in_interface_alt_0_desc.bInterfaceNumber = status;
+	as_in_interface_alt_1_desc.bInterfaceNumber = status;
+	uac1->as_in_intf = status;
+	uac1->as_in_alt = 0;
+
+	audio->gadget = gadget;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
+	if (!ep)
+		goto fail;
+	audio->out_ep = ep;
+	audio->out_ep->desc = &as_out_ep_desc;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
+	if (!ep)
+		goto fail;
+	audio->in_ep = ep;
+	audio->in_ep->desc = &as_in_ep_desc;
+
+	/* copy descriptors, and track endpoint copies */
+	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
+					NULL);
+	if (status)
+		goto fail;
+
+	audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
+	audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
+	audio->params.c_chmask = audio_opts->c_chmask;
+	audio->params.c_srate = audio_opts->c_srate;
+	audio->params.c_ssize = audio_opts->c_ssize;
+	audio->params.p_chmask = audio_opts->p_chmask;
+	audio->params.p_srate = audio_opts->p_srate;
+	audio->params.p_ssize = audio_opts->p_ssize;
+	audio->params.req_number = audio_opts->req_number;
+
+	status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
+	if (status)
+		goto err_card_register;
+
+	return 0;
+
+err_card_register:
+	usb_free_all_descriptors(f);
+fail:
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uac1_opts,
+			    func_inst.group);
+}
+
+static void f_uac1_attr_release(struct config_item *item)
+{
+	struct f_uac1_opts *opts = to_f_uac1_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac1_item_ops = {
+	.release	= f_uac1_attr_release,
+};
+
+#define UAC1_ATTRIBUTE(name)						\
+static ssize_t f_uac1_opts_##name##_show(				\
+					  struct config_item *item,	\
+					  char *page)			\
+{									\
+	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac1_opts_##name##_store(				\
+					  struct config_item *item,	\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
+UAC1_ATTRIBUTE(c_chmask);
+UAC1_ATTRIBUTE(c_srate);
+UAC1_ATTRIBUTE(c_ssize);
+UAC1_ATTRIBUTE(p_chmask);
+UAC1_ATTRIBUTE(p_srate);
+UAC1_ATTRIBUTE(p_ssize);
+UAC1_ATTRIBUTE(req_number);
+
+static struct configfs_attribute *f_uac1_attrs[] = {
+	&f_uac1_opts_attr_c_chmask,
+	&f_uac1_opts_attr_c_srate,
+	&f_uac1_opts_attr_c_ssize,
+	&f_uac1_opts_attr_p_chmask,
+	&f_uac1_opts_attr_p_srate,
+	&f_uac1_opts_attr_p_ssize,
+	&f_uac1_opts_attr_req_number,
+	NULL,
+};
+
+static const struct config_item_type f_uac1_func_type = {
+	.ct_item_ops	= &f_uac1_item_ops,
+	.ct_attrs	= f_uac1_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void f_audio_free_inst(struct usb_function_instance *f)
+{
+	struct f_uac1_opts *opts;
+
+	opts = container_of(f, struct f_uac1_opts, func_inst);
+	kfree(opts);
+}
+
+static struct usb_function_instance *f_audio_alloc_inst(void)
+{
+	struct f_uac1_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = f_audio_free_inst;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &f_uac1_func_type);
+
+	opts->c_chmask = UAC1_DEF_CCHMASK;
+	opts->c_srate = UAC1_DEF_CSRATE;
+	opts->c_ssize = UAC1_DEF_CSSIZE;
+	opts->p_chmask = UAC1_DEF_PCHMASK;
+	opts->p_srate = UAC1_DEF_PSRATE;
+	opts->p_ssize = UAC1_DEF_PSSIZE;
+	opts->req_number = UAC1_DEF_REQ_NUM;
+	return &opts->func_inst;
+}
+
+static void f_audio_free(struct usb_function *f)
+{
+	struct g_audio *audio;
+	struct f_uac1_opts *opts;
+
+	audio = func_to_g_audio(f);
+	opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+	kfree(audio);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct g_audio *audio = func_to_g_audio(f);
+
+	g_audio_cleanup(audio);
+	usb_free_all_descriptors(f);
+
+	audio->gadget = NULL;
+}
+
+static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
+{
+	struct f_uac1 *uac1;
+	struct f_uac1_opts *opts;
+
+	/* allocate and initialize one new instance */
+	uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
+	if (!uac1)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_uac1_opts, func_inst);
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
+	uac1->g_audio.func.name = "uac1_func";
+	uac1->g_audio.func.bind = f_audio_bind;
+	uac1->g_audio.func.unbind = f_audio_unbind;
+	uac1->g_audio.func.set_alt = f_audio_set_alt;
+	uac1->g_audio.func.get_alt = f_audio_get_alt;
+	uac1->g_audio.func.setup = f_audio_setup;
+	uac1->g_audio.func.disable = f_audio_disable;
+	uac1->g_audio.func.free_func = f_audio_free;
+
+	return &uac1->g_audio.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
new file mode 100644
index 0000000..24c086b
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -0,0 +1,1018 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_audio.c -- USB Audio class function driver
+  *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/atomic.h>
+
+#include "u_uac1_legacy.h"
+
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
+
+/*
+ * DESCRIPTORS ... most are static, but strings and full
+ * configuration descriptors are built on demand.
+ */
+
+/*
+ * We have two interfaces- AudioControl and AudioStreaming
+ * TODO: only supcard playback currently
+ */
+#define F_AUDIO_AC_INTERFACE	0
+#define F_AUDIO_AS_INTERFACE	1
+#define F_AUDIO_NUM_INTERFACES	1
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
+};
+
+/*
+ * The number of AudioStreaming and MIDIStreaming interfaces
+ * in the Audio Interface Collection
+ */
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+
+#define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
+	.bLength =		UAC_DT_AC_HEADER_LENGTH,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_HEADER,
+	.bcdADC =		__constant_cpu_to_le16(0x0100),
+	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+	.bInCollection =	F_AUDIO_NUM_INTERFACES,
+	.baInterfaceNr = {
+	/* Interface number of the first AudioStream interface */
+		[0] =		1,
+	}
+};
+
+#define INPUT_TERMINAL_ID	1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
+	.bTerminalID =		INPUT_TERMINAL_ID,
+	.wTerminalType =	UAC_TERMINAL_STREAMING,
+	.bAssocTerminal =	0,
+	.wChannelConfig =	0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID		2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+	.bLength		= UAC_DT_FEATURE_UNIT_SIZE(0),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_FEATURE_UNIT,
+	.bUnitID		= FEATURE_UNIT_ID,
+	.bSourceID		= INPUT_TERMINAL_ID,
+	.bControlSize		= 2,
+	.bmaControls[0]		= (UAC_FU_MUTE | UAC_FU_VOLUME),
+};
+
+static struct usb_audio_control mute_control = {
+	.list = LIST_HEAD_INIT(mute_control.list),
+	.name = "Mute Control",
+	.type = UAC_FU_MUTE,
+	/* Todo: add real Mute control code */
+	.set = generic_set_cmd,
+	.get = generic_get_cmd,
+};
+
+static struct usb_audio_control volume_control = {
+	.list = LIST_HEAD_INIT(volume_control.list),
+	.name = "Volume Control",
+	.type = UAC_FU_VOLUME,
+	/* Todo: add real Volume control code */
+	.set = generic_set_cmd,
+	.get = generic_get_cmd,
+};
+
+static struct usb_audio_control_selector feature_unit = {
+	.list = LIST_HEAD_INIT(feature_unit.list),
+	.id = FEATURE_UNIT_ID,
+	.name = "Mute & Volume Control",
+	.type = UAC_FEATURE_UNIT,
+	.desc = (struct usb_descriptor_header *)&feature_unit_desc,
+};
+
+#define OUTPUT_TERMINAL_ID	3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
+	.bTerminalID		= OUTPUT_TERMINAL_ID,
+	.wTerminalType		= UAC_OUTPUT_TERMINAL_SPEAKER,
+	.bAssocTerminal		= FEATURE_UNIT_ID,
+	.bSourceID		= FEATURE_UNIT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+	.bLength =		UAC_DT_AS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_AS_GENERAL,
+	.bTerminalLink =	INPUT_TERMINAL_ID,
+	.bDelay =		1,
+	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM,
+};
+
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
+	.bFormatType =		UAC_FORMAT_TYPE_I,
+	.bSubframeSize =	2,
+	.bBitResolution =	16,
+	.bSamFreqType =		1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_out_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_SYNC_ADAPTIVE
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize	=	cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
+	.bInterval =		4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
+	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	UAC_EP_GENERAL,
+	.bmAttributes = 	1,
+	.bLockDelayUnits =	1,
+	.wLockDelay =		__constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *f_audio_desc[] = {
+	(struct usb_descriptor_header *)&ac_interface_desc,
+	(struct usb_descriptor_header *)&ac_header_desc,
+
+	(struct usb_descriptor_header *)&input_terminal_desc,
+	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&feature_unit_desc,
+
+	(struct usb_descriptor_header *)&as_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_header_desc,
+
+	(struct usb_descriptor_header *)&as_type_i_desc,
+
+	(struct usb_descriptor_header *)&as_out_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+	NULL,
+};
+
+enum {
+	STR_AC_IF,
+	STR_INPUT_TERMINAL,
+	STR_INPUT_TERMINAL_CH_NAMES,
+	STR_FEAT_DESC_0,
+	STR_OUTPUT_TERMINAL,
+	STR_AS_IF_ALT0,
+	STR_AS_IF_ALT1,
+};
+
+static struct usb_string strings_uac1[] = {
+	[STR_AC_IF].s = "AC Interface",
+	[STR_INPUT_TERMINAL].s = "Input terminal",
+	[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
+	[STR_FEAT_DESC_0].s = "Volume control & mute",
+	[STR_OUTPUT_TERMINAL].s = "Output terminal",
+	[STR_AS_IF_ALT0].s = "AS Interface",
+	[STR_AS_IF_ALT1].s = "AS Interface",
+	{ },
+};
+
+static struct usb_gadget_strings str_uac1 = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings_uac1,
+};
+
+static struct usb_gadget_strings *uac1_strings[] = {
+	&str_uac1,
+	NULL,
+};
+
+/*
+ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
+ */
+
+/*-------------------------------------------------------------------------*/
+struct f_audio_buf {
+	u8 *buf;
+	int actual;
+	struct list_head list;
+};
+
+static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
+{
+	struct f_audio_buf *copy_buf;
+
+	copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
+	if (!copy_buf)
+		return ERR_PTR(-ENOMEM);
+
+	copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
+	if (!copy_buf->buf) {
+		kfree(copy_buf);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return copy_buf;
+}
+
+static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
+{
+	kfree(audio_buf->buf);
+	kfree(audio_buf);
+}
+/*-------------------------------------------------------------------------*/
+
+struct f_audio {
+	struct gaudio			card;
+
+	u8 ac_intf, ac_alt;
+	u8 as_intf, as_alt;
+
+	/* endpoints handle full and/or high speeds */
+	struct usb_ep			*out_ep;
+
+	spinlock_t			lock;
+	struct f_audio_buf *copy_buf;
+	struct work_struct playback_work;
+	struct list_head play_queue;
+
+	/* Control Set command */
+	struct list_head cs;
+	u8 set_cmd;
+	struct usb_audio_control *set_con;
+};
+
+static inline struct f_audio *func_to_audio(struct usb_function *f)
+{
+	return container_of(f, struct f_audio, card.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_playback_work(struct work_struct *data)
+{
+	struct f_audio *audio = container_of(data, struct f_audio,
+					playback_work);
+	struct f_audio_buf *play_buf;
+
+	spin_lock_irq(&audio->lock);
+	if (list_empty(&audio->play_queue)) {
+		spin_unlock_irq(&audio->lock);
+		return;
+	}
+	play_buf = list_first_entry(&audio->play_queue,
+			struct f_audio_buf, list);
+	list_del(&play_buf->list);
+	spin_unlock_irq(&audio->lock);
+
+	u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
+	f_audio_buffer_free(play_buf);
+}
+
+static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_audio *audio = req->context;
+	struct usb_composite_dev *cdev = audio->card.func.config->cdev;
+	struct f_audio_buf *copy_buf = audio->copy_buf;
+	struct f_uac1_legacy_opts *opts;
+	int audio_buf_size;
+	int err;
+
+	opts = container_of(audio->card.func.fi, struct f_uac1_legacy_opts,
+			    func_inst);
+	audio_buf_size = opts->audio_buf_size;
+
+	if (!copy_buf)
+		return -EINVAL;
+
+	/* Copy buffer is full, add it to the play_queue */
+	if (audio_buf_size - copy_buf->actual < req->actual) {
+		list_add_tail(&copy_buf->list, &audio->play_queue);
+		schedule_work(&audio->playback_work);
+		copy_buf = f_audio_buffer_alloc(audio_buf_size);
+		if (IS_ERR(copy_buf))
+			return -ENOMEM;
+	}
+
+	memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
+	copy_buf->actual += req->actual;
+	audio->copy_buf = copy_buf;
+
+	err = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (err)
+		ERROR(cdev, "%s queue req: %d\n", ep->name, err);
+
+	return 0;
+
+}
+
+static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_audio *audio = req->context;
+	int status = req->status;
+	u32 data = 0;
+	struct usb_ep *out_ep = audio->out_ep;
+
+	switch (status) {
+
+	case 0:				/* normal completion? */
+		if (ep == out_ep)
+			f_audio_out_ep_complete(ep, req);
+		else if (audio->set_con) {
+			memcpy(&data, req->buf, req->length);
+			audio->set_con->set(audio->set_con, audio->set_cmd,
+					le16_to_cpu(data));
+			audio->set_con = NULL;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static int audio_set_intf_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct f_audio		*audio = func_to_audio(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+	u16			len = le16_to_cpu(ctrl->wLength);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u8			con_sel = (w_value >> 8) & 0xFF;
+	u8			cmd = (ctrl->bRequest & 0x0F);
+	struct usb_audio_control_selector *cs;
+	struct usb_audio_control *con;
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+			ctrl->bRequest, w_value, len, id);
+
+	list_for_each_entry(cs, &audio->cs, list) {
+		if (cs->id == id) {
+			list_for_each_entry(con, &cs->control, list) {
+				if (con->type == con_sel) {
+					audio->set_con = con;
+					break;
+				}
+			}
+			break;
+		}
+	}
+
+	audio->set_cmd = cmd;
+	req->context = audio;
+	req->complete = f_audio_complete;
+
+	return len;
+}
+
+static int audio_get_intf_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct f_audio		*audio = func_to_audio(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u8			id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+	u16			len = le16_to_cpu(ctrl->wLength);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u8			con_sel = (w_value >> 8) & 0xFF;
+	u8			cmd = (ctrl->bRequest & 0x0F);
+	struct usb_audio_control_selector *cs;
+	struct usb_audio_control *con;
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+			ctrl->bRequest, w_value, len, id);
+
+	list_for_each_entry(cs, &audio->cs, list) {
+		if (cs->id == id) {
+			list_for_each_entry(con, &cs->control, list) {
+				if (con->type == con_sel && con->get) {
+					value = con->get(con, cmd);
+					break;
+				}
+			}
+			break;
+		}
+	}
+
+	req->context = audio;
+	req->complete = f_audio_complete;
+	len = min_t(size_t, sizeof(value), len);
+	memcpy(req->buf, &value, len);
+
+	return len;
+}
+
+static int audio_set_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int			value = -EOPNOTSUPP;
+	u16			ep = le16_to_cpu(ctrl->wIndex);
+	u16			len = le16_to_cpu(ctrl->wLength);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	switch (ctrl->bRequest) {
+	case UAC_SET_CUR:
+		value = len;
+		break;
+
+	case UAC_SET_MIN:
+		break;
+
+	case UAC_SET_MAX:
+		break;
+
+	case UAC_SET_RES:
+		break;
+
+	case UAC_SET_MEM:
+		break;
+
+	default:
+		break;
+	}
+
+	return value;
+}
+
+static int audio_get_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int value = -EOPNOTSUPP;
+	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+	u16 len = le16_to_cpu(ctrl->wLength);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+
+	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	switch (ctrl->bRequest) {
+	case UAC_GET_CUR:
+	case UAC_GET_MIN:
+	case UAC_GET_MAX:
+	case UAC_GET_RES:
+		value = len;
+		break;
+	case UAC_GET_MEM:
+		break;
+	default:
+		break;
+	}
+
+	return value;
+}
+
+static int
+f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything; interface
+	 * activation uses set_alt().
+	 */
+	switch (ctrl->bRequestType) {
+	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+		value = audio_set_intf_req(f, ctrl);
+		break;
+
+	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+		value = audio_get_intf_req(f, ctrl);
+		break;
+
+	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_set_endpoint_req(f, ctrl);
+		break;
+
+	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_get_endpoint_req(f, ctrl);
+		break;
+
+	default:
+		ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			ERROR(cdev, "audio response on err %d\n", value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct f_audio		*audio = func_to_audio(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_ep *out_ep = audio->out_ep;
+	struct usb_request *req;
+	struct f_uac1_legacy_opts *opts;
+	int req_buf_size, req_count, audio_buf_size;
+	int i = 0, err = 0;
+
+	DBG(cdev, "intf %d, alt %d\n", intf, alt);
+
+	opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst);
+	req_buf_size = opts->req_buf_size;
+	req_count = opts->req_count;
+	audio_buf_size = opts->audio_buf_size;
+
+	/* No i/f has more than 2 alt settings */
+	if (alt > 1) {
+		ERROR(cdev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	if (intf == audio->ac_intf) {
+		/* Control I/f has only 1 AltSetting - 0 */
+		if (alt) {
+			ERROR(cdev, "%s:%d Error!\n", __func__, __LINE__);
+			return -EINVAL;
+		}
+		return 0;
+	} else if (intf == audio->as_intf) {
+		if (alt == 1) {
+			err = config_ep_by_speed(cdev->gadget, f, out_ep);
+			if (err)
+				return err;
+
+			usb_ep_enable(out_ep);
+			audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
+			if (IS_ERR(audio->copy_buf))
+				return -ENOMEM;
+
+			/*
+			 * allocate a bunch of read buffers
+			 * and queue them all at once.
+			 */
+			for (i = 0; i < req_count && err == 0; i++) {
+				req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
+				if (req) {
+					req->buf = kzalloc(req_buf_size,
+							GFP_ATOMIC);
+					if (req->buf) {
+						req->length = req_buf_size;
+						req->context = audio;
+						req->complete =
+							f_audio_complete;
+						err = usb_ep_queue(out_ep,
+							req, GFP_ATOMIC);
+						if (err)
+							ERROR(cdev,
+							"%s queue req: %d\n",
+							out_ep->name, err);
+					} else
+						err = -ENOMEM;
+				} else
+					err = -ENOMEM;
+			}
+
+		} else {
+			struct f_audio_buf *copy_buf = audio->copy_buf;
+			if (copy_buf) {
+				list_add_tail(&copy_buf->list,
+						&audio->play_queue);
+				schedule_work(&audio->playback_work);
+			}
+		}
+		audio->as_alt = alt;
+	}
+
+	return err;
+}
+
+static int f_audio_get_alt(struct usb_function *f, unsigned intf)
+{
+	struct f_audio		*audio = func_to_audio(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if (intf == audio->ac_intf)
+		return audio->ac_alt;
+	else if (intf == audio->as_intf)
+		return audio->as_alt;
+	else
+		ERROR(cdev, "%s:%d Invalid Interface %d!\n",
+		      __func__, __LINE__, intf);
+
+	return -EINVAL;
+}
+
+static void f_audio_disable(struct usb_function *f)
+{
+	return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_build_desc(struct f_audio *audio)
+{
+	struct gaudio *card = &audio->card;
+	u8 *sam_freq;
+	int rate;
+
+	/* Set channel numbers */
+	input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
+	as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
+
+	/* Set sample rates */
+	rate = u_audio_get_playback_rate(card);
+	sam_freq = as_type_i_desc.tSamFreq[0];
+	memcpy(sam_freq, &rate, 3);
+
+	/* Todo: Set Sample bits and other parameters */
+
+	return;
+}
+
+/* audio function driver setup/binding */
+static int
+f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_audio		*audio = func_to_audio(f);
+	struct usb_string	*us;
+	int			status;
+	struct usb_ep		*ep = NULL;
+	struct f_uac1_legacy_opts	*audio_opts;
+
+	audio_opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst);
+	audio->card.gadget = c->cdev->gadget;
+	/* set up ASLA audio devices */
+	if (!audio_opts->bound) {
+		status = gaudio_setup(&audio->card);
+		if (status < 0)
+			return status;
+		audio_opts->bound = true;
+	}
+	us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	ac_interface_desc.iInterface = us[STR_AC_IF].id;
+	input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
+	input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
+	feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id;
+	output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
+	as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
+	as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
+
+
+	f_audio_build_desc(audio);
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	ac_interface_desc.bInterfaceNumber = status;
+	audio->ac_intf = status;
+	audio->ac_alt = 0;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	as_interface_alt_0_desc.bInterfaceNumber = status;
+	as_interface_alt_1_desc.bInterfaceNumber = status;
+	audio->as_intf = status;
+	audio->as_alt = 0;
+
+	status = -ENODEV;
+
+	/* allocate instance-specific endpoints */
+	ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
+	if (!ep)
+		goto fail;
+	audio->out_ep = ep;
+	audio->out_ep->desc = &as_out_ep_desc;
+
+	status = -ENOMEM;
+
+	/* copy descriptors, and track endpoint copies */
+	status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
+					NULL);
+	if (status)
+		goto fail;
+	return 0;
+
+fail:
+	gaudio_cleanup(&audio->card);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
+{
+	con->data[cmd] = value;
+
+	return 0;
+}
+
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
+{
+	return con->data[cmd];
+}
+
+/* Todo: add more control selecotor dynamically */
+static int control_selector_init(struct f_audio *audio)
+{
+	INIT_LIST_HEAD(&audio->cs);
+	list_add(&feature_unit.list, &audio->cs);
+
+	INIT_LIST_HEAD(&feature_unit.control);
+	list_add(&mute_control.list, &feature_unit.control);
+	list_add(&volume_control.list, &feature_unit.control);
+
+	volume_control.data[UAC__CUR] = 0xffc0;
+	volume_control.data[UAC__MIN] = 0xe3a0;
+	volume_control.data[UAC__MAX] = 0xfff0;
+	volume_control.data[UAC__RES] = 0x0030;
+
+	return 0;
+}
+
+static inline
+struct f_uac1_legacy_opts *to_f_uac1_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uac1_legacy_opts,
+			    func_inst.group);
+}
+
+static void f_uac1_attr_release(struct config_item *item)
+{
+	struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac1_item_ops = {
+	.release	= f_uac1_attr_release,
+};
+
+#define UAC1_INT_ATTRIBUTE(name)					\
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
+					 char *page)			\
+{									\
+	struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item,		\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
+UAC1_INT_ATTRIBUTE(req_buf_size);
+UAC1_INT_ATTRIBUTE(req_count);
+UAC1_INT_ATTRIBUTE(audio_buf_size);
+
+#define UAC1_STR_ATTRIBUTE(name)					\
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item,	\
+					 char *page)			\
+{									\
+	struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%s\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac1_legacy_opts *opts = to_f_uac1_opts(item);		\
+	int ret = -EBUSY;						\
+	char *tmp;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt)						\
+		goto end;						\
+									\
+	tmp = kstrndup(page, len, GFP_KERNEL);				\
+	if (tmp) {							\
+		ret = -ENOMEM;						\
+		goto end;						\
+	}								\
+	if (opts->name##_alloc)						\
+		kfree(opts->name);					\
+	opts->name##_alloc = true;					\
+	opts->name = tmp;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
+UAC1_STR_ATTRIBUTE(fn_play);
+UAC1_STR_ATTRIBUTE(fn_cap);
+UAC1_STR_ATTRIBUTE(fn_cntl);
+
+static struct configfs_attribute *f_uac1_attrs[] = {
+	&f_uac1_opts_attr_req_buf_size,
+	&f_uac1_opts_attr_req_count,
+	&f_uac1_opts_attr_audio_buf_size,
+	&f_uac1_opts_attr_fn_play,
+	&f_uac1_opts_attr_fn_cap,
+	&f_uac1_opts_attr_fn_cntl,
+	NULL,
+};
+
+static const struct config_item_type f_uac1_func_type = {
+	.ct_item_ops	= &f_uac1_item_ops,
+	.ct_attrs	= f_uac1_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void f_audio_free_inst(struct usb_function_instance *f)
+{
+	struct f_uac1_legacy_opts *opts;
+
+	opts = container_of(f, struct f_uac1_legacy_opts, func_inst);
+	if (opts->fn_play_alloc)
+		kfree(opts->fn_play);
+	if (opts->fn_cap_alloc)
+		kfree(opts->fn_cap);
+	if (opts->fn_cntl_alloc)
+		kfree(opts->fn_cntl);
+	kfree(opts);
+}
+
+static struct usb_function_instance *f_audio_alloc_inst(void)
+{
+	struct f_uac1_legacy_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = f_audio_free_inst;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &f_uac1_func_type);
+
+	opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
+	opts->req_count = UAC1_REQ_COUNT;
+	opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE;
+	opts->fn_play = FILE_PCM_PLAYBACK;
+	opts->fn_cap = FILE_PCM_CAPTURE;
+	opts->fn_cntl = FILE_CONTROL;
+	return &opts->func_inst;
+}
+
+static void f_audio_free(struct usb_function *f)
+{
+	struct f_audio *audio = func_to_audio(f);
+	struct f_uac1_legacy_opts *opts;
+
+	gaudio_cleanup(&audio->card);
+	opts = container_of(f->fi, struct f_uac1_legacy_opts, func_inst);
+	kfree(audio);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
+{
+	struct f_audio *audio;
+	struct f_uac1_legacy_opts *opts;
+
+	/* allocate and initialize one new instance */
+	audio = kzalloc(sizeof(*audio), GFP_KERNEL);
+	if (!audio)
+		return ERR_PTR(-ENOMEM);
+
+	audio->card.func.name = "g_audio";
+
+	opts = container_of(fi, struct f_uac1_legacy_opts, func_inst);
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+	INIT_LIST_HEAD(&audio->play_queue);
+	spin_lock_init(&audio->lock);
+
+	audio->card.func.bind = f_audio_bind;
+	audio->card.func.unbind = f_audio_unbind;
+	audio->card.func.set_alt = f_audio_set_alt;
+	audio->card.func.get_alt = f_audio_get_alt;
+	audio->card.func.setup = f_audio_setup;
+	audio->card.func.disable = f_audio_disable;
+	audio->card.func.free_func = f_audio_free;
+
+	control_selector_init(audio);
+
+	INIT_WORK(&audio->playback_work, f_audio_playback_work);
+
+	return &audio->card.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bryan Wu");
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
new file mode 100644
index 0000000..d582921
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -0,0 +1,1012 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_uac2.c -- USB Audio Class 2.0 Function
+ *
+ * Copyright (C) 2011
+ *    Yadwinder Singh (yadi.brar01@gmail.com)
+ *    Jaswinder Singh (jaswinder.singh@linaro.org)
+ */
+
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/module.h>
+
+#include "u_audio.h"
+#include "u_uac2.h"
+
+/*
+ * The driver implements a simple UAC_2 topology.
+ * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
+ * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN
+ * Capture and Playback sampling rates are independently
+ *  controlled by two clock sources :
+ *    CLK_5 := c_srate, and CLK_6 := p_srate
+ */
+#define USB_OUT_IT_ID	1
+#define IO_IN_IT_ID	2
+#define IO_OUT_OT_ID	3
+#define USB_IN_OT_ID	4
+#define USB_OUT_CLK_ID	5
+#define USB_IN_CLK_ID	6
+
+#define CONTROL_ABSENT	0
+#define CONTROL_RDONLY	1
+#define CONTROL_RDWR	3
+
+#define CLK_FREQ_CTRL	0
+#define CLK_VLD_CTRL	2
+
+#define COPY_CTRL	0
+#define CONN_CTRL	2
+#define OVRLD_CTRL	4
+#define CLSTR_CTRL	6
+#define UNFLW_CTRL	8
+#define OVFLW_CTRL	10
+
+struct f_uac2 {
+	struct g_audio g_audio;
+	u8 ac_intf, as_in_intf, as_out_intf;
+	u8 ac_alt, as_in_alt, as_out_alt;	/* needed for get_alt() */
+};
+
+static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
+{
+	return container_of(f, struct f_uac2, g_audio.func);
+}
+
+static inline
+struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev)
+{
+	return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
+}
+
+/* --------- USB Function Interface ------------- */
+
+enum {
+	STR_ASSOC,
+	STR_IF_CTRL,
+	STR_CLKSRC_IN,
+	STR_CLKSRC_OUT,
+	STR_USB_IT,
+	STR_IO_IT,
+	STR_USB_OT,
+	STR_IO_OT,
+	STR_AS_OUT_ALT0,
+	STR_AS_OUT_ALT1,
+	STR_AS_IN_ALT0,
+	STR_AS_IN_ALT1,
+};
+
+static char clksrc_in[8];
+static char clksrc_out[8];
+
+static struct usb_string strings_fn[] = {
+	[STR_ASSOC].s = "Source/Sink",
+	[STR_IF_CTRL].s = "Topology Control",
+	[STR_CLKSRC_IN].s = clksrc_in,
+	[STR_CLKSRC_OUT].s = clksrc_out,
+	[STR_USB_IT].s = "USBH Out",
+	[STR_IO_IT].s = "USBD Out",
+	[STR_USB_OT].s = "USBH In",
+	[STR_IO_OT].s = "USBD In",
+	[STR_AS_OUT_ALT0].s = "Playback Inactive",
+	[STR_AS_OUT_ALT1].s = "Playback Active",
+	[STR_AS_IN_ALT0].s = "Capture Inactive",
+	[STR_AS_IN_ALT1].s = "Capture Active",
+	{ },
+};
+
+static struct usb_gadget_strings str_fn = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings_fn,
+};
+
+static struct usb_gadget_strings *fn_strings[] = {
+	&str_fn,
+	NULL,
+};
+
+static struct usb_interface_assoc_descriptor iad_desc = {
+	.bLength = sizeof iad_desc,
+	.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+	.bFirstInterface = 0,
+	.bInterfaceCount = 3,
+	.bFunctionClass = USB_CLASS_AUDIO,
+	.bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED,
+	.bFunctionProtocol = UAC_VERSION_2,
+};
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor std_ac_if_desc = {
+	.bLength = sizeof std_ac_if_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Clock source for IN traffic */
+static struct uac_clock_source_descriptor in_clk_src_desc = {
+	.bLength = sizeof in_clk_src_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
+	.bClockID = USB_IN_CLK_ID,
+	.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
+	.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+	.bAssocTerminal = 0,
+};
+
+/* Clock source for OUT traffic */
+static struct uac_clock_source_descriptor out_clk_src_desc = {
+	.bLength = sizeof out_clk_src_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
+	.bClockID = USB_OUT_CLK_ID,
+	.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
+	.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+	.bAssocTerminal = 0,
+};
+
+/* Input Terminal for USB_OUT */
+static struct uac2_input_terminal_descriptor usb_out_it_desc = {
+	.bLength = sizeof usb_out_it_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
+	.bTerminalID = USB_OUT_IT_ID,
+	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal = 0,
+	.bCSourceID = USB_OUT_CLK_ID,
+	.iChannelNames = 0,
+	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Input Terminal for I/O-In */
+static struct uac2_input_terminal_descriptor io_in_it_desc = {
+	.bLength = sizeof io_in_it_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_INPUT_TERMINAL,
+	.bTerminalID = IO_IN_IT_ID,
+	.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
+	.bAssocTerminal = 0,
+	.bCSourceID = USB_IN_CLK_ID,
+	.iChannelNames = 0,
+	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Ouput Terminal for USB_IN */
+static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
+	.bLength = sizeof usb_in_ot_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+	.bTerminalID = USB_IN_OT_ID,
+	.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
+	.bAssocTerminal = 0,
+	.bSourceID = IO_IN_IT_ID,
+	.bCSourceID = USB_IN_CLK_ID,
+	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
+};
+
+/* Ouput Terminal for I/O-Out */
+static struct uac2_output_terminal_descriptor io_out_ot_desc = {
+	.bLength = sizeof io_out_ot_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+	.bTerminalID = IO_OUT_OT_ID,
+	.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
+	.bAssocTerminal = 0,
+	.bSourceID = USB_OUT_IT_ID,
+	.bCSourceID = USB_OUT_CLK_ID,
+	.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
+};
+
+static struct uac2_ac_header_descriptor ac_hdr_desc = {
+	.bLength = sizeof ac_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_MS_HEADER,
+	.bcdADC = cpu_to_le16(0x200),
+	.bCategory = UAC2_FUNCTION_IO_BOX,
+	.wTotalLength = cpu_to_le16(sizeof in_clk_src_desc
+			+ sizeof out_clk_src_desc + sizeof usb_out_it_desc
+			+ sizeof io_in_it_desc + sizeof usb_in_ot_desc
+			+ sizeof io_out_ot_desc),
+	.bmControls = 0,
+};
+
+/* Audio Streaming OUT Interface - Alt0 */
+static struct usb_interface_descriptor std_as_out_if0_desc = {
+	.bLength = sizeof std_as_out_if0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Streaming OUT Interface - Alt1 */
+static struct usb_interface_descriptor std_as_out_if1_desc = {
+	.bLength = sizeof std_as_out_if1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 1,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Stream OUT Intface Desc */
+static struct uac2_as_header_descriptor as_out_hdr_desc = {
+	.bLength = sizeof as_out_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_AS_GENERAL,
+	.bTerminalLink = USB_OUT_IT_ID,
+	.bmControls = 0,
+	.bFormatType = UAC_FORMAT_TYPE_I,
+	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
+	.iChannelNames = 0,
+};
+
+/* Audio USB_OUT Format */
+static struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
+	.bLength = sizeof as_out_fmt1_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = UAC_FORMAT_TYPE,
+	.bFormatType = UAC_FORMAT_TYPE_I,
+};
+
+/* STD AS ISO OUT Endpoint */
+static struct usb_endpoint_descriptor fs_epout_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_OUT,
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1023),
+	.bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_epout_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1024),
+	.bInterval = 4,
+};
+
+/* CS AS ISO OUT Endpoint */
+static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
+	.bLength = sizeof as_iso_out_desc,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+
+	.bDescriptorSubtype = UAC_EP_GENERAL,
+	.bmAttributes = 0,
+	.bmControls = 0,
+	.bLockDelayUnits = 0,
+	.wLockDelay = 0,
+};
+
+/* Audio Streaming IN Interface - Alt0 */
+static struct usb_interface_descriptor std_as_in_if0_desc = {
+	.bLength = sizeof std_as_in_if0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Streaming IN Interface - Alt1 */
+static struct usb_interface_descriptor std_as_in_if1_desc = {
+	.bLength = sizeof std_as_in_if1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+
+	.bAlternateSetting = 1,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
+	.bInterfaceProtocol = UAC_VERSION_2,
+};
+
+/* Audio Stream IN Intface Desc */
+static struct uac2_as_header_descriptor as_in_hdr_desc = {
+	.bLength = sizeof as_in_hdr_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+
+	.bDescriptorSubtype = UAC_AS_GENERAL,
+	.bTerminalLink = USB_IN_OT_ID,
+	.bmControls = 0,
+	.bFormatType = UAC_FORMAT_TYPE_I,
+	.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
+	.iChannelNames = 0,
+};
+
+/* Audio USB_IN Format */
+static struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
+	.bLength = sizeof as_in_fmt1_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = UAC_FORMAT_TYPE,
+	.bFormatType = UAC_FORMAT_TYPE_I,
+};
+
+/* STD AS ISO IN Endpoint */
+static struct usb_endpoint_descriptor fs_epin_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1023),
+	.bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_epin_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
+	.wMaxPacketSize = cpu_to_le16(1024),
+	.bInterval = 4,
+};
+
+/* CS AS ISO IN Endpoint */
+static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
+	.bLength = sizeof as_iso_in_desc,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+
+	.bDescriptorSubtype = UAC_EP_GENERAL,
+	.bmAttributes = 0,
+	.bmControls = 0,
+	.bLockDelayUnits = 0,
+	.wLockDelay = 0,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&out_clk_src_desc,
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_out_if0_desc,
+	(struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+	(struct usb_descriptor_header *)&as_out_hdr_desc,
+	(struct usb_descriptor_header *)&as_out_fmt1_desc,
+	(struct usb_descriptor_header *)&fs_epout_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&fs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&out_clk_src_desc,
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_out_if0_desc,
+	(struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+	(struct usb_descriptor_header *)&as_out_hdr_desc,
+	(struct usb_descriptor_header *)&as_out_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epout_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+struct cntrl_cur_lay3 {
+	__le32	dCUR;
+};
+
+struct cntrl_range_lay3 {
+	__le16	wNumSubRanges;
+	__le32	dMIN;
+	__le32	dMAX;
+	__le32	dRES;
+} __packed;
+
+static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
+	struct usb_endpoint_descriptor *ep_desc,
+	unsigned int factor, bool is_playback)
+{
+	int chmask, srate, ssize;
+	u16 max_packet_size;
+
+	if (is_playback) {
+		chmask = uac2_opts->p_chmask;
+		srate = uac2_opts->p_srate;
+		ssize = uac2_opts->p_ssize;
+	} else {
+		chmask = uac2_opts->c_chmask;
+		srate = uac2_opts->c_srate;
+		ssize = uac2_opts->c_ssize;
+	}
+
+	max_packet_size = num_channels(chmask) * ssize *
+		DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1)));
+	ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_packet_size,
+				le16_to_cpu(ep_desc->wMaxPacketSize)));
+}
+
+static int
+afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
+{
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct g_audio *agdev = func_to_g_audio(fn);
+	struct usb_composite_dev *cdev = cfg->cdev;
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	struct f_uac2_opts *uac2_opts;
+	struct usb_string *us;
+	int ret;
+
+	uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst);
+
+	us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+	iad_desc.iFunction = us[STR_ASSOC].id;
+	std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
+	in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
+	out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
+	usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
+	io_in_it_desc.iTerminal = us[STR_IO_IT].id;
+	usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
+	io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
+	std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
+	std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
+	std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
+	std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
+
+
+	/* Initialize the configurable parameters */
+	usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
+	usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
+	io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
+	io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
+	as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
+	as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
+	as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
+	as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
+	as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
+	as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
+	as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
+	as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
+
+	snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
+	snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return ret;
+	}
+	iad_desc.bFirstInterface = ret;
+
+	std_ac_if_desc.bInterfaceNumber = ret;
+	uac2->ac_intf = ret;
+	uac2->ac_alt = 0;
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return ret;
+	}
+	std_as_out_if0_desc.bInterfaceNumber = ret;
+	std_as_out_if1_desc.bInterfaceNumber = ret;
+	uac2->as_out_intf = ret;
+	uac2->as_out_alt = 0;
+
+	ret = usb_interface_id(cfg, fn);
+	if (ret < 0) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return ret;
+	}
+	std_as_in_if0_desc.bInterfaceNumber = ret;
+	std_as_in_if1_desc.bInterfaceNumber = ret;
+	uac2->as_in_intf = ret;
+	uac2->as_in_alt = 0;
+
+	/* Calculate wMaxPacketSize according to audio bandwidth */
+	set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
+	set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false);
+	set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
+	set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);
+
+	agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+	if (!agdev->out_ep) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -ENODEV;
+	}
+
+	agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
+	if (!agdev->in_ep) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -ENODEV;
+	}
+
+	agdev->in_ep_maxpsize = max_t(u16,
+				le16_to_cpu(fs_epin_desc.wMaxPacketSize),
+				le16_to_cpu(hs_epin_desc.wMaxPacketSize));
+	agdev->out_ep_maxpsize = max_t(u16,
+				le16_to_cpu(fs_epout_desc.wMaxPacketSize),
+				le16_to_cpu(hs_epout_desc.wMaxPacketSize));
+
+	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
+	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
+				     NULL);
+	if (ret)
+		return ret;
+
+	agdev->gadget = gadget;
+
+	agdev->params.p_chmask = uac2_opts->p_chmask;
+	agdev->params.p_srate = uac2_opts->p_srate;
+	agdev->params.p_ssize = uac2_opts->p_ssize;
+	agdev->params.c_chmask = uac2_opts->c_chmask;
+	agdev->params.c_srate = uac2_opts->c_srate;
+	agdev->params.c_ssize = uac2_opts->c_ssize;
+	agdev->params.req_number = uac2_opts->req_number;
+	ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
+	if (ret)
+		goto err_free_descs;
+	return 0;
+
+err_free_descs:
+	usb_free_all_descriptors(fn);
+	agdev->gadget = NULL;
+	return ret;
+}
+
+static int
+afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
+{
+	struct usb_composite_dev *cdev = fn->config->cdev;
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct usb_gadget *gadget = cdev->gadget;
+	struct device *dev = &gadget->dev;
+	int ret = 0;
+
+	/* No i/f has more than 2 alt settings */
+	if (alt > 1) {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	if (intf == uac2->ac_intf) {
+		/* Control I/f has only 1 AltSetting - 0 */
+		if (alt) {
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	if (intf == uac2->as_out_intf) {
+		uac2->as_out_alt = alt;
+
+		if (alt)
+			ret = u_audio_start_capture(&uac2->g_audio);
+		else
+			u_audio_stop_capture(&uac2->g_audio);
+	} else if (intf == uac2->as_in_intf) {
+		uac2->as_in_alt = alt;
+
+		if (alt)
+			ret = u_audio_start_playback(&uac2->g_audio);
+		else
+			u_audio_stop_playback(&uac2->g_audio);
+	} else {
+		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int
+afunc_get_alt(struct usb_function *fn, unsigned intf)
+{
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct g_audio *agdev = func_to_g_audio(fn);
+
+	if (intf == uac2->ac_intf)
+		return uac2->ac_alt;
+	else if (intf == uac2->as_out_intf)
+		return uac2->as_out_alt;
+	else if (intf == uac2->as_in_intf)
+		return uac2->as_in_alt;
+	else
+		dev_err(&agdev->gadget->dev,
+			"%s:%d Invalid Interface %d!\n",
+			__func__, __LINE__, intf);
+
+	return -EINVAL;
+}
+
+static void
+afunc_disable(struct usb_function *fn)
+{
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+
+	uac2->as_in_alt = 0;
+	uac2->as_out_alt = 0;
+	u_audio_stop_capture(&uac2->g_audio);
+	u_audio_stop_playback(&uac2->g_audio);
+}
+
+static int
+in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_request *req = fn->config->cdev->req;
+	struct g_audio *agdev = func_to_g_audio(fn);
+	struct f_uac2_opts *opts;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u8 entity_id = (w_index >> 8) & 0xff;
+	u8 control_selector = w_value >> 8;
+	int value = -EOPNOTSUPP;
+	int p_srate, c_srate;
+
+	opts = g_audio_to_uac2_opts(agdev);
+	p_srate = opts->p_srate;
+	c_srate = opts->c_srate;
+
+	if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+		struct cntrl_cur_lay3 c;
+		memset(&c, 0, sizeof(struct cntrl_cur_lay3));
+
+		if (entity_id == USB_IN_CLK_ID)
+			c.dCUR = cpu_to_le32(p_srate);
+		else if (entity_id == USB_OUT_CLK_ID)
+			c.dCUR = cpu_to_le32(c_srate);
+
+		value = min_t(unsigned, w_length, sizeof c);
+		memcpy(req->buf, &c, value);
+	} else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
+		*(u8 *)req->buf = 1;
+		value = min_t(unsigned, w_length, 1);
+	} else {
+		dev_err(&agdev->gadget->dev,
+			"%s:%d control_selector=%d TODO!\n",
+			__func__, __LINE__, control_selector);
+	}
+
+	return value;
+}
+
+static int
+in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_request *req = fn->config->cdev->req;
+	struct g_audio *agdev = func_to_g_audio(fn);
+	struct f_uac2_opts *opts;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u8 entity_id = (w_index >> 8) & 0xff;
+	u8 control_selector = w_value >> 8;
+	struct cntrl_range_lay3 r;
+	int value = -EOPNOTSUPP;
+	int p_srate, c_srate;
+
+	opts = g_audio_to_uac2_opts(agdev);
+	p_srate = opts->p_srate;
+	c_srate = opts->c_srate;
+
+	if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+		if (entity_id == USB_IN_CLK_ID)
+			r.dMIN = cpu_to_le32(p_srate);
+		else if (entity_id == USB_OUT_CLK_ID)
+			r.dMIN = cpu_to_le32(c_srate);
+		else
+			return -EOPNOTSUPP;
+
+		r.dMAX = r.dMIN;
+		r.dRES = 0;
+		r.wNumSubRanges = cpu_to_le16(1);
+
+		value = min_t(unsigned, w_length, sizeof r);
+		memcpy(req->buf, &r, value);
+	} else {
+		dev_err(&agdev->gadget->dev,
+			"%s:%d control_selector=%d TODO!\n",
+			__func__, __LINE__, control_selector);
+	}
+
+	return value;
+}
+
+static int
+ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	if (cr->bRequest == UAC2_CS_CUR)
+		return in_rq_cur(fn, cr);
+	else if (cr->bRequest == UAC2_CS_RANGE)
+		return in_rq_range(fn, cr);
+	else
+		return -EOPNOTSUPP;
+}
+
+static int
+out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	u16 w_length = le16_to_cpu(cr->wLength);
+	u16 w_value = le16_to_cpu(cr->wValue);
+	u8 control_selector = w_value >> 8;
+
+	if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+		return w_length;
+
+	return -EOPNOTSUPP;
+}
+
+static int
+setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct f_uac2 *uac2 = func_to_uac2(fn);
+	struct g_audio *agdev = func_to_g_audio(fn);
+	u16 w_index = le16_to_cpu(cr->wIndex);
+	u8 intf = w_index & 0xff;
+
+	if (intf != uac2->ac_intf) {
+		dev_err(&agdev->gadget->dev,
+			"%s:%d Error!\n", __func__, __LINE__);
+		return -EOPNOTSUPP;
+	}
+
+	if (cr->bRequestType & USB_DIR_IN)
+		return ac_rq_in(fn, cr);
+	else if (cr->bRequest == UAC2_CS_CUR)
+		return out_rq_cur(fn, cr);
+
+	return -EOPNOTSUPP;
+}
+
+static int
+afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+	struct usb_composite_dev *cdev = fn->config->cdev;
+	struct g_audio *agdev = func_to_g_audio(fn);
+	struct usb_request *req = cdev->req;
+	u16 w_length = le16_to_cpu(cr->wLength);
+	int value = -EOPNOTSUPP;
+
+	/* Only Class specific requests are supposed to reach here */
+	if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+		return -EOPNOTSUPP;
+
+	if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE)
+		value = setup_rq_inf(fn, cr);
+	else
+		dev_err(&agdev->gadget->dev, "%s:%d Error!\n",
+				__func__, __LINE__);
+
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			dev_err(&agdev->gadget->dev,
+				"%s:%d Error!\n", __func__, __LINE__);
+			req->status = 0;
+		}
+	}
+
+	return value;
+}
+
+static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uac2_opts,
+			    func_inst.group);
+}
+
+static void f_uac2_attr_release(struct config_item *item)
+{
+	struct f_uac2_opts *opts = to_f_uac2_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_uac2_item_ops = {
+	.release	= f_uac2_attr_release,
+};
+
+#define UAC2_ATTRIBUTE(name)						\
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item,	\
+					 char *page)			\
+{									\
+	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
+					  const char *page, size_t len)	\
+{									\
+	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
+UAC2_ATTRIBUTE(p_chmask);
+UAC2_ATTRIBUTE(p_srate);
+UAC2_ATTRIBUTE(p_ssize);
+UAC2_ATTRIBUTE(c_chmask);
+UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE(c_ssize);
+UAC2_ATTRIBUTE(req_number);
+
+static struct configfs_attribute *f_uac2_attrs[] = {
+	&f_uac2_opts_attr_p_chmask,
+	&f_uac2_opts_attr_p_srate,
+	&f_uac2_opts_attr_p_ssize,
+	&f_uac2_opts_attr_c_chmask,
+	&f_uac2_opts_attr_c_srate,
+	&f_uac2_opts_attr_c_ssize,
+	&f_uac2_opts_attr_req_number,
+	NULL,
+};
+
+static const struct config_item_type f_uac2_func_type = {
+	.ct_item_ops	= &f_uac2_item_ops,
+	.ct_attrs	= f_uac2_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static void afunc_free_inst(struct usb_function_instance *f)
+{
+	struct f_uac2_opts *opts;
+
+	opts = container_of(f, struct f_uac2_opts, func_inst);
+	kfree(opts);
+}
+
+static struct usb_function_instance *afunc_alloc_inst(void)
+{
+	struct f_uac2_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = afunc_free_inst;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &f_uac2_func_type);
+
+	opts->p_chmask = UAC2_DEF_PCHMASK;
+	opts->p_srate = UAC2_DEF_PSRATE;
+	opts->p_ssize = UAC2_DEF_PSSIZE;
+	opts->c_chmask = UAC2_DEF_CCHMASK;
+	opts->c_srate = UAC2_DEF_CSRATE;
+	opts->c_ssize = UAC2_DEF_CSSIZE;
+	opts->req_number = UAC2_DEF_REQ_NUM;
+	return &opts->func_inst;
+}
+
+static void afunc_free(struct usb_function *f)
+{
+	struct g_audio *agdev;
+	struct f_uac2_opts *opts;
+
+	agdev = func_to_g_audio(f);
+	opts = container_of(f->fi, struct f_uac2_opts, func_inst);
+	kfree(agdev);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct g_audio *agdev = func_to_g_audio(f);
+
+	g_audio_cleanup(agdev);
+	usb_free_all_descriptors(f);
+
+	agdev->gadget = NULL;
+}
+
+static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
+{
+	struct f_uac2	*uac2;
+	struct f_uac2_opts *opts;
+
+	uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL);
+	if (uac2 == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	opts = container_of(fi, struct f_uac2_opts, func_inst);
+	mutex_lock(&opts->lock);
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
+	uac2->g_audio.func.name = "uac2_func";
+	uac2->g_audio.func.bind = afunc_bind;
+	uac2->g_audio.func.unbind = afunc_unbind;
+	uac2->g_audio.func.set_alt = afunc_set_alt;
+	uac2->g_audio.func.get_alt = afunc_get_alt;
+	uac2->g_audio.func.disable = afunc_disable;
+	uac2->g_audio.func.setup = afunc_setup;
+	uac2->g_audio.func.free_func = afunc_free;
+
+	return &uac2->g_audio.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yadwinder Singh");
+MODULE_AUTHOR("Jaswinder Singh");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
new file mode 100644
index 0000000..d8ce786
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -0,0 +1,956 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	uvc_gadget.c  --  USB Video Class Gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_uvc.h>
+#include <linux/usb/video.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+
+#include "u_uvc.h"
+#include "uvc.h"
+#include "uvc_configfs.h"
+#include "uvc_v4l2.h"
+#include "uvc_video.h"
+
+unsigned int uvc_gadget_trace_param;
+module_param_named(trace, uvc_gadget_trace_param, uint, 0644);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+
+/* --------------------------------------------------------------------------
+ * Function descriptors
+ */
+
+/* string IDs are assigned dynamically */
+
+#define UVC_STRING_CONTROL_IDX			0
+#define UVC_STRING_STREAMING_IDX		1
+
+static struct usb_string uvc_en_us_strings[] = {
+	[UVC_STRING_CONTROL_IDX].s = "UVC Camera",
+	[UVC_STRING_STREAMING_IDX].s = "Video Streaming",
+	{  }
+};
+
+static struct usb_gadget_strings uvc_stringtab = {
+	.language = 0x0409,	/* en-us */
+	.strings = uvc_en_us_strings,
+};
+
+static struct usb_gadget_strings *uvc_function_strings[] = {
+	&uvc_stringtab,
+	NULL,
+};
+
+#define UVC_INTF_VIDEO_CONTROL			0
+#define UVC_INTF_VIDEO_STREAMING		1
+
+#define UVC_STATUS_MAX_PACKET_SIZE		16	/* 16 bytes status */
+
+static struct usb_interface_assoc_descriptor uvc_iad = {
+	.bLength		= sizeof(uvc_iad),
+	.bDescriptorType	= USB_DT_INTERFACE_ASSOCIATION,
+	.bFirstInterface	= 0,
+	.bInterfaceCount	= 2,
+	.bFunctionClass		= USB_CLASS_VIDEO,
+	.bFunctionSubClass	= UVC_SC_VIDEO_INTERFACE_COLLECTION,
+	.bFunctionProtocol	= 0x00,
+	.iFunction		= 0,
+};
+
+static struct usb_interface_descriptor uvc_control_intf = {
+	.bLength		= USB_DT_INTERFACE_SIZE,
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= UVC_INTF_VIDEO_CONTROL,
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 1,
+	.bInterfaceClass	= USB_CLASS_VIDEO,
+	.bInterfaceSubClass	= UVC_SC_VIDEOCONTROL,
+	.bInterfaceProtocol	= 0x00,
+	.iInterface		= 0,
+};
+
+static struct usb_endpoint_descriptor uvc_control_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize		= cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+	.bInterval		= 8,
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
+	.bLength		= sizeof(uvc_ss_control_comp),
+	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
+	/* The following 3 values can be tweaked if necessary. */
+	.bMaxBurst		= 0,
+	.bmAttributes		= 0,
+	.wBytesPerInterval	= cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+};
+
+static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
+	.bLength		= UVC_DT_CONTROL_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_CS_ENDPOINT,
+	.bDescriptorSubType	= UVC_EP_INTERRUPT,
+	.wMaxTransferSize	= cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
+};
+
+static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
+	.bLength		= USB_DT_INTERFACE_SIZE,
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= UVC_INTF_VIDEO_STREAMING,
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 0,
+	.bInterfaceClass	= USB_CLASS_VIDEO,
+	.bInterfaceSubClass	= UVC_SC_VIDEOSTREAMING,
+	.bInterfaceProtocol	= 0x00,
+	.iInterface		= 0,
+};
+
+static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
+	.bLength		= USB_DT_INTERFACE_SIZE,
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= UVC_INTF_VIDEO_STREAMING,
+	.bAlternateSetting	= 1,
+	.bNumEndpoints		= 1,
+	.bInterfaceClass	= USB_CLASS_VIDEO,
+	.bInterfaceSubClass	= UVC_SC_VIDEOSTREAMING,
+	.bInterfaceProtocol	= 0x00,
+	.iInterface		= 0,
+};
+
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_SYNC_ASYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_SYNC_ASYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
+static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_SYNC_ASYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
+static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
+	.bLength		= sizeof(uvc_ss_streaming_comp),
+	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
+	/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
+	 * initialized from module parameters.
+	 */
+};
+
+static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
+	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+	(struct usb_descriptor_header *) &uvc_fs_streaming_ep,
+	NULL,
+};
+
+static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
+	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+	(struct usb_descriptor_header *) &uvc_hs_streaming_ep,
+	NULL,
+};
+
+static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
+	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
+	(struct usb_descriptor_header *) &uvc_ss_streaming_ep,
+	(struct usb_descriptor_header *) &uvc_ss_streaming_comp,
+	NULL,
+};
+
+void uvc_set_trace_param(unsigned int trace)
+{
+	uvc_gadget_trace_param = trace;
+}
+EXPORT_SYMBOL(uvc_set_trace_param);
+
+/* --------------------------------------------------------------------------
+ * Control requests
+ */
+
+static void
+uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct uvc_device *uvc = req->context;
+	struct v4l2_event v4l2_event;
+	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+
+	if (uvc->event_setup_out) {
+		uvc->event_setup_out = 0;
+
+		memset(&v4l2_event, 0, sizeof(v4l2_event));
+		v4l2_event.type = UVC_EVENT_DATA;
+		uvc_event->data.length = req->actual;
+		memcpy(&uvc_event->data.data, req->buf, req->actual);
+		v4l2_event_queue(&uvc->vdev, &v4l2_event);
+	}
+}
+
+static int
+uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct v4l2_event v4l2_event;
+	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+
+	/* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
+	 *	ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
+	 *	le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
+	 */
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
+		INFO(f->config->cdev, "invalid request type\n");
+		return -EINVAL;
+	}
+
+	/* Stall too big requests. */
+	if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
+		return -EINVAL;
+
+	/* Tell the complete callback to generate an event for the next request
+	 * that will be enqueued by UVCIOC_SEND_RESPONSE.
+	 */
+	uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN);
+	uvc->event_length = le16_to_cpu(ctrl->wLength);
+
+	memset(&v4l2_event, 0, sizeof(v4l2_event));
+	v4l2_event.type = UVC_EVENT_SETUP;
+	memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
+	v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+	return 0;
+}
+
+void uvc_function_setup_continue(struct uvc_device *uvc)
+{
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+
+	usb_composite_setup_continue(cdev);
+}
+
+static int
+uvc_function_get_alt(struct usb_function *f, unsigned interface)
+{
+	struct uvc_device *uvc = to_uvc(f);
+
+	INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
+
+	if (interface == uvc->control_intf)
+		return 0;
+	else if (interface != uvc->streaming_intf)
+		return -EINVAL;
+	else
+		return uvc->video.ep->enabled ? 1 : 0;
+}
+
+static int
+uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct v4l2_event v4l2_event;
+	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+	int ret;
+
+	INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
+
+	if (interface == uvc->control_intf) {
+		if (alt)
+			return -EINVAL;
+
+		INFO(cdev, "reset UVC Control\n");
+		usb_ep_disable(uvc->control_ep);
+
+		if (!uvc->control_ep->desc)
+			if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep))
+				return -EINVAL;
+
+		usb_ep_enable(uvc->control_ep);
+
+		if (uvc->state == UVC_STATE_DISCONNECTED) {
+			memset(&v4l2_event, 0, sizeof(v4l2_event));
+			v4l2_event.type = UVC_EVENT_CONNECT;
+			uvc_event->speed = cdev->gadget->speed;
+			v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+			uvc->state = UVC_STATE_CONNECTED;
+		}
+
+		return 0;
+	}
+
+	if (interface != uvc->streaming_intf)
+		return -EINVAL;
+
+	/* TODO
+	if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
+		return alt ? -EINVAL : 0;
+	*/
+
+	switch (alt) {
+	case 0:
+		if (uvc->state != UVC_STATE_STREAMING)
+			return 0;
+
+		if (uvc->video.ep)
+			usb_ep_disable(uvc->video.ep);
+
+		memset(&v4l2_event, 0, sizeof(v4l2_event));
+		v4l2_event.type = UVC_EVENT_STREAMOFF;
+		v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+		uvc->state = UVC_STATE_CONNECTED;
+		return 0;
+
+	case 1:
+		if (uvc->state != UVC_STATE_CONNECTED)
+			return 0;
+
+		if (!uvc->video.ep)
+			return -EINVAL;
+
+		INFO(cdev, "reset UVC\n");
+		usb_ep_disable(uvc->video.ep);
+
+		ret = config_ep_by_speed(f->config->cdev->gadget,
+				&(uvc->func), uvc->video.ep);
+		if (ret)
+			return ret;
+		usb_ep_enable(uvc->video.ep);
+
+		memset(&v4l2_event, 0, sizeof(v4l2_event));
+		v4l2_event.type = UVC_EVENT_STREAMON;
+		v4l2_event_queue(&uvc->vdev, &v4l2_event);
+		return USB_GADGET_DELAYED_STATUS;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static void
+uvc_function_disable(struct usb_function *f)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct v4l2_event v4l2_event;
+
+	INFO(f->config->cdev, "uvc_function_disable\n");
+
+	memset(&v4l2_event, 0, sizeof(v4l2_event));
+	v4l2_event.type = UVC_EVENT_DISCONNECT;
+	v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+	uvc->state = UVC_STATE_DISCONNECTED;
+
+	usb_ep_disable(uvc->video.ep);
+	usb_ep_disable(uvc->control_ep);
+}
+
+/* --------------------------------------------------------------------------
+ * Connection / disconnection
+ */
+
+void
+uvc_function_connect(struct uvc_device *uvc)
+{
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+	int ret;
+
+	if ((ret = usb_function_activate(&uvc->func)) < 0)
+		INFO(cdev, "UVC connect failed with %d\n", ret);
+}
+
+void
+uvc_function_disconnect(struct uvc_device *uvc)
+{
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+	int ret;
+
+	if ((ret = usb_function_deactivate(&uvc->func)) < 0)
+		INFO(cdev, "UVC disconnect failed with %d\n", ret);
+}
+
+/* --------------------------------------------------------------------------
+ * USB probe and disconnect
+ */
+
+static ssize_t function_name_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct uvc_device *uvc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", uvc->func.fi->group.cg_item.ci_name);
+}
+
+static DEVICE_ATTR_RO(function_name);
+
+static int
+uvc_register_video(struct uvc_device *uvc)
+{
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+	int ret;
+
+	/* TODO reference counting. */
+	uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
+	uvc->vdev.fops = &uvc_v4l2_fops;
+	uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
+	uvc->vdev.release = video_device_release_empty;
+	uvc->vdev.vfl_dir = VFL_DIR_TX;
+	uvc->vdev.lock = &uvc->video.mutex;
+	strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
+
+	video_set_drvdata(&uvc->vdev, uvc);
+
+	ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+	if (ret < 0)
+		return ret;
+
+	ret = device_create_file(&uvc->vdev.dev, &dev_attr_function_name);
+	if (ret < 0) {
+		video_unregister_device(&uvc->vdev);
+		return ret;
+	}
+
+	return 0;
+}
+
+#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
+	do { \
+		memcpy(mem, desc, (desc)->bLength); \
+		*(dst)++ = mem; \
+		mem += (desc)->bLength; \
+	} while (0);
+
+#define UVC_COPY_DESCRIPTORS(mem, dst, src) \
+	do { \
+		const struct usb_descriptor_header * const *__src; \
+		for (__src = src; *__src; ++__src) { \
+			memcpy(mem, *__src, (*__src)->bLength); \
+			*dst++ = mem; \
+			mem += (*__src)->bLength; \
+		} \
+	} while (0)
+
+static struct usb_descriptor_header **
+uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
+{
+	struct uvc_input_header_descriptor *uvc_streaming_header;
+	struct uvc_header_descriptor *uvc_control_header;
+	const struct uvc_descriptor_header * const *uvc_control_desc;
+	const struct uvc_descriptor_header * const *uvc_streaming_cls;
+	const struct usb_descriptor_header * const *uvc_streaming_std;
+	const struct usb_descriptor_header * const *src;
+	struct usb_descriptor_header **dst;
+	struct usb_descriptor_header **hdr;
+	unsigned int control_size;
+	unsigned int streaming_size;
+	unsigned int n_desc;
+	unsigned int bytes;
+	void *mem;
+
+	switch (speed) {
+	case USB_SPEED_SUPER:
+		uvc_control_desc = uvc->desc.ss_control;
+		uvc_streaming_cls = uvc->desc.ss_streaming;
+		uvc_streaming_std = uvc_ss_streaming;
+		break;
+
+	case USB_SPEED_HIGH:
+		uvc_control_desc = uvc->desc.fs_control;
+		uvc_streaming_cls = uvc->desc.hs_streaming;
+		uvc_streaming_std = uvc_hs_streaming;
+		break;
+
+	case USB_SPEED_FULL:
+	default:
+		uvc_control_desc = uvc->desc.fs_control;
+		uvc_streaming_cls = uvc->desc.fs_streaming;
+		uvc_streaming_std = uvc_fs_streaming;
+		break;
+	}
+
+	if (!uvc_control_desc || !uvc_streaming_cls)
+		return ERR_PTR(-ENODEV);
+
+	/* Descriptors layout
+	 *
+	 * uvc_iad
+	 * uvc_control_intf
+	 * Class-specific UVC control descriptors
+	 * uvc_control_ep
+	 * uvc_control_cs_ep
+	 * uvc_ss_control_comp (for SS only)
+	 * uvc_streaming_intf_alt0
+	 * Class-specific UVC streaming descriptors
+	 * uvc_{fs|hs}_streaming
+	 */
+
+	/* Count descriptors and compute their size. */
+	control_size = 0;
+	streaming_size = 0;
+	bytes = uvc_iad.bLength + uvc_control_intf.bLength
+	      + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
+	      + uvc_streaming_intf_alt0.bLength;
+
+	if (speed == USB_SPEED_SUPER) {
+		bytes += uvc_ss_control_comp.bLength;
+		n_desc = 6;
+	} else {
+		n_desc = 5;
+	}
+
+	for (src = (const struct usb_descriptor_header **)uvc_control_desc;
+	     *src; ++src) {
+		control_size += (*src)->bLength;
+		bytes += (*src)->bLength;
+		n_desc++;
+	}
+	for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
+	     *src; ++src) {
+		streaming_size += (*src)->bLength;
+		bytes += (*src)->bLength;
+		n_desc++;
+	}
+	for (src = uvc_streaming_std; *src; ++src) {
+		bytes += (*src)->bLength;
+		n_desc++;
+	}
+
+	mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
+	if (mem == NULL)
+		return NULL;
+
+	hdr = mem;
+	dst = mem;
+	mem += (n_desc + 1) * sizeof(*src);
+
+	/* Copy the descriptors. */
+	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
+	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);
+
+	uvc_control_header = mem;
+	UVC_COPY_DESCRIPTORS(mem, dst,
+		(const struct usb_descriptor_header **)uvc_control_desc);
+	uvc_control_header->wTotalLength = cpu_to_le16(control_size);
+	uvc_control_header->bInCollection = 1;
+	uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
+
+	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
+	if (speed == USB_SPEED_SUPER)
+		UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
+
+	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
+	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
+
+	uvc_streaming_header = mem;
+	UVC_COPY_DESCRIPTORS(mem, dst,
+		(const struct usb_descriptor_header**)uvc_streaming_cls);
+	uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
+	uvc_streaming_header->bEndpointAddress = uvc->video.ep->address;
+
+	UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
+
+	*dst = NULL;
+	return hdr;
+}
+
+static int
+uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct uvc_device *uvc = to_uvc(f);
+	struct usb_string *us;
+	unsigned int max_packet_mult;
+	unsigned int max_packet_size;
+	struct usb_ep *ep;
+	struct f_uvc_opts *opts;
+	int ret = -EINVAL;
+
+	INFO(cdev, "uvc_function_bind\n");
+
+	opts = fi_to_f_uvc_opts(f->fi);
+	/* Sanity check the streaming endpoint module parameters.
+	 */
+	opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
+	opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
+	opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+
+	/* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
+	if (opts->streaming_maxburst &&
+	    (opts->streaming_maxpacket % 1024) != 0) {
+		opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);
+		INFO(cdev, "overriding streaming_maxpacket to %d\n",
+		     opts->streaming_maxpacket);
+	}
+
+	/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
+	 * module parameters.
+	 *
+	 * NOTE: We assume that the user knows what they are doing and won't
+	 * give parameters that their UDC doesn't support.
+	 */
+	if (opts->streaming_maxpacket <= 1024) {
+		max_packet_mult = 1;
+		max_packet_size = opts->streaming_maxpacket;
+	} else if (opts->streaming_maxpacket <= 2048) {
+		max_packet_mult = 2;
+		max_packet_size = opts->streaming_maxpacket / 2;
+	} else {
+		max_packet_mult = 3;
+		max_packet_size = opts->streaming_maxpacket / 3;
+	}
+
+	uvc_fs_streaming_ep.wMaxPacketSize =
+		cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
+	uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
+
+	uvc_hs_streaming_ep.wMaxPacketSize =
+		cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
+	uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
+
+	uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
+	uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
+	uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
+	uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
+	uvc_ss_streaming_comp.wBytesPerInterval =
+		cpu_to_le16(max_packet_size * max_packet_mult *
+			    (opts->streaming_maxburst + 1));
+
+	/* Allocate endpoints. */
+	ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
+	if (!ep) {
+		INFO(cdev, "Unable to allocate control EP\n");
+		goto error;
+	}
+	uvc->control_ep = ep;
+
+	if (gadget_is_superspeed(c->cdev->gadget))
+		ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
+					  &uvc_ss_streaming_comp);
+	else if (gadget_is_dualspeed(cdev->gadget))
+		ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
+	else
+		ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+
+	if (!ep) {
+		INFO(cdev, "Unable to allocate streaming EP\n");
+		goto error;
+	}
+	uvc->video.ep = ep;
+
+	uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+	uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+	uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+
+	us = usb_gstrings_attach(cdev, uvc_function_strings,
+				 ARRAY_SIZE(uvc_en_us_strings));
+	if (IS_ERR(us)) {
+		ret = PTR_ERR(us);
+		goto error;
+	}
+	uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
+	uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
+	ret = us[UVC_STRING_STREAMING_IDX].id;
+	uvc_streaming_intf_alt0.iInterface = ret;
+	uvc_streaming_intf_alt1.iInterface = ret;
+
+	/* Allocate interface IDs. */
+	if ((ret = usb_interface_id(c, f)) < 0)
+		goto error;
+	uvc_iad.bFirstInterface = ret;
+	uvc_control_intf.bInterfaceNumber = ret;
+	uvc->control_intf = ret;
+
+	if ((ret = usb_interface_id(c, f)) < 0)
+		goto error;
+	uvc_streaming_intf_alt0.bInterfaceNumber = ret;
+	uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+	uvc->streaming_intf = ret;
+
+	/* Copy descriptors */
+	f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
+	if (IS_ERR(f->fs_descriptors)) {
+		ret = PTR_ERR(f->fs_descriptors);
+		f->fs_descriptors = NULL;
+		goto error;
+	}
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+		if (IS_ERR(f->hs_descriptors)) {
+			ret = PTR_ERR(f->hs_descriptors);
+			f->hs_descriptors = NULL;
+			goto error;
+		}
+	}
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+		if (IS_ERR(f->ss_descriptors)) {
+			ret = PTR_ERR(f->ss_descriptors);
+			f->ss_descriptors = NULL;
+			goto error;
+		}
+	}
+
+	/* Preallocate control endpoint request. */
+	uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+	uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
+	if (uvc->control_req == NULL || uvc->control_buf == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	uvc->control_req->buf = uvc->control_buf;
+	uvc->control_req->complete = uvc_function_ep0_complete;
+	uvc->control_req->context = uvc;
+
+	if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
+		printk(KERN_INFO "v4l2_device_register failed\n");
+		goto error;
+	}
+
+	/* Initialise video. */
+	ret = uvcg_video_init(&uvc->video);
+	if (ret < 0)
+		goto error;
+
+	/* Register a V4L2 device. */
+	ret = uvc_register_video(uvc);
+	if (ret < 0) {
+		printk(KERN_INFO "Unable to register video device\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	v4l2_device_unregister(&uvc->v4l2_dev);
+
+	if (uvc->control_req)
+		usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+	kfree(uvc->control_buf);
+
+	usb_free_all_descriptors(f);
+	return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * USB gadget function
+ */
+
+static void uvc_free_inst(struct usb_function_instance *f)
+{
+	struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
+
+	mutex_destroy(&opts->lock);
+	kfree(opts);
+}
+
+static struct usb_function_instance *uvc_alloc_inst(void)
+{
+	struct f_uvc_opts *opts;
+	struct uvc_camera_terminal_descriptor *cd;
+	struct uvc_processing_unit_descriptor *pd;
+	struct uvc_output_terminal_descriptor *od;
+	struct uvc_color_matching_descriptor *md;
+	struct uvc_descriptor_header **ctl_cls;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+	opts->func_inst.free_func_inst = uvc_free_inst;
+	mutex_init(&opts->lock);
+
+	cd = &opts->uvc_camera_terminal;
+	cd->bLength			= UVC_DT_CAMERA_TERMINAL_SIZE(3);
+	cd->bDescriptorType		= USB_DT_CS_INTERFACE;
+	cd->bDescriptorSubType		= UVC_VC_INPUT_TERMINAL;
+	cd->bTerminalID			= 1;
+	cd->wTerminalType		= cpu_to_le16(0x0201);
+	cd->bAssocTerminal		= 0;
+	cd->iTerminal			= 0;
+	cd->wObjectiveFocalLengthMin	= cpu_to_le16(0);
+	cd->wObjectiveFocalLengthMax	= cpu_to_le16(0);
+	cd->wOcularFocalLength		= cpu_to_le16(0);
+	cd->bControlSize		= 3;
+	cd->bmControls[0]		= 2;
+	cd->bmControls[1]		= 0;
+	cd->bmControls[2]		= 0;
+
+	pd = &opts->uvc_processing;
+	pd->bLength			= UVC_DT_PROCESSING_UNIT_SIZE(2);
+	pd->bDescriptorType		= USB_DT_CS_INTERFACE;
+	pd->bDescriptorSubType		= UVC_VC_PROCESSING_UNIT;
+	pd->bUnitID			= 2;
+	pd->bSourceID			= 1;
+	pd->wMaxMultiplier		= cpu_to_le16(16*1024);
+	pd->bControlSize		= 2;
+	pd->bmControls[0]		= 1;
+	pd->bmControls[1]		= 0;
+	pd->iProcessing			= 0;
+
+	od = &opts->uvc_output_terminal;
+	od->bLength			= UVC_DT_OUTPUT_TERMINAL_SIZE;
+	od->bDescriptorType		= USB_DT_CS_INTERFACE;
+	od->bDescriptorSubType		= UVC_VC_OUTPUT_TERMINAL;
+	od->bTerminalID			= 3;
+	od->wTerminalType		= cpu_to_le16(0x0101);
+	od->bAssocTerminal		= 0;
+	od->bSourceID			= 2;
+	od->iTerminal			= 0;
+
+	md = &opts->uvc_color_matching;
+	md->bLength			= UVC_DT_COLOR_MATCHING_SIZE;
+	md->bDescriptorType		= USB_DT_CS_INTERFACE;
+	md->bDescriptorSubType		= UVC_VS_COLORFORMAT;
+	md->bColorPrimaries		= 1;
+	md->bTransferCharacteristics	= 1;
+	md->bMatrixCoefficients		= 4;
+
+	/* Prepare fs control class descriptors for configfs-based gadgets */
+	ctl_cls = opts->uvc_fs_control_cls;
+	ctl_cls[0] = NULL;	/* assigned elsewhere by configfs */
+	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+	ctl_cls[3] = (struct uvc_descriptor_header *)od;
+	ctl_cls[4] = NULL;	/* NULL-terminate */
+	opts->fs_control =
+		(const struct uvc_descriptor_header * const *)ctl_cls;
+
+	/* Prepare hs control class descriptors for configfs-based gadgets */
+	ctl_cls = opts->uvc_ss_control_cls;
+	ctl_cls[0] = NULL;	/* assigned elsewhere by configfs */
+	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+	ctl_cls[3] = (struct uvc_descriptor_header *)od;
+	ctl_cls[4] = NULL;	/* NULL-terminate */
+	opts->ss_control =
+		(const struct uvc_descriptor_header * const *)ctl_cls;
+
+	opts->streaming_interval = 1;
+	opts->streaming_maxpacket = 1024;
+
+	uvcg_attach_configfs(opts);
+	return &opts->func_inst;
+}
+
+static void uvc_free(struct usb_function *f)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
+					       func_inst);
+	--opts->refcnt;
+	kfree(uvc);
+}
+
+static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct uvc_device *uvc = to_uvc(f);
+
+	INFO(cdev, "%s\n", __func__);
+
+	device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
+	video_unregister_device(&uvc->vdev);
+	v4l2_device_unregister(&uvc->v4l2_dev);
+
+	usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+	kfree(uvc->control_buf);
+
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
+{
+	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
+	struct uvc_descriptor_header **strm_cls;
+
+	uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
+	if (uvc == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&uvc->video.mutex);
+	uvc->state = UVC_STATE_DISCONNECTED;
+	opts = fi_to_f_uvc_opts(fi);
+
+	mutex_lock(&opts->lock);
+	if (opts->uvc_fs_streaming_cls) {
+		strm_cls = opts->uvc_fs_streaming_cls;
+		opts->fs_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
+	if (opts->uvc_hs_streaming_cls) {
+		strm_cls = opts->uvc_hs_streaming_cls;
+		opts->hs_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
+	if (opts->uvc_ss_streaming_cls) {
+		strm_cls = opts->uvc_ss_streaming_cls;
+		opts->ss_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
+
+	uvc->desc.fs_control = opts->fs_control;
+	uvc->desc.ss_control = opts->ss_control;
+	uvc->desc.fs_streaming = opts->fs_streaming;
+	uvc->desc.hs_streaming = opts->hs_streaming;
+	uvc->desc.ss_streaming = opts->ss_streaming;
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
+
+	/* Register the function. */
+	uvc->func.name = "uvc";
+	uvc->func.bind = uvc_function_bind;
+	uvc->func.unbind = uvc_unbind;
+	uvc->func.get_alt = uvc_function_get_alt;
+	uvc->func.set_alt = uvc_function_set_alt;
+	uvc->func.disable = uvc_function_disable;
+	uvc->func.setup = uvc_function_setup;
+	uvc->func.free_func = uvc_free;
+	uvc->func.bind_deactivated = true;
+
+	return &uvc->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
new file mode 100644
index 0000000..a81a177
--- /dev/null
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	f_uvc.h  --  USB Video Class Gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef _F_UVC_H_
+#define _F_UVC_H_
+
+struct uvc_device;
+
+void uvc_function_setup_continue(struct uvc_device *uvc);
+
+void uvc_function_connect(struct uvc_device *uvc);
+
+void uvc_function_disconnect(struct uvc_device *uvc);
+
+#endif /* _F_UVC_H_ */
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
new file mode 100644
index 0000000..98b8462
--- /dev/null
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header declares the utility functions used by "Gadget Zero", plus
+ * interfaces to its two single-configuration function drivers.
+ */
+
+#ifndef __G_ZERO_H
+#define __G_ZERO_H
+
+#define GZERO_BULK_BUFLEN	4096
+#define GZERO_QLEN		32
+#define GZERO_ISOC_INTERVAL	4
+#define GZERO_ISOC_MAXPACKET	1024
+#define GZERO_SS_BULK_QLEN	1
+#define GZERO_SS_ISO_QLEN	8
+
+struct usb_zero_options {
+	unsigned pattern;
+	unsigned isoc_interval;
+	unsigned isoc_maxpacket;
+	unsigned isoc_mult;
+	unsigned isoc_maxburst;
+	unsigned bulk_buflen;
+	unsigned qlen;
+	unsigned ss_bulk_qlen;
+	unsigned ss_iso_qlen;
+};
+
+struct f_ss_opts {
+	struct usb_function_instance func_inst;
+	unsigned pattern;
+	unsigned isoc_interval;
+	unsigned isoc_maxpacket;
+	unsigned isoc_mult;
+	unsigned isoc_maxburst;
+	unsigned bulk_buflen;
+	unsigned bulk_qlen;
+	unsigned iso_qlen;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+struct f_lb_opts {
+	struct usb_function_instance func_inst;
+	unsigned bulk_buflen;
+	unsigned qlen;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+void lb_modexit(void);
+int lb_modinit(void);
+
+/* common utilities */
+void disable_endpoints(struct usb_composite_dev *cdev,
+		struct usb_ep *in, struct usb_ep *out,
+		struct usb_ep *iso_in, struct usb_ep *iso_out);
+
+#endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/function/ndis.h b/drivers/usb/gadget/function/ndis.h
new file mode 100644
index 0000000..a19f72d
--- /dev/null
+++ b/drivers/usb/gadget/function/ndis.h
@@ -0,0 +1,47 @@
+/*
+ * ndis.h
+ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+enum NDIS_DEVICE_POWER_STATE {
+	NdisDeviceStateUnspecified = 0,
+	NdisDeviceStateD0,
+	NdisDeviceStateD1,
+	NdisDeviceStateD2,
+	NdisDeviceStateD3,
+	NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+	enum NDIS_DEVICE_POWER_STATE  MinMagicPacketWakeUp;
+	enum NDIS_DEVICE_POWER_STATE  MinPatternWakeUp;
+	enum NDIS_DEVICE_POWER_STATE  MinLinkChangeWakeUp;
+};
+
+struct NDIS_PNP_CAPABILITIES {
+	__le32					Flags;
+	struct NDIS_PM_WAKE_UP_CAPABILITIES	WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+	__le32	Priority;
+	__le32	Reserved;
+	__le32	MaskSize;
+	__le32	PatternOffset;
+	__le32	PatternSize;
+	__le32	PatternFlags;
+};
+
+#endif /* _LINUX_NDIS_H */
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
new file mode 100644
index 0000000..04c142c
--- /dev/null
+++ b/drivers/usb/gadget/function/rndis.c
@@ -0,0 +1,1178 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RNDIS MSG parser
+ *
+ * Authors:	Benedikt Spranger, Pengutronix
+ *		Robert Schwebel, Pengutronix
+ *
+ *		This software was originally developed in conformance with
+ *		Microsoft's Remote NDIS Specification License Agreement.
+ *
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ *		Fixed message length bug in init_response
+ *
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ *		Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ *		updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/netdevice.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include "u_rndis.h"
+
+#undef	VERBOSE_DEBUG
+
+#include "rndis.h"
+
+
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#if 0
+static int rndis_debug = 0;
+module_param (rndis_debug, int, 0);
+MODULE_PARM_DESC (rndis_debug, "enable debugging");
+#else
+#define rndis_debug		0
+#endif
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#define	NAME_TEMPLATE "driver/rndis-%03d"
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+static DEFINE_IDA(rndis_ida);
+
+/* Driver Version */
+static const __le32 rndis_driver_version = cpu_to_le32(1);
+
+/* Function Prototypes */
+static rndis_resp_t *rndis_add_response(struct rndis_params *params,
+					u32 length);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static const struct file_operations rndis_proc_fops;
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/* supported OIDs */
+static const u32 oid_supported_list[] = {
+	/* the general stuff */
+	RNDIS_OID_GEN_SUPPORTED_LIST,
+	RNDIS_OID_GEN_HARDWARE_STATUS,
+	RNDIS_OID_GEN_MEDIA_SUPPORTED,
+	RNDIS_OID_GEN_MEDIA_IN_USE,
+	RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+	RNDIS_OID_GEN_LINK_SPEED,
+	RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
+	RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
+	RNDIS_OID_GEN_VENDOR_ID,
+	RNDIS_OID_GEN_VENDOR_DESCRIPTION,
+	RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
+	RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
+	RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
+	RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+	RNDIS_OID_GEN_PHYSICAL_MEDIUM,
+
+	/* the statistical stuff */
+	RNDIS_OID_GEN_XMIT_OK,
+	RNDIS_OID_GEN_RCV_OK,
+	RNDIS_OID_GEN_XMIT_ERROR,
+	RNDIS_OID_GEN_RCV_ERROR,
+	RNDIS_OID_GEN_RCV_NO_BUFFER,
+#ifdef	RNDIS_OPTIONAL_STATS
+	RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
+	RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
+	RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
+	RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
+	RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
+	RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
+	RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
+	RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
+	RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
+	RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
+	RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
+	RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
+	RNDIS_OID_GEN_RCV_CRC_ERROR,
+	RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+	/* mandatory 802.3 */
+	/* the general stuff */
+	RNDIS_OID_802_3_PERMANENT_ADDRESS,
+	RNDIS_OID_802_3_CURRENT_ADDRESS,
+	RNDIS_OID_802_3_MULTICAST_LIST,
+	RNDIS_OID_802_3_MAC_OPTIONS,
+	RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
+
+	/* the statistical stuff */
+	RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
+	RNDIS_OID_802_3_XMIT_ONE_COLLISION,
+	RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef	RNDIS_OPTIONAL_STATS
+	RNDIS_OID_802_3_XMIT_DEFERRED,
+	RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
+	RNDIS_OID_802_3_RCV_OVERRUN,
+	RNDIS_OID_802_3_XMIT_UNDERRUN,
+	RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
+	RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
+	RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
+#endif	/* RNDIS_OPTIONAL_STATS */
+
+#ifdef	RNDIS_PM
+	/* PM and wakeup are "mandatory" for USB, but the RNDIS specs
+	 * don't say what they mean ... and the NDIS specs are often
+	 * confusing and/or ambiguous in this context.  (That is, more
+	 * so than their specs for the other OIDs.)
+	 *
+	 * FIXME someone who knows what these should do, please
+	 * implement them!
+	 */
+
+	/* power management */
+	OID_PNP_CAPABILITIES,
+	OID_PNP_QUERY_POWER,
+	OID_PNP_SET_POWER,
+
+#ifdef	RNDIS_WAKEUP
+	/* wake up host */
+	OID_PNP_ENABLE_WAKE_UP,
+	OID_PNP_ADD_WAKE_UP_PATTERN,
+	OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif	/* RNDIS_WAKEUP */
+#endif	/* RNDIS_PM */
+};
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
+			       unsigned buf_len, rndis_resp_t *r)
+{
+	int retval = -ENOTSUPP;
+	u32 length = 4;	/* usually */
+	__le32 *outbuf;
+	int i, count;
+	rndis_query_cmplt_type *resp;
+	struct net_device *net;
+	struct rtnl_link_stats64 temp;
+	const struct rtnl_link_stats64 *stats;
+
+	if (!r) return -ENOMEM;
+	resp = (rndis_query_cmplt_type *)r->buf;
+
+	if (!resp) return -ENOMEM;
+
+	if (buf_len && rndis_debug > 1) {
+		pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
+		for (i = 0; i < buf_len; i += 16) {
+			pr_debug("%03d: %08x %08x %08x %08x\n", i,
+				get_unaligned_le32(&buf[i]),
+				get_unaligned_le32(&buf[i + 4]),
+				get_unaligned_le32(&buf[i + 8]),
+				get_unaligned_le32(&buf[i + 12]));
+		}
+	}
+
+	/* response goes here, right after the header */
+	outbuf = (__le32 *)&resp[1];
+	resp->InformationBufferOffset = cpu_to_le32(16);
+
+	net = params->dev;
+	stats = dev_get_stats(net, &temp);
+
+	switch (OID) {
+
+	/* general oids (table 4-1) */
+
+	/* mandatory */
+	case RNDIS_OID_GEN_SUPPORTED_LIST:
+		pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__);
+		length = sizeof(oid_supported_list);
+		count  = length / sizeof(u32);
+		for (i = 0; i < count; i++)
+			outbuf[i] = cpu_to_le32(oid_supported_list[i]);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_HARDWARE_STATUS:
+		pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__);
+		/* Bogus question!
+		 * Hardware must be ready to receive high level protocols.
+		 * BTW:
+		 * reddite ergo quae sunt Caesaris Caesari
+		 * et quae sunt Dei Deo!
+		 */
+		*outbuf = cpu_to_le32(0);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_MEDIA_SUPPORTED:
+		pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
+		*outbuf = cpu_to_le32(params->medium);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_MEDIA_IN_USE:
+		pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
+		/* one medium, one transport... (maybe you do it better) */
+		*outbuf = cpu_to_le32(params->medium);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
+		pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+		if (params->dev) {
+			*outbuf = cpu_to_le32(params->dev->mtu);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_LINK_SPEED:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
+		if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED)
+			*outbuf = cpu_to_le32(0);
+		else
+			*outbuf = cpu_to_le32(params->speed);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
+		pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+		if (params->dev) {
+			*outbuf = cpu_to_le32(params->dev->mtu);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
+		pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+		if (params->dev) {
+			*outbuf = cpu_to_le32(params->dev->mtu);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_VENDOR_ID:
+		pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
+		*outbuf = cpu_to_le32(params->vendorID);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
+		pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
+		if (params->vendorDescr) {
+			length = strlen(params->vendorDescr);
+			memcpy(outbuf, params->vendorDescr, length);
+		} else {
+			outbuf[0] = 0;
+		}
+		retval = 0;
+		break;
+
+	case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION:
+		pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
+		/* Created as LE */
+		*outbuf = rndis_driver_version;
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+		pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
+		*outbuf = cpu_to_le32(*params->filter);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE:
+		pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
+		*outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
+		*outbuf = cpu_to_le32(params->media_state);
+		retval = 0;
+		break;
+
+	case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
+		pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
+		*outbuf = cpu_to_le32(0);
+		retval = 0;
+		break;
+
+	/* The RNDIS specification is incomplete/wrong.   Some versions
+	 * of MS-Windows expect OIDs that aren't specified there.  Other
+	 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+	 */
+	case RNDIS_OID_GEN_MAC_OPTIONS:		/* from WinME */
+		pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__);
+		*outbuf = cpu_to_le32(
+			  RNDIS_MAC_OPTION_RECEIVE_SERIALIZED
+			| RNDIS_MAC_OPTION_FULL_DUPLEX);
+		retval = 0;
+		break;
+
+	/* statistics OIDs (table 4-2) */
+
+	/* mandatory */
+	case RNDIS_OID_GEN_XMIT_OK:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->tx_packets
+				- stats->tx_errors - stats->tx_dropped);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_RCV_OK:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->rx_packets
+				- stats->rx_errors - stats->rx_dropped);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_XMIT_ERROR:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->tx_errors);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_RCV_ERROR:
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->rx_errors);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_GEN_RCV_NO_BUFFER:
+		pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->rx_dropped);
+			retval = 0;
+		}
+		break;
+
+	/* ieee802.3 OIDs (table 4-3) */
+
+	/* mandatory */
+	case RNDIS_OID_802_3_PERMANENT_ADDRESS:
+		pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
+		if (params->dev) {
+			length = ETH_ALEN;
+			memcpy(outbuf, params->host_mac, length);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_802_3_CURRENT_ADDRESS:
+		pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
+		if (params->dev) {
+			length = ETH_ALEN;
+			memcpy(outbuf, params->host_mac, length);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_802_3_MULTICAST_LIST:
+		pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+		/* Multicast base address only */
+		*outbuf = cpu_to_le32(0xE0000000);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE:
+		pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
+		/* Multicast base address only */
+		*outbuf = cpu_to_le32(1);
+		retval = 0;
+		break;
+
+	case RNDIS_OID_802_3_MAC_OPTIONS:
+		pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__);
+		*outbuf = cpu_to_le32(0);
+		retval = 0;
+		break;
+
+	/* ieee802.3 statistics OIDs (table 4-4) */
+
+	/* mandatory */
+	case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
+		pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+		if (stats) {
+			*outbuf = cpu_to_le32(stats->rx_frame_errors);
+			retval = 0;
+		}
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_802_3_XMIT_ONE_COLLISION:
+		pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+		*outbuf = cpu_to_le32(0);
+		retval = 0;
+		break;
+
+	/* mandatory */
+	case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS:
+		pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
+		*outbuf = cpu_to_le32(0);
+		retval = 0;
+		break;
+
+	default:
+		pr_warn("%s: query unknown OID 0x%08X\n", __func__, OID);
+	}
+	if (retval < 0)
+		length = 0;
+
+	resp->InformationBufferLength = cpu_to_le32(length);
+	r->length = length + sizeof(*resp);
+	resp->MessageLength = cpu_to_le32(r->length);
+	return retval;
+}
+
+static int gen_ndis_set_resp(struct rndis_params *params, u32 OID,
+			     u8 *buf, u32 buf_len, rndis_resp_t *r)
+{
+	rndis_set_cmplt_type *resp;
+	int i, retval = -ENOTSUPP;
+
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_set_cmplt_type *)r->buf;
+	if (!resp)
+		return -ENOMEM;
+
+	if (buf_len && rndis_debug > 1) {
+		pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
+		for (i = 0; i < buf_len; i += 16) {
+			pr_debug("%03d: %08x %08x %08x %08x\n", i,
+				get_unaligned_le32(&buf[i]),
+				get_unaligned_le32(&buf[i + 4]),
+				get_unaligned_le32(&buf[i + 8]),
+				get_unaligned_le32(&buf[i + 12]));
+		}
+	}
+
+	switch (OID) {
+	case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+
+		/* these NDIS_PACKET_TYPE_* bitflags are shared with
+		 * cdc_filter; it's not RNDIS-specific
+		 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
+		 *	PROMISCUOUS, DIRECTED,
+		 *	MULTICAST, ALL_MULTICAST, BROADCAST
+		 */
+		*params->filter = (u16)get_unaligned_le32(buf);
+		pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+			__func__, *params->filter);
+
+		/* this call has a significant side effect:  it's
+		 * what makes the packet flow start and stop, like
+		 * activating the CDC Ethernet altsetting.
+		 */
+		retval = 0;
+		if (*params->filter) {
+			params->state = RNDIS_DATA_INITIALIZED;
+			netif_carrier_on(params->dev);
+			if (netif_running(params->dev))
+				netif_wake_queue(params->dev);
+		} else {
+			params->state = RNDIS_INITIALIZED;
+			netif_carrier_off(params->dev);
+			netif_stop_queue(params->dev);
+		}
+		break;
+
+	case RNDIS_OID_802_3_MULTICAST_LIST:
+		/* I think we can ignore this */
+		pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+		retval = 0;
+		break;
+
+	default:
+		pr_warn("%s: set unknown OID 0x%08X, size %d\n",
+			__func__, OID, buf_len);
+	}
+
+	return retval;
+}
+
+/*
+ * Response Functions
+ */
+
+static int rndis_init_response(struct rndis_params *params,
+			       rndis_init_msg_type *buf)
+{
+	rndis_init_cmplt_type *resp;
+	rndis_resp_t *r;
+
+	if (!params->dev)
+		return -ENOTSUPP;
+
+	r = rndis_add_response(params, sizeof(rndis_init_cmplt_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_init_cmplt_type *)r->buf;
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C);
+	resp->MessageLength = cpu_to_le32(52);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+	resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+	resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+	resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+	resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+	resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+	resp->MaxTransferSize = cpu_to_le32(
+		  params->dev->mtu
+		+ sizeof(struct ethhdr)
+		+ sizeof(struct rndis_packet_msg_type)
+		+ 22);
+	resp->PacketAlignmentFactor = cpu_to_le32(0);
+	resp->AFListOffset = cpu_to_le32(0);
+	resp->AFListSize = cpu_to_le32(0);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+static int rndis_query_response(struct rndis_params *params,
+				rndis_query_msg_type *buf)
+{
+	rndis_query_cmplt_type *resp;
+	rndis_resp_t *r;
+
+	/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
+	if (!params->dev)
+		return -ENOTSUPP;
+
+	/*
+	 * we need more memory:
+	 * gen_ndis_query_resp expects enough space for
+	 * rndis_query_cmplt_type followed by data.
+	 * oid_supported_list is the largest data reply
+	 */
+	r = rndis_add_response(params,
+		sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_query_cmplt_type *)r->buf;
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+
+	if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
+			le32_to_cpu(buf->InformationBufferOffset)
+					+ 8 + (u8 *)buf,
+			le32_to_cpu(buf->InformationBufferLength),
+			r)) {
+		/* OID not supported */
+		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+		resp->MessageLength = cpu_to_le32(sizeof *resp);
+		resp->InformationBufferLength = cpu_to_le32(0);
+		resp->InformationBufferOffset = cpu_to_le32(0);
+	} else
+		resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+static int rndis_set_response(struct rndis_params *params,
+			      rndis_set_msg_type *buf)
+{
+	u32 BufLength, BufOffset;
+	rndis_set_cmplt_type *resp;
+	rndis_resp_t *r;
+
+	r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_set_cmplt_type *)r->buf;
+
+	BufLength = le32_to_cpu(buf->InformationBufferLength);
+	BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+
+#ifdef	VERBOSE_DEBUG
+	pr_debug("%s: Length: %d\n", __func__, BufLength);
+	pr_debug("%s: Offset: %d\n", __func__, BufOffset);
+	pr_debug("%s: InfoBuffer: ", __func__);
+
+	for (i = 0; i < BufLength; i++) {
+		pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+	}
+
+	pr_debug("\n");
+#endif
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
+	resp->MessageLength = cpu_to_le32(16);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID),
+			((u8 *)buf) + 8 + BufOffset, BufLength, r))
+		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+	else
+		resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+static int rndis_reset_response(struct rndis_params *params,
+				rndis_reset_msg_type *buf)
+{
+	rndis_reset_cmplt_type *resp;
+	rndis_resp_t *r;
+	u8 *xbuf;
+	u32 length;
+
+	/* drain the response queue */
+	while ((xbuf = rndis_get_next_response(params, &length)))
+		rndis_free_response(params, xbuf);
+
+	r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_reset_cmplt_type *)r->buf;
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C);
+	resp->MessageLength = cpu_to_le32(16);
+	resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+	/* resent information */
+	resp->AddressingReset = cpu_to_le32(1);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+static int rndis_keepalive_response(struct rndis_params *params,
+				    rndis_keepalive_msg_type *buf)
+{
+	rndis_keepalive_cmplt_type *resp;
+	rndis_resp_t *r;
+
+	/* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+	r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_keepalive_cmplt_type *)r->buf;
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);
+	resp->MessageLength = cpu_to_le32(16);
+	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+	resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+
+/*
+ * Device to Host Comunication
+ */
+static int rndis_indicate_status_msg(struct rndis_params *params, u32 status)
+{
+	rndis_indicate_status_msg_type *resp;
+	rndis_resp_t *r;
+
+	if (params->state == RNDIS_UNINITIALIZED)
+		return -ENOTSUPP;
+
+	r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type));
+	if (!r)
+		return -ENOMEM;
+	resp = (rndis_indicate_status_msg_type *)r->buf;
+
+	resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
+	resp->MessageLength = cpu_to_le32(20);
+	resp->Status = cpu_to_le32(status);
+	resp->StatusBufferLength = cpu_to_le32(0);
+	resp->StatusBufferOffset = cpu_to_le32(0);
+
+	params->resp_avail(params->v);
+	return 0;
+}
+
+int rndis_signal_connect(struct rndis_params *params)
+{
+	params->media_state = RNDIS_MEDIA_STATE_CONNECTED;
+	return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT);
+}
+EXPORT_SYMBOL_GPL(rndis_signal_connect);
+
+int rndis_signal_disconnect(struct rndis_params *params)
+{
+	params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
+	return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT);
+}
+EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
+
+void rndis_uninit(struct rndis_params *params)
+{
+	u8 *buf;
+	u32 length;
+
+	if (!params)
+		return;
+	params->state = RNDIS_UNINITIALIZED;
+
+	/* drain the response queue */
+	while ((buf = rndis_get_next_response(params, &length)))
+		rndis_free_response(params, buf);
+}
+EXPORT_SYMBOL_GPL(rndis_uninit);
+
+void rndis_set_host_mac(struct rndis_params *params, const u8 *addr)
+{
+	params->host_mac = addr;
+}
+EXPORT_SYMBOL_GPL(rndis_set_host_mac);
+
+/*
+ * Message Parser
+ */
+int rndis_msg_parser(struct rndis_params *params, u8 *buf)
+{
+	u32 MsgType, MsgLength;
+	__le32 *tmp;
+
+	if (!buf)
+		return -ENOMEM;
+
+	tmp = (__le32 *)buf;
+	MsgType   = get_unaligned_le32(tmp++);
+	MsgLength = get_unaligned_le32(tmp++);
+
+	if (!params)
+		return -ENOTSUPP;
+
+	/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+	 * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+	 * and normal HC level polling to see if there's any IN traffic.
+	 */
+
+	/* For USB: responses may take up to 10 seconds */
+	switch (MsgType) {
+	case RNDIS_MSG_INIT:
+		pr_debug("%s: RNDIS_MSG_INIT\n",
+			__func__);
+		params->state = RNDIS_INITIALIZED;
+		return rndis_init_response(params, (rndis_init_msg_type *)buf);
+
+	case RNDIS_MSG_HALT:
+		pr_debug("%s: RNDIS_MSG_HALT\n",
+			__func__);
+		params->state = RNDIS_UNINITIALIZED;
+		if (params->dev) {
+			netif_carrier_off(params->dev);
+			netif_stop_queue(params->dev);
+		}
+		return 0;
+
+	case RNDIS_MSG_QUERY:
+		return rndis_query_response(params,
+					(rndis_query_msg_type *)buf);
+
+	case RNDIS_MSG_SET:
+		return rndis_set_response(params, (rndis_set_msg_type *)buf);
+
+	case RNDIS_MSG_RESET:
+		pr_debug("%s: RNDIS_MSG_RESET\n",
+			__func__);
+		return rndis_reset_response(params,
+					(rndis_reset_msg_type *)buf);
+
+	case RNDIS_MSG_KEEPALIVE:
+		/* For USB: host does this every 5 seconds */
+		if (rndis_debug > 1)
+			pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
+				__func__);
+		return rndis_keepalive_response(params,
+						 (rndis_keepalive_msg_type *)
+						 buf);
+
+	default:
+		/* At least Windows XP emits some undefined RNDIS messages.
+		 * In one case those messages seemed to relate to the host
+		 * suspending itself.
+		 */
+		pr_warn("%s: unknown RNDIS message 0x%08X len %d\n",
+			__func__, MsgType, MsgLength);
+		/* Garbled message can be huge, so limit what we display */
+		if (MsgLength > 16)
+			MsgLength = 16;
+		print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+				     buf, MsgLength);
+		break;
+	}
+
+	return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(rndis_msg_parser);
+
+static inline int rndis_get_nr(void)
+{
+	return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL);
+}
+
+static inline void rndis_put_nr(int nr)
+{
+	ida_simple_remove(&rndis_ida, nr);
+}
+
+struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
+{
+	struct rndis_params *params;
+	int i;
+
+	if (!resp_avail)
+		return ERR_PTR(-EINVAL);
+
+	i = rndis_get_nr();
+	if (i < 0) {
+		pr_debug("failed\n");
+
+		return ERR_PTR(-ENODEV);
+	}
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (!params) {
+		rndis_put_nr(i);
+
+		return ERR_PTR(-ENOMEM);
+	}
+
+#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
+	{
+		struct proc_dir_entry *proc_entry;
+		char name[20];
+
+		sprintf(name, NAME_TEMPLATE, i);
+		proc_entry = proc_create_data(name, 0660, NULL,
+					      &rndis_proc_fops, params);
+		if (!proc_entry) {
+			kfree(params);
+			rndis_put_nr(i);
+
+			return ERR_PTR(-EIO);
+		}
+	}
+#endif
+
+	params->confignr = i;
+	params->used = 1;
+	params->state = RNDIS_UNINITIALIZED;
+	params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
+	params->resp_avail = resp_avail;
+	params->v = v;
+	INIT_LIST_HEAD(&params->resp_queue);
+	pr_debug("%s: configNr = %d\n", __func__, i);
+
+	return params;
+}
+EXPORT_SYMBOL_GPL(rndis_register);
+
+void rndis_deregister(struct rndis_params *params)
+{
+	int i;
+
+	pr_debug("%s:\n", __func__);
+
+	if (!params)
+		return;
+
+	i = params->confignr;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	{
+		char name[20];
+
+		sprintf(name, NAME_TEMPLATE, i);
+		remove_proc_entry(name, NULL);
+	}
+#endif
+
+	kfree(params);
+	rndis_put_nr(i);
+}
+EXPORT_SYMBOL_GPL(rndis_deregister);
+int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
+			u16 *cdc_filter)
+{
+	pr_debug("%s:\n", __func__);
+	if (!dev)
+		return -EINVAL;
+	if (!params)
+		return -1;
+
+	params->dev = dev;
+	params->filter = cdc_filter;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_dev);
+
+int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
+			   const char *vendorDescr)
+{
+	pr_debug("%s:\n", __func__);
+	if (!vendorDescr) return -1;
+	if (!params)
+		return -1;
+
+	params->vendorID = vendorID;
+	params->vendorDescr = vendorDescr;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
+
+int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
+{
+	pr_debug("%s: %u %u\n", __func__, medium, speed);
+	if (!params)
+		return -1;
+
+	params->medium = medium;
+	params->speed = speed;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_set_param_medium);
+
+void rndis_add_hdr(struct sk_buff *skb)
+{
+	struct rndis_packet_msg_type *header;
+
+	if (!skb)
+		return;
+	header = skb_push(skb, sizeof(*header));
+	memset(header, 0, sizeof *header);
+	header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
+	header->MessageLength = cpu_to_le32(skb->len);
+	header->DataOffset = cpu_to_le32(36);
+	header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
+}
+EXPORT_SYMBOL_GPL(rndis_add_hdr);
+
+void rndis_free_response(struct rndis_params *params, u8 *buf)
+{
+	rndis_resp_t *r, *n;
+
+	list_for_each_entry_safe(r, n, &params->resp_queue, list) {
+		if (r->buf == buf) {
+			list_del(&r->list);
+			kfree(r);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(rndis_free_response);
+
+u8 *rndis_get_next_response(struct rndis_params *params, u32 *length)
+{
+	rndis_resp_t *r, *n;
+
+	if (!length) return NULL;
+
+	list_for_each_entry_safe(r, n, &params->resp_queue, list) {
+		if (!r->send) {
+			r->send = 1;
+			*length = r->length;
+			return r->buf;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(rndis_get_next_response);
+
+static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length)
+{
+	rndis_resp_t *r;
+
+	/* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
+	r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC);
+	if (!r) return NULL;
+
+	r->buf = (u8 *)(r + 1);
+	r->length = length;
+	r->send = 0;
+
+	list_add_tail(&r->list, &params->resp_queue);
+	return r;
+}
+
+int rndis_rm_hdr(struct gether *port,
+			struct sk_buff *skb,
+			struct sk_buff_head *list)
+{
+	/* tmp points to a struct rndis_packet_msg_type */
+	__le32 *tmp = (void *)skb->data;
+
+	/* MessageType, MessageLength */
+	if (cpu_to_le32(RNDIS_MSG_PACKET)
+			!= get_unaligned(tmp++)) {
+		dev_kfree_skb_any(skb);
+		return -EINVAL;
+	}
+	tmp++;
+
+	/* DataOffset, DataLength */
+	if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
+		dev_kfree_skb_any(skb);
+		return -EOVERFLOW;
+	}
+	skb_trim(skb, get_unaligned_le32(tmp++));
+
+	skb_queue_tail(list, skb);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rndis_rm_hdr);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static int rndis_proc_show(struct seq_file *m, void *v)
+{
+	rndis_params *param = m->private;
+
+	seq_printf(m,
+			 "Config Nr. %d\n"
+			 "used      : %s\n"
+			 "state     : %s\n"
+			 "medium    : 0x%08X\n"
+			 "speed     : %d\n"
+			 "cable     : %s\n"
+			 "vendor ID : 0x%08X\n"
+			 "vendor    : %s\n",
+			 param->confignr, (param->used) ? "y" : "n",
+			 ({ char *s = "?";
+			 switch (param->state) {
+			 case RNDIS_UNINITIALIZED:
+				s = "RNDIS_UNINITIALIZED"; break;
+			 case RNDIS_INITIALIZED:
+				s = "RNDIS_INITIALIZED"; break;
+			 case RNDIS_DATA_INITIALIZED:
+				s = "RNDIS_DATA_INITIALIZED"; break;
+			} s; }),
+			 param->medium,
+			 (param->media_state) ? 0 : param->speed*100,
+			 (param->media_state) ? "disconnected" : "connected",
+			 param->vendorID, param->vendorDescr);
+	return 0;
+}
+
+static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	rndis_params *p = PDE_DATA(file_inode(file));
+	u32 speed = 0;
+	int i, fl_speed = 0;
+
+	for (i = 0; i < count; i++) {
+		char c;
+		if (get_user(c, buffer))
+			return -EFAULT;
+		switch (c) {
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			fl_speed = 1;
+			speed = speed * 10 + c - '0';
+			break;
+		case 'C':
+		case 'c':
+			rndis_signal_connect(p);
+			break;
+		case 'D':
+		case 'd':
+			rndis_signal_disconnect(p);
+			break;
+		default:
+			if (fl_speed) p->speed = speed;
+			else pr_debug("%c is not valid\n", c);
+			break;
+		}
+
+		buffer++;
+	}
+
+	return count;
+}
+
+static int rndis_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rndis_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations rndis_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rndis_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.write		= rndis_proc_write,
+};
+
+#define	NAME_TEMPLATE "driver/rndis-%03d"
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
new file mode 100644
index 0000000..c7e3a70
--- /dev/null
+++ b/drivers/usb/gadget/function/rndis.h
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RNDIS	Definitions for Remote NDIS
+ *
+ * Authors:	Benedikt Spranger, Pengutronix
+ *		Robert Schwebel, Pengutronix
+ *
+ *		This software was originally developed in conformance with
+ *		Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _LINUX_RNDIS_H
+#define _LINUX_RNDIS_H
+
+#include <linux/rndis.h>
+#include "u_ether.h"
+#include "ndis.h"
+
+#define RNDIS_MAXIMUM_FRAME_SIZE	1518
+#define RNDIS_MAX_TOTAL_SIZE		1558
+
+typedef struct rndis_init_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	MajorVersion;
+	__le32	MinorVersion;
+	__le32	MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	Status;
+	__le32	MajorVersion;
+	__le32	MinorVersion;
+	__le32	DeviceFlags;
+	__le32	Medium;
+	__le32	MaxPacketsPerTransfer;
+	__le32	MaxTransferSize;
+	__le32	PacketAlignmentFactor;
+	__le32	AFListOffset;
+	__le32	AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	OID;
+	__le32	InformationBufferLength;
+	__le32	InformationBufferOffset;
+	__le32	DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	Status;
+	__le32	InformationBufferLength;
+	__le32	InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	OID;
+	__le32	InformationBufferLength;
+	__le32	InformationBufferOffset;
+	__le32	DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	Status;
+	__le32	AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	Status;
+	__le32	StatusBufferLength;
+	__le32	StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	RequestID;
+	__le32	Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type {
+	__le32	MessageType;
+	__le32	MessageLength;
+	__le32	DataOffset;
+	__le32	DataLength;
+	__le32	OOBDataOffset;
+	__le32	OOBDataLength;
+	__le32	NumOOBDataElements;
+	__le32	PerPacketInfoOffset;
+	__le32	PerPacketInfoLength;
+	__le32	VcHandle;
+	__le32	Reserved;
+} __attribute__ ((packed));
+
+struct rndis_config_parameter {
+	__le32	ParameterNameOffset;
+	__le32	ParameterNameLength;
+	__le32	ParameterType;
+	__le32	ParameterValueOffset;
+	__le32	ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state {
+	RNDIS_UNINITIALIZED,
+	RNDIS_INITIALIZED,
+	RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t {
+	struct list_head	list;
+	u8			*buf;
+	u32			length;
+	int			send;
+} rndis_resp_t;
+
+typedef struct rndis_params {
+	int			confignr;
+	u8			used;
+	u16			saved_filter;
+	enum rndis_state	state;
+	u32			medium;
+	u32			speed;
+	u32			media_state;
+
+	const u8		*host_mac;
+	u16			*filter;
+	struct net_device	*dev;
+
+	u32			vendorID;
+	const char		*vendorDescr;
+	void			(*resp_avail)(void *v);
+	void			*v;
+	struct list_head	resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int  rndis_msg_parser(struct rndis_params *params, u8 *buf);
+struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v);
+void rndis_deregister(struct rndis_params *params);
+int  rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
+			 u16 *cdc_filter);
+int  rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
+			    const char *vendorDescr);
+int  rndis_set_param_medium(struct rndis_params *params, u32 medium,
+			     u32 speed);
+void rndis_add_hdr(struct sk_buff *skb);
+int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
+			struct sk_buff_head *list);
+u8   *rndis_get_next_response(struct rndis_params *params, u32 *length);
+void rndis_free_response(struct rndis_params *params, u8 *buf);
+
+void rndis_uninit(struct rndis_params *params);
+int  rndis_signal_connect(struct rndis_params *params);
+int  rndis_signal_disconnect(struct rndis_params *params);
+int  rndis_state(struct rndis_params *params);
+extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr);
+
+#endif  /* _LINUX_RNDIS_H */
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
new file mode 100644
index 0000000..f7e6c42
--- /dev/null
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ */
+
+/*
+ * This file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ *  - fsg_string_interface    -- name of the interface
+ */
+
+/*
+ * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
+ * sets the number of pipeline buffers (length of the fsg_buffhd array).
+ * The valid range of num_buffers is: num >= 2 && num <= 4.
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/usb/composite.h>
+
+#include "storage_common.h"
+
+/* There is only one interface. */
+
+struct usb_interface_descriptor fsg_intf_desc = {
+	.bLength =		sizeof fsg_intf_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */
+	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
+	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */
+	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */
+	.iInterface =		FSG_STRING_INTERFACE,
+};
+EXPORT_SYMBOL_GPL(fsg_intf_desc);
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	/* wMaxPacketSize set by autoconfiguration */
+};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
+
+struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	/* wMaxPacketSize set by autoconfiguration */
+};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
+
+struct usb_descriptor_header *fsg_fs_function[] = {
+	(struct usb_descriptor_header *) &fsg_intf_desc,
+	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+	NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_fs_function);
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets).
+ */
+struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
+
+struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+	.bInterval =		1,	/* NAK every 1 uframe */
+};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
+
+
+struct usb_descriptor_header *fsg_hs_function[] = {
+	(struct usb_descriptor_header *) &fsg_intf_desc,
+	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+	NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_hs_function);
+
+struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/*.bMaxBurst =		DYNAMIC, */
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
+
+struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
+
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/*.bMaxBurst =		DYNAMIC, */
+};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
+
+struct usb_descriptor_header *fsg_ss_function[] = {
+	(struct usb_descriptor_header *) &fsg_intf_desc,
+	(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+	(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+	(struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+	(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+	NULL,
+};
+EXPORT_SYMBOL_GPL(fsg_ss_function);
+
+
+ /*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+void fsg_lun_close(struct fsg_lun *curlun)
+{
+	if (curlun->filp) {
+		LDBG(curlun, "close backing file\n");
+		fput(curlun->filp);
+		curlun->filp = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(fsg_lun_close);
+
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+	int				ro;
+	struct file			*filp = NULL;
+	int				rc = -EINVAL;
+	struct inode			*inode = NULL;
+	loff_t				size;
+	loff_t				num_sectors;
+	loff_t				min_sectors;
+	unsigned int			blkbits;
+	unsigned int			blksize;
+
+	/* R/W if we can, R/O if we must */
+	ro = curlun->initially_ro;
+	if (!ro) {
+		filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
+		if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
+			ro = 1;
+	}
+	if (ro)
+		filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
+	if (IS_ERR(filp)) {
+		LINFO(curlun, "unable to open backing file: %s\n", filename);
+		return PTR_ERR(filp);
+	}
+
+	if (!(filp->f_mode & FMODE_WRITE))
+		ro = 1;
+
+	inode = file_inode(filp);
+	if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+		LINFO(curlun, "invalid file type: %s\n", filename);
+		goto out;
+	}
+
+	/*
+	 * If we can't read the file, it's no good.
+	 * If we can't write the file, use it read-only.
+	 */
+	if (!(filp->f_mode & FMODE_CAN_READ)) {
+		LINFO(curlun, "file not readable: %s\n", filename);
+		goto out;
+	}
+	if (!(filp->f_mode & FMODE_CAN_WRITE))
+		ro = 1;
+
+	size = i_size_read(inode->i_mapping->host);
+	if (size < 0) {
+		LINFO(curlun, "unable to find file size: %s\n", filename);
+		rc = (int) size;
+		goto out;
+	}
+
+	if (curlun->cdrom) {
+		blksize = 2048;
+		blkbits = 11;
+	} else if (inode->i_bdev) {
+		blksize = bdev_logical_block_size(inode->i_bdev);
+		blkbits = blksize_bits(blksize);
+	} else {
+		blksize = 512;
+		blkbits = 9;
+	}
+
+	num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
+	min_sectors = 1;
+	if (curlun->cdrom) {
+		min_sectors = 300;	/* Smallest track is 300 frames */
+		if (num_sectors >= 256*60*75) {
+			num_sectors = 256*60*75 - 1;
+			LINFO(curlun, "file too big: %s\n", filename);
+			LINFO(curlun, "using only first %d blocks\n",
+					(int) num_sectors);
+		}
+	}
+	if (num_sectors < min_sectors) {
+		LINFO(curlun, "file too small: %s\n", filename);
+		rc = -ETOOSMALL;
+		goto out;
+	}
+
+	if (fsg_lun_is_open(curlun))
+		fsg_lun_close(curlun);
+
+	curlun->blksize = blksize;
+	curlun->blkbits = blkbits;
+	curlun->ro = ro;
+	curlun->filp = filp;
+	curlun->file_length = size;
+	curlun->num_sectors = num_sectors;
+	LDBG(curlun, "open backing file: %s\n", filename);
+	return 0;
+
+out:
+	fput(filp);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_lun_open);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+	struct file	*filp = curlun->filp;
+
+	if (curlun->ro || !filp)
+		return 0;
+	return vfs_fsync(filp, 1);
+}
+EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
+
+void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+	if (msf) {
+		/* Convert to Minutes-Seconds-Frames */
+		addr >>= 2;		/* Convert to 2048-byte frames */
+		addr += 2*75;		/* Lead-in occupies 2 seconds */
+		dest[3] = addr % 75;	/* Frames */
+		addr /= 75;
+		dest[2] = addr % 60;	/* Seconds */
+		addr /= 60;
+		dest[1] = addr;		/* Minutes */
+		dest[0] = 0;		/* Reserved */
+	} else {
+		/* Absolute sector */
+		put_unaligned_be32(addr, dest);
+	}
+}
+EXPORT_SYMBOL_GPL(store_cdrom_address);
+
+/*-------------------------------------------------------------------------*/
+
+
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
+{
+	return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+				  ? curlun->ro
+				  : curlun->initially_ro);
+}
+EXPORT_SYMBOL_GPL(fsg_show_ro);
+
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
+{
+	return sprintf(buf, "%u\n", curlun->nofua);
+}
+EXPORT_SYMBOL_GPL(fsg_show_nofua);
+
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		      char *buf)
+{
+	char		*p;
+	ssize_t		rc;
+
+	down_read(filesem);
+	if (fsg_lun_is_open(curlun)) {	/* Get the complete pathname */
+		p = file_path(curlun->filp, buf, PAGE_SIZE - 1);
+		if (IS_ERR(p))
+			rc = PTR_ERR(p);
+		else {
+			rc = strlen(p);
+			memmove(buf, p, rc);
+			buf[rc] = '\n';		/* Add a newline */
+			buf[++rc] = 0;
+		}
+	} else {				/* No file, return 0 bytes */
+		*buf = 0;
+		rc = 0;
+	}
+	up_read(filesem);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_show_file);
+
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
+{
+	return sprintf(buf, "%u\n", curlun->cdrom);
+}
+EXPORT_SYMBOL_GPL(fsg_show_cdrom);
+
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
+{
+	return sprintf(buf, "%u\n", curlun->removable);
+}
+EXPORT_SYMBOL_GPL(fsg_show_removable);
+
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
+{
+	return sprintf(buf, "%s\n", curlun->inquiry_string);
+}
+EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
+
+/*
+ * The caller must hold fsg->filesem for reading when calling this function.
+ */
+static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
+{
+	if (fsg_lun_is_open(curlun)) {
+		LDBG(curlun, "read-only status change prevented\n");
+		return -EBUSY;
+	}
+
+	curlun->ro = ro;
+	curlun->initially_ro = ro;
+	LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+
+	return 0;
+}
+
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		     const char *buf, size_t count)
+{
+	ssize_t		rc;
+	bool		ro;
+
+	rc = strtobool(buf, &ro);
+	if (rc)
+		return rc;
+
+	/*
+	 * Allow the write-enable status to change only while the
+	 * backing file is closed.
+	 */
+	down_read(filesem);
+	rc = _fsg_store_ro(curlun, ro);
+	if (!rc)
+		rc = count;
+	up_read(filesem);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_store_ro);
+
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
+{
+	bool		nofua;
+	int		ret;
+
+	ret = strtobool(buf, &nofua);
+	if (ret)
+		return ret;
+
+	/* Sync data when switching from async mode to sync */
+	if (!nofua && curlun->nofua)
+		fsg_lun_fsync_sub(curlun);
+
+	curlun->nofua = nofua;
+
+	return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_nofua);
+
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		       const char *buf, size_t count)
+{
+	int		rc = 0;
+
+	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+		LDBG(curlun, "eject attempt prevented\n");
+		return -EBUSY;				/* "Door is locked" */
+	}
+
+	/* Remove a trailing newline */
+	if (count > 0 && buf[count-1] == '\n')
+		((char *) buf)[count-1] = 0;		/* Ugh! */
+
+	/* Load new medium */
+	down_write(filesem);
+	if (count > 0 && buf[0]) {
+		/* fsg_lun_open() will close existing file if any. */
+		rc = fsg_lun_open(curlun, buf);
+		if (rc == 0)
+			curlun->unit_attention_data =
+					SS_NOT_READY_TO_READY_TRANSITION;
+	} else if (fsg_lun_is_open(curlun)) {
+		fsg_lun_close(curlun);
+		curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+	}
+	up_write(filesem);
+	return (rc < 0 ? rc : count);
+}
+EXPORT_SYMBOL_GPL(fsg_store_file);
+
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+			const char *buf, size_t count)
+{
+	bool		cdrom;
+	int		ret;
+
+	ret = strtobool(buf, &cdrom);
+	if (ret)
+		return ret;
+
+	down_read(filesem);
+	ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
+
+	if (!ret) {
+		curlun->cdrom = cdrom;
+		ret = count;
+	}
+	up_read(filesem);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fsg_store_cdrom);
+
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+			    size_t count)
+{
+	bool		removable;
+	int		ret;
+
+	ret = strtobool(buf, &removable);
+	if (ret)
+		return ret;
+
+	curlun->removable = removable;
+
+	return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_removable);
+
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+				 size_t count)
+{
+	const size_t len = min(count, sizeof(curlun->inquiry_string));
+
+	if (len == 0 || buf[0] == '\n') {
+		curlun->inquiry_string[0] = 0;
+	} else {
+		snprintf(curlun->inquiry_string,
+			 sizeof(curlun->inquiry_string), "%-28s", buf);
+		if (curlun->inquiry_string[len-1] == '\n')
+			curlun->inquiry_string[len-1] = ' ';
+	}
+
+	return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_inquiry_string);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
new file mode 100644
index 0000000..e5e3a25
--- /dev/null
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef USB_STORAGE_COMMON_H
+#define USB_STORAGE_COMMON_H
+
+#include <linux/device.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG	LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define _LMSG(func, lun, fmt, args...)					\
+	do {								\
+		if ((lun)->name_pfx && *(lun)->name_pfx)		\
+			func("%s/%s: " fmt, *(lun)->name_pfx,		\
+				 (lun)->name, ## args);			\
+		else							\
+			func("%s: " fmt, (lun)->name, ## args);		\
+	} while (0)
+
+#define LDBG(lun, fmt, args...)		_LMSG(pr_debug, lun, fmt, ## args)
+#define LERROR(lun, fmt, args...)	_LMSG(pr_err, lun, fmt, ## args)
+#define LWARN(lun, fmt, args...)	_LMSG(pr_warn, lun, fmt, ## args)
+#define LINFO(lun, fmt, args...)	_LMSG(pr_info, lun, fmt, ## args)
+
+
+#ifdef DUMP_MSGS
+
+#  define dump_msg(fsg, /* const char * */ label,			\
+		   /* const u8 * */ buf, /* unsigned */ length)		\
+do {									\
+	if (length < 512) {						\
+		DBG(fsg, "%s, length %u:\n", label, length);		\
+		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\
+			       16, 1, buf, length, 0);			\
+	}								\
+} while (0)
+
+#  define dump_cdb(fsg) do { } while (0)
+
+#else
+
+#  define dump_msg(fsg, /* const char * */ label, \
+		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+#  ifdef VERBOSE_DEBUG
+
+#    define dump_cdb(fsg)						\
+	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\
+		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\
+
+#  else
+
+#    define dump_cdb(fsg) do { } while (0)
+
+#  endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE	16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE				0
+#define SS_COMMUNICATION_FAILURE		0x040800
+#define SS_INVALID_COMMAND			0x052000
+#define SS_INVALID_FIELD_IN_CDB			0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500
+#define SS_MEDIUM_NOT_PRESENT			0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED		0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION	0x062800
+#define SS_RESET_OCCURRED			0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900
+#define SS_UNRECOVERED_READ_ERROR		0x031100
+#define SS_WRITE_ERROR				0x030c02
+#define SS_WRITE_PROTECTED			0x072700
+
+#define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */
+#define ASC(x)		((u8) ((x) >> 8))
+#define ASCQ(x)		((u8) (x))
+
+/*
+ * Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL
+ * byte
+ */
+#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1))
+
+struct fsg_lun {
+	struct file	*filp;
+	loff_t		file_length;
+	loff_t		num_sectors;
+
+	unsigned int	initially_ro:1;
+	unsigned int	ro:1;
+	unsigned int	removable:1;
+	unsigned int	cdrom:1;
+	unsigned int	prevent_medium_removal:1;
+	unsigned int	registered:1;
+	unsigned int	info_valid:1;
+	unsigned int	nofua:1;
+
+	u32		sense_data;
+	u32		sense_data_info;
+	u32		unit_attention_data;
+
+	unsigned int	blkbits; /* Bits of logical block size
+						       of bound block device */
+	unsigned int	blksize; /* logical block size of bound block device */
+	struct device	dev;
+	const char	*name;		/* "lun.name" */
+	const char	**name_pfx;	/* "function.name" */
+	char		inquiry_string[INQUIRY_STRING_LEN];
+};
+
+static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
+{
+	return curlun->filp != NULL;
+}
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN	((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS	16
+
+enum fsg_buffer_state {
+	BUF_STATE_SENDING = -2,
+	BUF_STATE_RECEIVING,
+	BUF_STATE_EMPTY = 0,
+	BUF_STATE_FULL
+};
+
+struct fsg_buffhd {
+	void				*buf;
+	enum fsg_buffer_state		state;
+	struct fsg_buffhd		*next;
+
+	/*
+	 * The NetChip 2280 is faster, and handles some protocol faults
+	 * better, if we don't submit any short bulk-out read requests.
+	 * So we will record the intended request length here.
+	 */
+	unsigned int			bulk_out_intended_length;
+
+	struct usb_request		*inreq;
+	struct usb_request		*outreq;
+};
+
+enum fsg_state {
+	FSG_STATE_NORMAL,
+	FSG_STATE_ABORT_BULK_OUT,
+	FSG_STATE_PROTOCOL_RESET,
+	FSG_STATE_CONFIG_CHANGE,
+	FSG_STATE_EXIT,
+	FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+	DATA_DIR_UNKNOWN = 0,
+	DATA_DIR_FROM_HOST,
+	DATA_DIR_TO_HOST,
+	DATA_DIR_NONE
+};
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+	return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+	return container_of(dev, struct fsg_lun, dev);
+}
+
+enum {
+	FSG_STRING_INTERFACE
+};
+
+extern struct usb_interface_descriptor fsg_intf_desc;
+
+extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_fs_function[];
+
+extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
+extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
+extern struct usb_descriptor_header *fsg_hs_function[];
+
+extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
+extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
+extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
+extern struct usb_descriptor_header *fsg_ss_function[];
+
+void fsg_lun_close(struct fsg_lun *curlun);
+int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
+int fsg_lun_fsync_sub(struct fsg_lun *curlun);
+void store_cdrom_address(u8 *dest, int msf, u32 addr);
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		      char *buf);
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		     const char *buf, size_t count);
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+		       const char *buf, size_t count);
+ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+			const char *buf, size_t count);
+ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
+			    size_t count);
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+				 size_t count);
+
+#endif /* USB_STORAGE_COMMON_H */
diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h
new file mode 100644
index 0000000..3cd5657
--- /dev/null
+++ b/drivers/usb/gadget/function/tcm.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __TARGET_USB_GADGET_H__
+#define __TARGET_USB_GADGET_H__
+
+#include <linux/kref.h>
+/* #include <linux/usb/uas.h> */
+#include <linux/usb/composite.h>
+#include <linux/usb/uas.h>
+#include <linux/usb/storage.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#define USBG_NAMELEN 32
+
+#define fuas_to_gadget(f)	(f->function.config->cdev->gadget)
+#define UASP_SS_EP_COMP_LOG_STREAMS 4
+#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
+
+enum {
+	USB_G_STR_INT_UAS = 0,
+	USB_G_STR_INT_BBB,
+};
+
+#define USB_G_ALT_INT_BBB       0
+#define USB_G_ALT_INT_UAS       1
+
+#define USB_G_DEFAULT_SESSION_TAGS	128
+
+struct tcm_usbg_nexus {
+	struct se_session *tvn_se_sess;
+};
+
+struct usbg_tpg {
+	struct mutex tpg_mutex;
+	/* SAS port target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to usbg_tport */
+	struct usbg_tport *tport;
+	struct workqueue_struct *workqueue;
+	/* Returned by usbg_make_tpg() */
+	struct se_portal_group se_tpg;
+	u32 gadget_connect;
+	struct tcm_usbg_nexus *tpg_nexus;
+	atomic_t tpg_port_count;
+
+	struct usb_function_instance *fi;
+};
+
+struct usbg_tport {
+	/* Binary World Wide unique Port Name for SAS Target port */
+	u64 tport_wwpn;
+	/* ASCII formatted WWPN for SAS Target port */
+	char tport_name[USBG_NAMELEN];
+	/* Returned by usbg_make_tport() */
+	struct se_wwn tport_wwn;
+};
+
+enum uas_state {
+	UASP_SEND_DATA,
+	UASP_RECEIVE_DATA,
+	UASP_SEND_STATUS,
+	UASP_QUEUE_COMMAND,
+};
+
+#define USBG_MAX_CMD    64
+struct usbg_cmd {
+	/* common */
+	u8 cmd_buf[USBG_MAX_CMD];
+	u32 data_len;
+	struct work_struct work;
+	int unpacked_lun;
+	struct se_cmd se_cmd;
+	void *data_buf; /* used if no sg support available */
+	struct f_uas *fu;
+	struct completion write_complete;
+	struct kref ref;
+
+	/* UAS only */
+	u16 tag;
+	u16 prio_attr;
+	struct sense_iu sense_iu;
+	enum uas_state state;
+	struct uas_stream *stream;
+
+	/* BOT only */
+	__le32 bot_tag;
+	unsigned int csw_code;
+	unsigned is_read:1;
+
+};
+
+struct uas_stream {
+	struct usb_request	*req_in;
+	struct usb_request	*req_out;
+	struct usb_request	*req_status;
+};
+
+struct usbg_cdb {
+	struct usb_request	*req;
+	void			*buf;
+};
+
+struct bot_status {
+	struct usb_request	*req;
+	struct bulk_cs_wrap	csw;
+};
+
+struct f_uas {
+	struct usbg_tpg		*tpg;
+	struct usb_function	function;
+	u16			iface;
+
+	u32			flags;
+#define USBG_ENABLED		(1 << 0)
+#define USBG_IS_UAS		(1 << 1)
+#define USBG_USE_STREAMS	(1 << 2)
+#define USBG_IS_BOT		(1 << 3)
+#define USBG_BOT_CMD_PEND	(1 << 4)
+
+	struct usbg_cdb		cmd;
+	struct usb_ep		*ep_in;
+	struct usb_ep		*ep_out;
+
+	/* UAS */
+	struct usb_ep		*ep_status;
+	struct usb_ep		*ep_cmd;
+	struct uas_stream	stream[UASP_SS_EP_COMP_NUM_STREAMS];
+
+	/* BOT */
+	struct bot_status	bot_status;
+	struct usb_request	*bot_req_in;
+	struct usb_request	*bot_req_out;
+};
+
+#endif /* __TARGET_USB_GADGET_H__ */
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
new file mode 100644
index 0000000..fb5ed97
--- /dev/null
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -0,0 +1,631 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_audio.c -- interface to USB gadget "ALSA sound card" utilities
+ *
+ * Copyright (C) 2016
+ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ *
+ * Sound card implementation was cut-and-pasted with changes
+ * from f_uac2.c and has:
+ *    Copyright (C) 2011
+ *    Yadwinder Singh (yadi.brar01@gmail.com)
+ *    Jaswinder Singh (jaswinder.singh@linaro.org)
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "u_audio.h"
+
+#define BUFF_SIZE_MAX	(PAGE_SIZE * 16)
+#define PRD_SIZE_MAX	PAGE_SIZE
+#define MIN_PERIODS	4
+
+struct uac_req {
+	struct uac_rtd_params *pp; /* parent param */
+	struct usb_request *req;
+};
+
+/* Runtime data params for one stream */
+struct uac_rtd_params {
+	struct snd_uac_chip *uac; /* parent chip */
+	bool ep_enabled; /* if the ep is enabled */
+
+	struct snd_pcm_substream *ss;
+
+	/* Ring buffer */
+	ssize_t hw_ptr;
+
+	void *rbuf;
+
+	unsigned max_psize;	/* MaxPacketSize of endpoint */
+	struct uac_req *ureq;
+
+	spinlock_t lock;
+};
+
+struct snd_uac_chip {
+	struct g_audio *audio_dev;
+
+	struct uac_rtd_params p_prm;
+	struct uac_rtd_params c_prm;
+
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+
+	/* timekeeping for the playback endpoint */
+	unsigned int p_interval;
+	unsigned int p_residue;
+
+	/* pre-calculated values for playback iso completion */
+	unsigned int p_pktsize;
+	unsigned int p_pktsize_residue;
+	unsigned int p_framesize;
+};
+
+static const struct snd_pcm_hardware uac_pcm_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
+		 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+		 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX,
+	.buffer_bytes_max = BUFF_SIZE_MAX,
+	.period_bytes_max = PRD_SIZE_MAX,
+	.periods_min = MIN_PERIODS,
+};
+
+static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned pending;
+	unsigned long flags, flags2;
+	unsigned int hw_ptr;
+	int status = req->status;
+	struct uac_req *ur = req->context;
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	struct uac_rtd_params *prm = ur->pp;
+	struct snd_uac_chip *uac = prm->uac;
+
+	/* i/f shutting down */
+	if (!prm->ep_enabled || req->status == -ESHUTDOWN)
+		return;
+
+	/*
+	 * We can't really do much about bad xfers.
+	 * Afterall, the ISOCH xfers could fail legitimately.
+	 */
+	if (status)
+		pr_debug("%s: iso_complete status(%d) %d/%d\n",
+			__func__, status, req->actual, req->length);
+
+	substream = prm->ss;
+
+	/* Do nothing if ALSA isn't active */
+	if (!substream)
+		goto exit;
+
+	snd_pcm_stream_lock_irqsave(substream, flags2);
+
+	runtime = substream->runtime;
+	if (!runtime || !snd_pcm_running(substream)) {
+		snd_pcm_stream_unlock_irqrestore(substream, flags2);
+		goto exit;
+	}
+
+	spin_lock_irqsave(&prm->lock, flags);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/*
+		 * For each IN packet, take the quotient of the current data
+		 * rate and the endpoint's interval as the base packet size.
+		 * If there is a residue from this division, add it to the
+		 * residue accumulator.
+		 */
+		req->length = uac->p_pktsize;
+		uac->p_residue += uac->p_pktsize_residue;
+
+		/*
+		 * Whenever there are more bytes in the accumulator than we
+		 * need to add one more sample frame, increase this packet's
+		 * size and decrease the accumulator.
+		 */
+		if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
+			req->length += uac->p_framesize;
+			uac->p_residue -= uac->p_framesize *
+					   uac->p_interval;
+		}
+
+		req->actual = req->length;
+	}
+
+	hw_ptr = prm->hw_ptr;
+
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	/* Pack USB load in ALSA ring buffer */
+	pending = runtime->dma_bytes - hw_ptr;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (unlikely(pending < req->actual)) {
+			memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
+			memcpy(req->buf + pending, runtime->dma_area,
+			       req->actual - pending);
+		} else {
+			memcpy(req->buf, runtime->dma_area + hw_ptr,
+			       req->actual);
+		}
+	} else {
+		if (unlikely(pending < req->actual)) {
+			memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
+			memcpy(runtime->dma_area, req->buf + pending,
+			       req->actual - pending);
+		} else {
+			memcpy(runtime->dma_area + hw_ptr, req->buf,
+			       req->actual);
+		}
+	}
+
+	spin_lock_irqsave(&prm->lock, flags);
+	/* update hw_ptr after data is copied to memory */
+	prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
+	hw_ptr = prm->hw_ptr;
+	spin_unlock_irqrestore(&prm->lock, flags);
+	snd_pcm_stream_unlock_irqrestore(substream, flags2);
+
+	if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
+		snd_pcm_period_elapsed(substream);
+
+exit:
+	if (usb_ep_queue(ep, req, GFP_ATOMIC))
+		dev_err(uac->card->dev, "%d Error!\n", __LINE__);
+}
+
+static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+	struct g_audio *audio_dev;
+	struct uac_params *params;
+	unsigned long flags;
+	int err = 0;
+
+	audio_dev = uac->audio_dev;
+	params = &audio_dev->params;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	spin_lock_irqsave(&prm->lock, flags);
+
+	/* Reset */
+	prm->hw_ptr = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		prm->ss = substream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		prm->ss = NULL;
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&prm->lock, flags);
+
+	/* Clear buffer after Play stops */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
+		memset(prm->rbuf, 0, prm->max_psize * params->req_number);
+
+	return err;
+}
+
+static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct uac_rtd_params *prm;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prm = &uac->p_prm;
+	else
+		prm = &uac->c_prm;
+
+	return bytes_to_frames(substream->runtime, prm->hw_ptr);
+}
+
+static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int uac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct g_audio *audio_dev;
+	struct uac_params *params;
+	int p_ssize, c_ssize;
+	int p_srate, c_srate;
+	int p_chmask, c_chmask;
+
+	audio_dev = uac->audio_dev;
+	params = &audio_dev->params;
+	p_ssize = params->p_ssize;
+	c_ssize = params->c_ssize;
+	p_srate = params->p_srate;
+	c_srate = params->c_srate;
+	p_chmask = params->p_chmask;
+	c_chmask = params->c_chmask;
+	uac->p_residue = 0;
+
+	runtime->hw = uac_pcm_hardware;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		spin_lock_init(&uac->p_prm.lock);
+		runtime->hw.rate_min = p_srate;
+		switch (p_ssize) {
+		case 3:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+			break;
+		case 4:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+			break;
+		default:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+			break;
+		}
+		runtime->hw.channels_min = num_channels(p_chmask);
+		runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
+						/ runtime->hw.periods_min;
+	} else {
+		spin_lock_init(&uac->c_prm.lock);
+		runtime->hw.rate_min = c_srate;
+		switch (c_ssize) {
+		case 3:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+			break;
+		case 4:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+			break;
+		default:
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+			break;
+		}
+		runtime->hw.channels_min = num_channels(c_chmask);
+		runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
+						/ runtime->hw.periods_min;
+	}
+
+	runtime->hw.rate_max = runtime->hw.rate_min;
+	runtime->hw.channels_max = runtime->hw.channels_min;
+
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+	return 0;
+}
+
+/* ALSA cries without these function pointers */
+static int uac_pcm_null(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static const struct snd_pcm_ops uac_pcm_ops = {
+	.open = uac_pcm_open,
+	.close = uac_pcm_null,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = uac_pcm_hw_params,
+	.hw_free = uac_pcm_hw_free,
+	.trigger = uac_pcm_trigger,
+	.pointer = uac_pcm_pointer,
+	.prepare = uac_pcm_null,
+};
+
+static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep)
+{
+	struct snd_uac_chip *uac = prm->uac;
+	struct g_audio *audio_dev;
+	struct uac_params *params;
+	int i;
+
+	if (!prm->ep_enabled)
+		return;
+
+	prm->ep_enabled = false;
+
+	audio_dev = uac->audio_dev;
+	params = &audio_dev->params;
+
+	for (i = 0; i < params->req_number; i++) {
+		if (prm->ureq[i].req) {
+			usb_ep_dequeue(ep, prm->ureq[i].req);
+			usb_ep_free_request(ep, prm->ureq[i].req);
+			prm->ureq[i].req = NULL;
+		}
+	}
+
+	if (usb_ep_disable(ep))
+		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
+}
+
+
+int u_audio_start_capture(struct g_audio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+	struct usb_gadget *gadget = audio_dev->gadget;
+	struct device *dev = &gadget->dev;
+	struct usb_request *req;
+	struct usb_ep *ep;
+	struct uac_rtd_params *prm;
+	struct uac_params *params = &audio_dev->params;
+	int req_len, i;
+
+	ep = audio_dev->out_ep;
+	prm = &uac->c_prm;
+	config_ep_by_speed(gadget, &audio_dev->func, ep);
+	req_len = prm->max_psize;
+
+	prm->ep_enabled = true;
+	usb_ep_enable(ep);
+
+	for (i = 0; i < params->req_number; i++) {
+		if (!prm->ureq[i].req) {
+			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+			if (req == NULL)
+				return -ENOMEM;
+
+			prm->ureq[i].req = req;
+			prm->ureq[i].pp = prm;
+
+			req->zero = 0;
+			req->context = &prm->ureq[i];
+			req->length = req_len;
+			req->complete = u_audio_iso_complete;
+			req->buf = prm->rbuf + i * prm->max_psize;
+		}
+
+		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_start_capture);
+
+void u_audio_stop_capture(struct g_audio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+
+	free_ep(&uac->c_prm, audio_dev->out_ep);
+}
+EXPORT_SYMBOL_GPL(u_audio_stop_capture);
+
+int u_audio_start_playback(struct g_audio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+	struct usb_gadget *gadget = audio_dev->gadget;
+	struct device *dev = &gadget->dev;
+	struct usb_request *req;
+	struct usb_ep *ep;
+	struct uac_rtd_params *prm;
+	struct uac_params *params = &audio_dev->params;
+	unsigned int factor, rate;
+	const struct usb_endpoint_descriptor *ep_desc;
+	int req_len, i;
+
+	ep = audio_dev->in_ep;
+	prm = &uac->p_prm;
+	config_ep_by_speed(gadget, &audio_dev->func, ep);
+
+	ep_desc = ep->desc;
+
+	/* pre-calculate the playback endpoint's interval */
+	if (gadget->speed == USB_SPEED_FULL)
+		factor = 1000;
+	else
+		factor = 8000;
+
+	/* pre-compute some values for iso_complete() */
+	uac->p_framesize = params->p_ssize *
+			    num_channels(params->p_chmask);
+	rate = params->p_srate * uac->p_framesize;
+	uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
+	uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval,
+				prm->max_psize);
+
+	if (uac->p_pktsize < prm->max_psize)
+		uac->p_pktsize_residue = rate % uac->p_interval;
+	else
+		uac->p_pktsize_residue = 0;
+
+	req_len = uac->p_pktsize;
+	uac->p_residue = 0;
+
+	prm->ep_enabled = true;
+	usb_ep_enable(ep);
+
+	for (i = 0; i < params->req_number; i++) {
+		if (!prm->ureq[i].req) {
+			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+			if (req == NULL)
+				return -ENOMEM;
+
+			prm->ureq[i].req = req;
+			prm->ureq[i].pp = prm;
+
+			req->zero = 0;
+			req->context = &prm->ureq[i];
+			req->length = req_len;
+			req->complete = u_audio_iso_complete;
+			req->buf = prm->rbuf + i * prm->max_psize;
+		}
+
+		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_start_playback);
+
+void u_audio_stop_playback(struct g_audio *audio_dev)
+{
+	struct snd_uac_chip *uac = audio_dev->uac;
+
+	free_ep(&uac->p_prm, audio_dev->in_ep);
+}
+EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+
+int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
+					const char *card_name)
+{
+	struct snd_uac_chip *uac;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct uac_params *params;
+	int p_chmask, c_chmask;
+	int err;
+
+	if (!g_audio)
+		return -EINVAL;
+
+	uac = kzalloc(sizeof(*uac), GFP_KERNEL);
+	if (!uac)
+		return -ENOMEM;
+	g_audio->uac = uac;
+	uac->audio_dev = g_audio;
+
+	params = &g_audio->params;
+	p_chmask = params->p_chmask;
+	c_chmask = params->c_chmask;
+
+	if (c_chmask) {
+		struct uac_rtd_params *prm = &uac->c_prm;
+
+		uac->c_prm.uac = uac;
+		prm->max_psize = g_audio->out_ep_maxpsize;
+
+		prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req),
+				GFP_KERNEL);
+		if (!prm->ureq) {
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
+				GFP_KERNEL);
+		if (!prm->rbuf) {
+			prm->max_psize = 0;
+			err = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (p_chmask) {
+		struct uac_rtd_params *prm = &uac->p_prm;
+
+		uac->p_prm.uac = uac;
+		prm->max_psize = g_audio->in_ep_maxpsize;
+
+		prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req),
+				GFP_KERNEL);
+		if (!prm->ureq) {
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		prm->rbuf = kcalloc(params->req_number, prm->max_psize,
+				GFP_KERNEL);
+		if (!prm->rbuf) {
+			prm->max_psize = 0;
+			err = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	/* Choose any slot, with no id */
+	err = snd_card_new(&g_audio->gadget->dev,
+			-1, NULL, THIS_MODULE, 0, &card);
+	if (err < 0)
+		goto fail;
+
+	uac->card = card;
+
+	/*
+	 * Create first PCM device
+	 * Create a substream only for non-zero channel streams
+	 */
+	err = snd_pcm_new(uac->card, pcm_name, 0,
+			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
+	if (err < 0)
+		goto snd_fail;
+
+	strlcpy(pcm->name, pcm_name, sizeof(pcm->name));
+	pcm->private_data = uac;
+	uac->pcm = pcm;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
+
+	strlcpy(card->driver, card_name, sizeof(card->driver));
+	strlcpy(card->shortname, card_name, sizeof(card->shortname));
+	sprintf(card->longname, "%s %i", card_name, card->dev->id);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
+
+	err = snd_card_register(card);
+
+	if (!err)
+		return 0;
+
+snd_fail:
+	snd_card_free(card);
+fail:
+	kfree(uac->p_prm.ureq);
+	kfree(uac->c_prm.ureq);
+	kfree(uac->p_prm.rbuf);
+	kfree(uac->c_prm.rbuf);
+	kfree(uac);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(g_audio_setup);
+
+void g_audio_cleanup(struct g_audio *g_audio)
+{
+	struct snd_uac_chip *uac;
+	struct snd_card *card;
+
+	if (!g_audio || !g_audio->uac)
+		return;
+
+	uac = g_audio->uac;
+	card = uac->card;
+	if (card)
+		snd_card_free(card);
+
+	kfree(uac->p_prm.ureq);
+	kfree(uac->c_prm.ureq);
+	kfree(uac->p_prm.rbuf);
+	kfree(uac->c_prm.rbuf);
+	kfree(uac);
+}
+EXPORT_SYMBOL_GPL(g_audio_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
new file mode 100644
index 0000000..81d3d4e
--- /dev/null
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_audio.h -- interface to USB gadget "ALSA sound card" utilities
+ *
+ * Copyright (C) 2016
+ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ */
+
+#ifndef __U_AUDIO_H
+#define __U_AUDIO_H
+
+#include <linux/usb/composite.h>
+
+struct uac_params {
+	/* playback */
+	int p_chmask;	/* channel mask */
+	int p_srate;	/* rate in Hz */
+	int p_ssize;	/* sample size */
+
+	/* capture */
+	int c_chmask;	/* channel mask */
+	int c_srate;	/* rate in Hz */
+	int c_ssize;	/* sample size */
+
+	int req_number; /* number of preallocated requests */
+};
+
+struct g_audio {
+	struct usb_function func;
+	struct usb_gadget *gadget;
+
+	struct usb_ep *in_ep;
+	struct usb_ep *out_ep;
+
+	/* Max packet size for all in_ep possible speeds */
+	unsigned int in_ep_maxpsize;
+	/* Max packet size for all out_ep possible speeds */
+	unsigned int out_ep_maxpsize;
+
+	/* The ALSA Sound Card it represents on the USB-Client side */
+	struct snd_uac_chip *uac;
+
+	struct uac_params params;
+};
+
+static inline struct g_audio *func_to_g_audio(struct usb_function *f)
+{
+	return container_of(f, struct g_audio, func);
+}
+
+static inline uint num_channels(uint chanmask)
+{
+	uint num = 0;
+
+	while (chanmask) {
+		num += (chanmask & 1);
+		chanmask >>= 1;
+	}
+
+	return num;
+}
+
+/*
+ * g_audio_setup - initialize one virtual ALSA sound card
+ * @g_audio: struct with filled params, in_ep_maxpsize, out_ep_maxpsize
+ * @pcm_name: the id string for a PCM instance of this sound card
+ * @card_name: name of this soundcard
+ *
+ * This sets up the single virtual ALSA sound card that may be exported by a
+ * gadget driver using this framework.
+ *
+ * Context: may sleep
+ *
+ * Returns zero on success, or a negative error on failure.
+ */
+int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
+					const char *card_name);
+void g_audio_cleanup(struct g_audio *g_audio);
+
+int u_audio_start_capture(struct g_audio *g_audio);
+void u_audio_stop_capture(struct g_audio *g_audio);
+int u_audio_start_playback(struct g_audio *g_audio);
+void u_audio_stop_playback(struct g_audio *g_audio);
+
+#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
new file mode 100644
index 0000000..050aa67
--- /dev/null
+++ b/drivers/usb/gadget/function/u_ecm.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_ecm.h
+ *
+ * Utility definitions for the ecm function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_ECM_H
+#define U_ECM_H
+
+#include <linux/usb/composite.h>
+
+struct f_ecm_opts {
+	struct usb_function_instance	func_inst;
+	struct net_device		*net;
+	bool				bound;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_ECM_H */
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
new file mode 100644
index 0000000..de3828d
--- /dev/null
+++ b/drivers/usb/gadget/function/u_eem.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_eem.h
+ *
+ * Utility definitions for the eem function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_EEM_H
+#define U_EEM_H
+
+#include <linux/usb/composite.h>
+
+struct f_eem_opts {
+	struct usb_function_instance	func_inst;
+	struct net_device		*net;
+	bool				bound;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_EEM_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
new file mode 100644
index 0000000..0f026d4
--- /dev/null
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+
+#include "u_ether.h"
+
+
+/*
+ * This component encapsulates the Ethernet link glue needed to provide
+ * one (!) network link through the USB gadget stack, normally "usb0".
+ *
+ * The control and data models are handled by the function driver which
+ * connects to this code; such as CDC Ethernet (ECM or EEM),
+ * "CDC Subset", or RNDIS.  That includes all descriptor and endpoint
+ * management.
+ *
+ * Link level addressing is handled by this component using module
+ * parameters; if no such parameters are provided, random link level
+ * addresses are used.  Each end of the link uses one address.  The
+ * host end address is exported in various ways, and is often recorded
+ * in configuration databases.
+ *
+ * The driver which assembles each configuration using such a link is
+ * responsible for ensuring that each configuration includes at most one
+ * instance of is network link.  (The network layer provides ways for
+ * this single "physical" link to be used by multiple virtual links.)
+ */
+
+#define UETH__VERSION	"29-May-2008"
+
+/* Experiments show that both Linux and Windows hosts allow up to 16k
+ * frame sizes. Set the max size to 15k+52 to prevent allocating 32k
+ * blocks and still have efficient handling. */
+#define GETHER_MAX_ETH_FRAME_LEN 15412
+
+struct eth_dev {
+	/* lock is held while accessing port_usb
+	 */
+	spinlock_t		lock;
+	struct gether		*port_usb;
+
+	struct net_device	*net;
+	struct usb_gadget	*gadget;
+
+	spinlock_t		req_lock;	/* guard {rx,tx}_reqs */
+	struct list_head	tx_reqs, rx_reqs;
+	atomic_t		tx_qlen;
+
+	struct sk_buff_head	rx_frames;
+
+	unsigned		qmult;
+
+	unsigned		header_len;
+	struct sk_buff		*(*wrap)(struct gether *, struct sk_buff *skb);
+	int			(*unwrap)(struct gether *,
+						struct sk_buff *skb,
+						struct sk_buff_head *list);
+
+	struct work_struct	work;
+
+	unsigned long		todo;
+#define	WORK_RX_MEMORY		0
+
+	bool			zlp;
+	bool			no_skb_reserve;
+	u8			host_mac[ETH_ALEN];
+	u8			dev_mac[ETH_ALEN];
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define RX_EXTRA	20	/* bytes guarding against rx overflows */
+
+#define DEFAULT_QLEN	2	/* double buffering by default */
+
+/* for dual-speed hardware, use deeper queues at high/super speed */
+static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
+{
+	if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+					    gadget->speed == USB_SPEED_SUPER))
+		return qmult * DEFAULT_QLEN;
+	else
+		return DEFAULT_QLEN;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* REVISIT there must be a better way than having two sets
+ * of debug calls ...
+ */
+
+#undef DBG
+#undef VDBG
+#undef ERROR
+#undef INFO
+
+#define xprintk(d, level, fmt, args...) \
+	printk(level "%s: " fmt , (d)->net->name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DBG(dev, fmt, args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev, fmt, args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VDBG	DBG
+#else
+#define VDBG(dev, fmt, args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev, fmt, args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define INFO(dev, fmt, args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+{
+	struct eth_dev *dev = netdev_priv(net);
+
+	strlcpy(p->driver, "g_ether", sizeof(p->driver));
+	strlcpy(p->version, UETH__VERSION, sizeof(p->version));
+	strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
+	strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+}
+
+/* REVISIT can also support:
+ *   - WOL (by tracking suspends and issuing remote wakeup)
+ *   - msglevel (implies updated messaging)
+ *   - ... probably more ethtool ops
+ */
+
+static const struct ethtool_ops ops = {
+	.get_drvinfo = eth_get_drvinfo,
+	.get_link = ethtool_op_get_link,
+};
+
+static void defer_kevent(struct eth_dev *dev, int flag)
+{
+	if (test_and_set_bit(flag, &dev->todo))
+		return;
+	if (!schedule_work(&dev->work))
+		ERROR(dev, "kevent %d may have been dropped\n", flag);
+	else
+		DBG(dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
+{
+	struct usb_gadget *g = dev->gadget;
+	struct sk_buff	*skb;
+	int		retval = -ENOMEM;
+	size_t		size = 0;
+	struct usb_ep	*out;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->port_usb)
+		out = dev->port_usb->out_ep;
+	else
+		out = NULL;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (!out)
+		return -ENOTCONN;
+
+
+	/* Padding up to RX_EXTRA handles minor disagreements with host.
+	 * Normally we use the USB "terminate on short read" convention;
+	 * so allow up to (N*maxpacket), since that memory is normally
+	 * already allocated.  Some hardware doesn't deal well with short
+	 * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+	 * byte off the end (to force hardware errors on overflow).
+	 *
+	 * RNDIS uses internal framing, and explicitly allows senders to
+	 * pad to end-of-packet.  That's potentially nice for speed, but
+	 * means receivers can't recover lost synch on their own (because
+	 * new packets don't only start after a short RX).
+	 */
+	size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
+	size += dev->port_usb->header_len;
+
+	if (g->quirk_ep_out_aligned_size) {
+		size += out->maxpacket - 1;
+		size -= size % out->maxpacket;
+	}
+
+	if (dev->port_usb->is_fixed)
+		size = max_t(size_t, size, dev->port_usb->fixed_out_len);
+
+	skb = __netdev_alloc_skb(dev->net, size + NET_IP_ALIGN, gfp_flags);
+	if (skb == NULL) {
+		DBG(dev, "no rx skb\n");
+		goto enomem;
+	}
+
+	/* Some platforms perform better when IP packets are aligned,
+	 * but on at least one, checksumming fails otherwise.  Note:
+	 * RNDIS headers involve variable numbers of LE32 values.
+	 */
+	if (likely(!dev->no_skb_reserve))
+		skb_reserve(skb, NET_IP_ALIGN);
+
+	req->buf = skb->data;
+	req->length = size;
+	req->complete = rx_complete;
+	req->context = skb;
+
+	retval = usb_ep_queue(out, req, gfp_flags);
+	if (retval == -ENOMEM)
+enomem:
+		defer_kevent(dev, WORK_RX_MEMORY);
+	if (retval) {
+		DBG(dev, "rx submit --> %d\n", retval);
+		if (skb)
+			dev_kfree_skb_any(skb);
+		spin_lock_irqsave(&dev->req_lock, flags);
+		list_add(&req->list, &dev->rx_reqs);
+		spin_unlock_irqrestore(&dev->req_lock, flags);
+	}
+	return retval;
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context, *skb2;
+	struct eth_dev	*dev = ep->driver_data;
+	int		status = req->status;
+
+	switch (status) {
+
+	/* normal completion */
+	case 0:
+		skb_put(skb, req->actual);
+
+		if (dev->unwrap) {
+			unsigned long	flags;
+
+			spin_lock_irqsave(&dev->lock, flags);
+			if (dev->port_usb) {
+				status = dev->unwrap(dev->port_usb,
+							skb,
+							&dev->rx_frames);
+			} else {
+				dev_kfree_skb_any(skb);
+				status = -ENOTCONN;
+			}
+			spin_unlock_irqrestore(&dev->lock, flags);
+		} else {
+			skb_queue_tail(&dev->rx_frames, skb);
+		}
+		skb = NULL;
+
+		skb2 = skb_dequeue(&dev->rx_frames);
+		while (skb2) {
+			if (status < 0
+					|| ETH_HLEN > skb2->len
+					|| skb2->len > GETHER_MAX_ETH_FRAME_LEN) {
+				dev->net->stats.rx_errors++;
+				dev->net->stats.rx_length_errors++;
+				DBG(dev, "rx length %d\n", skb2->len);
+				dev_kfree_skb_any(skb2);
+				goto next_frame;
+			}
+			skb2->protocol = eth_type_trans(skb2, dev->net);
+			dev->net->stats.rx_packets++;
+			dev->net->stats.rx_bytes += skb2->len;
+
+			/* no buffer copies needed, unless hardware can't
+			 * use skb buffers.
+			 */
+			status = netif_rx(skb2);
+next_frame:
+			skb2 = skb_dequeue(&dev->rx_frames);
+		}
+		break;
+
+	/* software-driven interface shutdown */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		VDBG(dev, "rx shutdown, code %d\n", status);
+		goto quiesce;
+
+	/* for hardware automagic (such as pxa) */
+	case -ECONNABORTED:		/* endpoint reset */
+		DBG(dev, "rx %s reset\n", ep->name);
+		defer_kevent(dev, WORK_RX_MEMORY);
+quiesce:
+		dev_kfree_skb_any(skb);
+		goto clean;
+
+	/* data overrun */
+	case -EOVERFLOW:
+		dev->net->stats.rx_over_errors++;
+		/* FALLTHROUGH */
+
+	default:
+		dev->net->stats.rx_errors++;
+		DBG(dev, "rx status %d\n", status);
+		break;
+	}
+
+	if (skb)
+		dev_kfree_skb_any(skb);
+	if (!netif_running(dev->net)) {
+clean:
+		spin_lock(&dev->req_lock);
+		list_add(&req->list, &dev->rx_reqs);
+		spin_unlock(&dev->req_lock);
+		req = NULL;
+	}
+	if (req)
+		rx_submit(dev, req, GFP_ATOMIC);
+}
+
+static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
+{
+	unsigned		i;
+	struct usb_request	*req;
+
+	if (!n)
+		return -ENOMEM;
+
+	/* queue/recycle up to N requests */
+	i = n;
+	list_for_each_entry(req, list, list) {
+		if (i-- == 0)
+			goto extra;
+	}
+	while (i--) {
+		req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+		if (!req)
+			return list_empty(list) ? -ENOMEM : 0;
+		list_add(&req->list, list);
+	}
+	return 0;
+
+extra:
+	/* free extras */
+	for (;;) {
+		struct list_head	*next;
+
+		next = req->list.next;
+		list_del(&req->list);
+		usb_ep_free_request(ep, req);
+
+		if (next == list)
+			break;
+
+		req = container_of(next, struct usb_request, list);
+	}
+	return 0;
+}
+
+static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
+{
+	int	status;
+
+	spin_lock(&dev->req_lock);
+	status = prealloc(&dev->tx_reqs, link->in_ep, n);
+	if (status < 0)
+		goto fail;
+	status = prealloc(&dev->rx_reqs, link->out_ep, n);
+	if (status < 0)
+		goto fail;
+	goto done;
+fail:
+	DBG(dev, "can't alloc requests\n");
+done:
+	spin_unlock(&dev->req_lock);
+	return status;
+}
+
+static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
+{
+	struct usb_request	*req;
+	unsigned long		flags;
+
+	/* fill unused rxq slots with some skb */
+	spin_lock_irqsave(&dev->req_lock, flags);
+	while (!list_empty(&dev->rx_reqs)) {
+		req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
+		list_del_init(&req->list);
+		spin_unlock_irqrestore(&dev->req_lock, flags);
+
+		if (rx_submit(dev, req, gfp_flags) < 0) {
+			defer_kevent(dev, WORK_RX_MEMORY);
+			return;
+		}
+
+		spin_lock_irqsave(&dev->req_lock, flags);
+	}
+	spin_unlock_irqrestore(&dev->req_lock, flags);
+}
+
+static void eth_work(struct work_struct *work)
+{
+	struct eth_dev	*dev = container_of(work, struct eth_dev, work);
+
+	if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
+		if (netif_running(dev->net))
+			rx_fill(dev, GFP_KERNEL);
+	}
+
+	if (dev->todo)
+		DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct sk_buff	*skb = req->context;
+	struct eth_dev	*dev = ep->driver_data;
+
+	switch (req->status) {
+	default:
+		dev->net->stats.tx_errors++;
+		VDBG(dev, "tx err %d\n", req->status);
+		/* FALLTHROUGH */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		dev_kfree_skb_any(skb);
+		break;
+	case 0:
+		dev->net->stats.tx_bytes += skb->len;
+		dev_consume_skb_any(skb);
+	}
+	dev->net->stats.tx_packets++;
+
+	spin_lock(&dev->req_lock);
+	list_add(&req->list, &dev->tx_reqs);
+	spin_unlock(&dev->req_lock);
+
+	atomic_dec(&dev->tx_qlen);
+	if (netif_carrier_ok(dev->net))
+		netif_wake_queue(dev->net);
+}
+
+static inline int is_promisc(u16 cdc_filter)
+{
+	return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+}
+
+static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
+					struct net_device *net)
+{
+	struct eth_dev		*dev = netdev_priv(net);
+	int			length = 0;
+	int			retval;
+	struct usb_request	*req = NULL;
+	unsigned long		flags;
+	struct usb_ep		*in;
+	u16			cdc_filter;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->port_usb) {
+		in = dev->port_usb->in_ep;
+		cdc_filter = dev->port_usb->cdc_filter;
+	} else {
+		in = NULL;
+		cdc_filter = 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (skb && !in) {
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
+
+	/* apply outgoing CDC or RNDIS filters */
+	if (skb && !is_promisc(cdc_filter)) {
+		u8		*dest = skb->data;
+
+		if (is_multicast_ether_addr(dest)) {
+			u16	type;
+
+			/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+			 * SET_ETHERNET_MULTICAST_FILTERS requests
+			 */
+			if (is_broadcast_ether_addr(dest))
+				type = USB_CDC_PACKET_TYPE_BROADCAST;
+			else
+				type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+			if (!(cdc_filter & type)) {
+				dev_kfree_skb_any(skb);
+				return NETDEV_TX_OK;
+			}
+		}
+		/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+	}
+
+	spin_lock_irqsave(&dev->req_lock, flags);
+	/*
+	 * this freelist can be empty if an interrupt triggered disconnect()
+	 * and reconfigured the gadget (shutting down this queue) after the
+	 * network stack decided to xmit but before we got the spinlock.
+	 */
+	if (list_empty(&dev->tx_reqs)) {
+		spin_unlock_irqrestore(&dev->req_lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
+	list_del(&req->list);
+
+	/* temporarily stop TX queue when the freelist empties */
+	if (list_empty(&dev->tx_reqs))
+		netif_stop_queue(net);
+	spin_unlock_irqrestore(&dev->req_lock, flags);
+
+	/* no buffer copies needed, unless the network stack did it
+	 * or the hardware can't use skb buffers.
+	 * or there's not enough space for extra headers we need
+	 */
+	if (dev->wrap) {
+		unsigned long	flags;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if (dev->port_usb)
+			skb = dev->wrap(dev->port_usb, skb);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		if (!skb) {
+			/* Multi frame CDC protocols may store the frame for
+			 * later which is not a dropped frame.
+			 */
+			if (dev->port_usb &&
+					dev->port_usb->supports_multi_frame)
+				goto multiframe;
+			goto drop;
+		}
+	}
+
+	length = skb->len;
+	req->buf = skb->data;
+	req->context = skb;
+	req->complete = tx_complete;
+
+	/* NCM requires no zlp if transfer is dwNtbInMaxSize */
+	if (dev->port_usb &&
+	    dev->port_usb->is_fixed &&
+	    length == dev->port_usb->fixed_in_len &&
+	    (length % in->maxpacket) == 0)
+		req->zero = 0;
+	else
+		req->zero = 1;
+
+	/* use zlp framing on tx for strict CDC-Ether conformance,
+	 * though any robust network rx path ignores extra padding.
+	 * and some hardware doesn't like to write zlps.
+	 */
+	if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
+		length++;
+
+	req->length = length;
+
+	retval = usb_ep_queue(in, req, GFP_ATOMIC);
+	switch (retval) {
+	default:
+		DBG(dev, "tx queue err %d\n", retval);
+		break;
+	case 0:
+		netif_trans_update(net);
+		atomic_inc(&dev->tx_qlen);
+	}
+
+	if (retval) {
+		dev_kfree_skb_any(skb);
+drop:
+		dev->net->stats.tx_dropped++;
+multiframe:
+		spin_lock_irqsave(&dev->req_lock, flags);
+		if (list_empty(&dev->tx_reqs))
+			netif_start_queue(net);
+		list_add(&req->list, &dev->tx_reqs);
+		spin_unlock_irqrestore(&dev->req_lock, flags);
+	}
+	return NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
+{
+	DBG(dev, "%s\n", __func__);
+
+	/* fill the rx queue */
+	rx_fill(dev, gfp_flags);
+
+	/* and open the tx floodgates */
+	atomic_set(&dev->tx_qlen, 0);
+	netif_wake_queue(dev->net);
+}
+
+static int eth_open(struct net_device *net)
+{
+	struct eth_dev	*dev = netdev_priv(net);
+	struct gether	*link;
+
+	DBG(dev, "%s\n", __func__);
+	if (netif_carrier_ok(dev->net))
+		eth_start(dev, GFP_KERNEL);
+
+	spin_lock_irq(&dev->lock);
+	link = dev->port_usb;
+	if (link && link->open)
+		link->open(link);
+	spin_unlock_irq(&dev->lock);
+
+	return 0;
+}
+
+static int eth_stop(struct net_device *net)
+{
+	struct eth_dev	*dev = netdev_priv(net);
+	unsigned long	flags;
+
+	VDBG(dev, "%s\n", __func__);
+	netif_stop_queue(net);
+
+	DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+		dev->net->stats.rx_packets, dev->net->stats.tx_packets,
+		dev->net->stats.rx_errors, dev->net->stats.tx_errors
+		);
+
+	/* ensure there are no more active requests */
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->port_usb) {
+		struct gether	*link = dev->port_usb;
+		const struct usb_endpoint_descriptor *in;
+		const struct usb_endpoint_descriptor *out;
+
+		if (link->close)
+			link->close(link);
+
+		/* NOTE:  we have no abort-queue primitive we could use
+		 * to cancel all pending I/O.  Instead, we disable then
+		 * reenable the endpoints ... this idiom may leave toggle
+		 * wrong, but that's a self-correcting error.
+		 *
+		 * REVISIT:  we *COULD* just let the transfers complete at
+		 * their own pace; the network stack can handle old packets.
+		 * For the moment we leave this here, since it works.
+		 */
+		in = link->in_ep->desc;
+		out = link->out_ep->desc;
+		usb_ep_disable(link->in_ep);
+		usb_ep_disable(link->out_ep);
+		if (netif_carrier_ok(net)) {
+			DBG(dev, "host still using in/out endpoints\n");
+			link->in_ep->desc = in;
+			link->out_ep->desc = out;
+			usb_ep_enable(link->in_ep);
+			usb_ep_enable(link->out_ep);
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int get_ether_addr(const char *str, u8 *dev_addr)
+{
+	if (str) {
+		unsigned	i;
+
+		for (i = 0; i < 6; i++) {
+			unsigned char num;
+
+			if ((*str == '.') || (*str == ':'))
+				str++;
+			num = hex_to_bin(*str++) << 4;
+			num |= hex_to_bin(*str++);
+			dev_addr [i] = num;
+		}
+		if (is_valid_ether_addr(dev_addr))
+			return 0;
+	}
+	eth_random_addr(dev_addr);
+	return 1;
+}
+
+static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
+{
+	if (len < 18)
+		return -EINVAL;
+
+	snprintf(str, len, "%pM", dev_addr);
+	return 18;
+}
+
+static const struct net_device_ops eth_netdev_ops = {
+	.ndo_open		= eth_open,
+	.ndo_stop		= eth_stop,
+	.ndo_start_xmit		= eth_start_xmit,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static struct device_type gadget_type = {
+	.name	= "gadget",
+};
+
+/**
+ * gether_setup_name - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ *	host side of the link is recorded
+ * @netname: name for network device (for example, "usb")
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns an eth_dev pointer on success, or an ERR_PTR on failure.
+ */
+struct eth_dev *gether_setup_name(struct usb_gadget *g,
+		const char *dev_addr, const char *host_addr,
+		u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname)
+{
+	struct eth_dev		*dev;
+	struct net_device	*net;
+	int			status;
+
+	net = alloc_etherdev(sizeof *dev);
+	if (!net)
+		return ERR_PTR(-ENOMEM);
+
+	dev = netdev_priv(net);
+	spin_lock_init(&dev->lock);
+	spin_lock_init(&dev->req_lock);
+	INIT_WORK(&dev->work, eth_work);
+	INIT_LIST_HEAD(&dev->tx_reqs);
+	INIT_LIST_HEAD(&dev->rx_reqs);
+
+	skb_queue_head_init(&dev->rx_frames);
+
+	/* network device setup */
+	dev->net = net;
+	dev->qmult = qmult;
+	snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+	if (get_ether_addr(dev_addr, net->dev_addr))
+		dev_warn(&g->dev,
+			"using random %s ethernet address\n", "self");
+	if (get_ether_addr(host_addr, dev->host_mac))
+		dev_warn(&g->dev,
+			"using random %s ethernet address\n", "host");
+
+	if (ethaddr)
+		memcpy(ethaddr, dev->host_mac, ETH_ALEN);
+
+	net->netdev_ops = &eth_netdev_ops;
+
+	net->ethtool_ops = &ops;
+
+	/* MTU range: 14 - 15412 */
+	net->min_mtu = ETH_HLEN;
+	net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+
+	dev->gadget = g;
+	SET_NETDEV_DEV(net, &g->dev);
+	SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+	status = register_netdev(net);
+	if (status < 0) {
+		dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+		free_netdev(net);
+		dev = ERR_PTR(status);
+	} else {
+		INFO(dev, "MAC %pM\n", net->dev_addr);
+		INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+		/*
+		 * two kinds of host-initiated state changes:
+		 *  - iff DATA transfer is active, carrier is "on"
+		 *  - tx queueing enabled if open *and* carrier is "on"
+		 */
+		netif_carrier_off(net);
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL_GPL(gether_setup_name);
+
+struct net_device *gether_setup_name_default(const char *netname)
+{
+	struct net_device	*net;
+	struct eth_dev		*dev;
+
+	net = alloc_etherdev(sizeof(*dev));
+	if (!net)
+		return ERR_PTR(-ENOMEM);
+
+	dev = netdev_priv(net);
+	spin_lock_init(&dev->lock);
+	spin_lock_init(&dev->req_lock);
+	INIT_WORK(&dev->work, eth_work);
+	INIT_LIST_HEAD(&dev->tx_reqs);
+	INIT_LIST_HEAD(&dev->rx_reqs);
+
+	skb_queue_head_init(&dev->rx_frames);
+
+	/* network device setup */
+	dev->net = net;
+	dev->qmult = QMULT_DEFAULT;
+	snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+	eth_random_addr(dev->dev_mac);
+	pr_warn("using random %s ethernet address\n", "self");
+	eth_random_addr(dev->host_mac);
+	pr_warn("using random %s ethernet address\n", "host");
+
+	net->netdev_ops = &eth_netdev_ops;
+
+	net->ethtool_ops = &ops;
+	SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+	/* MTU range: 14 - 15412 */
+	net->min_mtu = ETH_HLEN;
+	net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+
+	return net;
+}
+EXPORT_SYMBOL_GPL(gether_setup_name_default);
+
+int gether_register_netdev(struct net_device *net)
+{
+	struct eth_dev *dev;
+	struct usb_gadget *g;
+	struct sockaddr sa;
+	int status;
+
+	if (!net->dev.parent)
+		return -EINVAL;
+	dev = netdev_priv(net);
+	g = dev->gadget;
+	status = register_netdev(net);
+	if (status < 0) {
+		dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+		return status;
+	} else {
+		INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+		/* two kinds of host-initiated state changes:
+		 *  - iff DATA transfer is active, carrier is "on"
+		 *  - tx queueing enabled if open *and* carrier is "on"
+		 */
+		netif_carrier_off(net);
+	}
+	sa.sa_family = net->type;
+	memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
+	rtnl_lock();
+	status = dev_set_mac_address(net, &sa);
+	rtnl_unlock();
+	if (status)
+		pr_warn("cannot set self ethernet address: %d\n", status);
+	else
+		INFO(dev, "MAC %pM\n", dev->dev_mac);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gether_register_netdev);
+
+void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
+{
+	struct eth_dev *dev;
+
+	dev = netdev_priv(net);
+	dev->gadget = g;
+	SET_NETDEV_DEV(net, &g->dev);
+}
+EXPORT_SYMBOL_GPL(gether_set_gadget);
+
+int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
+{
+	struct eth_dev *dev;
+	u8 new_addr[ETH_ALEN];
+
+	dev = netdev_priv(net);
+	if (get_ether_addr(dev_addr, new_addr))
+		return -EINVAL;
+	memcpy(dev->dev_mac, new_addr, ETH_ALEN);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gether_set_dev_addr);
+
+int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
+{
+	struct eth_dev *dev;
+	int ret;
+
+	dev = netdev_priv(net);
+	ret = get_ether_addr_str(dev->dev_mac, dev_addr, len);
+	if (ret + 1 < len) {
+		dev_addr[ret++] = '\n';
+		dev_addr[ret] = '\0';
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gether_get_dev_addr);
+
+int gether_set_host_addr(struct net_device *net, const char *host_addr)
+{
+	struct eth_dev *dev;
+	u8 new_addr[ETH_ALEN];
+
+	dev = netdev_priv(net);
+	if (get_ether_addr(host_addr, new_addr))
+		return -EINVAL;
+	memcpy(dev->host_mac, new_addr, ETH_ALEN);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gether_set_host_addr);
+
+int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
+{
+	struct eth_dev *dev;
+	int ret;
+
+	dev = netdev_priv(net);
+	ret = get_ether_addr_str(dev->host_mac, host_addr, len);
+	if (ret + 1 < len) {
+		host_addr[ret++] = '\n';
+		host_addr[ret] = '\0';
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr);
+
+int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
+{
+	struct eth_dev *dev;
+
+	if (len < 13)
+		return -EINVAL;
+
+	dev = netdev_priv(net);
+	snprintf(host_addr, len, "%pm", dev->host_mac);
+
+	return strlen(host_addr);
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr_cdc);
+
+void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
+{
+	struct eth_dev *dev;
+
+	dev = netdev_priv(net);
+	memcpy(host_mac, dev->host_mac, ETH_ALEN);
+}
+EXPORT_SYMBOL_GPL(gether_get_host_addr_u8);
+
+void gether_set_qmult(struct net_device *net, unsigned qmult)
+{
+	struct eth_dev *dev;
+
+	dev = netdev_priv(net);
+	dev->qmult = qmult;
+}
+EXPORT_SYMBOL_GPL(gether_set_qmult);
+
+unsigned gether_get_qmult(struct net_device *net)
+{
+	struct eth_dev *dev;
+
+	dev = netdev_priv(net);
+	return dev->qmult;
+}
+EXPORT_SYMBOL_GPL(gether_get_qmult);
+
+int gether_get_ifname(struct net_device *net, char *name, int len)
+{
+	int ret;
+
+	rtnl_lock();
+	ret = snprintf(name, len, "%s\n", netdev_name(net));
+	rtnl_unlock();
+	return ret < len ? ret : len;
+}
+EXPORT_SYMBOL_GPL(gether_get_ifname);
+
+/**
+ * gether_cleanup - remove Ethernet-over-USB device
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gether_setup().
+ */
+void gether_cleanup(struct eth_dev *dev)
+{
+	if (!dev)
+		return;
+
+	unregister_netdev(dev->net);
+	flush_work(&dev->work);
+	free_netdev(dev->net);
+}
+EXPORT_SYMBOL_GPL(gether_cleanup);
+
+/**
+ * gether_connect - notify network layer that USB link is active
+ * @link: the USB link, set up with endpoints, descriptors matching
+ *	current device speed, and any framing wrapper(s) set up.
+ * Context: irqs blocked
+ *
+ * This is called to activate endpoints and let the network layer know
+ * the connection is active ("carrier detect").  It may cause the I/O
+ * queues to open and start letting network packets flow, but will in
+ * any case activate the endpoints so that they respond properly to the
+ * USB host.
+ *
+ * Verify net_device pointer returned using IS_ERR().  If it doesn't
+ * indicate some error code (negative errno), ep->driver_data values
+ * have been overwritten.
+ */
+struct net_device *gether_connect(struct gether *link)
+{
+	struct eth_dev		*dev = link->ioport;
+	int			result = 0;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	link->in_ep->driver_data = dev;
+	result = usb_ep_enable(link->in_ep);
+	if (result != 0) {
+		DBG(dev, "enable %s --> %d\n",
+			link->in_ep->name, result);
+		goto fail0;
+	}
+
+	link->out_ep->driver_data = dev;
+	result = usb_ep_enable(link->out_ep);
+	if (result != 0) {
+		DBG(dev, "enable %s --> %d\n",
+			link->out_ep->name, result);
+		goto fail1;
+	}
+
+	if (result == 0)
+		result = alloc_requests(dev, link, qlen(dev->gadget,
+					dev->qmult));
+
+	if (result == 0) {
+		dev->zlp = link->is_zlp_ok;
+		dev->no_skb_reserve = gadget_avoids_skb_reserve(dev->gadget);
+		DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
+
+		dev->header_len = link->header_len;
+		dev->unwrap = link->unwrap;
+		dev->wrap = link->wrap;
+
+		spin_lock(&dev->lock);
+		dev->port_usb = link;
+		if (netif_running(dev->net)) {
+			if (link->open)
+				link->open(link);
+		} else {
+			if (link->close)
+				link->close(link);
+		}
+		spin_unlock(&dev->lock);
+
+		netif_carrier_on(dev->net);
+		if (netif_running(dev->net))
+			eth_start(dev, GFP_ATOMIC);
+
+	/* on error, disable any endpoints  */
+	} else {
+		(void) usb_ep_disable(link->out_ep);
+fail1:
+		(void) usb_ep_disable(link->in_ep);
+	}
+fail0:
+	/* caller is responsible for cleanup on error */
+	if (result < 0)
+		return ERR_PTR(result);
+	return dev->net;
+}
+EXPORT_SYMBOL_GPL(gether_connect);
+
+/**
+ * gether_disconnect - notify network layer that USB link is inactive
+ * @link: the USB link, on which gether_connect() was called
+ * Context: irqs blocked
+ *
+ * This is called to deactivate endpoints and let the network layer know
+ * the connection went inactive ("no carrier").
+ *
+ * On return, the state is as if gether_connect() had never been called.
+ * The endpoints are inactive, and accordingly without active USB I/O.
+ * Pointers to endpoint descriptors and endpoint private data are nulled.
+ */
+void gether_disconnect(struct gether *link)
+{
+	struct eth_dev		*dev = link->ioport;
+	struct usb_request	*req;
+
+	WARN_ON(!dev);
+	if (!dev)
+		return;
+
+	DBG(dev, "%s\n", __func__);
+
+	netif_stop_queue(dev->net);
+	netif_carrier_off(dev->net);
+
+	/* disable endpoints, forcing (synchronous) completion
+	 * of all pending i/o.  then free the request objects
+	 * and forget about the endpoints.
+	 */
+	usb_ep_disable(link->in_ep);
+	spin_lock(&dev->req_lock);
+	while (!list_empty(&dev->tx_reqs)) {
+		req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock(&dev->req_lock);
+		usb_ep_free_request(link->in_ep, req);
+		spin_lock(&dev->req_lock);
+	}
+	spin_unlock(&dev->req_lock);
+	link->in_ep->desc = NULL;
+
+	usb_ep_disable(link->out_ep);
+	spin_lock(&dev->req_lock);
+	while (!list_empty(&dev->rx_reqs)) {
+		req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
+		list_del(&req->list);
+
+		spin_unlock(&dev->req_lock);
+		usb_ep_free_request(link->out_ep, req);
+		spin_lock(&dev->req_lock);
+	}
+	spin_unlock(&dev->req_lock);
+	link->out_ep->desc = NULL;
+
+	/* finish forgetting about this USB link episode */
+	dev->header_len = 0;
+	dev->unwrap = NULL;
+	dev->wrap = NULL;
+
+	spin_lock(&dev->lock);
+	dev->port_usb = NULL;
+	spin_unlock(&dev->lock);
+}
+EXPORT_SYMBOL_GPL(gether_disconnect);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
new file mode 100644
index 0000000..332307d
--- /dev/null
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_ether.h -- interface to USB gadget "ethernet link" utilities
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+#ifndef __U_ETHER_H
+#define __U_ETHER_H
+
+#include <linux/err.h>
+#include <linux/if_ether.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+#include <linux/netdevice.h>
+
+#define QMULT_DEFAULT 5
+
+/*
+ * dev_addr: initial value
+ * changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx"
+ * host_addr: this address is invisible to ifconfig
+ */
+#define USB_ETHERNET_MODULE_PARAMETERS() \
+	static unsigned qmult = QMULT_DEFAULT;				\
+	module_param(qmult, uint, S_IRUGO|S_IWUSR);			\
+	MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\
+									\
+	static char *dev_addr;						\
+	module_param(dev_addr, charp, S_IRUGO);				\
+	MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");		\
+									\
+	static char *host_addr;						\
+	module_param(host_addr, charp, S_IRUGO);			\
+	MODULE_PARM_DESC(host_addr, "Host Ethernet Address")
+
+struct eth_dev;
+
+/*
+ * This represents the USB side of an "ethernet" link, managed by a USB
+ * function which provides control and (maybe) framing.  Two functions
+ * in different configurations could share the same ethernet link/netdev,
+ * using different host interaction models.
+ *
+ * There is a current limitation that only one instance of this link may
+ * be present in any given configuration.  When that's a problem, network
+ * layer facilities can be used to package multiple logical links on this
+ * single "physical" one.
+ */
+struct gether {
+	struct usb_function		func;
+
+	/* updated by gether_{connect,disconnect} */
+	struct eth_dev			*ioport;
+
+	/* endpoints handle full and/or high speeds */
+	struct usb_ep			*in_ep;
+	struct usb_ep			*out_ep;
+
+	bool				is_zlp_ok;
+
+	u16				cdc_filter;
+
+	/* hooks for added framing, as needed for RNDIS and EEM. */
+	u32				header_len;
+	/* NCM requires fixed size bundles */
+	bool				is_fixed;
+	u32				fixed_out_len;
+	u32				fixed_in_len;
+	bool				supports_multi_frame;
+	struct sk_buff			*(*wrap)(struct gether *port,
+						struct sk_buff *skb);
+	int				(*unwrap)(struct gether *port,
+						struct sk_buff *skb,
+						struct sk_buff_head *list);
+
+	/* called on network open/close */
+	void				(*open)(struct gether *);
+	void				(*close)(struct gether *);
+};
+
+#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
+			|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+			|USB_CDC_PACKET_TYPE_PROMISCUOUS \
+			|USB_CDC_PACKET_TYPE_DIRECTED)
+
+/* variant of gether_setup that allows customizing network device name */
+struct eth_dev *gether_setup_name(struct usb_gadget *g,
+		const char *dev_addr, const char *host_addr,
+		u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname);
+
+/* netdev setup/teardown as directed by the gadget driver */
+/* gether_setup - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ *	host side of the link is recorded
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns a eth_dev pointer on success, or an ERR_PTR on failure
+ */
+static inline struct eth_dev *gether_setup(struct usb_gadget *g,
+		const char *dev_addr, const char *host_addr,
+		u8 ethaddr[ETH_ALEN], unsigned qmult)
+{
+	return gether_setup_name(g, dev_addr, host_addr, ethaddr, qmult, "usb");
+}
+
+/*
+ * variant of gether_setup_default that allows customizing
+ * network device name
+ */
+struct net_device *gether_setup_name_default(const char *netname);
+
+/*
+ * gether_register_netdev - register the net device
+ * @net: net device to register
+ *
+ * Registers the net device associated with this ethernet-over-usb link
+ *
+ */
+int gether_register_netdev(struct net_device *net);
+
+/* gether_setup_default - initialize one ethernet-over-usb link
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework.  The link layer addresses
+ * are set to random values.
+ *
+ * Returns negative errno, or zero on success
+ */
+static inline struct net_device *gether_setup_default(void)
+{
+	return gether_setup_name_default("usb");
+}
+
+/**
+ * gether_set_gadget - initialize one ethernet-over-usb link with a gadget
+ * @net: device representing this link
+ * @g: the gadget to initialize with
+ *
+ * This associates one ethernet-over-usb link with a gadget.
+ */
+void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
+
+/**
+ * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
+ * @net: device representing this link
+ * @dev_addr: eth address of this device
+ *
+ * This sets the device-side Ethernet address of this ethernet-over-usb link
+ * if dev_addr is correct.
+ * Returns negative errno if the new address is incorrect.
+ */
+int gether_set_dev_addr(struct net_device *net, const char *dev_addr);
+
+/**
+ * gether_get_dev_addr - get an ethernet-over-usb link eth address
+ * @net: device representing this link
+ * @dev_addr: place to store device's eth address
+ * @len: length of the @dev_addr buffer
+ *
+ * This gets the device-side Ethernet address of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len);
+
+/**
+ * gether_set_host_addr - initialize an ethernet-over-usb link with host address
+ * @net: device representing this link
+ * @host_addr: eth address of the host
+ *
+ * This sets the host-side Ethernet address of this ethernet-over-usb link
+ * if host_addr is correct.
+ * Returns negative errno if the new address is incorrect.
+ */
+int gether_set_host_addr(struct net_device *net, const char *host_addr);
+
+/**
+ * gether_get_host_addr - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_addr: place to store eth address of the host
+ * @len: length of the @host_addr buffer
+ *
+ * This gets the host-side Ethernet address of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_host_addr(struct net_device *net, char *host_addr, int len);
+
+/**
+ * gether_get_host_addr_cdc - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_addr: place to store eth address of the host
+ * @len: length of the @host_addr buffer
+ *
+ * This gets the CDC formatted host-side Ethernet address of this
+ * ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len);
+
+/**
+ * gether_get_host_addr_u8 - get an ethernet-over-usb link host address
+ * @net: device representing this link
+ * @host_mac: place to store the eth address of the host
+ *
+ * This gets the binary formatted host-side Ethernet address of this
+ * ethernet-over-usb link.
+ */
+void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]);
+
+/**
+ * gether_set_qmult - initialize an ethernet-over-usb link with a multiplier
+ * @net: device representing this link
+ * @qmult: queue multiplier
+ *
+ * This sets the queue length multiplier of this ethernet-over-usb link.
+ * For higher speeds use longer queues.
+ */
+void gether_set_qmult(struct net_device *net, unsigned qmult);
+
+/**
+ * gether_get_qmult - get an ethernet-over-usb link multiplier
+ * @net: device representing this link
+ *
+ * This gets the queue length multiplier of this ethernet-over-usb link.
+ */
+unsigned gether_get_qmult(struct net_device *net);
+
+/**
+ * gether_get_ifname - get an ethernet-over-usb link interface name
+ * @net: device representing this link
+ * @name: place to store the interface name
+ * @len: length of the @name buffer
+ *
+ * This gets the interface name of this ethernet-over-usb link.
+ * Returns zero on success, else negative errno.
+ */
+int gether_get_ifname(struct net_device *net, char *name, int len);
+
+void gether_cleanup(struct eth_dev *dev);
+
+/* connect/disconnect is handled by individual functions */
+struct net_device *gether_connect(struct gether *);
+void gether_disconnect(struct gether *);
+
+/* Some controllers can't support CDC Ethernet (ECM) ... */
+static inline bool can_support_ecm(struct usb_gadget *gadget)
+{
+	if (!gadget_is_altset_supported(gadget))
+		return false;
+
+	/* Everything else is *presumably* fine ... but this is a bit
+	 * chancy, so be **CERTAIN** there are no hardware issues with
+	 * your controller.  Add it above if it can't handle CDC.
+	 */
+	return true;
+}
+
+#endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
new file mode 100644
index 0000000..cd33cee
--- /dev/null
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_ether_configfs.h
+ *
+ * Utility definitions for configfs support in USB Ethernet functions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef __U_ETHER_CONFIGFS_H
+#define __U_ETHER_CONFIGFS_H
+
+#define USB_ETHERNET_CONFIGFS_ITEM(_f_)					\
+	static void _f_##_attr_release(struct config_item *item)	\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+									\
+		usb_put_function_instance(&opts->func_inst);		\
+	}								\
+									\
+	static struct configfs_item_operations _f_##_item_ops = {	\
+		.release	= _f_##_attr_release,			\
+	}
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_)			\
+	static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \
+						char *page)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int result;						\
+									\
+		mutex_lock(&opts->lock);				\
+		result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \
+		mutex_unlock(&opts->lock);				\
+									\
+		return result;						\
+	}								\
+									\
+	static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \
+						 const char *page, size_t len)\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret;						\
+									\
+		mutex_lock(&opts->lock);				\
+		if (opts->refcnt) {					\
+			mutex_unlock(&opts->lock);			\
+			return -EBUSY;					\
+		}							\
+									\
+		ret = gether_set_dev_addr(opts->net, page);		\
+		mutex_unlock(&opts->lock);				\
+		if (!ret)						\
+			ret = len;					\
+		return ret;						\
+	}								\
+									\
+	CONFIGFS_ATTR(_f_##_opts_, dev_addr)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_)			\
+	static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \
+						 char *page)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int result;						\
+									\
+		mutex_lock(&opts->lock);				\
+		result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \
+		mutex_unlock(&opts->lock);				\
+									\
+		return result;						\
+	}								\
+									\
+	static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \
+						  const char *page, size_t len)\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret;						\
+									\
+		mutex_lock(&opts->lock);				\
+		if (opts->refcnt) {					\
+			mutex_unlock(&opts->lock);			\
+			return -EBUSY;					\
+		}							\
+									\
+		ret = gether_set_host_addr(opts->net, page);		\
+		mutex_unlock(&opts->lock);				\
+		if (!ret)						\
+			ret = len;					\
+		return ret;						\
+	}								\
+									\
+	CONFIGFS_ATTR(_f_##_opts_, host_addr)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_)			\
+	static ssize_t _f_##_opts_qmult_show(struct config_item *item,	\
+					     char *page)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		unsigned qmult;						\
+									\
+		mutex_lock(&opts->lock);				\
+		qmult = gether_get_qmult(opts->net);			\
+		mutex_unlock(&opts->lock);				\
+		return sprintf(page, "%d\n", qmult);			\
+	}								\
+									\
+	static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
+					      const char *page, size_t len)\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		u8 val;							\
+		int ret;						\
+									\
+		mutex_lock(&opts->lock);				\
+		if (opts->refcnt) {					\
+			ret = -EBUSY;					\
+			goto out;					\
+		}							\
+									\
+		ret = kstrtou8(page, 0, &val);				\
+		if (ret)						\
+			goto out;					\
+									\
+		gether_set_qmult(opts->net, val);			\
+		ret = len;						\
+out:									\
+		mutex_unlock(&opts->lock);				\
+		return ret;						\
+	}								\
+									\
+	CONFIGFS_ATTR(_f_##_opts_, qmult)
+
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_)			\
+	static ssize_t _f_##_opts_ifname_show(struct config_item *item, \
+					      char *page)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret;						\
+									\
+		mutex_lock(&opts->lock);				\
+		ret = gether_get_ifname(opts->net, page, PAGE_SIZE);	\
+		mutex_unlock(&opts->lock);				\
+									\
+		return ret;						\
+	}								\
+									\
+	CONFIGFS_ATTR_RO(_f_##_opts_, ifname)
+
+#define USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(_f_, _n_)			\
+	static ssize_t _f_##_opts_##_n_##_show(struct config_item *item,\
+					       char *page)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret;						\
+									\
+		mutex_lock(&opts->lock);				\
+		ret = sprintf(page, "%02x\n", opts->_n_);		\
+		mutex_unlock(&opts->lock);				\
+									\
+		return ret;						\
+	}								\
+									\
+	static ssize_t _f_##_opts_##_n_##_store(struct config_item *item,\
+						const char *page,	\
+						size_t len)		\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret;						\
+		u8 val;							\
+									\
+		mutex_lock(&opts->lock);				\
+		ret = sscanf(page, "%02hhx", &val);			\
+		if (ret > 0) {						\
+			opts->_n_ = val;				\
+			ret = len;					\
+		}							\
+		mutex_unlock(&opts->lock);				\
+									\
+		return ret;						\
+	}								\
+									\
+	CONFIGFS_ATTR(_f_##_opts_, _n_)
+
+#endif /* __U_ETHER_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
new file mode 100644
index 0000000..c3aba4d
--- /dev/null
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_fs.h
+ *
+ * Utility definitions for the FunctionFS
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_FFS_H
+#define U_FFS_H
+
+#include <linux/usb/composite.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/refcount.h>
+
+#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
+#  define pr_vdebug pr_debug
+#endif /* pr_vdebug */
+#  define ffs_dump_mem(prefix, ptr, len) \
+	print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
+#else
+#ifndef pr_vdebug
+#  define pr_vdebug(...)                 do { } while (0)
+#endif /* pr_vdebug */
+#  define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER()    pr_vdebug("%s()\n", __func__)
+
+struct f_fs_opts;
+
+struct ffs_dev {
+	struct ffs_data *ffs_data;
+	struct f_fs_opts *opts;
+	struct list_head entry;
+
+	char name[41];
+
+	bool mounted;
+	bool desc_ready;
+	bool single;
+
+	int (*ffs_ready_callback)(struct ffs_data *ffs);
+	void (*ffs_closed_callback)(struct ffs_data *ffs);
+	void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
+	void (*ffs_release_dev_callback)(struct ffs_dev *dev);
+};
+
+extern struct mutex ffs_lock;
+
+static inline void ffs_dev_lock(void)
+{
+	mutex_lock(&ffs_lock);
+}
+
+static inline void ffs_dev_unlock(void)
+{
+	mutex_unlock(&ffs_lock);
+}
+
+int ffs_name_dev(struct ffs_dev *dev, const char *name);
+int ffs_single_dev(struct ffs_dev *dev);
+
+struct ffs_epfile;
+struct ffs_function;
+
+enum ffs_state {
+	/*
+	 * Waiting for descriptors and strings.
+	 *
+	 * In this state no open(2), read(2) or write(2) on epfiles
+	 * may succeed (which should not be the problem as there
+	 * should be no such files opened in the first place).
+	 */
+	FFS_READ_DESCRIPTORS,
+	FFS_READ_STRINGS,
+
+	/*
+	 * We've got descriptors and strings.  We are or have called
+	 * functionfs_ready_callback().  functionfs_bind() may have
+	 * been called but we don't know.
+	 *
+	 * This is the only state in which operations on epfiles may
+	 * succeed.
+	 */
+	FFS_ACTIVE,
+
+	/*
+	 * Function is visible to host, but it's not functional. All
+	 * setup requests are stalled and transfers on another endpoints
+	 * are refused. All epfiles, except ep0, are deleted so there
+	 * is no way to perform any operations on them.
+	 *
+	 * This state is set after closing all functionfs files, when
+	 * mount parameter "no_disconnect=1" has been set. Function will
+	 * remain in deactivated state until filesystem is umounted or
+	 * ep0 is opened again. In the second case functionfs state will
+	 * be reset, and it will be ready for descriptors and strings
+	 * writing.
+	 *
+	 * This is useful only when functionfs is composed to gadget
+	 * with another function which can perform some critical
+	 * operations, and it's strongly desired to have this operations
+	 * completed, even after functionfs files closure.
+	 */
+	FFS_DEACTIVATED,
+
+	/*
+	 * All endpoints have been closed.  This state is also set if
+	 * we encounter an unrecoverable error.  The only
+	 * unrecoverable error is situation when after reading strings
+	 * from user space we fail to initialise epfiles or
+	 * functionfs_ready_callback() returns with error (<0).
+	 *
+	 * In this state no open(2), read(2) or write(2) (both on ep0
+	 * as well as epfile) may succeed (at this point epfiles are
+	 * unlinked and all closed so this is not a problem; ep0 is
+	 * also closed but ep0 file exists and so open(2) on ep0 must
+	 * fail).
+	 */
+	FFS_CLOSING
+};
+
+enum ffs_setup_state {
+	/* There is no setup request pending. */
+	FFS_NO_SETUP,
+	/*
+	 * User has read events and there was a setup request event
+	 * there.  The next read/write on ep0 will handle the
+	 * request.
+	 */
+	FFS_SETUP_PENDING,
+	/*
+	 * There was event pending but before user space handled it
+	 * some other event was introduced which canceled existing
+	 * setup.  If this state is set read/write on ep0 return
+	 * -EIDRM.  This state is only set when adding event.
+	 */
+	FFS_SETUP_CANCELLED
+};
+
+struct ffs_data {
+	struct usb_gadget		*gadget;
+
+	/*
+	 * Protect access read/write operations, only one read/write
+	 * at a time.  As a consequence protects ep0req and company.
+	 * While setup request is being processed (queued) this is
+	 * held.
+	 */
+	struct mutex			mutex;
+
+	/*
+	 * Protect access to endpoint related structures (basically
+	 * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+	 * endpoint zero.
+	 */
+	spinlock_t			eps_lock;
+
+	/*
+	 * XXX REVISIT do we need our own request? Since we are not
+	 * handling setup requests immediately user space may be so
+	 * slow that another setup will be sent to the gadget but this
+	 * time not to us but another function and then there could be
+	 * a race.  Is that the case? Or maybe we can use cdev->req
+	 * after all, maybe we just need some spinlock for that?
+	 */
+	struct usb_request		*ep0req;		/* P: mutex */
+	struct completion		ep0req_completion;	/* P: mutex */
+
+	/* reference counter */
+	refcount_t			ref;
+	/* how many files are opened (EP0 and others) */
+	atomic_t			opened;
+
+	/* EP0 state */
+	enum ffs_state			state;
+
+	/*
+	 * Possible transitions:
+	 * + FFS_NO_SETUP        -> FFS_SETUP_PENDING  -- P: ev.waitq.lock
+	 *               happens only in ep0 read which is P: mutex
+	 * + FFS_SETUP_PENDING   -> FFS_NO_SETUP       -- P: ev.waitq.lock
+	 *               happens only in ep0 i/o  which is P: mutex
+	 * + FFS_SETUP_PENDING   -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
+	 * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP        -- cmpxchg
+	 *
+	 * This field should never be accessed directly and instead
+	 * ffs_setup_state_clear_cancelled function should be used.
+	 */
+	enum ffs_setup_state		setup_state;
+
+	/* Events & such. */
+	struct {
+		u8				types[4];
+		unsigned short			count;
+		/* XXX REVISIT need to update it in some places, or do we? */
+		unsigned short			can_stall;
+		struct usb_ctrlrequest		setup;
+
+		wait_queue_head_t		waitq;
+	} ev; /* the whole structure, P: ev.waitq.lock */
+
+	/* Flags */
+	unsigned long			flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND                1
+
+	/* For waking up blocked threads when function is enabled. */
+	wait_queue_head_t		wait;
+
+	/* Active function */
+	struct ffs_function		*func;
+
+	/*
+	 * Device name, write once when file system is mounted.
+	 * Intended for user to read if she wants.
+	 */
+	const char			*dev_name;
+	/* Private data for our user (ie. gadget).  Managed by user. */
+	void				*private_data;
+
+	/* filled by __ffs_data_got_descs() */
+	/*
+	 * raw_descs is what you kfree, real_descs points inside of raw_descs,
+	 * where full speed, high speed and super speed descriptors start.
+	 * real_descs_length is the length of all those descriptors.
+	 */
+	const void			*raw_descs_data;
+	const void			*raw_descs;
+	unsigned			raw_descs_length;
+	unsigned			fs_descs_count;
+	unsigned			hs_descs_count;
+	unsigned			ss_descs_count;
+	unsigned			ms_os_descs_count;
+	unsigned			ms_os_descs_ext_prop_count;
+	unsigned			ms_os_descs_ext_prop_name_len;
+	unsigned			ms_os_descs_ext_prop_data_len;
+	void				*ms_os_descs_ext_prop_avail;
+	void				*ms_os_descs_ext_prop_name_avail;
+	void				*ms_os_descs_ext_prop_data_avail;
+
+	unsigned			user_flags;
+
+#define FFS_MAX_EPS_COUNT 31
+	u8				eps_addrmap[FFS_MAX_EPS_COUNT];
+
+	unsigned short			strings_count;
+	unsigned short			interfaces_count;
+	unsigned short			eps_count;
+	unsigned short			_pad1;
+
+	/* filled by __ffs_data_got_strings() */
+	/* ids in stringtabs are set in functionfs_bind() */
+	const void			*raw_strings;
+	struct usb_gadget_strings	**stringtabs;
+
+	/*
+	 * File system's super block, write once when file system is
+	 * mounted.
+	 */
+	struct super_block		*sb;
+
+	/* File permissions, written once when fs is mounted */
+	struct ffs_file_perms {
+		umode_t				mode;
+		kuid_t				uid;
+		kgid_t				gid;
+	}				file_perms;
+
+	struct eventfd_ctx *ffs_eventfd;
+	struct workqueue_struct *io_completion_wq;
+	bool no_disconnect;
+	struct work_struct reset_work;
+
+	/*
+	 * The endpoint files, filled by ffs_epfiles_create(),
+	 * destroyed by ffs_epfiles_destroy().
+	 */
+	struct ffs_epfile		*epfiles;
+};
+
+
+struct f_fs_opts {
+	struct usb_function_instance	func_inst;
+	struct ffs_dev			*dev;
+	unsigned			refcnt;
+	bool				no_configfs;
+};
+
+static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi)
+{
+	return container_of(fi, struct f_fs_opts, func_inst);
+}
+
+#endif /* U_FFS_H */
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
new file mode 100644
index 0000000..5b7e2eb
--- /dev/null
+++ b/drivers/usb/gadget/function/u_gether.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_gether.h
+ *
+ * Utility definitions for the subset function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_GETHER_H
+#define U_GETHER_H
+
+#include <linux/usb/composite.h>
+
+struct f_gether_opts {
+	struct usb_function_instance	func_inst;
+	struct net_device		*net;
+	bool				bound;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_GETHER_H */
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
new file mode 100644
index 0000000..2f5ca4b
--- /dev/null
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_hid.h
+ *
+ * Utility definitions for the hid function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_HID_H
+#define U_HID_H
+
+#include <linux/usb/composite.h>
+
+struct f_hid_opts {
+	struct usb_function_instance	func_inst;
+	int				minor;
+	unsigned char			subclass;
+	unsigned char			protocol;
+	unsigned short			report_length;
+	unsigned short			report_desc_length;
+	unsigned char			*report_desc;
+	bool				report_desc_alloc;
+
+	/*
+	 * Protect the data form concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	 struct mutex			lock;
+	 int				refcnt;
+};
+
+int ghid_setup(struct usb_gadget *g, int count);
+void ghid_cleanup(void);
+
+#endif /* U_HID_H */
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
new file mode 100644
index 0000000..5599aa5
--- /dev/null
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_midi.h
+ *
+ * Utility definitions for the midi function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_MIDI_H
+#define U_MIDI_H
+
+#include <linux/usb/composite.h>
+
+struct f_midi_opts {
+	struct usb_function_instance	func_inst;
+	int				index;
+	char				*id;
+	bool				id_allocated;
+	unsigned int			in_ports;
+	unsigned int			out_ports;
+	unsigned int			buflen;
+	unsigned int			qlen;
+
+	/*
+	 * Protect the data form concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	 struct mutex			lock;
+	 int				refcnt;
+};
+
+#endif /* U_MIDI_H */
+
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
new file mode 100644
index 0000000..67324f9
--- /dev/null
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_ncm.h
+ *
+ * Utility definitions for the ncm function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_NCM_H
+#define U_NCM_H
+
+#include <linux/usb/composite.h>
+
+struct f_ncm_opts {
+	struct usb_function_instance	func_inst;
+	struct net_device		*net;
+	bool				bound;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_NCM_H */
diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
new file mode 100644
index 0000000..12fb613
--- /dev/null
+++ b/drivers/usb/gadget/function/u_phonet.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_phonet.h - interface to Phonet
+ *
+ * Copyright (C) 2007-2008 by Nokia Corporation
+ */
+
+#ifndef __U_PHONET_H
+#define __U_PHONET_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+struct f_phonet_opts {
+	struct usb_function_instance func_inst;
+	bool bound;
+	struct net_device *net;
+};
+
+struct net_device *gphonet_setup_default(void);
+void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
+int gphonet_register_netdev(struct net_device *net);
+int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
+void gphonet_cleanup(struct net_device *dev);
+
+#endif /* __U_PHONET_H */
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
new file mode 100644
index 0000000..6088ff7
--- /dev/null
+++ b/drivers/usb/gadget/function/u_printer.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_printer.h
+ *
+ * Utility definitions for the printer function
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_PRINTER_H
+#define U_PRINTER_H
+
+#include <linux/usb/composite.h>
+
+struct f_printer_opts {
+	struct usb_function_instance	func_inst;
+	int				minor;
+	char				*pnp_string;
+	bool				pnp_string_allocated;
+	unsigned			q_len;
+
+	/*
+	 * Protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_PRINTER_H */
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
new file mode 100644
index 0000000..d65fb4e
--- /dev/null
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_rndis.h
+ *
+ * Utility definitions for the subset function
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_RNDIS_H
+#define U_RNDIS_H
+
+#include <linux/usb/composite.h>
+
+struct f_rndis_opts {
+	struct usb_function_instance	func_inst;
+	u32				vendor_id;
+	const char			*manufacturer;
+	struct net_device		*net;
+	bool				bound;
+	bool				borrowed_net;
+
+	struct config_group		*rndis_interf_group;
+	struct usb_os_desc		rndis_os_desc;
+	char				rndis_ext_compat_id[16];
+
+	u8				class;
+	u8				subclass;
+	u8				protocol;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This is to protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
+
+#endif /* U_RNDIS_H */
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
new file mode 100644
index 0000000..29436f7
--- /dev/null
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -0,0 +1,1460 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_serial.c - utilities for USB gadget "serial port"/TTY support
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This component encapsulates the TTY layer glue needed to provide basic
+ * "serial port" functionality through the USB gadget stack.  Each such
+ * port is exposed through a /dev/ttyGS* node.
+ *
+ * After this module has been loaded, the individual TTY port can be requested
+ * (gserial_alloc_line()) and it will stay available until they are removed
+ * (gserial_free_line()). Each one may be connected to a USB function
+ * (gserial_connect), or disconnected (with gserial_disconnect) when the USB
+ * host issues a config change event. Data can only flow when the port is
+ * connected to the host.
+ *
+ * A given TTY port can be made available in multiple configurations.
+ * For example, each one might expose a ttyGS0 node which provides a
+ * login application.  In one case that might use CDC ACM interface 0,
+ * while another configuration might use interface 3 for that.  The
+ * work to handle that (including descriptor management) is not part
+ * of this component.
+ *
+ * Configurations may expose more than one TTY port.  For example, if
+ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
+ * for a telephone or fax link.  And ttyGS2 might be something that just
+ * needs a simple byte stream interface for some messaging protocol that
+ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
+ *
+ *
+ * gserial is the lifecycle interface, used by USB functions
+ * gs_port is the I/O nexus, used by the tty driver
+ * tty_struct links to the tty/filesystem framework
+ *
+ * gserial <---> gs_port ... links will be null when the USB link is
+ * inactive; managed by gserial_{connect,disconnect}().  each gserial
+ * instance can wrap its own USB control protocol.
+ *	gserial->ioport == usb_ep->driver_data ... gs_port
+ *	gs_port->port_usb ... gserial
+ *
+ * gs_port <---> tty_struct ... links will be null when the TTY file
+ * isn't opened; managed by gs_open()/gs_close()
+ *	gserial->port_tty ... tty_struct
+ *	tty_struct->driver_data ... gserial
+ */
+
+/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
+ * next layer of buffering.  For TX that's a circular buffer; for RX
+ * consider it a NOP.  A third layer is provided by the TTY code.
+ */
+#define QUEUE_SIZE		16
+#define WRITE_BUF_SIZE		8192		/* TX only */
+#define GS_CONSOLE_BUF_SIZE	8192
+
+/* console info */
+struct gscons_info {
+	struct gs_port		*port;
+	struct task_struct	*console_thread;
+	struct kfifo		con_buf;
+	/* protect the buf and busy flag */
+	spinlock_t		con_lock;
+	int			req_busy;
+	struct usb_request	*console_req;
+};
+
+/*
+ * The port structure holds info for each port, one for each minor number
+ * (and thus for each /dev/ node).
+ */
+struct gs_port {
+	struct tty_port		port;
+	spinlock_t		port_lock;	/* guard port_* access */
+
+	struct gserial		*port_usb;
+
+	bool			openclose;	/* open/close in progress */
+	u8			port_num;
+
+	struct list_head	read_pool;
+	int read_started;
+	int read_allocated;
+	struct list_head	read_queue;
+	unsigned		n_read;
+	struct tasklet_struct	push;
+
+	struct list_head	write_pool;
+	int write_started;
+	int write_allocated;
+	struct kfifo		port_write_buf;
+	wait_queue_head_t	drain_wait;	/* wait while writes drain */
+	bool                    write_busy;
+	wait_queue_head_t	close_wait;
+
+	/* REVISIT this state ... */
+	struct usb_cdc_line_coding port_line_coding;	/* 8-N-1 etc */
+};
+
+static struct portmaster {
+	struct mutex	lock;			/* protect open/close */
+	struct gs_port	*port;
+} ports[MAX_U_SERIAL_PORTS];
+
+#define GS_CLOSE_TIMEOUT		15		/* seconds */
+
+
+
+#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
+#define pr_vdebug(fmt, arg...) \
+	pr_debug(fmt, ##arg)
+#endif /* pr_vdebug */
+#else
+#ifndef pr_vdebug
+#define pr_vdebug(fmt, arg...) \
+	({ if (0) pr_debug(fmt, ##arg); })
+#endif /* pr_vdebug */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* I/O glue between TTY (upper) and USB function (lower) driver layers */
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			return NULL;
+		}
+	}
+
+	return req;
+}
+EXPORT_SYMBOL_GPL(gs_alloc_req);
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+EXPORT_SYMBOL_GPL(gs_free_req);
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned.  If there is no data to
+ * send, 0 is returned.
+ *
+ * Called with port_lock held.
+ */
+static unsigned
+gs_send_packet(struct gs_port *port, char *packet, unsigned size)
+{
+	unsigned len;
+
+	len = kfifo_len(&port->port_write_buf);
+	if (len < size)
+		size = len;
+	if (size != 0)
+		size = kfifo_out(&port->port_write_buf, packet, size);
+	return size;
+}
+
+/*
+ * gs_start_tx
+ *
+ * This function finds available write requests, calls
+ * gs_send_packet to fill these packets with data, and
+ * continues until either there are no more write requests
+ * available or no more data to send.  This function is
+ * run whenever data arrives or write requests are available.
+ *
+ * Context: caller owns port_lock; port_usb is non-null.
+ */
+static int gs_start_tx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+	struct list_head	*pool = &port->write_pool;
+	struct usb_ep		*in;
+	int			status = 0;
+	bool			do_tty_wake = false;
+
+	if (!port->port_usb)
+		return status;
+
+	in = port->port_usb->in;
+
+	while (!port->write_busy && !list_empty(pool)) {
+		struct usb_request	*req;
+		int			len;
+
+		if (port->write_started >= QUEUE_SIZE)
+			break;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		len = gs_send_packet(port, req->buf, in->maxpacket);
+		if (len == 0) {
+			wake_up_interruptible(&port->drain_wait);
+			break;
+		}
+		do_tty_wake = true;
+
+		req->length = len;
+		list_del(&req->list);
+		req->zero = kfifo_is_empty(&port->port_write_buf);
+
+		pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+			  port->port_num, len, *((u8 *)req->buf),
+			  *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+
+		/* Drop lock while we call out of driver; completions
+		 * could be issued while we do so.  Disconnection may
+		 * happen too; maybe immediately before we queue this!
+		 *
+		 * NOTE that we may keep sending data for a while after
+		 * the TTY closed (dev->ioport->port_tty is NULL).
+		 */
+		port->write_busy = true;
+		spin_unlock(&port->port_lock);
+		status = usb_ep_queue(in, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock);
+		port->write_busy = false;
+
+		if (status) {
+			pr_debug("%s: %s %s err %d\n",
+					__func__, "queue", in->name, status);
+			list_add(&req->list, pool);
+			break;
+		}
+
+		port->write_started++;
+
+		/* abort immediately after disconnect */
+		if (!port->port_usb)
+			break;
+	}
+
+	if (do_tty_wake && port->port.tty)
+		tty_wakeup(port->port.tty);
+	return status;
+}
+
+/*
+ * Context: caller owns port_lock, and port_usb is set
+ */
+static unsigned gs_start_rx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+	struct list_head	*pool = &port->read_pool;
+	struct usb_ep		*out = port->port_usb->out;
+
+	while (!list_empty(pool)) {
+		struct usb_request	*req;
+		int			status;
+		struct tty_struct	*tty;
+
+		/* no more rx if closed */
+		tty = port->port.tty;
+		if (!tty)
+			break;
+
+		if (port->read_started >= QUEUE_SIZE)
+			break;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del(&req->list);
+		req->length = out->maxpacket;
+
+		/* drop lock while we call out; the controller driver
+		 * may need to call us back (e.g. for disconnect)
+		 */
+		spin_unlock(&port->port_lock);
+		status = usb_ep_queue(out, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock);
+
+		if (status) {
+			pr_debug("%s: %s %s err %d\n",
+					__func__, "queue", out->name, status);
+			list_add(&req->list, pool);
+			break;
+		}
+		port->read_started++;
+
+		/* abort immediately after disconnect */
+		if (!port->port_usb)
+			break;
+	}
+	return port->read_started;
+}
+
+/*
+ * RX tasklet takes data out of the RX queue and hands it up to the TTY
+ * layer until it refuses to take any more data (or is throttled back).
+ * Then it issues reads for any further data.
+ *
+ * If the RX queue becomes full enough that no usb_request is queued,
+ * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
+ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
+ * can be buffered before the TTY layer's buffers (currently 64 KB).
+ */
+static void gs_rx_push(unsigned long _port)
+{
+	struct gs_port		*port = (void *)_port;
+	struct tty_struct	*tty;
+	struct list_head	*queue = &port->read_queue;
+	bool			disconnect = false;
+	bool			do_push = false;
+
+	/* hand any queued data to the tty */
+	spin_lock_irq(&port->port_lock);
+	tty = port->port.tty;
+	while (!list_empty(queue)) {
+		struct usb_request	*req;
+
+		req = list_first_entry(queue, struct usb_request, list);
+
+		/* leave data queued if tty was rx throttled */
+		if (tty && tty_throttled(tty))
+			break;
+
+		switch (req->status) {
+		case -ESHUTDOWN:
+			disconnect = true;
+			pr_vdebug("ttyGS%d: shutdown\n", port->port_num);
+			break;
+
+		default:
+			/* presumably a transient fault */
+			pr_warn("ttyGS%d: unexpected RX status %d\n",
+				port->port_num, req->status);
+			/* FALLTHROUGH */
+		case 0:
+			/* normal completion */
+			break;
+		}
+
+		/* push data to (open) tty */
+		if (req->actual && tty) {
+			char		*packet = req->buf;
+			unsigned	size = req->actual;
+			unsigned	n;
+			int		count;
+
+			/* we may have pushed part of this packet already... */
+			n = port->n_read;
+			if (n) {
+				packet += n;
+				size -= n;
+			}
+
+			count = tty_insert_flip_string(&port->port, packet,
+					size);
+			if (count)
+				do_push = true;
+			if (count != size) {
+				/* stop pushing; TTY layer can't handle more */
+				port->n_read += count;
+				pr_vdebug("ttyGS%d: rx block %d/%d\n",
+					  port->port_num, count, req->actual);
+				break;
+			}
+			port->n_read = 0;
+		}
+
+		list_move(&req->list, &port->read_pool);
+		port->read_started--;
+	}
+
+	/* Push from tty to ldisc; this is handled by a workqueue,
+	 * so we won't get callbacks and can hold port_lock
+	 */
+	if (do_push)
+		tty_flip_buffer_push(&port->port);
+
+
+	/* We want our data queue to become empty ASAP, keeping data
+	 * in the tty and ldisc (not here).  If we couldn't push any
+	 * this time around, there may be trouble unless there's an
+	 * implicit tty_unthrottle() call on its way...
+	 *
+	 * REVISIT we should probably add a timer to keep the tasklet
+	 * from starving ... but it's not clear that case ever happens.
+	 */
+	if (!list_empty(queue) && tty) {
+		if (!tty_throttled(tty)) {
+			if (do_push)
+				tasklet_schedule(&port->push);
+			else
+				pr_warn("ttyGS%d: RX not scheduled?\n",
+					port->port_num);
+		}
+	}
+
+	/* If we're still connected, refill the USB RX queue. */
+	if (!disconnect && port->port_usb)
+		gs_start_rx(port);
+
+	spin_unlock_irq(&port->port_lock);
+}
+
+static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gs_port	*port = ep->driver_data;
+
+	/* Queue all received data until the tty layer is ready for it. */
+	spin_lock(&port->port_lock);
+	list_add_tail(&req->list, &port->read_queue);
+	tasklet_schedule(&port->push);
+	spin_unlock(&port->port_lock);
+}
+
+static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gs_port	*port = ep->driver_data;
+
+	spin_lock(&port->port_lock);
+	list_add(&req->list, &port->write_pool);
+	port->write_started--;
+
+	switch (req->status) {
+	default:
+		/* presumably a transient fault */
+		pr_warn("%s: unexpected %s status %d\n",
+			__func__, ep->name, req->status);
+		/* FALL THROUGH */
+	case 0:
+		/* normal completion */
+		gs_start_tx(port);
+		break;
+
+	case -ESHUTDOWN:
+		/* disconnect */
+		pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+		break;
+	}
+
+	spin_unlock(&port->port_lock);
+}
+
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
+							 int *allocated)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del(&req->list);
+		gs_free_req(ep, req);
+		if (allocated)
+			(*allocated)--;
+	}
+}
+
+static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		void (*fn)(struct usb_ep *, struct usb_request *),
+		int *allocated)
+{
+	int			i;
+	struct usb_request	*req;
+	int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
+
+	/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
+	 * do quite that many this time, don't fail ... we just won't
+	 * be as speedy as we might otherwise be.
+	 */
+	for (i = 0; i < n; i++) {
+		req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+		if (!req)
+			return list_empty(head) ? -ENOMEM : 0;
+		req->complete = fn;
+		list_add_tail(&req->list, head);
+		if (allocated)
+			(*allocated)++;
+	}
+	return 0;
+}
+
+/**
+ * gs_start_io - start USB I/O streams
+ * @dev: encapsulates endpoints to use
+ * Context: holding port_lock; port_tty and port_usb are non-null
+ *
+ * We only start I/O when something is connected to both sides of
+ * this port.  If nothing is listening on the host side, we may
+ * be pointlessly filling up our TX buffers and FIFO.
+ */
+static int gs_start_io(struct gs_port *port)
+{
+	struct list_head	*head = &port->read_pool;
+	struct usb_ep		*ep = port->port_usb->out;
+	int			status;
+	unsigned		started;
+
+	/* Allocate RX and TX I/O buffers.  We can't easily do this much
+	 * earlier (with GFP_KERNEL) because the requests are coupled to
+	 * endpoints, as are the packet sizes we'll be using.  Different
+	 * configurations may use different endpoints with a given port;
+	 * and high speed vs full speed changes packet sizes too.
+	 */
+	status = gs_alloc_requests(ep, head, gs_read_complete,
+		&port->read_allocated);
+	if (status)
+		return status;
+
+	status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
+			gs_write_complete, &port->write_allocated);
+	if (status) {
+		gs_free_requests(ep, head, &port->read_allocated);
+		return status;
+	}
+
+	/* queue read requests */
+	port->n_read = 0;
+	started = gs_start_rx(port);
+
+	/* unblock any pending writes into our circular buffer */
+	if (started) {
+		tty_wakeup(port->port.tty);
+	} else {
+		gs_free_requests(ep, head, &port->read_allocated);
+		gs_free_requests(port->port_usb->in, &port->write_pool,
+			&port->write_allocated);
+		status = -EIO;
+	}
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* TTY Driver */
+
+/*
+ * gs_open sets up the link between a gs_port and its associated TTY.
+ * That link is broken *only* by TTY close(), and all driver methods
+ * know that.
+ */
+static int gs_open(struct tty_struct *tty, struct file *file)
+{
+	int		port_num = tty->index;
+	struct gs_port	*port;
+	int		status;
+
+	do {
+		mutex_lock(&ports[port_num].lock);
+		port = ports[port_num].port;
+		if (!port)
+			status = -ENODEV;
+		else {
+			spin_lock_irq(&port->port_lock);
+
+			/* already open?  Great. */
+			if (port->port.count) {
+				status = 0;
+				port->port.count++;
+
+			/* currently opening/closing? wait ... */
+			} else if (port->openclose) {
+				status = -EBUSY;
+
+			/* ... else we do the work */
+			} else {
+				status = -EAGAIN;
+				port->openclose = true;
+			}
+			spin_unlock_irq(&port->port_lock);
+		}
+		mutex_unlock(&ports[port_num].lock);
+
+		switch (status) {
+		default:
+			/* fully handled */
+			return status;
+		case -EAGAIN:
+			/* must do the work */
+			break;
+		case -EBUSY:
+			/* wait for EAGAIN task to finish */
+			msleep(1);
+			/* REVISIT could have a waitchannel here, if
+			 * concurrent open performance is important
+			 */
+			break;
+		}
+	} while (status != -EAGAIN);
+
+	/* Do the "real open" */
+	spin_lock_irq(&port->port_lock);
+
+	/* allocate circular buffer on first open */
+	if (!kfifo_initialized(&port->port_write_buf)) {
+
+		spin_unlock_irq(&port->port_lock);
+		status = kfifo_alloc(&port->port_write_buf,
+				     WRITE_BUF_SIZE, GFP_KERNEL);
+		spin_lock_irq(&port->port_lock);
+
+		if (status) {
+			pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
+				port->port_num, tty, file);
+			port->openclose = false;
+			goto exit_unlock_port;
+		}
+	}
+
+	/* REVISIT if REMOVED (ports[].port NULL), abort the open
+	 * to let rmmod work faster (but this way isn't wrong).
+	 */
+
+	/* REVISIT maybe wait for "carrier detect" */
+
+	tty->driver_data = port;
+	port->port.tty = tty;
+
+	port->port.count = 1;
+	port->openclose = false;
+
+	/* if connected, start the I/O stream */
+	if (port->port_usb) {
+		struct gserial	*gser = port->port_usb;
+
+		pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+		gs_start_io(port);
+
+		if (gser->connect)
+			gser->connect(gser);
+	}
+
+	pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
+
+	status = 0;
+
+exit_unlock_port:
+	spin_unlock_irq(&port->port_lock);
+	return status;
+}
+
+static int gs_writes_finished(struct gs_port *p)
+{
+	int cond;
+
+	/* return true on disconnect or empty buffer */
+	spin_lock_irq(&p->port_lock);
+	cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf);
+	spin_unlock_irq(&p->port_lock);
+
+	return cond;
+}
+
+static void gs_close(struct tty_struct *tty, struct file *file)
+{
+	struct gs_port *port = tty->driver_data;
+	struct gserial	*gser;
+
+	spin_lock_irq(&port->port_lock);
+
+	if (port->port.count != 1) {
+		if (port->port.count == 0)
+			WARN_ON(1);
+		else
+			--port->port.count;
+		goto exit;
+	}
+
+	pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
+
+	/* mark port as closing but in use; we can drop port lock
+	 * and sleep if necessary
+	 */
+	port->openclose = true;
+	port->port.count = 0;
+
+	gser = port->port_usb;
+	if (gser && gser->disconnect)
+		gser->disconnect(gser);
+
+	/* wait for circular write buffer to drain, disconnect, or at
+	 * most GS_CLOSE_TIMEOUT seconds; then discard the rest
+	 */
+	if (kfifo_len(&port->port_write_buf) > 0 && gser) {
+		spin_unlock_irq(&port->port_lock);
+		wait_event_interruptible_timeout(port->drain_wait,
+					gs_writes_finished(port),
+					GS_CLOSE_TIMEOUT * HZ);
+		spin_lock_irq(&port->port_lock);
+		gser = port->port_usb;
+	}
+
+	/* Iff we're disconnected, there can be no I/O in flight so it's
+	 * ok to free the circular buffer; else just scrub it.  And don't
+	 * let the push tasklet fire again until we're re-opened.
+	 */
+	if (gser == NULL)
+		kfifo_free(&port->port_write_buf);
+	else
+		kfifo_reset(&port->port_write_buf);
+
+	port->port.tty = NULL;
+
+	port->openclose = false;
+
+	pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
+			port->port_num, tty, file);
+
+	wake_up(&port->close_wait);
+exit:
+	spin_unlock_irq(&port->port_lock);
+}
+
+static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+	struct gs_port	*port = tty->driver_data;
+	unsigned long	flags;
+
+	pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
+			port->port_num, tty, count);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (count)
+		count = kfifo_in(&port->port_write_buf, buf, count);
+	/* treat count == 0 as flush_chars() */
+	if (port->port_usb)
+		gs_start_tx(port);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return count;
+}
+
+static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct gs_port	*port = tty->driver_data;
+	unsigned long	flags;
+	int		status;
+
+	pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
+		port->port_num, tty, ch, __builtin_return_address(0));
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	status = kfifo_put(&port->port_write_buf, ch);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return status;
+}
+
+static void gs_flush_chars(struct tty_struct *tty)
+{
+	struct gs_port	*port = tty->driver_data;
+	unsigned long	flags;
+
+	pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		gs_start_tx(port);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_write_room(struct tty_struct *tty)
+{
+	struct gs_port	*port = tty->driver_data;
+	unsigned long	flags;
+	int		room = 0;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb)
+		room = kfifo_avail(&port->port_write_buf);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
+		port->port_num, tty, room);
+
+	return room;
+}
+
+static int gs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct gs_port	*port = tty->driver_data;
+	unsigned long	flags;
+	int		chars = 0;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	chars = kfifo_len(&port->port_write_buf);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+		port->port_num, tty, chars);
+
+	return chars;
+}
+
+/* undo side effects of setting TTY_THROTTLED */
+static void gs_unthrottle(struct tty_struct *tty)
+{
+	struct gs_port		*port = tty->driver_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port_usb) {
+		/* Kickstart read queue processing.  We don't do xon/xoff,
+		 * rts/cts, or other handshaking with the host, but if the
+		 * read queue backs up enough we'll be NAKing OUT packets.
+		 */
+		tasklet_schedule(&port->push);
+		pr_vdebug("ttyGS%d: unthrottle\n", port->port_num);
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_break_ctl(struct tty_struct *tty, int duration)
+{
+	struct gs_port	*port = tty->driver_data;
+	int		status = 0;
+	struct gserial	*gser;
+
+	pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
+			port->port_num, duration);
+
+	spin_lock_irq(&port->port_lock);
+	gser = port->port_usb;
+	if (gser && gser->send_break)
+		status = gser->send_break(gser, duration);
+	spin_unlock_irq(&port->port_lock);
+
+	return status;
+}
+
+static const struct tty_operations gs_tty_ops = {
+	.open =			gs_open,
+	.close =		gs_close,
+	.write =		gs_write,
+	.put_char =		gs_put_char,
+	.flush_chars =		gs_flush_chars,
+	.write_room =		gs_write_room,
+	.chars_in_buffer =	gs_chars_in_buffer,
+	.unthrottle =		gs_unthrottle,
+	.break_ctl =		gs_break_ctl,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct tty_driver *gs_tty_driver;
+
+#ifdef CONFIG_U_SERIAL_CONSOLE
+
+static struct gscons_info gscons_info;
+static struct console gserial_cons;
+
+static struct usb_request *gs_request_new(struct usb_ep *ep)
+{
+	struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (!req)
+		return NULL;
+
+	req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+
+	return req;
+}
+
+static void gs_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+	if (!req)
+		return;
+
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void gs_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gscons_info *info = &gscons_info;
+
+	switch (req->status) {
+	default:
+		pr_warn("%s: unexpected %s status %d\n",
+			__func__, ep->name, req->status);
+		/* fall through */
+	case 0:
+		/* normal completion */
+		spin_lock(&info->con_lock);
+		info->req_busy = 0;
+		spin_unlock(&info->con_lock);
+
+		wake_up_process(info->console_thread);
+		break;
+	case -ESHUTDOWN:
+		/* disconnect */
+		pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+		break;
+	}
+}
+
+static int gs_console_connect(int port_num)
+{
+	struct gscons_info *info = &gscons_info;
+	struct gs_port *port;
+	struct usb_ep *ep;
+
+	if (port_num != gserial_cons.index) {
+		pr_err("%s: port num [%d] is not support console\n",
+		       __func__, port_num);
+		return -ENXIO;
+	}
+
+	port = ports[port_num].port;
+	ep = port->port_usb->in;
+	if (!info->console_req) {
+		info->console_req = gs_request_new(ep);
+		if (!info->console_req)
+			return -ENOMEM;
+		info->console_req->complete = gs_complete_out;
+	}
+
+	info->port = port;
+	spin_lock(&info->con_lock);
+	info->req_busy = 0;
+	spin_unlock(&info->con_lock);
+	pr_vdebug("port[%d] console connect!\n", port_num);
+	return 0;
+}
+
+static void gs_console_disconnect(struct usb_ep *ep)
+{
+	struct gscons_info *info = &gscons_info;
+	struct usb_request *req = info->console_req;
+
+	gs_request_free(req, ep);
+	info->console_req = NULL;
+}
+
+static int gs_console_thread(void *data)
+{
+	struct gscons_info *info = &gscons_info;
+	struct gs_port *port;
+	struct usb_request *req;
+	struct usb_ep *ep;
+	int xfer, ret, count, size;
+
+	do {
+		port = info->port;
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!port || !port->port_usb
+		    || !port->port_usb->in || !info->console_req)
+			goto sched;
+
+		req = info->console_req;
+		ep = port->port_usb->in;
+
+		spin_lock_irq(&info->con_lock);
+		count = kfifo_len(&info->con_buf);
+		size = ep->maxpacket;
+
+		if (count > 0 && !info->req_busy) {
+			set_current_state(TASK_RUNNING);
+			if (count < size)
+				size = count;
+
+			xfer = kfifo_out(&info->con_buf, req->buf, size);
+			req->length = xfer;
+
+			spin_unlock(&info->con_lock);
+			ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+			spin_lock(&info->con_lock);
+			if (ret < 0)
+				info->req_busy = 0;
+			else
+				info->req_busy = 1;
+
+			spin_unlock_irq(&info->con_lock);
+		} else {
+			spin_unlock_irq(&info->con_lock);
+sched:
+			if (kthread_should_stop()) {
+				set_current_state(TASK_RUNNING);
+				break;
+			}
+			schedule();
+		}
+	} while (1);
+
+	return 0;
+}
+
+static int gs_console_setup(struct console *co, char *options)
+{
+	struct gscons_info *info = &gscons_info;
+	int status;
+
+	info->port = NULL;
+	info->console_req = NULL;
+	info->req_busy = 0;
+	spin_lock_init(&info->con_lock);
+
+	status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL);
+	if (status) {
+		pr_err("%s: allocate console buffer failed\n", __func__);
+		return status;
+	}
+
+	info->console_thread = kthread_create(gs_console_thread,
+					      co, "gs_console");
+	if (IS_ERR(info->console_thread)) {
+		pr_err("%s: cannot create console thread\n", __func__);
+		kfifo_free(&info->con_buf);
+		return PTR_ERR(info->console_thread);
+	}
+	wake_up_process(info->console_thread);
+
+	return 0;
+}
+
+static void gs_console_write(struct console *co,
+			     const char *buf, unsigned count)
+{
+	struct gscons_info *info = &gscons_info;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->con_lock, flags);
+	kfifo_in(&info->con_buf, buf, count);
+	spin_unlock_irqrestore(&info->con_lock, flags);
+
+	wake_up_process(info->console_thread);
+}
+
+static struct tty_driver *gs_console_device(struct console *co, int *index)
+{
+	struct tty_driver **p = (struct tty_driver **)co->data;
+
+	if (!*p)
+		return NULL;
+
+	*index = co->index;
+	return *p;
+}
+
+static struct console gserial_cons = {
+	.name =		"ttyGS",
+	.write =	gs_console_write,
+	.device =	gs_console_device,
+	.setup =	gs_console_setup,
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+	.data =		&gs_tty_driver,
+};
+
+static void gserial_console_init(void)
+{
+	register_console(&gserial_cons);
+}
+
+static void gserial_console_exit(void)
+{
+	struct gscons_info *info = &gscons_info;
+
+	unregister_console(&gserial_cons);
+	if (!IS_ERR_OR_NULL(info->console_thread))
+		kthread_stop(info->console_thread);
+	kfifo_free(&info->con_buf);
+}
+
+#else
+
+static int gs_console_connect(int port_num)
+{
+	return 0;
+}
+
+static void gs_console_disconnect(struct usb_ep *ep)
+{
+}
+
+static void gserial_console_init(void)
+{
+}
+
+static void gserial_console_exit(void)
+{
+}
+
+#endif
+
+static int
+gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
+{
+	struct gs_port	*port;
+	int		ret = 0;
+
+	mutex_lock(&ports[port_num].lock);
+	if (ports[port_num].port) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+	if (port == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	tty_port_init(&port->port);
+	spin_lock_init(&port->port_lock);
+	init_waitqueue_head(&port->drain_wait);
+	init_waitqueue_head(&port->close_wait);
+
+	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_LIST_HEAD(&port->write_pool);
+
+	port->port_num = port_num;
+	port->port_line_coding = *coding;
+
+	ports[port_num].port = port;
+out:
+	mutex_unlock(&ports[port_num].lock);
+	return ret;
+}
+
+static int gs_closed(struct gs_port *port)
+{
+	int cond;
+
+	spin_lock_irq(&port->port_lock);
+	cond = (port->port.count == 0) && !port->openclose;
+	spin_unlock_irq(&port->port_lock);
+	return cond;
+}
+
+static void gserial_free_port(struct gs_port *port)
+{
+	tasklet_kill(&port->push);
+	/* wait for old opens to finish */
+	wait_event(port->close_wait, gs_closed(port));
+	WARN_ON(port->port_usb != NULL);
+	tty_port_destroy(&port->port);
+	kfree(port);
+}
+
+void gserial_free_line(unsigned char port_num)
+{
+	struct gs_port	*port;
+
+	mutex_lock(&ports[port_num].lock);
+	if (WARN_ON(!ports[port_num].port)) {
+		mutex_unlock(&ports[port_num].lock);
+		return;
+	}
+	port = ports[port_num].port;
+	ports[port_num].port = NULL;
+	mutex_unlock(&ports[port_num].lock);
+
+	gserial_free_port(port);
+	tty_unregister_device(gs_tty_driver, port_num);
+	gserial_console_exit();
+}
+EXPORT_SYMBOL_GPL(gserial_free_line);
+
+int gserial_alloc_line(unsigned char *line_num)
+{
+	struct usb_cdc_line_coding	coding;
+	struct device			*tty_dev;
+	int				ret;
+	int				port_num;
+
+	coding.dwDTERate = cpu_to_le32(9600);
+	coding.bCharFormat = 8;
+	coding.bParityType = USB_CDC_NO_PARITY;
+	coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+	for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
+		ret = gs_port_alloc(port_num, &coding);
+		if (ret == -EBUSY)
+			continue;
+		if (ret)
+			return ret;
+		break;
+	}
+	if (ret)
+		return ret;
+
+	/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
+
+	tty_dev = tty_port_register_device(&ports[port_num].port->port,
+			gs_tty_driver, port_num, NULL);
+	if (IS_ERR(tty_dev)) {
+		struct gs_port	*port;
+		pr_err("%s: failed to register tty for port %d, err %ld\n",
+				__func__, port_num, PTR_ERR(tty_dev));
+
+		ret = PTR_ERR(tty_dev);
+		port = ports[port_num].port;
+		ports[port_num].port = NULL;
+		gserial_free_port(port);
+		goto err;
+	}
+	*line_num = port_num;
+	gserial_console_init();
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(gserial_alloc_line);
+
+/**
+ * gserial_connect - notify TTY I/O glue that USB link is active
+ * @gser: the function, set up with endpoints and descriptors
+ * @port_num: which port is active
+ * Context: any (usually from irq)
+ *
+ * This is called activate endpoints and let the TTY layer know that
+ * the connection is active ... not unlike "carrier detect".  It won't
+ * necessarily start I/O queues; unless the TTY is held open by any
+ * task, there would be no point.  However, the endpoints will be
+ * activated so the USB host can perform I/O, subject to basic USB
+ * hardware flow control.
+ *
+ * Caller needs to have set up the endpoints and USB function in @dev
+ * before calling this, as well as the appropriate (speed-specific)
+ * endpoint descriptors, and also have allocate @port_num by calling
+ * @gserial_alloc_line().
+ *
+ * Returns negative errno or zero.
+ * On success, ep->driver_data will be overwritten.
+ */
+int gserial_connect(struct gserial *gser, u8 port_num)
+{
+	struct gs_port	*port;
+	unsigned long	flags;
+	int		status;
+
+	if (port_num >= MAX_U_SERIAL_PORTS)
+		return -ENXIO;
+
+	port = ports[port_num].port;
+	if (!port) {
+		pr_err("serial line %d not allocated.\n", port_num);
+		return -EINVAL;
+	}
+	if (port->port_usb) {
+		pr_err("serial line %d is in use.\n", port_num);
+		return -EBUSY;
+	}
+
+	/* activate the endpoints */
+	status = usb_ep_enable(gser->in);
+	if (status < 0)
+		return status;
+	gser->in->driver_data = port;
+
+	status = usb_ep_enable(gser->out);
+	if (status < 0)
+		goto fail_out;
+	gser->out->driver_data = port;
+
+	/* then tell the tty glue that I/O can work */
+	spin_lock_irqsave(&port->port_lock, flags);
+	gser->ioport = port;
+	port->port_usb = gser;
+
+	/* REVISIT unclear how best to handle this state...
+	 * we don't really couple it with the Linux TTY.
+	 */
+	gser->port_line_coding = port->port_line_coding;
+
+	/* REVISIT if waiting on "carrier detect", signal. */
+
+	/* if it's already open, start I/O ... and notify the serial
+	 * protocol about open/close status (connect/disconnect).
+	 */
+	if (port->port.count) {
+		pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
+		gs_start_io(port);
+		if (gser->connect)
+			gser->connect(gser);
+	} else {
+		if (gser->disconnect)
+			gser->disconnect(gser);
+	}
+
+	status = gs_console_connect(port_num);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return status;
+
+fail_out:
+	usb_ep_disable(gser->in);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gserial_connect);
+/**
+ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
+ * @gser: the function, on which gserial_connect() was called
+ * Context: any (usually from irq)
+ *
+ * This is called to deactivate endpoints and let the TTY layer know
+ * that the connection went inactive ... not unlike "hangup".
+ *
+ * On return, the state is as if gserial_connect() had never been called;
+ * there is no active USB I/O on these endpoints.
+ */
+void gserial_disconnect(struct gserial *gser)
+{
+	struct gs_port	*port = gser->ioport;
+	unsigned long	flags;
+
+	if (!port)
+		return;
+
+	/* tell the TTY glue not to do I/O here any more */
+	spin_lock_irqsave(&port->port_lock, flags);
+
+	/* REVISIT as above: how best to track this? */
+	port->port_line_coding = gser->port_line_coding;
+
+	port->port_usb = NULL;
+	gser->ioport = NULL;
+	if (port->port.count > 0 || port->openclose) {
+		wake_up_interruptible(&port->drain_wait);
+		if (port->port.tty)
+			tty_hangup(port->port.tty);
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(gser->out);
+	usb_ep_disable(gser->in);
+
+	/* finally, free any unused/unusable I/O buffers */
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->port.count == 0 && !port->openclose)
+		kfifo_free(&port->port_write_buf);
+	gs_free_requests(gser->out, &port->read_pool, NULL);
+	gs_free_requests(gser->out, &port->read_queue, NULL);
+	gs_free_requests(gser->in, &port->write_pool, NULL);
+
+	port->read_allocated = port->read_started =
+		port->write_allocated = port->write_started = 0;
+
+	gs_console_disconnect(gser->in);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_disconnect);
+
+static int userial_init(void)
+{
+	unsigned			i;
+	int				status;
+
+	gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
+	if (!gs_tty_driver)
+		return -ENOMEM;
+
+	gs_tty_driver->driver_name = "g_serial";
+	gs_tty_driver->name = "ttyGS";
+	/* uses dynamically assigned dev_t values */
+
+	gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	gs_tty_driver->init_termios = tty_std_termios;
+
+	/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+	 * MS-Windows.  Otherwise, most of these flags shouldn't affect
+	 * anything unless we were to actually hook up to a serial line.
+	 */
+	gs_tty_driver->init_termios.c_cflag =
+			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	gs_tty_driver->init_termios.c_ispeed = 9600;
+	gs_tty_driver->init_termios.c_ospeed = 9600;
+
+	tty_set_operations(gs_tty_driver, &gs_tty_ops);
+	for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
+		mutex_init(&ports[i].lock);
+
+	/* export the driver ... */
+	status = tty_register_driver(gs_tty_driver);
+	if (status) {
+		pr_err("%s: cannot register, err %d\n",
+				__func__, status);
+		goto fail;
+	}
+
+	pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+			MAX_U_SERIAL_PORTS,
+			(MAX_U_SERIAL_PORTS == 1) ? "" : "s");
+
+	return status;
+fail:
+	put_tty_driver(gs_tty_driver);
+	gs_tty_driver = NULL;
+	return status;
+}
+module_init(userial_init);
+
+static void userial_cleanup(void)
+{
+	tty_unregister_driver(gs_tty_driver);
+	put_tty_driver(gs_tty_driver);
+	gs_tty_driver = NULL;
+}
+module_exit(userial_cleanup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
new file mode 100644
index 0000000..9acaac1
--- /dev/null
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_serial.h - interface to USB gadget "serial port"/TTY utilities
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+#ifndef __U_SERIAL_H
+#define __U_SERIAL_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+#define MAX_U_SERIAL_PORTS	4
+
+struct f_serial_opts {
+	struct usb_function_instance func_inst;
+	u8 port_num;
+};
+
+/*
+ * One non-multiplexed "serial" I/O port ... there can be several of these
+ * on any given USB peripheral device, if it provides enough endpoints.
+ *
+ * The "u_serial" utility component exists to do one thing:  manage TTY
+ * style I/O using the USB peripheral endpoints listed here, including
+ * hookups to sysfs and /dev for each logical "tty" device.
+ *
+ * REVISIT at least ACM could support tiocmget() if needed.
+ *
+ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
+ */
+struct gserial {
+	struct usb_function		func;
+
+	/* port is managed by gserial_{connect,disconnect} */
+	struct gs_port			*ioport;
+
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+
+	/* REVISIT avoid this CDC-ACM support harder ... */
+	struct usb_cdc_line_coding port_line_coding;	/* 9600-8-N-1 etc */
+
+	/* notification callbacks */
+	void (*connect)(struct gserial *p);
+	void (*disconnect)(struct gserial *p);
+	int (*send_break)(struct gserial *p, int duration);
+};
+
+/* utilities to allocate/free request and buffer */
+struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
+void gs_free_req(struct usb_ep *, struct usb_request *req);
+
+/* management of individual TTY ports */
+int gserial_alloc_line(unsigned char *port_line);
+void gserial_free_line(unsigned char port_line);
+
+/* connect/disconnect is handled by individual functions */
+int gserial_connect(struct gserial *, u8 port_num);
+void gserial_disconnect(struct gserial *);
+
+/* functions are bound to configurations by a config or gadget driver */
+int gser_bind_config(struct usb_configuration *c, u8 port_num);
+int obex_bind_config(struct usb_configuration *c, u8 port_num);
+
+#endif /* __U_SERIAL_H */
diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h
new file mode 100644
index 0000000..3f7ccec
--- /dev/null
+++ b/drivers/usb/gadget/function/u_tcm.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_tcm.h
+ *
+ * Utility definitions for the tcm function
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx>
+ */
+
+#ifndef U_TCM_H
+#define U_TCM_H
+
+#include <linux/usb/composite.h>
+
+/**
+ * @dependent: optional dependent module. Meant for legacy gadget.
+ * If non-null its refcount will be increased when a tpg is created and
+ * decreased when tpg is dropped.
+ * @dep_lock: lock for dependent module operations.
+ * @ready: true if the dependent module information is set.
+ * @can_attach: true a function can be bound to gadget
+ * @has_dep: true if there is a dependent module
+ *
+ */
+struct f_tcm_opts {
+	struct usb_function_instance	func_inst;
+	struct module			*dependent;
+	struct mutex			dep_lock;
+	bool				ready;
+	bool				can_attach;
+	bool				has_dep;
+
+	/*
+	 * Callbacks to be removed when legacy tcm gadget disappears.
+	 *
+	 * If you use the new function registration interface
+	 * programmatically, you MUST set these callbacks to
+	 * something sensible (e.g. probe/remove the composite).
+	 */
+	int (*tcm_register_callback)(struct usb_function_instance *);
+	void (*tcm_unregister_callback)(struct usb_function_instance *);
+};
+
+#endif /* U_TCM_H */
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
new file mode 100644
index 0000000..6f1a9d7
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_uac1.h - Utility definitions for UAC1 function
+ *
+ * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ */
+
+#ifndef __U_UAC1_H
+#define __U_UAC1_H
+
+#include <linux/usb/composite.h>
+
+#define UAC1_OUT_EP_MAX_PACKET_SIZE	200
+#define UAC1_DEF_CCHMASK	0x3
+#define UAC1_DEF_CSRATE		48000
+#define UAC1_DEF_CSSIZE		2
+#define UAC1_DEF_PCHMASK	0x3
+#define UAC1_DEF_PSRATE		48000
+#define UAC1_DEF_PSSIZE		2
+#define UAC1_DEF_REQ_NUM	2
+
+
+struct f_uac1_opts {
+	struct usb_function_instance	func_inst;
+	int				c_chmask;
+	int				c_srate;
+	int				c_ssize;
+	int				p_chmask;
+	int				p_srate;
+	int				p_ssize;
+	int				req_number;
+	unsigned			bound:1;
+
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* __U_UAC1_H */
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c
new file mode 100644
index 0000000..5393e5c
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac1_legacy.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_uac1.c -- ALSA audio utilities for Gadget stack
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+
+#include "u_uac1_legacy.h"
+
+/*
+ * This component encapsulates the ALSA devices for USB audio gadget
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Some ALSA internal helper functions
+ */
+static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
+{
+	struct snd_interval t;
+	t.empty = 0;
+	t.min = t.max = val;
+	t.openmin = t.openmax = 0;
+	t.integer = 1;
+	return snd_interval_refine(i, &t);
+}
+
+static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
+				 snd_pcm_hw_param_t var, unsigned int val,
+				 int dir)
+{
+	int changed;
+	if (hw_is_mask(var)) {
+		struct snd_mask *m = hw_param_mask(params, var);
+		if (val == 0 && dir < 0) {
+			changed = -EINVAL;
+			snd_mask_none(m);
+		} else {
+			if (dir > 0)
+				val++;
+			else if (dir < 0)
+				val--;
+			changed = snd_mask_refine_set(
+					hw_param_mask(params, var), val);
+		}
+	} else if (hw_is_interval(var)) {
+		struct snd_interval *i = hw_param_interval(params, var);
+		if (val == 0 && dir < 0) {
+			changed = -EINVAL;
+			snd_interval_none(i);
+		} else if (dir == 0)
+			changed = snd_interval_refine_set(i, val);
+		else {
+			struct snd_interval t;
+			t.openmin = 1;
+			t.openmax = 1;
+			t.empty = 0;
+			t.integer = 0;
+			if (dir < 0) {
+				t.min = val - 1;
+				t.max = val;
+			} else {
+				t.min = val;
+				t.max = val+1;
+			}
+			changed = snd_interval_refine(i, &t);
+		}
+	} else
+		return -EINVAL;
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Set default hardware params
+ */
+static int playback_default_hw_params(struct gaudio_snd_dev *snd)
+{
+	struct snd_pcm_substream *substream = snd->substream;
+	struct snd_pcm_hw_params *params;
+	snd_pcm_sframes_t result;
+
+       /*
+	* SNDRV_PCM_ACCESS_RW_INTERLEAVED,
+	* SNDRV_PCM_FORMAT_S16_LE
+	* CHANNELS: 2
+	* RATE: 48000
+	*/
+	snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+	snd->format = SNDRV_PCM_FORMAT_S16_LE;
+	snd->channels = 2;
+	snd->rate = 48000;
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	_snd_pcm_hw_params_any(params);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+			snd->access, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			snd->format, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+			snd->channels, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+			snd->rate, 0);
+
+	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
+
+	result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
+	if (result < 0) {
+		ERROR(snd->card,
+			"Preparing sound card failed: %d\n", (int)result);
+		kfree(params);
+		return result;
+	}
+
+	/* Store the hardware parameters */
+	snd->access = params_access(params);
+	snd->format = params_format(params);
+	snd->channels = params_channels(params);
+	snd->rate = params_rate(params);
+
+	kfree(params);
+
+	INFO(snd->card,
+		"Hardware params: access %x, format %x, channels %d, rate %d\n",
+		snd->access, snd->format, snd->channels, snd->rate);
+
+	return 0;
+}
+
+/**
+ * Playback audio buffer data by ALSA PCM device
+ */
+size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
+{
+	struct gaudio_snd_dev	*snd = &card->playback;
+	struct snd_pcm_substream *substream = snd->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t result;
+	snd_pcm_sframes_t frames;
+
+try_again:
+	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+		runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+		result = snd_pcm_kernel_ioctl(substream,
+				SNDRV_PCM_IOCTL_PREPARE, NULL);
+		if (result < 0) {
+			ERROR(card, "Preparing sound card failed: %d\n",
+					(int)result);
+			return result;
+		}
+	}
+
+	frames = bytes_to_frames(runtime, count);
+	result = snd_pcm_kernel_write(snd->substream, buf, frames);
+	if (result != frames) {
+		ERROR(card, "Playback error: %d\n", (int)result);
+		goto try_again;
+	}
+
+	return 0;
+}
+
+int u_audio_get_playback_channels(struct gaudio *card)
+{
+	return card->playback.channels;
+}
+
+int u_audio_get_playback_rate(struct gaudio *card)
+{
+	return card->playback.rate;
+}
+
+/**
+ * Open ALSA PCM and control device files
+ * Initial the PCM or control device
+ */
+static int gaudio_open_snd_dev(struct gaudio *card)
+{
+	struct snd_pcm_file *pcm_file;
+	struct gaudio_snd_dev *snd;
+	struct f_uac1_legacy_opts *opts;
+	char *fn_play, *fn_cap, *fn_cntl;
+
+	opts = container_of(card->func.fi, struct f_uac1_legacy_opts,
+			    func_inst);
+	fn_play = opts->fn_play;
+	fn_cap = opts->fn_cap;
+	fn_cntl = opts->fn_cntl;
+
+	/* Open control device */
+	snd = &card->control;
+	snd->filp = filp_open(fn_cntl, O_RDWR, 0);
+	if (IS_ERR(snd->filp)) {
+		int ret = PTR_ERR(snd->filp);
+		ERROR(card, "unable to open sound control device file: %s\n",
+				fn_cntl);
+		snd->filp = NULL;
+		return ret;
+	}
+	snd->card = card;
+
+	/* Open PCM playback device and setup substream */
+	snd = &card->playback;
+	snd->filp = filp_open(fn_play, O_WRONLY, 0);
+	if (IS_ERR(snd->filp)) {
+		int ret = PTR_ERR(snd->filp);
+
+		ERROR(card, "No such PCM playback device: %s\n", fn_play);
+		snd->filp = NULL;
+		return ret;
+	}
+	pcm_file = snd->filp->private_data;
+	snd->substream = pcm_file->substream;
+	snd->card = card;
+	playback_default_hw_params(snd);
+
+	/* Open PCM capture device and setup substream */
+	snd = &card->capture;
+	snd->filp = filp_open(fn_cap, O_RDONLY, 0);
+	if (IS_ERR(snd->filp)) {
+		ERROR(card, "No such PCM capture device: %s\n", fn_cap);
+		snd->substream = NULL;
+		snd->card = NULL;
+		snd->filp = NULL;
+	} else {
+		pcm_file = snd->filp->private_data;
+		snd->substream = pcm_file->substream;
+		snd->card = card;
+	}
+
+	return 0;
+}
+
+/**
+ * Close ALSA PCM and control device files
+ */
+static int gaudio_close_snd_dev(struct gaudio *gau)
+{
+	struct gaudio_snd_dev	*snd;
+
+	/* Close control device */
+	snd = &gau->control;
+	if (snd->filp)
+		filp_close(snd->filp, NULL);
+
+	/* Close PCM playback device and setup substream */
+	snd = &gau->playback;
+	if (snd->filp)
+		filp_close(snd->filp, NULL);
+
+	/* Close PCM capture device and setup substream */
+	snd = &gau->capture;
+	if (snd->filp)
+		filp_close(snd->filp, NULL);
+
+	return 0;
+}
+
+/**
+ * gaudio_setup - setup ALSA interface and preparing for USB transfer
+ *
+ * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
+ *
+ * Returns negative errno, or zero on success
+ */
+int gaudio_setup(struct gaudio *card)
+{
+	int	ret;
+
+	ret = gaudio_open_snd_dev(card);
+	if (ret)
+		ERROR(card, "we need at least one control device\n");
+
+	return ret;
+
+}
+
+/**
+ * gaudio_cleanup - remove ALSA device interface
+ *
+ * This is called to free all resources allocated by @gaudio_setup().
+ */
+void gaudio_cleanup(struct gaudio *the_card)
+{
+	if (the_card)
+		gaudio_close_snd_dev(the_card);
+}
+
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h
new file mode 100644
index 0000000..5c1bdf4
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac1_legacy.h
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ */
+
+#ifndef __U_UAC1_LEGACY_H
+#define __U_UAC1_LEGACY_H
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/composite.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#define FILE_PCM_PLAYBACK	"/dev/snd/pcmC0D0p"
+#define FILE_PCM_CAPTURE	"/dev/snd/pcmC0D0c"
+#define FILE_CONTROL		"/dev/snd/controlC0"
+
+#define UAC1_OUT_EP_MAX_PACKET_SIZE	200
+#define UAC1_REQ_COUNT			256
+#define UAC1_AUDIO_BUF_SIZE		48000
+
+/*
+ * This represents the USB side of an audio card device, managed by a USB
+ * function which provides control and stream interfaces.
+ */
+
+struct gaudio_snd_dev {
+	struct gaudio			*card;
+	struct file			*filp;
+	struct snd_pcm_substream	*substream;
+	int				access;
+	int				format;
+	int				channels;
+	int				rate;
+};
+
+struct gaudio {
+	struct usb_function		func;
+	struct usb_gadget		*gadget;
+
+	/* ALSA sound device interfaces */
+	struct gaudio_snd_dev		control;
+	struct gaudio_snd_dev		playback;
+	struct gaudio_snd_dev		capture;
+
+	/* TODO */
+};
+
+struct f_uac1_legacy_opts {
+	struct usb_function_instance	func_inst;
+	int				req_buf_size;
+	int				req_count;
+	int				audio_buf_size;
+	char				*fn_play;
+	char				*fn_cap;
+	char				*fn_cntl;
+	unsigned			bound:1;
+	unsigned			fn_play_alloc:1;
+	unsigned			fn_cap_alloc:1;
+	unsigned			fn_cntl_alloc:1;
+	struct mutex			lock;
+	int				refcnt;
+};
+
+int gaudio_setup(struct gaudio *card);
+void gaudio_cleanup(struct gaudio *the_card);
+
+size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
+int u_audio_get_playback_channels(struct gaudio *card);
+int u_audio_get_playback_rate(struct gaudio *card);
+
+#endif /* __U_UAC1_LEGACY_H */
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
new file mode 100644
index 0000000..8362ee5
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_uac2.h
+ *
+ * Utility definitions for UAC2 function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_UAC2_H
+#define U_UAC2_H
+
+#include <linux/usb/composite.h>
+
+#define UAC2_DEF_PCHMASK 0x3
+#define UAC2_DEF_PSRATE 48000
+#define UAC2_DEF_PSSIZE 2
+#define UAC2_DEF_CCHMASK 0x3
+#define UAC2_DEF_CSRATE 64000
+#define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_REQ_NUM 2
+
+struct f_uac2_opts {
+	struct usb_function_instance	func_inst;
+	int				p_chmask;
+	int				p_srate;
+	int				p_ssize;
+	int				c_chmask;
+	int				c_srate;
+	int				c_ssize;
+	int				req_number;
+	bool				bound;
+
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
new file mode 100644
index 0000000..2ed292e
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_uvc.h
+ *
+ * Utility definitions for the uvc function
+ *
+ * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef U_UVC_H
+#define U_UVC_H
+
+#include <linux/mutex.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/video.h>
+
+#define fi_to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)
+
+struct f_uvc_opts {
+	struct usb_function_instance			func_inst;
+	unsigned int					streaming_interval;
+	unsigned int					streaming_maxpacket;
+	unsigned int					streaming_maxburst;
+
+	/*
+	 * Control descriptors array pointers for full-/high-speed and
+	 * super-speed. They point by default to the uvc_fs_control_cls and
+	 * uvc_ss_control_cls arrays respectively. Legacy gadgets must
+	 * override them in their gadget bind callback.
+	 */
+	const struct uvc_descriptor_header * const	*fs_control;
+	const struct uvc_descriptor_header * const	*ss_control;
+
+	/*
+	 * Streaming descriptors array pointers for full-speed, high-speed and
+	 * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays
+	 * for configfs-based gadgets. Legacy gadgets must initialize them in
+	 * their gadget bind callback.
+	 */
+	const struct uvc_descriptor_header * const	*fs_streaming;
+	const struct uvc_descriptor_header * const	*hs_streaming;
+	const struct uvc_descriptor_header * const	*ss_streaming;
+
+	/* Default control descriptors for configfs-based gadgets. */
+	struct uvc_camera_terminal_descriptor		uvc_camera_terminal;
+	struct uvc_processing_unit_descriptor		uvc_processing;
+	struct uvc_output_terminal_descriptor		uvc_output_terminal;
+	struct uvc_color_matching_descriptor		uvc_color_matching;
+
+	/*
+	 * Control descriptors pointers arrays for full-/high-speed and
+	 * super-speed. The first element is a configurable control header
+	 * descriptor, the other elements point to the fixed default control
+	 * descriptors. Used by configfs only, must not be touched by legacy
+	 * gadgets.
+	 */
+	struct uvc_descriptor_header			*uvc_fs_control_cls[5];
+	struct uvc_descriptor_header			*uvc_ss_control_cls[5];
+
+	/*
+	 * Streaming descriptors for full-speed, high-speed and super-speed.
+	 * Used by configfs only, must not be touched by legacy gadgets. The
+	 * arrays are allocated at runtime as the number of descriptors isn't
+	 * known in advance.
+	 */
+	struct uvc_descriptor_header			**uvc_fs_streaming_cls;
+	struct uvc_descriptor_header			**uvc_hs_streaming_cls;
+	struct uvc_descriptor_header			**uvc_ss_streaming_cls;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This lock protects the descriptors from concurrent access by
+	 * read/write and symlink creation/removal.
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_UVC_H */
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
new file mode 100644
index 0000000..93cf78b
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc.h
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	uvc_gadget.h  --  USB Video Class Gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef _UVC_GADGET_H_
+#define _UVC_GADGET_H_
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/usb/composite.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+
+#include "uvc_queue.h"
+
+struct usb_ep;
+struct usb_request;
+struct uvc_descriptor_header;
+
+/* ------------------------------------------------------------------------
+ * Debugging, printing and logging
+ */
+
+#define UVC_TRACE_PROBE				(1 << 0)
+#define UVC_TRACE_DESCR				(1 << 1)
+#define UVC_TRACE_CONTROL			(1 << 2)
+#define UVC_TRACE_FORMAT			(1 << 3)
+#define UVC_TRACE_CAPTURE			(1 << 4)
+#define UVC_TRACE_CALLS				(1 << 5)
+#define UVC_TRACE_IOCTL				(1 << 6)
+#define UVC_TRACE_FRAME				(1 << 7)
+#define UVC_TRACE_SUSPEND			(1 << 8)
+#define UVC_TRACE_STATUS			(1 << 9)
+
+#define UVC_WARN_MINMAX				0
+#define UVC_WARN_PROBE_DEF			1
+
+extern unsigned int uvc_gadget_trace_param;
+
+#define uvc_trace(flag, msg...) \
+	do { \
+		if (uvc_gadget_trace_param & flag) \
+			printk(KERN_DEBUG "uvcvideo: " msg); \
+	} while (0)
+
+#define uvc_warn_once(dev, warn, msg...) \
+	do { \
+		if (!test_and_set_bit(warn, &dev->warnings)) \
+			printk(KERN_INFO "uvcvideo: " msg); \
+	} while (0)
+
+#define uvc_printk(level, msg...) \
+	printk(level "uvcvideo: " msg)
+
+/* ------------------------------------------------------------------------
+ * Driver specific constants
+ */
+
+#define UVC_NUM_REQUESTS			4
+#define UVC_MAX_REQUEST_SIZE			64
+#define UVC_MAX_EVENTS				4
+
+/* ------------------------------------------------------------------------
+ * Structures
+ */
+
+struct uvc_video {
+	struct usb_ep *ep;
+
+	/* Frame parameters */
+	u8 bpp;
+	u32 fcc;
+	unsigned int width;
+	unsigned int height;
+	unsigned int imagesize;
+	struct mutex mutex;	/* protects frame parameters */
+
+	/* Requests */
+	unsigned int req_size;
+	struct usb_request *req[UVC_NUM_REQUESTS];
+	__u8 *req_buffer[UVC_NUM_REQUESTS];
+	struct list_head req_free;
+	spinlock_t req_lock;
+
+	void (*encode) (struct usb_request *req, struct uvc_video *video,
+			struct uvc_buffer *buf);
+
+	/* Context data used by the completion handler */
+	__u32 payload_size;
+	__u32 max_payload_size;
+
+	struct uvc_video_queue queue;
+	unsigned int fid;
+};
+
+enum uvc_state {
+	UVC_STATE_DISCONNECTED,
+	UVC_STATE_CONNECTED,
+	UVC_STATE_STREAMING,
+};
+
+struct uvc_device {
+	struct video_device vdev;
+	struct v4l2_device v4l2_dev;
+	enum uvc_state state;
+	struct usb_function func;
+	struct uvc_video video;
+
+	/* Descriptors */
+	struct {
+		const struct uvc_descriptor_header * const *fs_control;
+		const struct uvc_descriptor_header * const *ss_control;
+		const struct uvc_descriptor_header * const *fs_streaming;
+		const struct uvc_descriptor_header * const *hs_streaming;
+		const struct uvc_descriptor_header * const *ss_streaming;
+	} desc;
+
+	unsigned int control_intf;
+	struct usb_ep *control_ep;
+	struct usb_request *control_req;
+	void *control_buf;
+
+	unsigned int streaming_intf;
+
+	/* Events */
+	unsigned int event_length;
+	unsigned int event_setup_out : 1;
+};
+
+static inline struct uvc_device *to_uvc(struct usb_function *f)
+{
+	return container_of(f, struct uvc_device, func);
+}
+
+struct uvc_file_handle {
+	struct v4l2_fh vfh;
+	struct uvc_video *device;
+};
+
+#define to_uvc_file_handle(handle) \
+	container_of(handle, struct uvc_file_handle, vfh)
+
+/* ------------------------------------------------------------------------
+ * Functions
+ */
+
+extern void uvc_function_setup_continue(struct uvc_device *uvc);
+extern void uvc_endpoint_stream(struct uvc_device *dev);
+
+extern void uvc_function_connect(struct uvc_device *uvc);
+extern void uvc_function_disconnect(struct uvc_device *uvc);
+
+#endif /* _UVC_GADGET_H_ */
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
new file mode 100644
index 0000000..b51f0d2
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -0,0 +1,2307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * uvc_configfs.c
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+#include "u_uvc.h"
+#include "uvc_configfs.h"
+
+#define UVCG_STREAMING_CONTROL_SIZE	1
+
+#define UVC_ATTR(prefix, cname, aname) \
+static struct configfs_attribute prefix##attr_##cname = { \
+	.ca_name	= __stringify(aname),				\
+	.ca_mode	= S_IRUGO | S_IWUGO,				\
+	.ca_owner	= THIS_MODULE,					\
+	.show		= prefix##cname##_show,				\
+	.store		= prefix##cname##_store,			\
+}
+
+#define UVC_ATTR_RO(prefix, cname, aname) \
+static struct configfs_attribute prefix##attr_##cname = { \
+	.ca_name	= __stringify(aname),				\
+	.ca_mode	= S_IRUGO,					\
+	.ca_owner	= THIS_MODULE,					\
+	.show		= prefix##cname##_show,				\
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uvc_opts,
+			    func_inst.group);
+}
+
+/* control/header/<NAME> */
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+	struct config_item		item;
+	struct UVC_HEADER_DESCRIPTOR(1)	desc;
+	unsigned			linked;
+};
+
+static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+	return container_of(item, struct uvcg_control_header, item);
+}
+
+#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit)	\
+static ssize_t uvcg_control_header_##cname##_show(			\
+	struct config_item *item, char *page)			\
+{									\
+	struct uvcg_control_header *ch = to_uvcg_control_header(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(ch->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_control_header_##cname##_store(struct config_item *item,		\
+			   const char *page, size_t len)		\
+{									\
+	struct uvcg_control_header *ch = to_uvcg_control_header(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+	int ret;							\
+	uxx num;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (ch->linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = str2u(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > limit) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	ch->desc.aname = vnoc(num);					\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_control_header_, cname, aname)
+
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+		   0xffff);
+
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
+		   u32, cpu_to_le32, 0x7fffffff);
+
+#undef UVCG_CTRL_HDR_ATTR
+
+static struct configfs_attribute *uvcg_control_header_attrs[] = {
+	&uvcg_control_header_attr_bcd_uvc,
+	&uvcg_control_header_attr_dw_clock_frequency,
+	NULL,
+};
+
+static const struct config_item_type uvcg_control_header_type = {
+	.ct_attrs	= uvcg_control_header_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item *uvcg_control_header_make(struct config_group *group,
+						    const char *name)
+{
+	struct uvcg_control_header *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_HEADER_SIZE(1);
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VC_HEADER;
+	h->desc.bcdUVC			= cpu_to_le16(0x0100);
+	h->desc.dwClockFrequency	= cpu_to_le32(48000000);
+
+	config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
+
+	return &h->item;
+}
+
+static void uvcg_control_header_drop(struct config_group *group,
+			      struct config_item *item)
+{
+	struct uvcg_control_header *h = to_uvcg_control_header(item);
+
+	kfree(h);
+}
+
+/* control/header */
+static struct uvcg_control_header_grp {
+	struct config_group	group;
+} uvcg_control_header_grp;
+
+static struct configfs_group_operations uvcg_control_header_grp_ops = {
+	.make_item		= uvcg_control_header_make,
+	.drop_item		= uvcg_control_header_drop,
+};
+
+static const struct config_item_type uvcg_control_header_grp_type = {
+	.ct_group_ops	= &uvcg_control_header_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* control/processing/default */
+static struct uvcg_default_processing {
+	struct config_group	group;
+} uvcg_default_processing;
+
+static inline struct uvcg_default_processing
+*to_uvcg_default_processing(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_processing, group);
+}
+
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv)		\
+static ssize_t uvcg_default_processing_##cname##_show(			\
+	struct config_item *item, char *page)				\
+{									\
+	struct uvcg_default_processing *dp = to_uvcg_default_processing(item); \
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;	\
+	struct uvc_processing_unit_descriptor *pd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	pd = &opts->uvc_processing;					\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(pd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_default_processing_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
+UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_PROCESSING_ATTR
+
+static ssize_t uvcg_default_processing_bm_controls_show(
+	struct config_item *item, char *page)
+{
+	struct uvcg_default_processing *dp = to_uvcg_default_processing(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
+	struct uvc_processing_unit_descriptor *pd;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	pd = &opts->uvc_processing;
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < pd->bControlSize; ++i) {
+		result += sprintf(pg, "%d\n", pd->bmControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+
+	return result;
+}
+
+UVC_ATTR_RO(uvcg_default_processing_, bm_controls, bmControls);
+
+static struct configfs_attribute *uvcg_default_processing_attrs[] = {
+	&uvcg_default_processing_attr_b_unit_id,
+	&uvcg_default_processing_attr_b_source_id,
+	&uvcg_default_processing_attr_w_max_multiplier,
+	&uvcg_default_processing_attr_bm_controls,
+	&uvcg_default_processing_attr_i_processing,
+	NULL,
+};
+
+static const struct config_item_type uvcg_default_processing_type = {
+	.ct_attrs	= uvcg_default_processing_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_processing {}; */
+
+/* control/processing */
+static struct uvcg_processing_grp {
+	struct config_group	group;
+} uvcg_processing_grp;
+
+static const struct config_item_type uvcg_processing_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/camera/default */
+static struct uvcg_default_camera {
+	struct config_group	group;
+} uvcg_default_camera;
+
+static inline struct uvcg_default_camera
+*to_uvcg_default_camera(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_camera, group);
+}
+
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_default_camera_##cname##_show(			\
+	struct config_item *item, char *page)				\
+{									\
+	struct uvcg_default_camera *dc = to_uvcg_default_camera(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;	\
+	struct uvc_camera_terminal_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->	\
+			ci_parent;					\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_camera_terminal;				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+									\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_default_camera_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin,
+			 le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
+			 le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
+			 le16_to_cpu);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_CAMERA_ATTR
+
+static ssize_t uvcg_default_camera_bm_controls_show(
+	struct config_item *item, char *page)
+{
+	struct uvcg_default_camera *dc = to_uvcg_default_camera(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
+	struct uvc_camera_terminal_descriptor *cd;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
+			ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	cd = &opts->uvc_camera_terminal;
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < cd->bControlSize; ++i) {
+		result += sprintf(pg, "%d\n", cd->bmControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+UVC_ATTR_RO(uvcg_default_camera_, bm_controls, bmControls);
+
+static struct configfs_attribute *uvcg_default_camera_attrs[] = {
+	&uvcg_default_camera_attr_b_terminal_id,
+	&uvcg_default_camera_attr_w_terminal_type,
+	&uvcg_default_camera_attr_b_assoc_terminal,
+	&uvcg_default_camera_attr_i_terminal,
+	&uvcg_default_camera_attr_w_objective_focal_length_min,
+	&uvcg_default_camera_attr_w_objective_focal_length_max,
+	&uvcg_default_camera_attr_w_ocular_focal_length,
+	&uvcg_default_camera_attr_bm_controls,
+	NULL,
+};
+
+static const struct config_item_type uvcg_default_camera_type = {
+	.ct_attrs	= uvcg_default_camera_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_camera {}; */
+
+/* control/terminal/camera */
+static struct uvcg_camera_grp {
+	struct config_group	group;
+} uvcg_camera_grp;
+
+static const struct config_item_type uvcg_camera_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/output/default */
+static struct uvcg_default_output {
+	struct config_group	group;
+} uvcg_default_output;
+
+static inline struct uvcg_default_output
+*to_uvcg_default_output(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_output, group);
+}
+
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_default_output_##cname##_show(			\
+	struct config_item *item, char *page)			\
+{									\
+	struct uvcg_default_output *dout = to_uvcg_default_output(item); \
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex;	\
+	struct uvc_output_terminal_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dout->group.cg_item.ci_parent->ci_parent->		\
+			ci_parent->ci_parent;				\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_output_terminal;				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+									\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_default_output_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_OUTPUT_ATTR
+
+static struct configfs_attribute *uvcg_default_output_attrs[] = {
+	&uvcg_default_output_attr_b_terminal_id,
+	&uvcg_default_output_attr_w_terminal_type,
+	&uvcg_default_output_attr_b_assoc_terminal,
+	&uvcg_default_output_attr_b_source_id,
+	&uvcg_default_output_attr_i_terminal,
+	NULL,
+};
+
+static const struct config_item_type uvcg_default_output_type = {
+	.ct_attrs	= uvcg_default_output_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_output {}; */
+
+/* control/terminal/output */
+static struct uvcg_output_grp {
+	struct config_group	group;
+} uvcg_output_grp;
+
+static const struct config_item_type uvcg_output_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/terminal */
+static struct uvcg_terminal_grp {
+	struct config_group	group;
+} uvcg_terminal_grp;
+
+static const struct config_item_type uvcg_terminal_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/class/{fs} */
+static struct uvcg_control_class {
+	struct config_group	group;
+} uvcg_control_class_fs, uvcg_control_class_ss;
+
+
+static inline struct uvc_descriptor_header
+**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+	struct uvcg_control_class *cl = container_of(to_config_group(i),
+		struct uvcg_control_class, group);
+
+	if (cl == &uvcg_control_class_fs)
+		return o->uvc_fs_control_cls;
+
+	if (cl == &uvcg_control_class_ss)
+		return o->uvc_ss_control_cls;
+
+	return NULL;
+}
+
+static int uvcg_control_class_allow_link(struct config_item *src,
+					 struct config_item *target)
+{
+	struct config_item *control, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header **class_array;
+	struct uvcg_control_header *target_hdr;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	control = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(control), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(control->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = uvcg_get_ctl_class_arr(src, opts);
+	if (!class_array)
+		goto unlock;
+	if (opts->refcnt || class_array[0]) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_control_header(target);
+	++target_hdr->linked;
+	class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static void uvcg_control_class_drop_link(struct config_item *src,
+					struct config_item *target)
+{
+	struct config_item *control, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header **class_array;
+	struct uvcg_control_header *target_hdr;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	control = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(control), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(control->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = uvcg_get_ctl_class_arr(src, opts);
+	if (!class_array || opts->refcnt)
+		goto unlock;
+
+	target_hdr = to_uvcg_control_header(target);
+	--target_hdr->linked;
+	class_array[0] = NULL;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+}
+
+static struct configfs_item_operations uvcg_control_class_item_ops = {
+	.allow_link	= uvcg_control_class_allow_link,
+	.drop_link	= uvcg_control_class_drop_link,
+};
+
+static const struct config_item_type uvcg_control_class_type = {
+	.ct_item_ops	= &uvcg_control_class_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* control/class */
+static struct uvcg_control_class_grp {
+	struct config_group	group;
+} uvcg_control_class_grp;
+
+static const struct config_item_type uvcg_control_class_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control */
+static struct uvcg_control_grp {
+	struct config_group	group;
+} uvcg_control_grp;
+
+static const struct config_item_type uvcg_control_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* streaming/uncompressed */
+static struct uvcg_uncompressed_grp {
+	struct config_group	group;
+} uvcg_uncompressed_grp;
+
+/* streaming/mjpeg */
+static struct uvcg_mjpeg_grp {
+	struct config_group	group;
+} uvcg_mjpeg_grp;
+
+static struct config_item *fmt_parent[] = {
+	&uvcg_uncompressed_grp.group.cg_item,
+	&uvcg_mjpeg_grp.group.cg_item,
+};
+
+enum uvcg_format_type {
+	UVCG_UNCOMPRESSED = 0,
+	UVCG_MJPEG,
+};
+
+struct uvcg_format {
+	struct config_group	group;
+	enum uvcg_format_type	type;
+	unsigned		linked;
+	unsigned		num_frames;
+	__u8			bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+static struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &f->group.cg_subsys->su_mutex;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	result = sprintf(pg, "0x");
+	pg += result;
+	for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) {
+		result += sprintf(pg, "%x\n", f->bmaControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch,
+					      const char *page, size_t len)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	if (ch->linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	if (len < 4 || *page != '0' ||
+	    (*(page + 1) != 'x' && *(page + 1) != 'X'))
+		goto end;
+	ret = hex2bin(ch->bmaControls, page + 2, 1);
+	if (ret < 0)
+		goto end;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+struct uvcg_format_ptr {
+	struct uvcg_format	*fmt;
+	struct list_head	entry;
+};
+
+/* streaming/header/<NAME> */
+struct uvcg_streaming_header {
+	struct config_item				item;
+	struct uvc_input_header_descriptor		desc;
+	unsigned					linked;
+	struct list_head				formats;
+	unsigned					num_fmt;
+};
+
+static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+	return container_of(item, struct uvcg_streaming_header, item);
+}
+
+static int uvcg_streaming_header_allow_link(struct config_item *src,
+					    struct config_item *target)
+{
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct config_item *opts_item;
+	struct f_uvc_opts *opts;
+	struct uvcg_streaming_header *src_hdr;
+	struct uvcg_format *target_fmt = NULL;
+	struct uvcg_format_ptr *format_ptr;
+	int i, ret = -EINVAL;
+
+	src_hdr = to_uvcg_streaming_header(src);
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = src->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+
+	if (src_hdr->linked) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
+		if (target->ci_parent == fmt_parent[i])
+			break;
+	if (i == ARRAY_SIZE(fmt_parent))
+		goto out;
+
+	target_fmt = container_of(to_config_group(target), struct uvcg_format,
+				  group);
+	if (!target_fmt)
+		goto out;
+
+	format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+	if (!format_ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	ret = 0;
+	format_ptr->fmt = target_fmt;
+	list_add_tail(&format_ptr->entry, &src_hdr->formats);
+	++src_hdr->num_fmt;
+
+out:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static void uvcg_streaming_header_drop_link(struct config_item *src,
+					   struct config_item *target)
+{
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct config_item *opts_item;
+	struct f_uvc_opts *opts;
+	struct uvcg_streaming_header *src_hdr;
+	struct uvcg_format *target_fmt = NULL;
+	struct uvcg_format_ptr *format_ptr, *tmp;
+
+	src_hdr = to_uvcg_streaming_header(src);
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = src->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	target_fmt = container_of(to_config_group(target), struct uvcg_format,
+				  group);
+	if (!target_fmt)
+		goto out;
+
+	list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
+		if (format_ptr->fmt == target_fmt) {
+			list_del(&format_ptr->entry);
+			kfree(format_ptr);
+			--src_hdr->num_fmt;
+			break;
+		}
+
+out:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+}
+
+static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+	.allow_link		= uvcg_streaming_header_allow_link,
+	.drop_link		= uvcg_streaming_header_drop_link,
+};
+
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_streaming_header_##cname##_show(			\
+	struct config_item *item, char *page)			\
+{									\
+	struct uvcg_streaming_header *sh = to_uvcg_streaming_header(item); \
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = sh->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(sh->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_streaming_header_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod,
+			   identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_STREAMING_HEADER_ATTR
+
+static struct configfs_attribute *uvcg_streaming_header_attrs[] = {
+	&uvcg_streaming_header_attr_bm_info,
+	&uvcg_streaming_header_attr_b_terminal_link,
+	&uvcg_streaming_header_attr_b_still_capture_method,
+	&uvcg_streaming_header_attr_b_trigger_support,
+	&uvcg_streaming_header_attr_b_trigger_usage,
+	NULL,
+};
+
+static const struct config_item_type uvcg_streaming_header_type = {
+	.ct_item_ops	= &uvcg_streaming_header_item_ops,
+	.ct_attrs	= uvcg_streaming_header_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item
+*uvcg_streaming_header_make(struct config_group *group, const char *name)
+{
+	struct uvcg_streaming_header *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&h->formats);
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_INPUT_HEADER;
+	h->desc.bTerminalLink		= 3;
+	h->desc.bControlSize		= UVCG_STREAMING_CONTROL_SIZE;
+
+	config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type);
+
+	return &h->item;
+}
+
+static void uvcg_streaming_header_drop(struct config_group *group,
+			      struct config_item *item)
+{
+	struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
+
+	kfree(h);
+}
+
+/* streaming/header */
+static struct uvcg_streaming_header_grp {
+	struct config_group	group;
+} uvcg_streaming_header_grp;
+
+static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+	.make_item		= uvcg_streaming_header_make,
+	.drop_item		= uvcg_streaming_header_drop,
+};
+
+static const struct config_item_type uvcg_streaming_header_grp_type = {
+	.ct_group_ops	= &uvcg_streaming_header_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/<mode>/<format>/<NAME> */
+struct uvcg_frame {
+	struct {
+		u8	b_length;
+		u8	b_descriptor_type;
+		u8	b_descriptor_subtype;
+		u8	b_frame_index;
+		u8	bm_capabilities;
+		u16	w_width;
+		u16	w_height;
+		u32	dw_min_bit_rate;
+		u32	dw_max_bit_rate;
+		u32	dw_max_video_frame_buffer_size;
+		u32	dw_default_frame_interval;
+		u8	b_frame_interval_type;
+	} __attribute__((packed)) frame;
+	u32 *dw_frame_interval;
+	enum uvcg_format_type	fmt_type;
+	struct config_item	item;
+};
+
+static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+	return container_of(item, struct uvcg_frame, item);
+}
+
+#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
+static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\
+{									\
+	struct uvcg_frame *f = to_uvcg_frame(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname));	\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t  uvcg_frame_##cname##_store(struct config_item *item,	\
+					   const char *page, size_t len)\
+{									\
+	struct uvcg_frame *f = to_uvcg_frame(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct uvcg_format *fmt;					\
+	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+	int ret;							\
+	u##bits num;							\
+									\
+	ret = kstrtou##bits(page, 0, &num);				\
+	if (ret)							\
+		return ret;						\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	fmt = to_uvcg_format(f->item.ci_parent);			\
+									\
+	mutex_lock(&opts->lock);					\
+	if (fmt->linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	f->frame.cname = to_little_endian(num);				\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_frame_, cname, aname);
+
+#define noop_conversion(x) (x)
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
+		noop_conversion, 8);
+UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+		le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+		le32_to_cpu, cpu_to_le32, 32);
+
+#undef noop_conversion
+
+#undef UVCG_FRAME_ATTR
+
+static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item,
+						 char *page)
+{
+	struct uvcg_frame *frm = to_uvcg_frame(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
+		result += sprintf(pg, "%d\n",
+				  le32_to_cpu(frm->dw_frame_interval[i]));
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+static inline int __uvcg_count_frm_intrv(char *buf, void *priv)
+{
+	++*((int *)priv);
+	return 0;
+}
+
+static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
+{
+	u32 num, **interv;
+	int ret;
+
+	ret = kstrtou32(buf, 0, &num);
+	if (ret)
+		return ret;
+
+	interv = priv;
+	**interv = cpu_to_le32(num);
+	++*interv;
+
+	return 0;
+}
+
+static int __uvcg_iter_frm_intrv(const char *page, size_t len,
+				 int (*fun)(char *, void *), void *priv)
+{
+	/* sign, base 2 representation, newline, terminator */
+	char buf[1 + sizeof(u32) * 8 + 1 + 1];
+	const char *pg = page;
+	int i, ret;
+
+	if (!fun)
+		return -EINVAL;
+
+	while (pg - page < len) {
+		i = 0;
+		while (i < sizeof(buf) && (pg - page < len) &&
+				*pg != '\0' && *pg != '\n')
+			buf[i++] = *pg++;
+		if (i == sizeof(buf))
+			return -EINVAL;
+		while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
+			++pg;
+		buf[i] = '\0';
+		ret = fun(buf, priv);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item,
+						  const char *page, size_t len)
+{
+	struct uvcg_frame *ch = to_uvcg_frame(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct uvcg_format *fmt;
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
+	int ret = 0, n = 0;
+	u32 *frm_intrv, *tmp;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	fmt = to_uvcg_format(ch->item.ci_parent);
+
+	mutex_lock(&opts->lock);
+	if (fmt->linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
+	if (ret)
+		goto end;
+
+	tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
+	if (!frm_intrv) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
+	if (ret) {
+		kfree(frm_intrv);
+		goto end;
+	}
+
+	kfree(ch->dw_frame_interval);
+	ch->dw_frame_interval = frm_intrv;
+	ch->frame.b_frame_interval_type = n;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval);
+
+static struct configfs_attribute *uvcg_frame_attrs[] = {
+	&uvcg_frame_attr_bm_capabilities,
+	&uvcg_frame_attr_w_width,
+	&uvcg_frame_attr_w_height,
+	&uvcg_frame_attr_dw_min_bit_rate,
+	&uvcg_frame_attr_dw_max_bit_rate,
+	&uvcg_frame_attr_dw_max_video_frame_buffer_size,
+	&uvcg_frame_attr_dw_default_frame_interval,
+	&uvcg_frame_attr_dw_frame_interval,
+	NULL,
+};
+
+static const struct config_item_type uvcg_frame_type = {
+	.ct_attrs	= uvcg_frame_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item *uvcg_frame_make(struct config_group *group,
+					   const char *name)
+{
+	struct uvcg_frame *h;
+	struct uvcg_format *fmt;
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->frame.b_descriptor_type		= USB_DT_CS_INTERFACE;
+	h->frame.b_frame_index			= 1;
+	h->frame.w_width			= cpu_to_le16(640);
+	h->frame.w_height			= cpu_to_le16(360);
+	h->frame.dw_min_bit_rate		= cpu_to_le32(18432000);
+	h->frame.dw_max_bit_rate		= cpu_to_le32(55296000);
+	h->frame.dw_max_video_frame_buffer_size	= cpu_to_le32(460800);
+	h->frame.dw_default_frame_interval	= cpu_to_le32(666666);
+
+	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	fmt = to_uvcg_format(&group->cg_item);
+	if (fmt->type == UVCG_UNCOMPRESSED) {
+		h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+		h->fmt_type = UVCG_UNCOMPRESSED;
+	} else if (fmt->type == UVCG_MJPEG) {
+		h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+		h->fmt_type = UVCG_MJPEG;
+	} else {
+		mutex_unlock(&opts->lock);
+		kfree(h);
+		return ERR_PTR(-EINVAL);
+	}
+	++fmt->num_frames;
+	mutex_unlock(&opts->lock);
+
+	config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+
+	return &h->item;
+}
+
+static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
+{
+	struct uvcg_frame *h = to_uvcg_frame(item);
+	struct uvcg_format *fmt;
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+
+	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	fmt = to_uvcg_format(&group->cg_item);
+	--fmt->num_frames;
+	kfree(h);
+	mutex_unlock(&opts->lock);
+}
+
+/* streaming/uncompressed/<NAME> */
+struct uvcg_uncompressed {
+	struct uvcg_format		fmt;
+	struct uvc_format_uncompressed	desc;
+};
+
+static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_uncompressed, fmt);
+}
+
+static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+static ssize_t uvcg_uncompressed_guid_format_show(struct config_item *item,
+							char *page)
+{
+	struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+
+	return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item,
+						   const char *page, size_t len)
+{
+	struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+	int ret;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	if (ch->fmt.linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	memcpy(ch->desc.guidFormat, page,
+	       min(sizeof(ch->desc.guidFormat), len));
+	ret = sizeof(ch->desc.guidFormat);
+
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+UVC_ATTR(uvcg_uncompressed_, guid_format, guidFormat);
+
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv)			\
+static ssize_t uvcg_uncompressed_##cname##_show(			\
+	struct config_item *item, char *page)				\
+{									\
+	struct uvcg_uncompressed *u = to_uvcg_uncompressed(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_uncompressed_, cname, aname);
+
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_uncompressed_##cname##_show(			\
+	struct config_item *item, char *page)				\
+{									\
+	struct uvcg_uncompressed *u = to_uvcg_uncompressed(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_uncompressed_##cname##_store(struct config_item *item,		\
+				    const char *page, size_t len)	\
+{									\
+	struct uvcg_uncompressed *u = to_uvcg_uncompressed(item);	\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_uncompressed_, cname, aname);
+
+#define identity_conv(x) (x)
+
+UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv);
+UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex,
+		       identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_UNCOMPRESSED_ATTR
+#undef UVCG_UNCOMPRESSED_ATTR_RO
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_show(struct config_item *item, char *page)
+{
+	struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item);
+	return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_store(struct config_item *item,
+				     const char *page, size_t len)
+{
+	struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item);
+	return uvcg_format_bma_controls_store(&unc->fmt, page, len);
+}
+
+UVC_ATTR(uvcg_uncompressed_, bma_controls, bmaControls);
+
+static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+	&uvcg_uncompressed_attr_guid_format,
+	&uvcg_uncompressed_attr_b_bits_per_pixel,
+	&uvcg_uncompressed_attr_b_default_frame_index,
+	&uvcg_uncompressed_attr_b_aspect_ratio_x,
+	&uvcg_uncompressed_attr_b_aspect_ratio_y,
+	&uvcg_uncompressed_attr_bm_interface_flags,
+	&uvcg_uncompressed_attr_bma_controls,
+	NULL,
+};
+
+static const struct config_item_type uvcg_uncompressed_type = {
+	.ct_group_ops	= &uvcg_uncompressed_group_ops,
+	.ct_attrs	= uvcg_uncompressed_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_uncompressed_make(struct config_group *group,
+						   const char *name)
+{
+	static char guid[] = {
+		'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
+		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+	};
+	struct uvcg_uncompressed *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_FORMAT_UNCOMPRESSED_SIZE;
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_UNCOMPRESSED;
+	memcpy(h->desc.guidFormat, guid, sizeof(guid));
+	h->desc.bBitsPerPixel		= 16;
+	h->desc.bDefaultFrameIndex	= 1;
+	h->desc.bAspectRatioX		= 0;
+	h->desc.bAspectRatioY		= 0;
+	h->desc.bmInterfaceFlags	= 0;
+	h->desc.bCopyProtect		= 0;
+
+	h->fmt.type = UVCG_UNCOMPRESSED;
+	config_group_init_type_name(&h->fmt.group, name,
+				    &uvcg_uncompressed_type);
+
+	return &h->fmt.group;
+}
+
+static void uvcg_uncompressed_drop(struct config_group *group,
+			    struct config_item *item)
+{
+	struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
+
+	kfree(h);
+}
+
+static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+	.make_group		= uvcg_uncompressed_make,
+	.drop_item		= uvcg_uncompressed_drop,
+};
+
+static const struct config_item_type uvcg_uncompressed_grp_type = {
+	.ct_group_ops	= &uvcg_uncompressed_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/mjpeg/<NAME> */
+struct uvcg_mjpeg {
+	struct uvcg_format		fmt;
+	struct uvc_format_mjpeg		desc;
+};
+
+static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_mjpeg, fmt);
+}
+
+static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+#define UVCG_MJPEG_ATTR_RO(cname, aname, conv)				\
+static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
+{									\
+	struct uvcg_mjpeg *u = to_uvcg_mjpeg(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_mjpeg_, cname, aname)
+
+#define UVCG_MJPEG_ATTR(cname, aname, conv)				\
+static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
+{									\
+	struct uvcg_mjpeg *u = to_uvcg_mjpeg(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_mjpeg_##cname##_store(struct config_item *item,			\
+			   const char *page, size_t len)		\
+{									\
+	struct uvcg_mjpeg *u = to_uvcg_mjpeg(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_mjpeg_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex,
+		       identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_MJPEG_ATTR
+#undef UVCG_MJPEG_ATTR_RO
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_show(struct config_item *item, char *page)
+{
+	struct uvcg_mjpeg *u = to_uvcg_mjpeg(item);
+	return uvcg_format_bma_controls_show(&u->fmt, page);
+}
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_store(struct config_item *item,
+				     const char *page, size_t len)
+{
+	struct uvcg_mjpeg *u = to_uvcg_mjpeg(item);
+	return uvcg_format_bma_controls_store(&u->fmt, page, len);
+}
+
+UVC_ATTR(uvcg_mjpeg_, bma_controls, bmaControls);
+
+static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+	&uvcg_mjpeg_attr_b_default_frame_index,
+	&uvcg_mjpeg_attr_bm_flags,
+	&uvcg_mjpeg_attr_b_aspect_ratio_x,
+	&uvcg_mjpeg_attr_b_aspect_ratio_y,
+	&uvcg_mjpeg_attr_bm_interface_flags,
+	&uvcg_mjpeg_attr_bma_controls,
+	NULL,
+};
+
+static const struct config_item_type uvcg_mjpeg_type = {
+	.ct_group_ops	= &uvcg_mjpeg_group_ops,
+	.ct_attrs	= uvcg_mjpeg_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_mjpeg_make(struct config_group *group,
+						   const char *name)
+{
+	struct uvcg_mjpeg *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_FORMAT_MJPEG_SIZE;
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_MJPEG;
+	h->desc.bDefaultFrameIndex	= 1;
+	h->desc.bAspectRatioX		= 0;
+	h->desc.bAspectRatioY		= 0;
+	h->desc.bmInterfaceFlags	= 0;
+	h->desc.bCopyProtect		= 0;
+
+	h->fmt.type = UVCG_MJPEG;
+	config_group_init_type_name(&h->fmt.group, name,
+				    &uvcg_mjpeg_type);
+
+	return &h->fmt.group;
+}
+
+static void uvcg_mjpeg_drop(struct config_group *group,
+			    struct config_item *item)
+{
+	struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
+
+	kfree(h);
+}
+
+static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+	.make_group		= uvcg_mjpeg_make,
+	.drop_item		= uvcg_mjpeg_drop,
+};
+
+static const struct config_item_type uvcg_mjpeg_grp_type = {
+	.ct_group_ops	= &uvcg_mjpeg_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/color_matching/default */
+static struct uvcg_default_color_matching {
+	struct config_group	group;
+} uvcg_default_color_matching;
+
+static inline struct uvcg_default_color_matching
+*to_uvcg_default_color_matching(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_color_matching, group);
+}
+
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv)		\
+static ssize_t uvcg_default_color_matching_##cname##_show(		\
+	struct config_item *item, char *page)		\
+{									\
+	struct uvcg_default_color_matching *dc =			\
+		to_uvcg_default_color_matching(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;	\
+	struct uvc_color_matching_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_color_matching;					\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_default_color_matching_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
+				 identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics,
+				 bTransferCharacteristics, identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients,
+				 identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
+
+static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
+	&uvcg_default_color_matching_attr_b_color_primaries,
+	&uvcg_default_color_matching_attr_b_transfer_characteristics,
+	&uvcg_default_color_matching_attr_b_matrix_coefficients,
+	NULL,
+};
+
+static const struct config_item_type uvcg_default_color_matching_type = {
+	.ct_attrs	= uvcg_default_color_matching_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_color_matching {}; */
+
+/* streaming/color_matching */
+static struct uvcg_color_matching_grp {
+	struct config_group	group;
+} uvcg_color_matching_grp;
+
+static const struct config_item_type uvcg_color_matching_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* streaming/class/{fs|hs|ss} */
+static struct uvcg_streaming_class {
+	struct config_group	group;
+} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss;
+
+
+static inline struct uvc_descriptor_header
+***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+	struct uvcg_streaming_class *cl = container_of(to_config_group(i),
+		struct uvcg_streaming_class, group);
+
+	if (cl == &uvcg_streaming_class_fs)
+		return &o->uvc_fs_streaming_cls;
+
+	if (cl == &uvcg_streaming_class_hs)
+		return &o->uvc_hs_streaming_cls;
+
+	if (cl == &uvcg_streaming_class_ss)
+		return &o->uvc_ss_streaming_cls;
+
+	return NULL;
+}
+
+enum uvcg_strm_type {
+	UVCG_HEADER = 0,
+	UVCG_FORMAT,
+	UVCG_FRAME
+};
+
+/*
+ * Iterate over a hierarchy of streaming descriptors' config items.
+ * The items are created by the user with configfs.
+ *
+ * It "processes" the header pointed to by @priv1, then for each format
+ * that follows the header "processes" the format itself and then for
+ * each frame inside a format "processes" the frame.
+ *
+ * As a "processing" function the @fun is used.
+ *
+ * __uvcg_iter_strm_cls() is used in two context: first, to calculate
+ * the amount of memory needed for an array of streaming descriptors
+ * and second, to actually fill the array.
+ *
+ * @h: streaming header pointer
+ * @priv2: an "inout" parameter (the caller might want to see the changes to it)
+ * @priv3: an "inout" parameter (the caller might want to see the changes to it)
+ * @fun: callback function for processing each level of the hierarchy
+ */
+static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h,
+	void *priv2, void *priv3,
+	int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type))
+{
+	struct uvcg_format_ptr *f;
+	struct config_group *grp;
+	struct config_item *item;
+	struct uvcg_frame *frm;
+	int ret, i, j;
+
+	if (!fun)
+		return -EINVAL;
+
+	i = j = 0;
+	ret = fun(h, priv2, priv3, 0, UVCG_HEADER);
+	if (ret)
+		return ret;
+	list_for_each_entry(f, &h->formats, entry) {
+		ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT);
+		if (ret)
+			return ret;
+		grp = &f->fmt->group;
+		list_for_each_entry(item, &grp->cg_children, ci_entry) {
+			frm = to_uvcg_frame(item);
+			ret = fun(frm, priv2, priv3, j++, UVCG_FRAME);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Count how many bytes are needed for an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, accumulated size of the array
+ * @priv3: inout parameter, accumulated number of the array elements
+ * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls
+ */
+static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
+			   enum uvcg_strm_type type)
+{
+	size_t *size = priv2;
+	size_t *count = priv3;
+
+	switch (type) {
+	case UVCG_HEADER: {
+		struct uvcg_streaming_header *h = priv1;
+
+		*size += sizeof(h->desc);
+		/* bmaControls */
+		*size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE;
+	}
+	break;
+	case UVCG_FORMAT: {
+		struct uvcg_format *fmt = priv1;
+
+		if (fmt->type == UVCG_UNCOMPRESSED) {
+			struct uvcg_uncompressed *u =
+				container_of(fmt, struct uvcg_uncompressed,
+					     fmt);
+
+			*size += sizeof(u->desc);
+		} else if (fmt->type == UVCG_MJPEG) {
+			struct uvcg_mjpeg *m =
+				container_of(fmt, struct uvcg_mjpeg, fmt);
+
+			*size += sizeof(m->desc);
+		} else {
+			return -EINVAL;
+		}
+	}
+	break;
+	case UVCG_FRAME: {
+		struct uvcg_frame *frm = priv1;
+		int sz = sizeof(frm->dw_frame_interval);
+
+		*size += sizeof(frm->frame);
+		*size += frm->frame.b_frame_interval_type * sz;
+	}
+	break;
+	}
+
+	++*count;
+
+	return 0;
+}
+
+/*
+ * Fill an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, pointer into a block of memory
+ * @priv3: inout parameter, pointer to a 2-dimensional array
+ */
+static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
+			    enum uvcg_strm_type type)
+{
+	void **dest = priv2;
+	struct uvc_descriptor_header ***array = priv3;
+	size_t sz;
+
+	**array = *dest;
+	++*array;
+
+	switch (type) {
+	case UVCG_HEADER: {
+		struct uvc_input_header_descriptor *ihdr = *dest;
+		struct uvcg_streaming_header *h = priv1;
+		struct uvcg_format_ptr *f;
+
+		memcpy(*dest, &h->desc, sizeof(h->desc));
+		*dest += sizeof(h->desc);
+		sz = UVCG_STREAMING_CONTROL_SIZE;
+		list_for_each_entry(f, &h->formats, entry) {
+			memcpy(*dest, f->fmt->bmaControls, sz);
+			*dest += sz;
+		}
+		ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz;
+		ihdr->bNumFormats = h->num_fmt;
+	}
+	break;
+	case UVCG_FORMAT: {
+		struct uvcg_format *fmt = priv1;
+
+		if (fmt->type == UVCG_UNCOMPRESSED) {
+			struct uvc_format_uncompressed *unc = *dest;
+			struct uvcg_uncompressed *u =
+				container_of(fmt, struct uvcg_uncompressed,
+					     fmt);
+
+			memcpy(*dest, &u->desc, sizeof(u->desc));
+			*dest += sizeof(u->desc);
+			unc->bNumFrameDescriptors = fmt->num_frames;
+			unc->bFormatIndex = n + 1;
+		} else if (fmt->type == UVCG_MJPEG) {
+			struct uvc_format_mjpeg *mjp = *dest;
+			struct uvcg_mjpeg *m =
+				container_of(fmt, struct uvcg_mjpeg, fmt);
+
+			memcpy(*dest, &m->desc, sizeof(m->desc));
+			*dest += sizeof(m->desc);
+			mjp->bNumFrameDescriptors = fmt->num_frames;
+			mjp->bFormatIndex = n + 1;
+		} else {
+			return -EINVAL;
+		}
+	}
+	break;
+	case UVCG_FRAME: {
+		struct uvcg_frame *frm = priv1;
+		struct uvc_descriptor_header *h = *dest;
+
+		sz = sizeof(frm->frame);
+		memcpy(*dest, &frm->frame, sz);
+		*dest += sz;
+		sz = frm->frame.b_frame_interval_type *
+			sizeof(*frm->dw_frame_interval);
+		memcpy(*dest, frm->dw_frame_interval, sz);
+		*dest += sz;
+		if (frm->fmt_type == UVCG_UNCOMPRESSED)
+			h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+				frm->frame.b_frame_interval_type);
+		else if (frm->fmt_type == UVCG_MJPEG)
+			h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+				frm->frame.b_frame_interval_type);
+	}
+	break;
+	}
+
+	return 0;
+}
+
+static int uvcg_streaming_class_allow_link(struct config_item *src,
+					   struct config_item *target)
+{
+	struct config_item *streaming, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header ***class_array, **cl_arr;
+	struct uvcg_streaming_header *target_hdr;
+	void *data, *data_save;
+	size_t size = 0, count = 0;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	streaming = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(streaming), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(streaming->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = __uvcg_get_stream_class_arr(src, opts);
+	if (!class_array || *class_array || opts->refcnt) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_streaming_header(target);
+	ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm);
+	if (ret)
+		goto unlock;
+
+	count += 2; /* color_matching, NULL */
+	*class_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
+	if (!*class_array) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	data = data_save = kzalloc(size, GFP_KERNEL);
+	if (!data) {
+		kfree(*class_array);
+		*class_array = NULL;
+		ret = -ENOMEM;
+		goto unlock;
+	}
+	cl_arr = *class_array;
+	ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr,
+				   __uvcg_fill_strm);
+	if (ret) {
+		kfree(*class_array);
+		*class_array = NULL;
+		/*
+		 * __uvcg_fill_strm() called from __uvcg_iter_stream_cls()
+		 * might have advanced the "data", so use a backup copy
+		 */
+		kfree(data_save);
+		goto unlock;
+	}
+	*cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching;
+
+	++target_hdr->linked;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static void uvcg_streaming_class_drop_link(struct config_item *src,
+					  struct config_item *target)
+{
+	struct config_item *streaming, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header ***class_array;
+	struct uvcg_streaming_header *target_hdr;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	streaming = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(streaming), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(streaming->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = __uvcg_get_stream_class_arr(src, opts);
+	if (!class_array || !*class_array)
+		goto unlock;
+
+	if (opts->refcnt)
+		goto unlock;
+
+	target_hdr = to_uvcg_streaming_header(target);
+	--target_hdr->linked;
+	kfree(**class_array);
+	kfree(*class_array);
+	*class_array = NULL;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+}
+
+static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+	.allow_link	= uvcg_streaming_class_allow_link,
+	.drop_link	= uvcg_streaming_class_drop_link,
+};
+
+static const struct config_item_type uvcg_streaming_class_type = {
+	.ct_item_ops	= &uvcg_streaming_class_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/class */
+static struct uvcg_streaming_class_grp {
+	struct config_group	group;
+} uvcg_streaming_class_grp;
+
+static const struct config_item_type uvcg_streaming_class_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* streaming */
+static struct uvcg_streaming_grp {
+	struct config_group	group;
+} uvcg_streaming_grp;
+
+static const struct config_item_type uvcg_streaming_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static void uvc_attr_release(struct config_item *item)
+{
+	struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+	.release		= uvc_attr_release,
+};
+
+#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit)	\
+static ssize_t f_uvc_opts_##cname##_show(				\
+	struct config_item *item, char *page)				\
+{									\
+	struct f_uvc_opts *opts = to_f_uvc_opts(item);			\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(opts->cname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+f_uvc_opts_##cname##_store(struct config_item *item,			\
+			   const char *page, size_t len)		\
+{									\
+	struct f_uvc_opts *opts = to_f_uvc_opts(item);			\
+	int ret;							\
+	uxx num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = str2u(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > limit) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	opts->cname = vnoc(num);					\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(f_uvc_opts_, cname, cname)
+
+#define identity_conv(x) (x)
+
+UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv,
+	       kstrtou8, u8, identity_conv, 16);
+UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu,
+	       kstrtou16, u16, le16_to_cpu, 3072);
+UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv,
+	       kstrtou8, u8, identity_conv, 15);
+
+#undef identity_conv
+
+#undef UVCG_OPTS_ATTR
+
+static struct configfs_attribute *uvc_attrs[] = {
+	&f_uvc_opts_attr_streaming_interval,
+	&f_uvc_opts_attr_streaming_maxpacket,
+	&f_uvc_opts_attr_streaming_maxburst,
+	NULL,
+};
+
+static const struct config_item_type uvc_func_type = {
+	.ct_item_ops	= &uvc_item_ops,
+	.ct_attrs	= uvc_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts)
+{
+	config_group_init_type_name(&uvcg_control_header_grp.group,
+				    "header",
+				    &uvcg_control_header_grp_type);
+
+	config_group_init_type_name(&uvcg_default_processing.group,
+			"default", &uvcg_default_processing_type);
+	config_group_init_type_name(&uvcg_processing_grp.group,
+			"processing", &uvcg_processing_grp_type);
+	configfs_add_default_group(&uvcg_default_processing.group,
+			&uvcg_processing_grp.group);
+
+	config_group_init_type_name(&uvcg_default_camera.group,
+			"default", &uvcg_default_camera_type);
+	config_group_init_type_name(&uvcg_camera_grp.group,
+			"camera", &uvcg_camera_grp_type);
+	configfs_add_default_group(&uvcg_default_camera.group,
+			&uvcg_camera_grp.group);
+
+	config_group_init_type_name(&uvcg_default_output.group,
+			"default", &uvcg_default_output_type);
+	config_group_init_type_name(&uvcg_output_grp.group,
+			"output", &uvcg_output_grp_type);
+	configfs_add_default_group(&uvcg_default_output.group,
+			&uvcg_output_grp.group);
+
+	config_group_init_type_name(&uvcg_terminal_grp.group,
+			"terminal", &uvcg_terminal_grp_type);
+	configfs_add_default_group(&uvcg_camera_grp.group,
+			&uvcg_terminal_grp.group);
+	configfs_add_default_group(&uvcg_output_grp.group,
+			&uvcg_terminal_grp.group);
+
+	config_group_init_type_name(&uvcg_control_class_fs.group,
+			"fs", &uvcg_control_class_type);
+	config_group_init_type_name(&uvcg_control_class_ss.group,
+			"ss", &uvcg_control_class_type);
+	config_group_init_type_name(&uvcg_control_class_grp.group,
+			"class",
+			&uvcg_control_class_grp_type);
+	configfs_add_default_group(&uvcg_control_class_fs.group,
+			&uvcg_control_class_grp.group);
+	configfs_add_default_group(&uvcg_control_class_ss.group,
+			&uvcg_control_class_grp.group);
+
+	config_group_init_type_name(&uvcg_control_grp.group,
+			"control",
+			&uvcg_control_grp_type);
+	configfs_add_default_group(&uvcg_control_header_grp.group,
+			&uvcg_control_grp.group);
+	configfs_add_default_group(&uvcg_processing_grp.group,
+			&uvcg_control_grp.group);
+	configfs_add_default_group(&uvcg_terminal_grp.group,
+			&uvcg_control_grp.group);
+	configfs_add_default_group(&uvcg_control_class_grp.group,
+			&uvcg_control_grp.group);
+
+	config_group_init_type_name(&uvcg_streaming_header_grp.group,
+				    "header",
+				    &uvcg_streaming_header_grp_type);
+	config_group_init_type_name(&uvcg_uncompressed_grp.group,
+				    "uncompressed",
+				    &uvcg_uncompressed_grp_type);
+	config_group_init_type_name(&uvcg_mjpeg_grp.group,
+				    "mjpeg",
+				    &uvcg_mjpeg_grp_type);
+	config_group_init_type_name(&uvcg_default_color_matching.group,
+				    "default",
+				    &uvcg_default_color_matching_type);
+	config_group_init_type_name(&uvcg_color_matching_grp.group,
+			"color_matching",
+			&uvcg_color_matching_grp_type);
+	configfs_add_default_group(&uvcg_default_color_matching.group,
+			&uvcg_color_matching_grp.group);
+
+	config_group_init_type_name(&uvcg_streaming_class_fs.group,
+			"fs", &uvcg_streaming_class_type);
+	config_group_init_type_name(&uvcg_streaming_class_hs.group,
+			"hs", &uvcg_streaming_class_type);
+	config_group_init_type_name(&uvcg_streaming_class_ss.group,
+			"ss", &uvcg_streaming_class_type);
+	config_group_init_type_name(&uvcg_streaming_class_grp.group,
+			"class", &uvcg_streaming_class_grp_type);
+	configfs_add_default_group(&uvcg_streaming_class_fs.group,
+			&uvcg_streaming_class_grp.group);
+	configfs_add_default_group(&uvcg_streaming_class_hs.group,
+			&uvcg_streaming_class_grp.group);
+	configfs_add_default_group(&uvcg_streaming_class_ss.group,
+			&uvcg_streaming_class_grp.group);
+
+	config_group_init_type_name(&uvcg_streaming_grp.group,
+			"streaming", &uvcg_streaming_grp_type);
+	configfs_add_default_group(&uvcg_streaming_header_grp.group,
+			&uvcg_streaming_grp.group);
+	configfs_add_default_group(&uvcg_uncompressed_grp.group,
+			&uvcg_streaming_grp.group);
+	configfs_add_default_group(&uvcg_mjpeg_grp.group,
+			&uvcg_streaming_grp.group);
+	configfs_add_default_group(&uvcg_color_matching_grp.group,
+			&uvcg_streaming_grp.group);
+	configfs_add_default_group(&uvcg_streaming_class_grp.group,
+			&uvcg_streaming_grp.group);
+
+	config_group_init_type_name(&opts->func_inst.group,
+			"",
+			&uvc_func_type);
+	configfs_add_default_group(&uvcg_control_grp.group,
+			&opts->func_inst.group);
+	configfs_add_default_group(&uvcg_streaming_grp.group,
+			&opts->func_inst.group);
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
new file mode 100644
index 0000000..8549c0b
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * uvc_configfs.h
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+#ifndef UVC_CONFIGFS_H
+#define UVC_CONFIGFS_H
+
+struct f_uvc_opts;
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts);
+
+#endif /* UVC_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
new file mode 100644
index 0000000..9e33d52
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	uvc_queue.c  --  USB Video Class driver - Buffers management
+ *
+ *	Copyright (C) 2005-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-common.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "uvc.h"
+
+/* ------------------------------------------------------------------------
+ * Video buffers queue management.
+ *
+ * Video queues is initialized by uvcg_queue_init(). The function performs
+ * basic initialization of the uvc_video_queue struct and never fails.
+ *
+ * Video buffers are managed by videobuf2. The driver uses a mutex to protect
+ * the videobuf2 queue operations by serializing calls to videobuf2 and a
+ * spinlock to protect the IRQ queue that holds the buffers to be processed by
+ * the driver.
+ */
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
+ */
+
+static int uvc_queue_setup(struct vb2_queue *vq,
+			   unsigned int *nbuffers, unsigned int *nplanes,
+			   unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+	struct uvc_video *video = container_of(queue, struct uvc_video, queue);
+
+	if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
+		*nbuffers = UVC_MAX_VIDEO_BUFFERS;
+
+	*nplanes = 1;
+
+	sizes[0] = video->imagesize;
+
+	return 0;
+}
+
+static int uvc_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf);
+
+	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+		return -EINVAL;
+	}
+
+	if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
+		return -ENODEV;
+
+	buf->state = UVC_BUF_STATE_QUEUED;
+	buf->mem = vb2_plane_vaddr(vb, 0);
+	buf->length = vb2_plane_size(vb, 0);
+	if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		buf->bytesused = 0;
+	else
+		buf->bytesused = vb2_get_plane_payload(vb, 0);
+
+	return 0;
+}
+
+static void uvc_buffer_queue(struct vb2_buffer *vb)
+{
+	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf);
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->irqlock, flags);
+
+	if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+		list_add_tail(&buf->queue, &queue->irqqueue);
+	} else {
+		/* If the device is disconnected return the buffer to userspace
+		 * directly. The next QBUF call will fail with -ENODEV.
+		 */
+		buf->state = UVC_BUF_STATE_ERROR;
+		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+	}
+
+	spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+static struct vb2_ops uvc_queue_qops = {
+	.queue_setup = uvc_queue_setup,
+	.buf_prepare = uvc_buffer_prepare,
+	.buf_queue = uvc_buffer_queue,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+};
+
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+		    struct mutex *lock)
+{
+	int ret;
+
+	queue->queue.type = type;
+	queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	queue->queue.drv_priv = queue;
+	queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
+	queue->queue.ops = &uvc_queue_qops;
+	queue->queue.lock = lock;
+	queue->queue.mem_ops = &vb2_vmalloc_memops;
+	queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+				     | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+	ret = vb2_queue_init(&queue->queue);
+	if (ret)
+		return ret;
+
+	spin_lock_init(&queue->irqlock);
+	INIT_LIST_HEAD(&queue->irqqueue);
+	queue->flags = 0;
+
+	return 0;
+}
+
+/*
+ * Free the video buffers.
+ */
+void uvcg_free_buffers(struct uvc_video_queue *queue)
+{
+	vb2_queue_release(&queue->queue);
+}
+
+/*
+ * Allocate the video buffers.
+ */
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+			      struct v4l2_requestbuffers *rb)
+{
+	int ret;
+
+	ret = vb2_reqbufs(&queue->queue, rb);
+
+	return ret ? ret : rb->count;
+}
+
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
+{
+	return vb2_querybuf(&queue->queue, buf);
+}
+
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
+{
+	unsigned long flags;
+	int ret;
+
+	ret = vb2_qbuf(&queue->queue, buf);
+	if (ret < 0)
+		return ret;
+
+	spin_lock_irqsave(&queue->irqlock, flags);
+	ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
+	queue->flags &= ~UVC_QUEUE_PAUSED;
+	spin_unlock_irqrestore(&queue->irqlock, flags);
+	return ret;
+}
+
+/*
+ * Dequeue a video buffer. If nonblocking is false, block until a buffer is
+ * available.
+ */
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
+			int nonblocking)
+{
+	return vb2_dqbuf(&queue->queue, buf, nonblocking);
+}
+
+/*
+ * Poll the video queue.
+ *
+ * This function implements video queue polling and is intended to be used by
+ * the device poll handler.
+ */
+__poll_t uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file,
+			     poll_table *wait)
+{
+	return vb2_poll(&queue->queue, file, wait);
+}
+
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
+{
+	return vb2_mmap(&queue->queue, vma);
+}
+
+#ifndef CONFIG_MMU
+/*
+ * Get unmapped area.
+ *
+ * NO-MMU arch need this function to make mmap() work correctly.
+ */
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+					   unsigned long pgoff)
+{
+	return vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
+}
+#endif
+
+/*
+ * Cancel the video buffers queue.
+ *
+ * Cancelling the queue marks all buffers on the irq queue as erroneous,
+ * wakes them up and removes them from the queue.
+ *
+ * If the disconnect parameter is set, further calls to uvc_queue_buffer will
+ * fail with -ENODEV.
+ *
+ * This function acquires the irq spinlock and can be called from interrupt
+ * context.
+ */
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+{
+	struct uvc_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->irqlock, flags);
+	while (!list_empty(&queue->irqqueue)) {
+		buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+				       queue);
+		list_del(&buf->queue);
+		buf->state = UVC_BUF_STATE_ERROR;
+		vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	/* This must be protected by the irqlock spinlock to avoid race
+	 * conditions between uvc_queue_buffer and the disconnection event that
+	 * could result in an interruptible wait in uvc_dequeue_buffer. Do not
+	 * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
+	 * state outside the queue code.
+	 */
+	if (disconnect)
+		queue->flags |= UVC_QUEUE_DISCONNECTED;
+	spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+/*
+ * Enable or disable the video buffers queue.
+ *
+ * The queue must be enabled before starting video acquisition and must be
+ * disabled after stopping it. This ensures that the video buffers queue
+ * state can be properly initialized before buffers are accessed from the
+ * interrupt handler.
+ *
+ * Enabling the video queue initializes parameters (such as sequence number,
+ * sync pattern, ...). If the queue is already enabled, return -EBUSY.
+ *
+ * Disabling the video queue cancels the queue and removes all buffers from
+ * the main queue.
+ *
+ * This function can't be called from interrupt context. Use
+ * uvcg_queue_cancel() instead.
+ */
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	if (enable) {
+		ret = vb2_streamon(&queue->queue, queue->queue.type);
+		if (ret < 0)
+			return ret;
+
+		queue->sequence = 0;
+		queue->buf_used = 0;
+	} else {
+		ret = vb2_streamoff(&queue->queue, queue->queue.type);
+		if (ret < 0)
+			return ret;
+
+		spin_lock_irqsave(&queue->irqlock, flags);
+		INIT_LIST_HEAD(&queue->irqqueue);
+
+		/*
+		 * FIXME: We need to clear the DISCONNECTED flag to ensure that
+		 * applications will be able to queue buffers for the next
+		 * streaming run. However, clearing it here doesn't guarantee
+		 * that the device will be reconnected in the meantime.
+		 */
+		queue->flags &= ~UVC_QUEUE_DISCONNECTED;
+		spin_unlock_irqrestore(&queue->irqlock, flags);
+	}
+
+	return ret;
+}
+
+/* called with &queue_irqlock held.. */
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+					  struct uvc_buffer *buf)
+{
+	struct uvc_buffer *nextbuf;
+
+	if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
+	     buf->length != buf->bytesused) {
+		buf->state = UVC_BUF_STATE_QUEUED;
+		vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
+		return buf;
+	}
+
+	list_del(&buf->queue);
+	if (!list_empty(&queue->irqqueue))
+		nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+					   queue);
+	else
+		nextbuf = NULL;
+
+	buf->buf.field = V4L2_FIELD_NONE;
+	buf->buf.sequence = queue->sequence++;
+	buf->buf.vb2_buf.timestamp = ktime_get_ns();
+
+	vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
+	vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
+
+	return nextbuf;
+}
+
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
+{
+	struct uvc_buffer *buf = NULL;
+
+	if (!list_empty(&queue->irqqueue))
+		buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+				       queue);
+	else
+		queue->flags |= UVC_QUEUE_PAUSED;
+
+	return buf;
+}
+
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
new file mode 100644
index 0000000..2f0fff7
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UVC_QUEUE_H_
+#define _UVC_QUEUE_H_
+
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+
+#include <media/videobuf2-v4l2.h>
+
+struct file;
+struct mutex;
+
+/* Maximum frame size in bytes, for sanity checking. */
+#define UVC_MAX_FRAME_SIZE	(16*1024*1024)
+/* Maximum number of video buffers. */
+#define UVC_MAX_VIDEO_BUFFERS	32
+
+/* ------------------------------------------------------------------------
+ * Structures.
+ */
+
+enum uvc_buffer_state {
+	UVC_BUF_STATE_IDLE	= 0,
+	UVC_BUF_STATE_QUEUED	= 1,
+	UVC_BUF_STATE_ACTIVE	= 2,
+	UVC_BUF_STATE_DONE	= 3,
+	UVC_BUF_STATE_ERROR	= 4,
+};
+
+struct uvc_buffer {
+	struct vb2_v4l2_buffer buf;
+	struct list_head queue;
+
+	enum uvc_buffer_state state;
+	void *mem;
+	unsigned int length;
+	unsigned int bytesused;
+};
+
+#define UVC_QUEUE_DISCONNECTED		(1 << 0)
+#define UVC_QUEUE_DROP_INCOMPLETE	(1 << 1)
+#define UVC_QUEUE_PAUSED		(1 << 2)
+
+struct uvc_video_queue {
+	struct vb2_queue queue;
+
+	unsigned int flags;
+	__u32 sequence;
+
+	unsigned int buf_used;
+
+	spinlock_t irqlock;	/* Protects flags and irqqueue */
+	struct list_head irqqueue;
+};
+
+static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
+{
+	return vb2_is_streaming(&queue->queue);
+}
+
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+		    struct mutex *lock);
+
+void uvcg_free_buffers(struct uvc_video_queue *queue);
+
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+		       struct v4l2_requestbuffers *rb);
+
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue,
+			struct v4l2_buffer *buf, int nonblocking);
+
+__poll_t uvcg_queue_poll(struct uvc_video_queue *queue,
+			     struct file *file, poll_table *wait);
+
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma);
+
+#ifndef CONFIG_MMU
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+					   unsigned long pgoff);
+#endif /* CONFIG_MMU */
+
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
+
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+					  struct uvc_buffer *buf);
+
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
+
+#endif /* _UVC_QUEUE_H_ */
+
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
new file mode 100644
index 0000000..7f1ca3b
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	uvc_v4l2.c  --  USB Video Class Gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb/g_uvc.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#include "f_uvc.h"
+#include "uvc.h"
+#include "uvc_queue.h"
+#include "uvc_video.h"
+#include "uvc_v4l2.h"
+
+/* --------------------------------------------------------------------------
+ * Requests handling
+ */
+
+static int
+uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
+{
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+	struct usb_request *req = uvc->control_req;
+
+	if (data->length < 0)
+		return usb_ep_set_halt(cdev->gadget->ep0);
+
+	req->length = min_t(unsigned int, uvc->event_length, data->length);
+	req->zero = data->length < uvc->event_length;
+
+	memcpy(req->buf, data->data, req->length);
+
+	return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+struct uvc_format {
+	u8 bpp;
+	u32 fcc;
+};
+
+static struct uvc_format uvc_formats[] = {
+	{ 16, V4L2_PIX_FMT_YUYV  },
+	{ 0,  V4L2_PIX_FMT_MJPEG },
+};
+
+static int
+uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct usb_composite_dev *cdev = uvc->func.config->cdev;
+
+	strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
+	strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
+	strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
+		sizeof(cap->bus_info));
+
+	cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int
+uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+
+	fmt->fmt.pix.pixelformat = video->fcc;
+	fmt->fmt.pix.width = video->width;
+	fmt->fmt.pix.height = video->height;
+	fmt->fmt.pix.field = V4L2_FIELD_NONE;
+	fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
+	fmt->fmt.pix.sizeimage = video->imagesize;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->fmt.pix.priv = 0;
+
+	return 0;
+}
+
+static int
+uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+	struct uvc_format *format;
+	unsigned int imagesize;
+	unsigned int bpl;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) {
+		format = &uvc_formats[i];
+		if (format->fcc == fmt->fmt.pix.pixelformat)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(uvc_formats)) {
+		printk(KERN_INFO "Unsupported format 0x%08x.\n",
+			fmt->fmt.pix.pixelformat);
+		return -EINVAL;
+	}
+
+	bpl = format->bpp * fmt->fmt.pix.width / 8;
+	imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
+
+	video->fcc = format->fcc;
+	video->bpp = format->bpp;
+	video->width = fmt->fmt.pix.width;
+	video->height = fmt->fmt.pix.height;
+	video->imagesize = imagesize;
+
+	fmt->fmt.pix.field = V4L2_FIELD_NONE;
+	fmt->fmt.pix.bytesperline = bpl;
+	fmt->fmt.pix.sizeimage = imagesize;
+	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->fmt.pix.priv = 0;
+
+	return 0;
+}
+
+static int
+uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+
+	if (b->type != video->queue.queue.type)
+		return -EINVAL;
+
+	return uvcg_alloc_buffers(&video->queue, b);
+}
+
+static int
+uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+
+	return uvcg_query_buffer(&video->queue, b);
+}
+
+static int
+uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+	int ret;
+
+	ret = uvcg_queue_buffer(&video->queue, b);
+	if (ret < 0)
+		return ret;
+
+	return uvcg_video_pump(video);
+}
+
+static int
+uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+
+	return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
+}
+
+static int
+uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+	int ret;
+
+	if (type != video->queue.queue.type)
+		return -EINVAL;
+
+	/* Enable UVC video. */
+	ret = uvcg_video_enable(video, 1);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Complete the alternate setting selection setup phase now that
+	 * userspace is ready to provide video frames.
+	 */
+	uvc_function_setup_continue(uvc);
+	uvc->state = UVC_STATE_STREAMING;
+
+	return 0;
+}
+
+static int
+uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_video *video = &uvc->video;
+
+	if (type != video->queue.queue.type)
+		return -EINVAL;
+
+	return uvcg_video_enable(video, 0);
+}
+
+static int
+uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
+			 const struct v4l2_event_subscription *sub)
+{
+	if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
+		return -EINVAL;
+
+	return v4l2_event_subscribe(fh, sub, 2, NULL);
+}
+
+static int
+uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+			   const struct v4l2_event_subscription *sub)
+{
+	return v4l2_event_unsubscribe(fh, sub);
+}
+
+static long
+uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
+		       unsigned int cmd, void *arg)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+
+	switch (cmd) {
+	case UVCIOC_SEND_RESPONSE:
+		return uvc_send_response(uvc, arg);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
+	.vidioc_querycap = uvc_v4l2_querycap,
+	.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
+	.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
+	.vidioc_reqbufs = uvc_v4l2_reqbufs,
+	.vidioc_querybuf = uvc_v4l2_querybuf,
+	.vidioc_qbuf = uvc_v4l2_qbuf,
+	.vidioc_dqbuf = uvc_v4l2_dqbuf,
+	.vidioc_streamon = uvc_v4l2_streamon,
+	.vidioc_streamoff = uvc_v4l2_streamoff,
+	.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
+	.vidioc_default = uvc_v4l2_ioctl_default,
+};
+
+/* --------------------------------------------------------------------------
+ * V4L2
+ */
+
+static int
+uvc_v4l2_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_file_handle *handle;
+
+	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+	if (handle == NULL)
+		return -ENOMEM;
+
+	v4l2_fh_init(&handle->vfh, vdev);
+	v4l2_fh_add(&handle->vfh);
+
+	handle->device = &uvc->video;
+	file->private_data = &handle->vfh;
+
+	uvc_function_connect(uvc);
+	return 0;
+}
+
+static int
+uvc_v4l2_release(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+	struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
+	struct uvc_video *video = handle->device;
+
+	uvc_function_disconnect(uvc);
+
+	mutex_lock(&video->mutex);
+	uvcg_video_enable(video, 0);
+	uvcg_free_buffers(&video->queue);
+	mutex_unlock(&video->mutex);
+
+	file->private_data = NULL;
+	v4l2_fh_del(&handle->vfh);
+	v4l2_fh_exit(&handle->vfh);
+	kfree(handle);
+
+	return 0;
+}
+
+static int
+uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+
+	return uvcg_queue_mmap(&uvc->video.queue, vma);
+}
+
+static __poll_t
+uvc_v4l2_poll(struct file *file, poll_table *wait)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+
+	return uvcg_queue_poll(&uvc->video.queue, file, wait);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file,
+		unsigned long addr, unsigned long len, unsigned long pgoff,
+		unsigned long flags)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct uvc_device *uvc = video_get_drvdata(vdev);
+
+	return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
+}
+#endif
+
+const struct v4l2_file_operations uvc_v4l2_fops = {
+	.owner		= THIS_MODULE,
+	.open		= uvc_v4l2_open,
+	.release	= uvc_v4l2_release,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= uvc_v4l2_mmap,
+	.poll		= uvc_v4l2_poll,
+#ifndef CONFIG_MMU
+	.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
+#endif
+};
+
diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h
new file mode 100644
index 0000000..a75e9c3
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_v4l2.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *	uvc_v4l2.h  --  USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ *		Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *		Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef __UVC_V4L2_H__
+#define __UVC_V4L2_H__
+
+extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops;
+extern const struct v4l2_file_operations uvc_v4l2_fops;
+
+#endif /* __UVC_V4L2_H__ */
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
new file mode 100644
index 0000000..d3567b9
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	uvc_video.c  --  USB Video Class Gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/video.h>
+
+#include <media/v4l2-dev.h>
+
+#include "uvc.h"
+#include "uvc_queue.h"
+#include "uvc_video.h"
+
+/* --------------------------------------------------------------------------
+ * Video codecs
+ */
+
+static int
+uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
+		u8 *data, int len)
+{
+	data[0] = 2;
+	data[1] = UVC_STREAM_EOH | video->fid;
+
+	if (buf->bytesused - video->queue.buf_used <= len - 2)
+		data[1] |= UVC_STREAM_EOF;
+
+	return 2;
+}
+
+static int
+uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
+		u8 *data, int len)
+{
+	struct uvc_video_queue *queue = &video->queue;
+	unsigned int nbytes;
+	void *mem;
+
+	/* Copy video data to the USB buffer. */
+	mem = buf->mem + queue->buf_used;
+	nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
+
+	memcpy(data, mem, nbytes);
+	queue->buf_used += nbytes;
+
+	return nbytes;
+}
+
+static void
+uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
+		struct uvc_buffer *buf)
+{
+	void *mem = req->buf;
+	int len = video->req_size;
+	int ret;
+
+	/* Add a header at the beginning of the payload. */
+	if (video->payload_size == 0) {
+		ret = uvc_video_encode_header(video, buf, mem, len);
+		video->payload_size += ret;
+		mem += ret;
+		len -= ret;
+	}
+
+	/* Process video data. */
+	len = min((int)(video->max_payload_size - video->payload_size), len);
+	ret = uvc_video_encode_data(video, buf, mem, len);
+
+	video->payload_size += ret;
+	len -= ret;
+
+	req->length = video->req_size - len;
+	req->zero = video->payload_size == video->max_payload_size;
+
+	if (buf->bytesused == video->queue.buf_used) {
+		video->queue.buf_used = 0;
+		buf->state = UVC_BUF_STATE_DONE;
+		uvcg_queue_next_buffer(&video->queue, buf);
+		video->fid ^= UVC_STREAM_FID;
+
+		video->payload_size = 0;
+	}
+
+	if (video->payload_size == video->max_payload_size ||
+	    buf->bytesused == video->queue.buf_used)
+		video->payload_size = 0;
+}
+
+static void
+uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
+		struct uvc_buffer *buf)
+{
+	void *mem = req->buf;
+	int len = video->req_size;
+	int ret;
+
+	/* Add the header. */
+	ret = uvc_video_encode_header(video, buf, mem, len);
+	mem += ret;
+	len -= ret;
+
+	/* Process video data. */
+	ret = uvc_video_encode_data(video, buf, mem, len);
+	len -= ret;
+
+	req->length = video->req_size - len;
+
+	if (buf->bytesused == video->queue.buf_used) {
+		video->queue.buf_used = 0;
+		buf->state = UVC_BUF_STATE_DONE;
+		uvcg_queue_next_buffer(&video->queue, buf);
+		video->fid ^= UVC_STREAM_FID;
+	}
+}
+
+/* --------------------------------------------------------------------------
+ * Request handling
+ */
+
+/*
+ * I somehow feel that synchronisation won't be easy to achieve here. We have
+ * three events that control USB requests submission:
+ *
+ * - USB request completion: the completion handler will resubmit the request
+ *   if a video buffer is available.
+ *
+ * - USB interface setting selection: in response to a SET_INTERFACE request,
+ *   the handler will start streaming if a video buffer is available and if
+ *   video is not currently streaming.
+ *
+ * - V4L2 buffer queueing: the driver will start streaming if video is not
+ *   currently streaming.
+ *
+ * Race conditions between those 3 events might lead to deadlocks or other
+ * nasty side effects.
+ *
+ * The "video currently streaming" condition can't be detected by the irqqueue
+ * being empty, as a request can still be in flight. A separate "queue paused"
+ * flag is thus needed.
+ *
+ * The paused flag will be set when we try to retrieve the irqqueue head if the
+ * queue is empty, and cleared when we queue a buffer.
+ *
+ * The USB request completion handler will get the buffer at the irqqueue head
+ * under protection of the queue spinlock. If the queue is empty, the streaming
+ * paused flag will be set. Right after releasing the spinlock a userspace
+ * application can queue a buffer. The flag will then cleared, and the ioctl
+ * handler will restart the video stream.
+ */
+static void
+uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct uvc_video *video = req->context;
+	struct uvc_video_queue *queue = &video->queue;
+	struct uvc_buffer *buf;
+	unsigned long flags;
+	int ret;
+
+	switch (req->status) {
+	case 0:
+		break;
+
+	case -ESHUTDOWN:	/* disconnect from host. */
+		printk(KERN_DEBUG "VS request cancelled.\n");
+		uvcg_queue_cancel(queue, 1);
+		goto requeue;
+
+	default:
+		printk(KERN_INFO "VS request completed with status %d.\n",
+			req->status);
+		uvcg_queue_cancel(queue, 0);
+		goto requeue;
+	}
+
+	spin_lock_irqsave(&video->queue.irqlock, flags);
+	buf = uvcg_queue_head(&video->queue);
+	if (buf == NULL) {
+		spin_unlock_irqrestore(&video->queue.irqlock, flags);
+		goto requeue;
+	}
+
+	video->encode(req, video, buf);
+
+	if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
+		printk(KERN_INFO "Failed to queue request (%d).\n", ret);
+		usb_ep_set_halt(ep);
+		spin_unlock_irqrestore(&video->queue.irqlock, flags);
+		uvcg_queue_cancel(queue, 0);
+		goto requeue;
+	}
+	spin_unlock_irqrestore(&video->queue.irqlock, flags);
+
+	return;
+
+requeue:
+	spin_lock_irqsave(&video->req_lock, flags);
+	list_add_tail(&req->list, &video->req_free);
+	spin_unlock_irqrestore(&video->req_lock, flags);
+}
+
+static int
+uvc_video_free_requests(struct uvc_video *video)
+{
+	unsigned int i;
+
+	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+		if (video->req[i]) {
+			usb_ep_free_request(video->ep, video->req[i]);
+			video->req[i] = NULL;
+		}
+
+		if (video->req_buffer[i]) {
+			kfree(video->req_buffer[i]);
+			video->req_buffer[i] = NULL;
+		}
+	}
+
+	INIT_LIST_HEAD(&video->req_free);
+	video->req_size = 0;
+	return 0;
+}
+
+static int
+uvc_video_alloc_requests(struct uvc_video *video)
+{
+	unsigned int req_size;
+	unsigned int i;
+	int ret = -ENOMEM;
+
+	BUG_ON(video->req_size);
+
+	req_size = video->ep->maxpacket
+		 * max_t(unsigned int, video->ep->maxburst, 1)
+		 * (video->ep->mult);
+
+	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+		video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
+		if (video->req_buffer[i] == NULL)
+			goto error;
+
+		video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
+		if (video->req[i] == NULL)
+			goto error;
+
+		video->req[i]->buf = video->req_buffer[i];
+		video->req[i]->length = 0;
+		video->req[i]->complete = uvc_video_complete;
+		video->req[i]->context = video;
+
+		list_add_tail(&video->req[i]->list, &video->req_free);
+	}
+
+	video->req_size = req_size;
+
+	return 0;
+
+error:
+	uvc_video_free_requests(video);
+	return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Video streaming
+ */
+
+/*
+ * uvcg_video_pump - Pump video data into the USB requests
+ *
+ * This function fills the available USB requests (listed in req_free) with
+ * video data from the queued buffers.
+ */
+int uvcg_video_pump(struct uvc_video *video)
+{
+	struct uvc_video_queue *queue = &video->queue;
+	struct usb_request *req;
+	struct uvc_buffer *buf;
+	unsigned long flags;
+	int ret;
+
+	/* FIXME TODO Race between uvcg_video_pump and requests completion
+	 * handler ???
+	 */
+
+	while (1) {
+		/* Retrieve the first available USB request, protected by the
+		 * request lock.
+		 */
+		spin_lock_irqsave(&video->req_lock, flags);
+		if (list_empty(&video->req_free)) {
+			spin_unlock_irqrestore(&video->req_lock, flags);
+			return 0;
+		}
+		req = list_first_entry(&video->req_free, struct usb_request,
+					list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&video->req_lock, flags);
+
+		/* Retrieve the first available video buffer and fill the
+		 * request, protected by the video queue irqlock.
+		 */
+		spin_lock_irqsave(&queue->irqlock, flags);
+		buf = uvcg_queue_head(queue);
+		if (buf == NULL) {
+			spin_unlock_irqrestore(&queue->irqlock, flags);
+			break;
+		}
+
+		video->encode(req, video, buf);
+
+		/* Queue the USB request */
+		ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+		if (ret < 0) {
+			printk(KERN_INFO "Failed to queue request (%d)\n", ret);
+			usb_ep_set_halt(video->ep);
+			spin_unlock_irqrestore(&queue->irqlock, flags);
+			uvcg_queue_cancel(queue, 0);
+			break;
+		}
+		spin_unlock_irqrestore(&queue->irqlock, flags);
+	}
+
+	spin_lock_irqsave(&video->req_lock, flags);
+	list_add_tail(&req->list, &video->req_free);
+	spin_unlock_irqrestore(&video->req_lock, flags);
+	return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+int uvcg_video_enable(struct uvc_video *video, int enable)
+{
+	unsigned int i;
+	int ret;
+
+	if (video->ep == NULL) {
+		printk(KERN_INFO "Video enable failed, device is "
+			"uninitialized.\n");
+		return -ENODEV;
+	}
+
+	if (!enable) {
+		for (i = 0; i < UVC_NUM_REQUESTS; ++i)
+			if (video->req[i])
+				usb_ep_dequeue(video->ep, video->req[i]);
+
+		uvc_video_free_requests(video);
+		uvcg_queue_enable(&video->queue, 0);
+		return 0;
+	}
+
+	if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
+		return ret;
+
+	if ((ret = uvc_video_alloc_requests(video)) < 0)
+		return ret;
+
+	if (video->max_payload_size) {
+		video->encode = uvc_video_encode_bulk;
+		video->payload_size = 0;
+	} else
+		video->encode = uvc_video_encode_isoc;
+
+	return uvcg_video_pump(video);
+}
+
+/*
+ * Initialize the UVC video stream.
+ */
+int uvcg_video_init(struct uvc_video *video)
+{
+	INIT_LIST_HEAD(&video->req_free);
+	spin_lock_init(&video->req_lock);
+
+	video->fcc = V4L2_PIX_FMT_YUYV;
+	video->bpp = 16;
+	video->width = 320;
+	video->height = 240;
+	video->imagesize = 320 * 240 * 2;
+
+	/* Initialize the video buffers queue. */
+	uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+			&video->mutex);
+	return 0;
+}
+
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
new file mode 100644
index 0000000..7d77122
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *	uvc_video.h  --  USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ *		Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *		Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+#ifndef __UVC_VIDEO_H__
+#define __UVC_VIDEO_H__
+
+struct uvc_video;
+
+int uvcg_video_pump(struct uvc_video *video);
+
+int uvcg_video_enable(struct uvc_video *video, int enable);
+
+int uvcg_video_init(struct uvc_video *video);
+
+#endif /* __UVC_VIDEO_H__ */
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
new file mode 100644
index 0000000..203361a
--- /dev/null
+++ b/drivers/usb/gadget/functions.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <linux/usb/composite.h>
+
+static LIST_HEAD(func_list);
+static DEFINE_MUTEX(func_lock);
+
+static struct usb_function_instance *try_get_usb_function_instance(const char *name)
+{
+	struct usb_function_driver *fd;
+	struct usb_function_instance *fi;
+
+	fi = ERR_PTR(-ENOENT);
+	mutex_lock(&func_lock);
+	list_for_each_entry(fd, &func_list, list) {
+
+		if (strcmp(name, fd->name))
+			continue;
+
+		if (!try_module_get(fd->mod)) {
+			fi = ERR_PTR(-EBUSY);
+			break;
+		}
+		fi = fd->alloc_inst();
+		if (IS_ERR(fi))
+			module_put(fd->mod);
+		else
+			fi->fd = fd;
+		break;
+	}
+	mutex_unlock(&func_lock);
+	return fi;
+}
+
+struct usb_function_instance *usb_get_function_instance(const char *name)
+{
+	struct usb_function_instance *fi;
+	int ret;
+
+	fi = try_get_usb_function_instance(name);
+	if (!IS_ERR(fi))
+		return fi;
+	ret = PTR_ERR(fi);
+	if (ret != -ENOENT)
+		return fi;
+	ret = request_module("usbfunc:%s", name);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	return try_get_usb_function_instance(name);
+}
+EXPORT_SYMBOL_GPL(usb_get_function_instance);
+
+struct usb_function *usb_get_function(struct usb_function_instance *fi)
+{
+	struct usb_function *f;
+
+	f = fi->fd->alloc_func(fi);
+	if (IS_ERR(f))
+		return f;
+	f->fi = fi;
+	return f;
+}
+EXPORT_SYMBOL_GPL(usb_get_function);
+
+void usb_put_function_instance(struct usb_function_instance *fi)
+{
+	struct module *mod;
+
+	if (!fi)
+		return;
+
+	mod = fi->fd->mod;
+	fi->free_func_inst(fi);
+	module_put(mod);
+}
+EXPORT_SYMBOL_GPL(usb_put_function_instance);
+
+void usb_put_function(struct usb_function *f)
+{
+	if (!f)
+		return;
+
+	f->free_func(f);
+}
+EXPORT_SYMBOL_GPL(usb_put_function);
+
+int usb_function_register(struct usb_function_driver *newf)
+{
+	struct usb_function_driver *fd;
+	int ret;
+
+	ret = -EEXIST;
+
+	mutex_lock(&func_lock);
+	list_for_each_entry(fd, &func_list, list) {
+		if (!strcmp(fd->name, newf->name))
+			goto out;
+	}
+	ret = 0;
+	list_add_tail(&newf->list, &func_list);
+out:
+	mutex_unlock(&func_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_function_register);
+
+void usb_function_unregister(struct usb_function_driver *fd)
+{
+	mutex_lock(&func_lock);
+	list_del(&fd->list);
+	mutex_unlock(&func_lock);
+}
+EXPORT_SYMBOL_GPL(usb_function_unregister);
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
new file mode 100644
index 0000000..784bf86
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -0,0 +1,492 @@
+#
+# USB Gadget support on a system involves
+#    (a) a peripheral controller, and
+#    (b) the gadget driver using it.
+#
+# NOTE:  Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+#  - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+#  - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+#  - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+config USB_ZERO
+	tristate "Gadget Zero (DEVELOPMENT)"
+	select USB_LIBCOMPOSITE
+	select USB_F_SS_LB
+	help
+	  Gadget Zero is a two-configuration device.  It either sinks and
+	  sources bulk data; or it loops back a configurable number of
+	  transfers.  It also implements control requests, for "chapter 9"
+	  conformance.  The driver needs only two bulk-capable endpoints, so
+	  it can work on top of most device-side usb controllers.  It's
+	  useful for testing, and is also a working example showing how
+	  USB "gadget drivers" can be written.
+
+	  Make this be the first driver you try using on top of any new
+	  USB peripheral controller driver.  Then you can use host-side
+	  test software, like the "usbtest" driver, to put your hardware
+	  and its driver through a basic set of functional tests.
+
+	  Gadget Zero also works with the host-side "usb-skeleton" driver,
+	  and with many kinds of host-side test software.  You may need
+	  to tweak product and vendor IDs before host software knows about
+	  this device, and arrange to select an appropriate configuration.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_zero".
+
+config USB_ZERO_HNPTEST
+	bool "HNP Test Device"
+	depends on USB_ZERO && USB_OTG
+	help
+	  You can configure this device to enumerate using the device
+	  identifiers of the USB-OTG test device.  That means that when
+	  this gadget connects to another OTG device, with this one using
+	  the "B-Peripheral" role, that device will use HNP to let this
+	  one serve as the USB host instead (in the "B-Host" role).
+
+config USB_AUDIO
+	tristate "Audio Gadget"
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_PCM
+	select USB_F_UAC1 if (GADGET_UAC1 && !GADGET_UAC1_LEGACY)
+	select USB_F_UAC1_LEGACY if (GADGET_UAC1 && GADGET_UAC1_LEGACY)
+	select USB_F_UAC2 if !GADGET_UAC1
+	select USB_U_AUDIO if (USB_F_UAC2 || USB_F_UAC1)
+	help
+	  This Gadget Audio driver is compatible with USB Audio Class
+	  specification 2.0. It implements 1 AudioControl interface,
+	  1 AudioStreaming Interface each for USB-OUT and USB-IN.
+	  Number of channels, sample rate and sample size can be
+	  specified as module parameters.
+	  This driver doesn't expect any real Audio codec to be present
+	  on the device - the audio streams are simply sinked to and
+	  sourced from a virtual ALSA sound card created. The user-space
+	  application may choose to do whatever it wants with the data
+	  received from the USB Host and choose to provide whatever it
+	  wants as audio data to the USB Host.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_audio".
+
+config GADGET_UAC1
+	bool "UAC 1.0"
+	depends on USB_AUDIO
+	help
+	  If you instead want older USB Audio Class specification 1.0 support
+	  with similar driver capabilities.
+
+config GADGET_UAC1_LEGACY
+	bool "UAC 1.0 (Legacy)"
+	depends on GADGET_UAC1
+	help
+	  If you instead want legacy UAC Spec-1.0 driver that also has audio
+	  paths hardwired to the Audio codec chip on-board and doesn't work
+	  without one.
+
+config USB_ETH
+	tristate "Ethernet Gadget (with CDC Ethernet support)"
+	depends on NET
+	select USB_LIBCOMPOSITE
+	select USB_U_ETHER
+	select USB_F_ECM
+	select USB_F_SUBSET
+	select CRC32
+	help
+	  This driver implements Ethernet style communication, in one of
+	  several ways:
+
+	   - The "Communication Device Class" (CDC) Ethernet Control Model.
+	     That protocol is often avoided with pure Ethernet adapters, in
+	     favor of simpler vendor-specific hardware, but is widely
+	     supported by firmware for smart network devices.
+
+	   - On hardware can't implement that protocol, a simple CDC subset
+	     is used, placing fewer demands on USB.
+
+	   - CDC Ethernet Emulation Model (EEM) is a newer standard that has
+	     a simpler interface that can be used by more USB hardware.
+
+	  RNDIS support is an additional option, more demanding than subset.
+
+	  Within the USB device, this gadget driver exposes a network device
+	  "usbX", where X depends on what other networking devices you have.
+	  Treat it like a two-node Ethernet link:  host, and gadget.
+
+	  The Linux-USB host-side "usbnet" driver interoperates with this
+	  driver, so that deep I/O queues can be supported.  On 2.4 kernels,
+	  use "CDCEther" instead, if you're using the CDC option. That CDC
+	  mode should also interoperate with standard CDC Ethernet class
+	  drivers on other host operating systems.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_ether".
+
+config USB_ETH_RNDIS
+	bool "RNDIS support"
+	depends on USB_ETH
+	select USB_LIBCOMPOSITE
+	select USB_F_RNDIS
+	default y
+	help
+	   Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+	   and Microsoft provides redistributable binary RNDIS drivers for
+	   older versions of Windows.
+
+	   If you say "y" here, the Ethernet gadget driver will try to provide
+	   a second device configuration, supporting RNDIS to talk to such
+	   Microsoft USB hosts.
+
+	   To make MS-Windows work with this, use Documentation/usb/linux.inf
+	   as the "driver info file".  For versions of MS-Windows older than
+	   XP, you'll need to download drivers from Microsoft's website; a URL
+	   is given in comments found in that info file.
+
+config USB_ETH_EEM
+       bool "Ethernet Emulation Model (EEM) support"
+       depends on USB_ETH
+	select USB_LIBCOMPOSITE
+	select USB_F_EEM
+       default n
+       help
+         CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+         and therefore can be supported by more hardware.  Technically ECM and
+         EEM are designed for different applications.  The ECM model extends
+         the network interface to the target (e.g. a USB cable modem), and the
+         EEM model is for mobile devices to communicate with hosts using
+         ethernet over USB.  For Linux gadgets, however, the interface with
+         the host is the same (a usbX device), so the differences are minimal.
+
+         If you say "y" here, the Ethernet gadget driver will use the EEM
+         protocol rather than ECM.  If unsure, say "n".
+
+config USB_G_NCM
+	tristate "Network Control Model (NCM) support"
+	depends on NET
+	select USB_LIBCOMPOSITE
+	select USB_U_ETHER
+	select USB_F_NCM
+	select CRC32
+	help
+	  This driver implements USB CDC NCM subclass standard. NCM is
+	  an advanced protocol for Ethernet encapsulation, allows grouping
+	  of several ethernet frames into one USB transfer and different
+	  alignment possibilities.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_ncm".
+
+config USB_GADGETFS
+	tristate "Gadget Filesystem"
+	help
+	  This driver provides a filesystem based API that lets user mode
+	  programs implement a single-configuration USB device, including
+	  endpoint I/O and control requests that don't relate to enumeration.
+	  All endpoints, transfer speeds, and transfer types supported by
+	  the hardware are available, through read() and write() calls.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "gadgetfs".
+
+config USB_FUNCTIONFS
+	tristate "Function Filesystem"
+	select USB_LIBCOMPOSITE
+	select USB_F_FS
+	select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+	help
+	  The Function Filesystem (FunctionFS) lets one create USB
+	  composite functions in user space in the same way GadgetFS
+	  lets one create USB gadgets in user space.  This allows creation
+	  of composite gadgets such that some of the functions are
+	  implemented in kernel space (for instance Ethernet, serial or
+	  mass storage) and other are implemented in user space.
+
+	  If you say "y" or "m" here you will be able what kind of
+	  configurations the gadget will provide.
+
+	  Say "y" to link the driver statically, or "m" to build
+	  a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+	bool "Include configuration with CDC ECM (Ethernet)"
+	depends on USB_FUNCTIONFS && NET
+	select USB_U_ETHER
+	select USB_F_ECM
+	select USB_F_SUBSET
+	help
+	  Include a configuration with CDC ECM function (Ethernet) and the
+	  Function Filesystem.
+
+config USB_FUNCTIONFS_RNDIS
+	bool "Include configuration with RNDIS (Ethernet)"
+	depends on USB_FUNCTIONFS && NET
+	select USB_U_ETHER
+	select USB_F_RNDIS
+	help
+	  Include a configuration with RNDIS function (Ethernet) and the Filesystem.
+
+config USB_FUNCTIONFS_GENERIC
+	bool "Include 'pure' configuration"
+	depends on USB_FUNCTIONFS
+	help
+	  Include a configuration with the Function Filesystem alone with
+	  no Ethernet interface.
+
+config USB_MASS_STORAGE
+	tristate "Mass Storage Gadget"
+	depends on BLOCK
+	select USB_LIBCOMPOSITE
+	select USB_F_MASS_STORAGE
+	help
+	  The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+	  As its storage repository it can use a regular file or a block
+	  device (in much the same way as the "loop" device driver),
+	  specified as a module parameter or sysfs option.
+
+	  This driver is a replacement for now removed 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".
+
+config USB_GADGET_TARGET
+	tristate "USB Gadget Target Fabric Module"
+	depends on TARGET_CORE
+	select USB_LIBCOMPOSITE
+	select USB_F_TCM
+	help
+	  This fabric is an USB gadget. Two USB protocols are supported that is
+	  BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
+	  advertised on alternative interface 0 (primary) and UAS is on
+	  alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
+	  UAS utilizes the USB 3.0 feature called streams support.
+
+config USB_G_SERIAL
+	tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+	depends on TTY
+	select USB_U_SERIAL
+	select USB_F_ACM
+	select USB_F_SERIAL
+	select USB_F_OBEX
+	select USB_LIBCOMPOSITE
+	help
+	  The Serial Gadget talks to the Linux-USB generic serial driver.
+	  This driver supports a CDC-ACM module option, which can be used
+	  to interoperate with MS-Windows hosts or with the Linux-USB
+	  "cdc-acm" driver.
+
+	  This driver also supports a CDC-OBEX option.  You will need a
+	  user space OBEX server talking to /dev/ttyGS*, since the kernel
+	  itself doesn't implement the OBEX protocol.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_serial".
+
+	  For more information, see Documentation/usb/gadget_serial.txt
+	  which includes instructions and a "driver info file" needed to
+	  make MS-Windows work with CDC ACM.
+
+config USB_MIDI_GADGET
+	tristate "MIDI Gadget"
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_RAWMIDI
+	select USB_F_MIDI
+	help
+	  The MIDI Gadget acts as a USB Audio device, with one MIDI
+	  input and one MIDI output. These MIDI jacks appear as
+	  a sound "card" in the ALSA sound system. Other MIDI
+	  connections can then be made on the gadget system, using
+	  ALSA's aconnect utility etc.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_midi".
+
+config USB_G_PRINTER
+	tristate "Printer Gadget"
+	select USB_LIBCOMPOSITE
+	select USB_F_PRINTER
+	help
+	  The Printer Gadget channels data between the USB host and a
+	  userspace program driving the print engine. The user space
+	  program reads and writes the device file /dev/g_printer to
+	  receive or send printer data. It can use ioctl calls to
+	  the device file to get or set printer status.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_printer".
+
+	  For more information, see Documentation/usb/gadget_printer.txt
+	  which includes sample code for accessing the device file.
+
+if TTY
+
+config USB_CDC_COMPOSITE
+	tristate "CDC Composite Device (Ethernet and ACM)"
+	depends on NET
+	select USB_LIBCOMPOSITE
+	select USB_U_SERIAL
+	select USB_U_ETHER
+	select USB_F_ACM
+	select USB_F_ECM
+	help
+	  This driver provides two functions in one configuration:
+	  a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
+
+	  This driver requires four bulk and two interrupt endpoints,
+	  plus the ability to handle altsettings.  Not all peripheral
+	  controllers are that capable.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module.
+
+config USB_G_NOKIA
+	tristate "Nokia composite gadget"
+	depends on PHONET
+	depends on BLOCK
+	select USB_LIBCOMPOSITE
+	select USB_U_SERIAL
+	select USB_U_ETHER
+	select USB_F_ACM
+	select USB_F_OBEX
+	select USB_F_PHONET
+	select USB_F_ECM
+	select USB_F_MASS_STORAGE
+	help
+	  The Nokia composite gadget provides support for acm, obex
+	  and phonet in only one composite gadget driver.
+
+	  It's only really useful for N900 hardware. If you're building
+	  a kernel for N900, say Y or M here. If unsure, say N.
+
+config USB_G_ACM_MS
+	tristate "CDC Composite Device (ACM and mass storage)"
+	depends on BLOCK
+	select USB_LIBCOMPOSITE
+	select USB_U_SERIAL
+	select USB_F_ACM
+	select USB_F_MASS_STORAGE
+	help
+	  This driver provides two functions in one configuration:
+	  a mass storage, and a CDC ACM (serial port) link.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_acm_ms".
+
+config USB_G_MULTI
+	tristate "Multifunction Composite Gadget"
+	depends on BLOCK && NET
+	select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+	select USB_LIBCOMPOSITE
+	select USB_U_SERIAL
+	select USB_U_ETHER
+	select USB_F_ACM
+	select USB_F_MASS_STORAGE
+	help
+	  The Multifunction Composite Gadget provides Ethernet (RNDIS
+	  and/or CDC Ethernet), mass storage and ACM serial link
+	  interfaces.
+
+	  You will be asked to choose which of the two configurations is
+	  to be available in the gadget.  At least one configuration must
+	  be chosen to make the gadget usable.  Selecting more than one
+	  configuration will prevent Windows from automatically detecting
+	  the gadget as a composite gadget, so an INF file will be needed to
+	  use the gadget.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_multi".
+
+config USB_G_MULTI_RNDIS
+	bool "RNDIS + CDC Serial + Storage configuration"
+	depends on USB_G_MULTI
+	select USB_F_RNDIS
+	default y
+	help
+	  This option enables a configuration with RNDIS, CDC Serial and
+	  Mass Storage functions available in the Multifunction Composite
+	  Gadget.  This is the configuration dedicated for Windows since RNDIS
+	  is Microsoft's protocol.
+
+	  If unsure, say "y".
+
+config USB_G_MULTI_CDC
+	bool "CDC Ethernet + CDC Serial + Storage configuration"
+	depends on USB_G_MULTI
+	default n
+	select USB_F_ECM
+	help
+	  This option enables a configuration with CDC Ethernet (ECM), CDC
+	  Serial and Mass Storage functions available in the Multifunction
+	  Composite Gadget.
+
+	  If unsure, say "y".
+
+endif # TTY
+
+config USB_G_HID
+	tristate "HID Gadget"
+	select USB_LIBCOMPOSITE
+	select USB_F_HID
+	help
+	  The HID gadget driver provides generic emulation of USB
+	  Human Interface Devices (HID).
+
+	  For more information, see Documentation/usb/gadget_hid.txt which
+	  includes sample code for accessing the device files.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_hid".
+
+# Standalone / single function gadgets
+config USB_G_DBGP
+	tristate "EHCI Debug Device Gadget"
+	depends on TTY
+	select USB_LIBCOMPOSITE
+	help
+	  This gadget emulates an EHCI Debug device. This is useful when you want
+	  to interact with an EHCI Debug Port.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+	prompt "EHCI Debug Device mode"
+	default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+	depends on USB_G_DBGP
+	bool "printk"
+	help
+	  Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+	depends on USB_G_DBGP
+	select USB_U_SERIAL
+	bool "serial"
+	help
+	  Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
+# put drivers that need isochronous transfer support (for audio
+# or video class gadget drivers), or specific hardware, here.
+config USB_G_WEBCAM
+	tristate "USB Webcam Gadget"
+	depends on VIDEO_V4L2
+	select USB_LIBCOMPOSITE
+	select VIDEOBUF2_VMALLOC
+	select USB_F_UVC
+	help
+	  The Webcam Gadget acts as a composite USB Audio and Video Class
+	  device. It provides a userspace API to process UVC control requests
+	  and stream video data to the host.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_webcam".
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
new file mode 100644
index 0000000..abd0c3e
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# USB gadget drivers
+#
+
+ccflags-y			:= -I$(srctree)/drivers/usb/gadget/
+ccflags-y			+= -I$(srctree)/drivers/usb/gadget/udc/
+ccflags-y			+= -I$(srctree)/drivers/usb/gadget/function/
+
+g_zero-y			:= zero.o
+g_audio-y			:= audio.o
+g_ether-y			:= ether.o
+g_serial-y			:= serial.o
+g_midi-y			:= gmidi.o
+gadgetfs-y			:= inode.o
+g_mass_storage-y		:= mass_storage.o
+g_printer-y			:= printer.o
+g_cdc-y				:= cdc2.o
+g_multi-y			:= multi.o
+g_hid-y				:= hid.o
+g_dbgp-y			:= dbgp.o
+g_nokia-y			:= nokia.o
+g_webcam-y			:= webcam.o
+g_ncm-y				:= ncm.o
+g_acm_ms-y			:= acm_ms.o
+g_tcm_usb_gadget-y		:= tcm_usb_gadget.o
+
+obj-$(CONFIG_USB_ZERO)		+= g_zero.o
+obj-$(CONFIG_USB_AUDIO)		+= g_audio.o
+obj-$(CONFIG_USB_ETH)		+= g_ether.o
+obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS)	+= g_ffs.o
+obj-$(CONFIG_USB_MASS_STORAGE)	+= g_mass_storage.o
+obj-$(CONFIG_USB_G_SERIAL)	+= g_serial.o
+obj-$(CONFIG_USB_G_PRINTER)	+= g_printer.o
+obj-$(CONFIG_USB_MIDI_GADGET)	+= g_midi.o
+obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+obj-$(CONFIG_USB_G_HID)		+= g_hid.o
+obj-$(CONFIG_USB_G_DBGP)	+= g_dbgp.o
+obj-$(CONFIG_USB_G_MULTI)	+= g_multi.o
+obj-$(CONFIG_USB_G_NOKIA)	+= g_nokia.o
+obj-$(CONFIG_USB_G_WEBCAM)	+= g_webcam.o
+obj-$(CONFIG_USB_G_NCM)		+= g_ncm.o
+obj-$(CONFIG_USB_G_ACM_MS)	+= g_acm_ms.o
+obj-$(CONFIG_USB_GADGET_TARGET)	+= tcm_usb_gadget.o
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
new file mode 100644
index 0000000..af16672
--- /dev/null
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * acm_ms.c -- Composite driver, with ACM and mass storage support
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: David Brownell
+ * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de>
+ *
+ * Heavily based on multi.c and cdc2.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "u_serial.h"
+
+#define DRIVER_DESC		"Composite Gadget (ACM + MS)"
+#define DRIVER_VERSION		"2011/10/10"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define ACM_MS_VENDOR_NUM	0x1d6b	/* Linux Foundation */
+#define ACM_MS_PRODUCT_NUM	0x0106	/* Composite Gadget: ACM + MS*/
+
+#include "f_mass_storage.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	.bDeviceClass =		USB_CLASS_MISC /* 0xEF */,
+	.bDeviceSubClass =	2,
+	.bDeviceProtocol =	1,
+
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	.idVendor =		cpu_to_le16(ACM_MS_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(ACM_MS_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	/*.bNumConfigurations =	DYNAMIC*/
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm;
+static struct usb_function_instance *f_acm_inst;
+
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
+/*
+ * We _always_ have both ACM and mass storage functions.
+ */
+static int acm_ms_do_config(struct usb_configuration *c)
+{
+	struct fsg_opts *opts;
+	int	status;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	opts = fsg_opts_from_func_inst(fi_msg);
+
+	f_acm = usb_get_function(f_acm_inst);
+	if (IS_ERR(f_acm))
+		return PTR_ERR(f_acm);
+
+	f_msg = usb_get_function(fi_msg);
+	if (IS_ERR(f_msg)) {
+		status = PTR_ERR(f_msg);
+		goto put_acm;
+	}
+
+	status = usb_add_function(c, f_acm);
+	if (status < 0)
+		goto put_msg;
+
+	status = usb_add_function(c, f_msg);
+	if (status)
+		goto remove_acm;
+
+	return 0;
+remove_acm:
+	usb_remove_function(c, f_acm);
+put_msg:
+	usb_put_function(f_msg);
+put_acm:
+	usb_put_function(f_acm);
+	return status;
+}
+
+static struct usb_configuration acm_ms_config_driver = {
+	.label			= DRIVER_DESC,
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int acm_ms_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct fsg_opts		*opts;
+	struct fsg_config	config;
+	int			status;
+
+	f_acm_inst = usb_get_function_instance("acm");
+	if (IS_ERR(f_acm_inst))
+		return PTR_ERR(f_acm_inst);
+
+	fi_msg = usb_get_function_instance("mass_storage");
+	if (IS_ERR(fi_msg)) {
+		status = PTR_ERR(fi_msg);
+		goto fail_get_msg;
+	}
+
+	/* set up mass storage function */
+	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+	opts = fsg_opts_from_func_inst(fi_msg);
+
+	opts->no_configfs = true;
+	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+	if (status)
+		goto fail;
+
+	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_sysfs(opts->common, true);
+	status = fsg_common_create_luns(opts->common, &config);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+				      config.product_name);
+	/*
+	 * Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail_string_ids;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto fail_string_ids;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* register our configuration */
+	status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
+	if (status < 0)
+		goto fail_otg_desc;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+			DRIVER_DESC);
+	return 0;
+
+	/* error recovery */
+fail_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail_string_ids:
+	fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+	fsg_common_free_buffers(opts->common);
+fail:
+	usb_put_function_instance(fi_msg);
+fail_get_msg:
+	usb_put_function_instance(f_acm_inst);
+	return status;
+}
+
+static int acm_ms_unbind(struct usb_composite_dev *cdev)
+{
+	usb_put_function(f_msg);
+	usb_put_function_instance(fi_msg);
+	usb_put_function(f_acm);
+	usb_put_function_instance(f_acm_inst);
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver acm_ms_driver = {
+	.name		= "g_acm_ms",
+	.dev		= &device_desc,
+	.max_speed	= USB_SPEED_SUPER,
+	.strings	= dev_strings,
+	.bind		= acm_ms_bind,
+	.unbind		= acm_ms_unbind,
+};
+
+module_usb_composite_driver(acm_ms_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
new file mode 100644
index 0000000..dd81fd5
--- /dev/null
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * audio.c -- Audio gadget driver
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+
+#define DRIVER_DESC		"Linux USB Audio Gadget"
+#define DRIVER_VERSION		"Feb 2, 2012"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#ifndef CONFIG_GADGET_UAC1
+#include "u_uac2.h"
+
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = UAC2_DEF_PCHMASK;
+module_param(p_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
+
+/* Playback Default 48 KHz */
+static int p_srate = UAC2_DEF_PSRATE;
+module_param(p_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = UAC2_DEF_PSSIZE;
+module_param(p_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+
+/* Capture(USB-OUT) Default Stereo - Fl/Fr */
+static int c_chmask = UAC2_DEF_CCHMASK;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
+
+/* Capture Default 64 KHz */
+static int c_srate = UAC2_DEF_CSRATE;
+module_param(c_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+
+/* Capture Default 16bits/sample */
+static int c_ssize = UAC2_DEF_CSSIZE;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#else
+#ifndef CONFIG_GADGET_UAC1_LEGACY
+#include "u_uac1.h"
+
+/* Playback(USB-IN) Default Stereo - Fl/Fr */
+static int p_chmask = UAC1_DEF_PCHMASK;
+module_param(p_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
+
+/* Playback Default 48 KHz */
+static int p_srate = UAC1_DEF_PSRATE;
+module_param(p_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
+
+/* Playback Default 16bits/sample */
+static int p_ssize = UAC1_DEF_PSSIZE;
+module_param(p_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
+
+/* Capture(USB-OUT) Default Stereo - Fl/Fr */
+static int c_chmask = UAC1_DEF_CCHMASK;
+module_param(c_chmask, uint, S_IRUGO);
+MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
+
+/* Capture Default 48 KHz */
+static int c_srate = UAC1_DEF_CSRATE;
+module_param(c_srate, uint, S_IRUGO);
+MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
+
+/* Capture Default 16bits/sample */
+static int c_ssize = UAC1_DEF_CSSIZE;
+module_param(c_ssize, uint, S_IRUGO);
+MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#else /* CONFIG_GADGET_UAC1_LEGACY */
+#include "u_uac1_legacy.h"
+
+static char *fn_play = FILE_PCM_PLAYBACK;
+module_param(fn_play, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
+
+static char *fn_cap = FILE_PCM_CAPTURE;
+module_param(fn_cap, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
+
+static char *fn_cntl = FILE_CONTROL;
+module_param(fn_cntl, charp, S_IRUGO);
+MODULE_PARM_DESC(fn_cntl, "Control device file name");
+
+static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
+module_param(req_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+
+static int req_count = UAC1_REQ_COUNT;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+#endif /* CONFIG_GADGET_UAC1_LEGACY */
+#endif
+
+/* string IDs are assigned dynamically */
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings_dev,
+};
+
+static struct usb_gadget_strings *audio_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+#ifndef CONFIG_GADGET_UAC1
+static struct usb_function_instance *fi_uac2;
+static struct usb_function *f_uac2;
+#else
+static struct usb_function_instance *fi_uac1;
+static struct usb_function *f_uac1;
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to Linux Foundation for donating this product ID. */
+#define AUDIO_VENDOR_NUM		0x1d6b	/* Linux Foundation */
+#define AUDIO_PRODUCT_NUM		0x0101	/* Linux-USB Audio Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+#ifdef CONFIG_GADGET_UAC1_LEGACY
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+#else
+	.bDeviceClass =		USB_CLASS_MISC,
+	.bDeviceSubClass =	0x02,
+	.bDeviceProtocol =	0x01,
+#endif
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id defaults change according to what configs
+	 * we support.  (As does bNumConfigurations.)  These values can
+	 * also be overridden by module parameters.
+	 */
+	.idVendor =		cpu_to_le16(AUDIO_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(AUDIO_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/*-------------------------------------------------------------------------*/
+
+static int audio_do_config(struct usb_configuration *c)
+{
+	int status;
+
+	/* FIXME alloc iConfiguration string, set it in c->strings */
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+#ifdef CONFIG_GADGET_UAC1
+	f_uac1 = usb_get_function(fi_uac1);
+	if (IS_ERR(f_uac1)) {
+		status = PTR_ERR(f_uac1);
+		return status;
+	}
+
+	status = usb_add_function(c, f_uac1);
+	if (status < 0) {
+		usb_put_function(f_uac1);
+		return status;
+	}
+#else
+	f_uac2 = usb_get_function(fi_uac2);
+	if (IS_ERR(f_uac2)) {
+		status = PTR_ERR(f_uac2);
+		return status;
+	}
+
+	status = usb_add_function(c, f_uac2);
+	if (status < 0) {
+		usb_put_function(f_uac2);
+		return status;
+	}
+#endif
+
+	return 0;
+}
+
+static struct usb_configuration audio_config_driver = {
+	.label			= DRIVER_DESC,
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int audio_bind(struct usb_composite_dev *cdev)
+{
+#ifndef CONFIG_GADGET_UAC1
+	struct f_uac2_opts	*uac2_opts;
+#else
+#ifndef CONFIG_GADGET_UAC1_LEGACY
+	struct f_uac1_opts	*uac1_opts;
+#else
+	struct f_uac1_legacy_opts	*uac1_opts;
+#endif
+#endif
+	int			status;
+
+#ifndef CONFIG_GADGET_UAC1
+	fi_uac2 = usb_get_function_instance("uac2");
+	if (IS_ERR(fi_uac2))
+		return PTR_ERR(fi_uac2);
+#else
+#ifndef CONFIG_GADGET_UAC1_LEGACY
+	fi_uac1 = usb_get_function_instance("uac1");
+#else
+	fi_uac1 = usb_get_function_instance("uac1_legacy");
+#endif
+	if (IS_ERR(fi_uac1))
+		return PTR_ERR(fi_uac1);
+#endif
+
+#ifndef CONFIG_GADGET_UAC1
+	uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
+	uac2_opts->p_chmask = p_chmask;
+	uac2_opts->p_srate = p_srate;
+	uac2_opts->p_ssize = p_ssize;
+	uac2_opts->c_chmask = c_chmask;
+	uac2_opts->c_srate = c_srate;
+	uac2_opts->c_ssize = c_ssize;
+	uac2_opts->req_number = UAC2_DEF_REQ_NUM;
+#else
+#ifndef CONFIG_GADGET_UAC1_LEGACY
+	uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
+	uac1_opts->p_chmask = p_chmask;
+	uac1_opts->p_srate = p_srate;
+	uac1_opts->p_ssize = p_ssize;
+	uac1_opts->c_chmask = c_chmask;
+	uac1_opts->c_srate = c_srate;
+	uac1_opts->c_ssize = c_ssize;
+	uac1_opts->req_number = UAC1_DEF_REQ_NUM;
+#else /* CONFIG_GADGET_UAC1_LEGACY */
+	uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst);
+	uac1_opts->fn_play = fn_play;
+	uac1_opts->fn_cap = fn_cap;
+	uac1_opts->fn_cntl = fn_cntl;
+	uac1_opts->req_buf_size = req_buf_size;
+	uac1_opts->req_count = req_count;
+	uac1_opts->audio_buf_size = audio_buf_size;
+#endif /* CONFIG_GADGET_UAC1_LEGACY */
+#endif
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+		if (!usb_desc)
+			goto fail;
+		usb_otg_descriptor_init(cdev->gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
+	if (status < 0)
+		goto fail_otg_desc;
+	usb_composite_overwrite_options(cdev, &coverwrite);
+
+	INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
+	return 0;
+
+fail_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail:
+#ifndef CONFIG_GADGET_UAC1
+	usb_put_function_instance(fi_uac2);
+#else
+	usb_put_function_instance(fi_uac1);
+#endif
+	return status;
+}
+
+static int audio_unbind(struct usb_composite_dev *cdev)
+{
+#ifdef CONFIG_GADGET_UAC1
+	if (!IS_ERR_OR_NULL(f_uac1))
+		usb_put_function(f_uac1);
+	if (!IS_ERR_OR_NULL(fi_uac1))
+		usb_put_function_instance(fi_uac1);
+#else
+	if (!IS_ERR_OR_NULL(f_uac2))
+		usb_put_function(f_uac2);
+	if (!IS_ERR_OR_NULL(fi_uac2))
+		usb_put_function_instance(fi_uac2);
+#endif
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver audio_driver = {
+	.name		= "g_audio",
+	.dev		= &device_desc,
+	.strings	= audio_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= audio_bind,
+	.unbind		= audio_unbind,
+};
+
+module_usb_composite_driver(audio_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
new file mode 100644
index 0000000..da1c379
--- /dev/null
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cdc2.c -- CDC Composite driver, with ECM and ACM support
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "u_ether.h"
+#include "u_serial.h"
+#include "u_ecm.h"
+
+
+#define DRIVER_DESC		"CDC Composite Gadget"
+#define DRIVER_VERSION		"King Kamehameha Day 2008"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only this composite CDC configuration.
+ */
+#define CDC_VENDOR_NUM		0x0525	/* NetChip */
+#define CDC_PRODUCT_NUM		0xa4aa	/* CDC Composite: ECM + ACM */
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	.bDeviceClass =		USB_CLASS_COMM,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	.idVendor =		cpu_to_le16(CDC_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(CDC_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm;
+static struct usb_function_instance *fi_serial;
+
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_ecm;
+
+/*
+ * We _always_ have both CDC ECM and CDC ACM functions.
+ */
+static int cdc_do_config(struct usb_configuration *c)
+{
+	int	status;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_ecm = usb_get_function(fi_ecm);
+	if (IS_ERR(f_ecm)) {
+		status = PTR_ERR(f_ecm);
+		goto err_get_ecm;
+	}
+
+	status = usb_add_function(c, f_ecm);
+	if (status)
+		goto err_add_ecm;
+
+	f_acm = usb_get_function(fi_serial);
+	if (IS_ERR(f_acm)) {
+		status = PTR_ERR(f_acm);
+		goto err_get_acm;
+	}
+
+	status = usb_add_function(c, f_acm);
+	if (status)
+		goto err_add_acm;
+	return 0;
+
+err_add_acm:
+	usb_put_function(f_acm);
+err_get_acm:
+	usb_remove_function(c, f_ecm);
+err_add_ecm:
+	usb_put_function(f_ecm);
+err_get_ecm:
+	return status;
+}
+
+static struct usb_configuration cdc_config_driver = {
+	.label			= "CDC Composite (ECM + ACM)",
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int cdc_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct f_ecm_opts	*ecm_opts;
+	int			status;
+
+	if (!can_support_ecm(cdev->gadget)) {
+		dev_err(&gadget->dev, "controller '%s' not usable\n",
+				gadget->name);
+		return -EINVAL;
+	}
+
+	fi_ecm = usb_get_function_instance("ecm");
+	if (IS_ERR(fi_ecm))
+		return PTR_ERR(fi_ecm);
+
+	ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+	gether_set_qmult(ecm_opts->net, qmult);
+	if (!gether_set_host_addr(ecm_opts->net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+
+	fi_serial = usb_get_function_instance("acm");
+	if (IS_ERR(fi_serial)) {
+		status = PTR_ERR(fi_serial);
+		goto fail;
+	}
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail1;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto fail1;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* register our configuration */
+	status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
+	if (status < 0)
+		goto fail2;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+			DRIVER_DESC);
+
+	return 0;
+
+fail2:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail1:
+	usb_put_function_instance(fi_serial);
+fail:
+	usb_put_function_instance(fi_ecm);
+	return status;
+}
+
+static int cdc_unbind(struct usb_composite_dev *cdev)
+{
+	usb_put_function(f_acm);
+	usb_put_function_instance(fi_serial);
+	if (!IS_ERR_OR_NULL(f_ecm))
+		usb_put_function(f_ecm);
+	if (!IS_ERR_OR_NULL(fi_ecm))
+		usb_put_function_instance(fi_ecm);
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver cdc_driver = {
+	.name		= "g_cdc",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= cdc_bind,
+	.unbind		= cdc_unbind,
+};
+
+module_usb_composite_driver(cdc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
new file mode 100644
index 0000000..e1d566c
--- /dev/null
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dbgp.c -- EHCI Debug Port device gadget
+ *
+ * Copyright (C) 2010 Stephane Duverger
+ *
+ * Released under the GPLv2.
+ */
+
+/* verbose messages */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "u_serial.h"
+
+#define DRIVER_VENDOR_ID	0x0525 /* NetChip */
+#define DRIVER_PRODUCT_ID	0xc0de /* undefined */
+
+#define USB_DEBUG_MAX_PACKET_SIZE     8
+#define DBGP_REQ_EP0_LEN              128
+#define DBGP_REQ_LEN                  512
+
+static struct dbgp {
+	struct usb_gadget  *gadget;
+	struct usb_request *req;
+	struct usb_ep      *i_ep;
+	struct usb_ep      *o_ep;
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+	struct gserial     *serial;
+#endif
+} dbgp;
+
+static struct usb_device_descriptor device_desc = {
+	.bLength = sizeof device_desc,
+	.bDescriptorType = USB_DT_DEVICE,
+	.bcdUSB = cpu_to_le16(0x0200),
+	.bDeviceClass =	USB_CLASS_VENDOR_SPEC,
+	.idVendor = cpu_to_le16(DRIVER_VENDOR_ID),
+	.idProduct = cpu_to_le16(DRIVER_PRODUCT_ID),
+	.bNumConfigurations = 1,
+};
+
+static struct usb_debug_descriptor dbg_desc = {
+	.bLength = sizeof dbg_desc,
+	.bDescriptorType = USB_DT_DEBUG,
+};
+
+static struct usb_endpoint_descriptor i_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.bEndpointAddress = USB_DIR_IN,
+};
+
+static struct usb_endpoint_descriptor o_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.bEndpointAddress = USB_DIR_OUT,
+};
+
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+static int dbgp_consume(char *buf, unsigned len)
+{
+	char c;
+
+	if (!len)
+		return 0;
+
+	c = buf[len-1];
+	if (c != 0)
+		buf[len-1] = 0;
+
+	printk(KERN_NOTICE "%s%c", buf, c);
+	return 0;
+}
+
+static void __disable_ep(struct usb_ep *ep)
+{
+	usb_ep_disable(ep);
+}
+
+static void dbgp_disable_ep(void)
+{
+	__disable_ep(dbgp.i_ep);
+	__disable_ep(dbgp.o_ep);
+}
+
+static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int stp;
+	int err = 0;
+	int status = req->status;
+
+	if (ep == dbgp.i_ep) {
+		stp = 1;
+		goto fail;
+	}
+
+	if (status != 0) {
+		stp = 2;
+		goto release_req;
+	}
+
+	dbgp_consume(req->buf, req->actual);
+
+	req->length = DBGP_REQ_LEN;
+	err = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (err < 0) {
+		stp = 3;
+		goto release_req;
+	}
+
+	return;
+
+release_req:
+	kfree(req->buf);
+	usb_ep_free_request(dbgp.o_ep, req);
+	dbgp_disable_ep();
+fail:
+	dev_dbg(&dbgp.gadget->dev,
+		"complete: failure (%d:%d) ==> %d\n", stp, err, status);
+}
+
+static int dbgp_enable_ep_req(struct usb_ep *ep)
+{
+	int err, stp;
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req) {
+		err = -ENOMEM;
+		stp = 1;
+		goto fail_1;
+	}
+
+	req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
+	if (!req->buf) {
+		err = -ENOMEM;
+		stp = 2;
+		goto fail_2;
+	}
+
+	req->complete = dbgp_complete;
+	req->length = DBGP_REQ_LEN;
+	err = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (err < 0) {
+		stp = 3;
+		goto fail_3;
+	}
+
+	return 0;
+
+fail_3:
+	kfree(req->buf);
+fail_2:
+	usb_ep_free_request(dbgp.o_ep, req);
+fail_1:
+	dev_dbg(&dbgp.gadget->dev,
+		"enable ep req: failure (%d:%d)\n", stp, err);
+	return err;
+}
+
+static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
+{
+	int err;
+	ep->desc = desc;
+	err = usb_ep_enable(ep);
+	return err;
+}
+
+static int dbgp_enable_ep(void)
+{
+	int err, stp;
+
+	err = __enable_ep(dbgp.i_ep, &i_desc);
+	if (err < 0) {
+		stp = 1;
+		goto fail_1;
+	}
+
+	err = __enable_ep(dbgp.o_ep, &o_desc);
+	if (err < 0) {
+		stp = 2;
+		goto fail_2;
+	}
+
+	err = dbgp_enable_ep_req(dbgp.o_ep);
+	if (err < 0) {
+		stp = 3;
+		goto fail_3;
+	}
+
+	return 0;
+
+fail_3:
+	__disable_ep(dbgp.o_ep);
+fail_2:
+	__disable_ep(dbgp.i_ep);
+fail_1:
+	dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
+	return err;
+}
+#endif
+
+static void dbgp_disconnect(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+	dbgp_disable_ep();
+#else
+	gserial_disconnect(dbgp.serial);
+#endif
+}
+
+static void dbgp_unbind(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+	kfree(dbgp.serial);
+	dbgp.serial = NULL;
+#endif
+	if (dbgp.req) {
+		kfree(dbgp.req->buf);
+		usb_ep_free_request(gadget->ep0, dbgp.req);
+		dbgp.req = NULL;
+	}
+}
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+static unsigned char tty_line;
+#endif
+
+static int dbgp_configure_endpoints(struct usb_gadget *gadget)
+{
+	int stp;
+
+	usb_ep_autoconfig_reset(gadget);
+
+	dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
+	if (!dbgp.i_ep) {
+		stp = 1;
+		goto fail_1;
+	}
+
+	i_desc.wMaxPacketSize =
+		cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+	dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
+	if (!dbgp.o_ep) {
+		stp = 2;
+		goto fail_1;
+	}
+
+	o_desc.wMaxPacketSize =
+		cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+	dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress;
+	dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+	dbgp.serial->in = dbgp.i_ep;
+	dbgp.serial->out = dbgp.o_ep;
+
+	dbgp.serial->in->desc = &i_desc;
+	dbgp.serial->out->desc = &o_desc;
+#endif
+
+	return 0;
+
+fail_1:
+	dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
+	return -ENODEV;
+}
+
+static int dbgp_bind(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	int err, stp;
+
+	dbgp.gadget = gadget;
+
+	dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!dbgp.req) {
+		err = -ENOMEM;
+		stp = 1;
+		goto fail;
+	}
+
+	dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
+	if (!dbgp.req->buf) {
+		err = -ENOMEM;
+		stp = 2;
+		goto fail;
+	}
+
+	dbgp.req->length = DBGP_REQ_EP0_LEN;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+	dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
+	if (!dbgp.serial) {
+		stp = 3;
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	if (gserial_alloc_line(&tty_line)) {
+		stp = 4;
+		err = -ENODEV;
+		goto fail;
+	}
+#endif
+
+	err = dbgp_configure_endpoints(gadget);
+	if (err < 0) {
+		stp = 5;
+		goto fail;
+	}
+
+	dev_dbg(&dbgp.gadget->dev, "bind: success\n");
+	return 0;
+
+fail:
+	dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
+	dbgp_unbind(gadget);
+	return err;
+}
+
+static void dbgp_setup_complete(struct usb_ep *ep,
+				struct usb_request *req)
+{
+	dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
+		req->status, req->actual, req->length);
+}
+
+static int dbgp_setup(struct usb_gadget *gadget,
+		      const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request *req = dbgp.req;
+	u8 request = ctrl->bRequest;
+	u16 value = le16_to_cpu(ctrl->wValue);
+	u16 length = le16_to_cpu(ctrl->wLength);
+	int err = -EOPNOTSUPP;
+	void *data = NULL;
+	u16 len = 0;
+
+	if (request == USB_REQ_GET_DESCRIPTOR) {
+		switch (value>>8) {
+		case USB_DT_DEVICE:
+			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");
+			len = sizeof dbg_desc;
+			data = &dbg_desc;
+			break;
+		default:
+			goto fail;
+		}
+		err = 0;
+	} else if (request == USB_REQ_SET_FEATURE &&
+		   value == USB_DEVICE_DEBUG_MODE) {
+		dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+		err = dbgp_enable_ep();
+#else
+		err = dbgp_configure_endpoints(gadget);
+		if (err < 0) {
+			goto fail;
+		}
+		err = gserial_connect(dbgp.serial, tty_line);
+#endif
+		if (err < 0)
+			goto fail;
+	} else
+		goto fail;
+
+	req->length = min(length, len);
+	req->zero = len < req->length;
+	if (data && req->length)
+		memcpy(req->buf, data, req->length);
+
+	req->complete = dbgp_setup_complete;
+	return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+
+fail:
+	dev_dbg(&dbgp.gadget->dev,
+		"setup: failure req %x v %x\n", request, value);
+	return err;
+}
+
+static struct usb_gadget_driver dbgp_driver = {
+	.function = "dbgp",
+	.max_speed = USB_SPEED_HIGH,
+	.bind = dbgp_bind,
+	.unbind = dbgp_unbind,
+	.setup = dbgp_setup,
+	.reset = dbgp_disconnect,
+	.disconnect = dbgp_disconnect,
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = "dbgp"
+	},
+};
+
+static int __init dbgp_init(void)
+{
+	return usb_gadget_probe_driver(&dbgp_driver);
+}
+
+static void __exit dbgp_exit(void)
+{
+	usb_gadget_unregister_driver(&dbgp_driver);
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+	gserial_free_line(tty_line);
+#endif
+}
+
+MODULE_AUTHOR("Stephane Duverger");
+MODULE_LICENSE("GPL");
+module_init(dbgp_init);
+module_exit(dbgp_exit);
diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c
new file mode 100644
index 0000000..30313b2
--- /dev/null
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+
+#if defined USB_ETH_RNDIS
+#  undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+#  define USB_ETH_RNDIS y
+#endif
+
+#include "u_ether.h"
+
+
+/*
+ * Ethernet gadget driver -- with CDC and non-CDC options
+ * Builds on hardware support for a full duplex link.
+ *
+ * CDC Ethernet is the standard USB solution for sending Ethernet frames
+ * using USB.  Real hardware tends to use the same framing protocol but look
+ * different for control features.  This driver strongly prefers to use
+ * this USB-IF standard as its open-systems interoperability solution;
+ * most host side USB stacks (except from Microsoft) support it.
+ *
+ * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
+ * TLA-soup.  "CDC ACM" (Abstract Control Model) is for modems, and a new
+ * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
+ *
+ * There's some hardware that can't talk CDC ECM.  We make that hardware
+ * implement a "minimalist" vendor-agnostic CDC core:  same framing, but
+ * link-level setup only requires activating the configuration.  Only the
+ * endpoint descriptors, and product/vendor IDs, are relevant; no control
+ * operations are available.  Linux supports it, but other host operating
+ * systems may not.  (This is a subset of CDC Ethernet.)
+ *
+ * It turns out that if you add a few descriptors to that "CDC Subset",
+ * (Windows) host side drivers from MCCI can treat it as one submode of
+ * a proprietary scheme called "SAFE" ... without needing to know about
+ * specific product/vendor IDs.  So we do that, making it easier to use
+ * those MS-Windows drivers.  Those added descriptors make it resemble a
+ * CDC MDLM device, but they don't change device behavior at all.  (See
+ * MCCI Engineering report 950198 "SAFE Networking Functions".)
+ *
+ * A third option is also in use.  Rather than CDC Ethernet, or something
+ * simpler, Microsoft pushes their own approach: RNDIS.  The published
+ * RNDIS specs are ambiguous and appear to be incomplete, and are also
+ * needlessly complex.  They borrow more from CDC ACM than CDC ECM.
+ */
+
+#define DRIVER_DESC		"Ethernet Gadget"
+#define DRIVER_VERSION		"Memorial Day 2008"
+
+#ifdef USB_ETH_RNDIS
+#define PREFIX			"RNDIS/"
+#else
+#define PREFIX			""
+#endif
+
+/*
+ * This driver aims for interoperability by using CDC ECM unless
+ *
+ *		can_support_ecm()
+ *
+ * returns false, in which case it supports the CDC Subset.  By default,
+ * that returns true; most hardware has no problems with CDC ECM, that's
+ * a good default.  Previous versions of this driver had no default; this
+ * version changes that, removing overhead for new controller support.
+ *
+ *	IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
+ */
+
+static inline bool has_rndis(void)
+{
+#ifdef	USB_ETH_RNDIS
+	return true;
+#else
+	return false;
+#endif
+}
+
+#include <linux/module.h>
+
+#include "u_ecm.h"
+#include "u_gether.h"
+#ifdef	USB_ETH_RNDIS
+#include "u_rndis.h"
+#include "rndis.h"
+#else
+#define rndis_borrow_net(...) do {} while (0)
+#endif
+#include "u_eem.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM		0x0525	/* NetChip */
+#define CDC_PRODUCT_NUM		0xa4a1	/* Linux-USB Ethernet Gadget */
+
+/* For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+ * with pxa250.  We're protocol-compatible, if the host-side drivers
+ * use the endpoint descriptors.  bcdDevice (version) is nonzero, so
+ * drivers that need to hard-wire endpoint numbers have a hook.
+ *
+ * The protocol is a minimal subset of CDC Ether, which works on any bulk
+ * hardware that's not deeply broken ... even on hardware that can't talk
+ * RNDIS (like SA-1100, with no interrupt endpoint, or anything that
+ * doesn't handle control-OUT).
+ */
+#define	SIMPLE_VENDOR_NUM	0x049f
+#define	SIMPLE_PRODUCT_NUM	0x505a
+
+/* For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it.  Unless it's
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
+ */
+#define RNDIS_VENDOR_NUM	0x0525	/* NetChip */
+#define RNDIS_PRODUCT_NUM	0xa4a2	/* Ethernet/RNDIS Gadget */
+
+/* For EEM gadgets */
+#define EEM_VENDOR_NUM		0x1d6b	/* Linux Foundation */
+#define EEM_PRODUCT_NUM		0x0102	/* EEM Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	.bDeviceClass =		USB_CLASS_COMM,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id defaults change according to what configs
+	 * we support.  (As does bNumConfigurations.)  These values can
+	 * also be overridden by module parameters.
+	 */
+	.idVendor =		cpu_to_le16 (CDC_VENDOR_NUM),
+	.idProduct =		cpu_to_le16 (CDC_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+
+static struct usb_function_instance *fi_eem;
+static struct usb_function *f_eem;
+
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We may not have an RNDIS configuration, but if we do it needs to be
+ * the first one present.  That's to make Microsoft's drivers happy,
+ * and to follow DOCSIS 1.0 (cable modem standard).
+ */
+static int rndis_do_config(struct usb_configuration *c)
+{
+	int status;
+
+	/* FIXME alloc iConfiguration string, set it in c->strings */
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_rndis = usb_get_function(fi_rndis);
+	if (IS_ERR(f_rndis))
+		return PTR_ERR(f_rndis);
+
+	status = usb_add_function(c, f_rndis);
+	if (status < 0)
+		usb_put_function(f_rndis);
+
+	return status;
+}
+
+static struct usb_configuration rndis_config_driver = {
+	.label			= "RNDIS",
+	.bConfigurationValue	= 2,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_ETH_EEM
+static bool use_eem = 1;
+#else
+static bool use_eem;
+#endif
+module_param(use_eem, bool, 0);
+MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
+
+/*
+ * We _always_ have an ECM, CDC Subset, or EEM configuration.
+ */
+static int eth_do_config(struct usb_configuration *c)
+{
+	int status = 0;
+
+	/* FIXME alloc iConfiguration string, set it in c->strings */
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	if (use_eem) {
+		f_eem = usb_get_function(fi_eem);
+		if (IS_ERR(f_eem))
+			return PTR_ERR(f_eem);
+
+		status = usb_add_function(c, f_eem);
+		if (status < 0)
+			usb_put_function(f_eem);
+
+		return status;
+	} else if (can_support_ecm(c->cdev->gadget)) {
+		f_ecm = usb_get_function(fi_ecm);
+		if (IS_ERR(f_ecm))
+			return PTR_ERR(f_ecm);
+
+		status = usb_add_function(c, f_ecm);
+		if (status < 0)
+			usb_put_function(f_ecm);
+
+		return status;
+	} else {
+		f_geth = usb_get_function(fi_geth);
+		if (IS_ERR(f_geth))
+			return PTR_ERR(f_geth);
+
+		status = usb_add_function(c, f_geth);
+		if (status < 0)
+			usb_put_function(f_geth);
+
+		return status;
+	}
+
+}
+
+static struct usb_configuration eth_config_driver = {
+	/* .label = f(hardware) */
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int eth_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct f_eem_opts	*eem_opts = NULL;
+	struct f_ecm_opts	*ecm_opts = NULL;
+	struct f_gether_opts	*geth_opts = NULL;
+	struct net_device	*net;
+	int			status;
+
+	/* set up main config label and device descriptor */
+	if (use_eem) {
+		/* EEM */
+		fi_eem = usb_get_function_instance("eem");
+		if (IS_ERR(fi_eem))
+			return PTR_ERR(fi_eem);
+
+		eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
+
+		net = eem_opts->net;
+
+		eth_config_driver.label = "CDC Ethernet (EEM)";
+		device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
+		device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
+	} else if (can_support_ecm(gadget)) {
+		/* ECM */
+
+		fi_ecm = usb_get_function_instance("ecm");
+		if (IS_ERR(fi_ecm))
+			return PTR_ERR(fi_ecm);
+
+		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+		net = ecm_opts->net;
+
+		eth_config_driver.label = "CDC Ethernet (ECM)";
+	} else {
+		/* CDC Subset */
+
+		fi_geth = usb_get_function_instance("geth");
+		if (IS_ERR(fi_geth))
+			return PTR_ERR(fi_geth);
+
+		geth_opts = container_of(fi_geth, struct f_gether_opts,
+					 func_inst);
+
+		net = geth_opts->net;
+
+		eth_config_driver.label = "CDC Subset/SAFE";
+
+		device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
+		device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
+		if (!has_rndis())
+			device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+	}
+
+	gether_set_qmult(net, qmult);
+	if (!gether_set_host_addr(net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+
+	if (has_rndis()) {
+		/* RNDIS plus ECM-or-Subset */
+		gether_set_gadget(net, cdev->gadget);
+		status = gether_register_netdev(net);
+		if (status)
+			goto fail;
+
+		if (use_eem)
+			eem_opts->bound = true;
+		else if (can_support_ecm(gadget))
+			ecm_opts->bound = true;
+		else
+			geth_opts->bound = true;
+
+		fi_rndis = usb_get_function_instance("rndis");
+		if (IS_ERR(fi_rndis)) {
+			status = PTR_ERR(fi_rndis);
+			goto fail;
+		}
+
+		rndis_borrow_net(fi_rndis, net);
+
+		device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
+		device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
+		device_desc.bNumConfigurations = 2;
+	}
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail1;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto fail1;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* register our configuration(s); RNDIS first, if it's used */
+	if (has_rndis()) {
+		status = usb_add_config(cdev, &rndis_config_driver,
+				rndis_do_config);
+		if (status < 0)
+			goto fail2;
+	}
+
+	status = usb_add_config(cdev, &eth_config_driver, eth_do_config);
+	if (status < 0)
+		goto fail2;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
+			DRIVER_DESC);
+
+	return 0;
+
+fail2:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail1:
+	if (has_rndis())
+		usb_put_function_instance(fi_rndis);
+fail:
+	if (use_eem)
+		usb_put_function_instance(fi_eem);
+	else if (can_support_ecm(gadget))
+		usb_put_function_instance(fi_ecm);
+	else
+		usb_put_function_instance(fi_geth);
+	return status;
+}
+
+static int eth_unbind(struct usb_composite_dev *cdev)
+{
+	if (has_rndis()) {
+		usb_put_function(f_rndis);
+		usb_put_function_instance(fi_rndis);
+	}
+	if (use_eem) {
+		usb_put_function(f_eem);
+		usb_put_function_instance(fi_eem);
+	} else if (can_support_ecm(cdev->gadget)) {
+		usb_put_function(f_ecm);
+		usb_put_function_instance(fi_ecm);
+	} else {
+		usb_put_function(f_geth);
+		usb_put_function_instance(fi_geth);
+	}
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver eth_driver = {
+	.name		= "g_ether",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_SUPER,
+	.bind		= eth_bind,
+	.unbind		= eth_unbind,
+};
+
+module_usb_composite_driver(eth_driver);
+
+MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
+MODULE_AUTHOR("David Brownell, Benedikt Spanger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
new file mode 100644
index 0000000..b640ed3
--- /dev/null
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * g_ffs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ */
+
+#define pr_fmt(fmt) "g_ffs: " fmt
+
+#include <linux/module.h>
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+#include <linux/netdevice.h>
+
+#  if defined USB_ETH_RNDIS
+#    undef USB_ETH_RNDIS
+#  endif
+#  ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+#    define USB_ETH_RNDIS y
+#  endif
+
+#  include "u_ecm.h"
+#  include "u_gether.h"
+#  ifdef USB_ETH_RNDIS
+#    include "u_rndis.h"
+#    include "rndis.h"
+#  endif
+#  include "u_ether.h"
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+#  ifdef CONFIG_USB_FUNCTIONFS_ETH
+static int eth_bind_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+#  endif
+#  ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+static int bind_rndis_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
+#  endif
+#endif
+
+#include "u_fs.h"
+
+#define DRIVER_NAME	"g_ffs"
+#define DRIVER_DESC	"USB Function Filesystem"
+#define DRIVER_VERSION	"24 Aug 2004"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+#define GFS_VENDOR_ID	0x1d6b	/* Linux Foundation */
+#define GFS_PRODUCT_ID	0x0105	/* FunctionFS Gadget */
+
+#define GFS_MAX_DEVS	10
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor gfs_dev_desc = {
+	.bLength		= sizeof gfs_dev_desc,
+	.bDescriptorType	= USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass		= USB_CLASS_PER_INTERFACE,
+
+	.idVendor		= cpu_to_le16(GFS_VENDOR_ID),
+	.idProduct		= cpu_to_le16(GFS_PRODUCT_ID),
+};
+
+static char *func_names[GFS_MAX_DEVS];
+static unsigned int func_num;
+
+module_param_named(bDeviceClass,    gfs_dev_desc.bDeviceClass,    byte,   0644);
+MODULE_PARM_DESC(bDeviceClass, "USB Device class");
+module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte,   0644);
+MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
+module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte,   0644);
+MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
+module_param_array_named(functions, func_names, charp, &func_num, 0);
+MODULE_PARM_DESC(functions, "USB Functions list");
+
+static const struct usb_descriptor_header *gfs_otg_desc[2];
+
+/* String IDs are assigned dynamically */
+static struct usb_string gfs_strings[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	{ .s = "FunctionFS + RNDIS" },
+#endif
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	{ .s = "FunctionFS + ECM" },
+#endif
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+	{ .s = "FunctionFS" },
+#endif
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings *gfs_dev_strings[] = {
+	&(struct usb_gadget_strings) {
+		.language	= 0x0409,	/* en-us */
+		.strings	= gfs_strings,
+	},
+	NULL,
+};
+
+struct gfs_configuration {
+	struct usb_configuration c;
+	int (*eth)(struct usb_configuration *c);
+	int num;
+};
+
+static struct gfs_configuration gfs_configurations[] = {
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	{
+		.eth		= bind_rndis_config,
+	},
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	{
+		.eth		= eth_bind_config,
+	},
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+	{
+	},
+#endif
+};
+
+static void *functionfs_acquire_dev(struct ffs_dev *dev);
+static void functionfs_release_dev(struct ffs_dev *dev);
+static int functionfs_ready_callback(struct ffs_data *ffs);
+static void functionfs_closed_callback(struct ffs_data *ffs);
+static int gfs_bind(struct usb_composite_dev *cdev);
+static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
+
+
+static struct usb_composite_driver gfs_driver = {
+	.name		= DRIVER_NAME,
+	.dev		= &gfs_dev_desc,
+	.strings	= gfs_dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= gfs_bind,
+	.unbind		= gfs_unbind,
+};
+
+static unsigned int missing_funcs;
+static bool gfs_registered;
+static bool gfs_single_func;
+static struct usb_function_instance **fi_ffs;
+static struct usb_function **f_ffs[] = {
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+	NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+	NULL,
+#endif
+};
+
+#define N_CONF ARRAY_SIZE(f_ffs)
+
+static int __init gfs_init(void)
+{
+	struct f_fs_opts *opts;
+	int i;
+	int ret = 0;
+
+	ENTER();
+
+	if (func_num < 2) {
+		gfs_single_func = true;
+		func_num = 1;
+	}
+
+	/*
+	 * Allocate in one chunk for easier maintenance
+	 */
+	f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
+	if (!f_ffs[0]) {
+		ret = -ENOMEM;
+		goto no_func;
+	}
+	for (i = 1; i < N_CONF; ++i)
+		f_ffs[i] = f_ffs[0] + i * func_num;
+
+	fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
+	if (!fi_ffs) {
+		ret = -ENOMEM;
+		goto no_func;
+	}
+
+	for (i = 0; i < func_num; i++) {
+		fi_ffs[i] = usb_get_function_instance("ffs");
+		if (IS_ERR(fi_ffs[i])) {
+			ret = PTR_ERR(fi_ffs[i]);
+			--i;
+			goto no_dev;
+		}
+		opts = to_f_fs_opts(fi_ffs[i]);
+		if (gfs_single_func)
+			ret = ffs_single_dev(opts->dev);
+		else
+			ret = ffs_name_dev(opts->dev, func_names[i]);
+		if (ret)
+			goto no_dev;
+		opts->dev->ffs_ready_callback = functionfs_ready_callback;
+		opts->dev->ffs_closed_callback = functionfs_closed_callback;
+		opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev;
+		opts->dev->ffs_release_dev_callback = functionfs_release_dev;
+		opts->no_configfs = true;
+	}
+
+	missing_funcs = func_num;
+
+	return 0;
+no_dev:
+	while (i >= 0)
+		usb_put_function_instance(fi_ffs[i--]);
+	kfree(fi_ffs);
+no_func:
+	kfree(f_ffs[0]);
+	return ret;
+}
+module_init(gfs_init);
+
+static void __exit gfs_exit(void)
+{
+	int i;
+
+	ENTER();
+
+	if (gfs_registered)
+		usb_composite_unregister(&gfs_driver);
+	gfs_registered = false;
+
+	kfree(f_ffs[0]);
+
+	for (i = 0; i < func_num; i++)
+		usb_put_function_instance(fi_ffs[i]);
+
+	kfree(fi_ffs);
+}
+module_exit(gfs_exit);
+
+static void *functionfs_acquire_dev(struct ffs_dev *dev)
+{
+	if (!try_module_get(THIS_MODULE))
+		return ERR_PTR(-ENOENT);
+
+	return NULL;
+}
+
+static void functionfs_release_dev(struct ffs_dev *dev)
+{
+	module_put(THIS_MODULE);
+}
+
+/*
+ * The caller of this function takes ffs_lock
+ */
+static int functionfs_ready_callback(struct ffs_data *ffs)
+{
+	int ret = 0;
+
+	if (--missing_funcs)
+		return 0;
+
+	if (gfs_registered)
+		return -EBUSY;
+
+	gfs_registered = true;
+
+	ret = usb_composite_probe(&gfs_driver);
+	if (unlikely(ret < 0)) {
+		++missing_funcs;
+		gfs_registered = false;
+	}
+
+	return ret;
+}
+
+/*
+ * The caller of this function takes ffs_lock
+ */
+static void functionfs_closed_callback(struct ffs_data *ffs)
+{
+	missing_funcs++;
+
+	if (gfs_registered)
+		usb_composite_unregister(&gfs_driver);
+	gfs_registered = false;
+}
+
+/*
+ * It is assumed that gfs_bind is called from a context where ffs_lock is held
+ */
+static int gfs_bind(struct usb_composite_dev *cdev)
+{
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+	struct net_device *net;
+#endif
+	int ret, i;
+
+	ENTER();
+
+	if (missing_funcs)
+		return -ENODEV;
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+	if (can_support_ecm(cdev->gadget)) {
+		struct f_ecm_opts *ecm_opts;
+
+		fi_ecm = usb_get_function_instance("ecm");
+		if (IS_ERR(fi_ecm))
+			return PTR_ERR(fi_ecm);
+		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+		net = ecm_opts->net;
+	} else {
+		struct f_gether_opts *geth_opts;
+
+		fi_geth = usb_get_function_instance("geth");
+		if (IS_ERR(fi_geth))
+			return PTR_ERR(fi_geth);
+		geth_opts = container_of(fi_geth, struct f_gether_opts,
+					 func_inst);
+		net = geth_opts->net;
+	}
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	{
+		fi_rndis = usb_get_function_instance("rndis");
+		if (IS_ERR(fi_rndis)) {
+			ret = PTR_ERR(fi_rndis);
+			goto error;
+		}
+#ifndef CONFIG_USB_FUNCTIONFS_ETH
+		net = container_of(fi_rndis, struct f_rndis_opts,
+				   func_inst)->net;
+#endif
+	}
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+	gether_set_qmult(net, qmult);
+	if (!gether_set_host_addr(net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH
+	gether_set_gadget(net, cdev->gadget);
+	ret = gether_register_netdev(net);
+	if (ret)
+		goto error_rndis;
+
+	if (can_support_ecm(cdev->gadget)) {
+		struct f_ecm_opts *ecm_opts;
+
+		ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+		ecm_opts->bound = true;
+	} else {
+		struct f_gether_opts *geth_opts;
+
+		geth_opts = container_of(fi_geth, struct f_gether_opts,
+					 func_inst);
+		geth_opts->bound = true;
+	}
+
+	rndis_borrow_net(fi_rndis, net);
+#endif
+
+	/* TODO: gstrings_attach? */
+	ret = usb_string_ids_tab(cdev, gfs_strings);
+	if (unlikely(ret < 0))
+		goto error_rndis;
+	gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(cdev->gadget) && !gfs_otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+		if (!usb_desc)
+			goto error_rndis;
+		usb_otg_descriptor_init(cdev->gadget, usb_desc);
+		gfs_otg_desc[0] = usb_desc;
+		gfs_otg_desc[1] = NULL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+		struct gfs_configuration *c = gfs_configurations + i;
+		int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
+
+		c->c.label			= gfs_strings[sid].s;
+		c->c.iConfiguration		= gfs_strings[sid].id;
+		c->c.bConfigurationValue	= 1 + i;
+		c->c.bmAttributes		= USB_CONFIG_ATT_SELFPOWER;
+
+		c->num = i;
+
+		ret = usb_add_config(cdev, &c->c, gfs_do_config);
+		if (unlikely(ret < 0))
+			goto error_unbind;
+	}
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	return 0;
+
+/* TODO */
+error_unbind:
+	kfree(gfs_otg_desc[0]);
+	gfs_otg_desc[0] = NULL;
+error_rndis:
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	usb_put_function_instance(fi_rndis);
+error:
+#endif
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+	if (can_support_ecm(cdev->gadget))
+		usb_put_function_instance(fi_ecm);
+	else
+		usb_put_function_instance(fi_geth);
+#endif
+	return ret;
+}
+
+/*
+ * It is assumed that gfs_unbind is called from a context where ffs_lock is held
+ */
+static int gfs_unbind(struct usb_composite_dev *cdev)
+{
+	int i;
+
+	ENTER();
+
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+	usb_put_function(f_rndis);
+	usb_put_function_instance(fi_rndis);
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+	if (can_support_ecm(cdev->gadget)) {
+		usb_put_function(f_ecm);
+		usb_put_function_instance(fi_ecm);
+	} else {
+		usb_put_function(f_geth);
+		usb_put_function_instance(fi_geth);
+	}
+#endif
+	for (i = 0; i < N_CONF * func_num; ++i)
+		usb_put_function(*(f_ffs[0] + i));
+
+	kfree(gfs_otg_desc[0]);
+	gfs_otg_desc[0] = NULL;
+
+	return 0;
+}
+
+/*
+ * It is assumed that gfs_do_config is called from a context where
+ * ffs_lock is held
+ */
+static int gfs_do_config(struct usb_configuration *c)
+{
+	struct gfs_configuration *gc =
+		container_of(c, struct gfs_configuration, c);
+	int i;
+	int ret;
+
+	if (missing_funcs)
+		return -ENODEV;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = gfs_otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	if (gc->eth) {
+		ret = gc->eth(c);
+		if (unlikely(ret < 0))
+			return ret;
+	}
+
+	for (i = 0; i < func_num; i++) {
+		f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]);
+		if (IS_ERR(f_ffs[gc->num][i])) {
+			ret = PTR_ERR(f_ffs[gc->num][i]);
+			goto error;
+		}
+		ret = usb_add_function(c, f_ffs[gc->num][i]);
+		if (ret < 0) {
+			usb_put_function(f_ffs[gc->num][i]);
+			goto error;
+		}
+	}
+
+	/*
+	 * After previous do_configs there may be some invalid
+	 * pointers in c->interface array.  This happens every time
+	 * a user space function with fewer interfaces than a user
+	 * space function that was run before the new one is run.  The
+	 * compasit's set_config() assumes that if there is no more
+	 * then MAX_CONFIG_INTERFACES interfaces in a configuration
+	 * then there is a NULL pointer after the last interface in
+	 * c->interface array.  We need to make sure this is true.
+	 */
+	if (c->next_interface_id < ARRAY_SIZE(c->interface))
+		c->interface[c->next_interface_id] = NULL;
+
+	return 0;
+error:
+	while (--i >= 0) {
+		if (!IS_ERR(f_ffs[gc->num][i]))
+			usb_remove_function(c, f_ffs[gc->num][i]);
+		usb_put_function(f_ffs[gc->num][i]);
+	}
+	return ret;
+}
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+
+static int eth_bind_config(struct usb_configuration *c)
+{
+	int status = 0;
+
+	if (can_support_ecm(c->cdev->gadget)) {
+		f_ecm = usb_get_function(fi_ecm);
+		if (IS_ERR(f_ecm))
+			return PTR_ERR(f_ecm);
+
+		status = usb_add_function(c, f_ecm);
+		if (status < 0)
+			usb_put_function(f_ecm);
+
+	} else {
+		f_geth = usb_get_function(fi_geth);
+		if (IS_ERR(f_geth))
+			return PTR_ERR(f_geth);
+
+		status = usb_add_function(c, f_geth);
+		if (status < 0)
+			usb_put_function(f_geth);
+	}
+	return status;
+}
+
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+
+static int bind_rndis_config(struct usb_configuration *c)
+{
+	int status = 0;
+
+	f_rndis = usb_get_function(fi_rndis);
+	if (IS_ERR(f_rndis))
+		return PTR_ERR(f_rndis);
+
+	status = usb_add_function(c, f_rndis);
+	if (status < 0)
+		usb_put_function(f_rndis);
+
+	return status;
+}
+
+#endif
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
new file mode 100644
index 0000000..9eea2d1
--- /dev/null
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <sound/initval.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+
+#include "u_midi.h"
+
+/*-------------------------------------------------------------------------*/
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+static const char longname[] = "MIDI Gadget";
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static int index = SNDRV_DEFAULT_IDX1;
+module_param(index, int, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(id, charp, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+static unsigned int buflen = 512;
+module_param(buflen, uint, S_IRUGO);
+MODULE_PARM_DESC(buflen, "MIDI buffer length");
+
+static unsigned int qlen = 32;
+module_param(qlen, uint, S_IRUGO);
+MODULE_PARM_DESC(qlen, "USB read and write request queue length");
+
+static unsigned int in_ports = 1;
+module_param(in_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(in_ports, "Number of MIDI input ports");
+
+static unsigned int out_ports = 1;
+module_param(out_ports, uint, S_IRUGO);
+MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x17b3		/* Grey Innovation */
+#define DRIVER_PRODUCT_NUM	0x0004		/* Linux-USB "MIDI Gadget" */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		USB_DT_DEVICE_SIZE,
+	.bDescriptorType =	USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.idVendor =		cpu_to_le16(DRIVER_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(DRIVER_PRODUCT_NUM),
+	/* .iManufacturer =	DYNAMIC */
+	/* .iProduct =		DYNAMIC */
+	.bNumConfigurations =	1,
+};
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s	= "Grey Innovation",
+	[USB_GADGET_PRODUCT_IDX].s	= "MIDI Gadget",
+	[USB_GADGET_SERIAL_IDX].s	= "",
+	[STRING_DESCRIPTION_IDX].s	= "MIDI",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_function_instance *fi_midi;
+static struct usb_function *f_midi;
+
+static int midi_unbind(struct usb_composite_dev *dev)
+{
+	usb_put_function(f_midi);
+	usb_put_function_instance(fi_midi);
+	return 0;
+}
+
+static struct usb_configuration midi_config = {
+	.label		= "MIDI Gadget",
+	.bConfigurationValue = 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes	= USB_CONFIG_ATT_ONE,
+	.MaxPower	= CONFIG_USB_GADGET_VBUS_DRAW,
+};
+
+static int midi_bind_config(struct usb_configuration *c)
+{
+	int status;
+
+	f_midi = usb_get_function(fi_midi);
+	if (IS_ERR(f_midi))
+		return PTR_ERR(f_midi);
+
+	status = usb_add_function(c, f_midi);
+	if (status < 0) {
+		usb_put_function(f_midi);
+		return status;
+	}
+
+	return 0;
+}
+
+static int midi_bind(struct usb_composite_dev *cdev)
+{
+	struct f_midi_opts *midi_opts;
+	int status;
+
+	fi_midi = usb_get_function_instance("midi");
+	if (IS_ERR(fi_midi))
+		return PTR_ERR(fi_midi);
+
+	midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst);
+	midi_opts->index = index;
+	midi_opts->id = id;
+	midi_opts->in_ports = in_ports;
+	midi_opts->out_ports = out_ports;
+	midi_opts->buflen = buflen;
+	midi_opts->qlen = qlen;
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto put;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+	midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
+
+	status = usb_add_config(cdev, &midi_config, midi_bind_config);
+	if (status < 0)
+		goto put;
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	pr_info("%s\n", longname);
+	return 0;
+put:
+	usb_put_function_instance(fi_midi);
+	return status;
+}
+
+static struct usb_composite_driver midi_driver = {
+	.name		= (char *) longname,
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= midi_bind,
+	.unbind		= midi_unbind,
+};
+
+module_usb_composite_driver(midi_driver);
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
new file mode 100644
index 0000000..c4eda7f
--- /dev/null
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * hid.c -- HID Composite driver
+ *
+ * Based on multi.c
+ *
+ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/g_hid.h>
+
+#define DRIVER_DESC		"HID Gadget"
+#define DRIVER_VERSION		"2010/03/16"
+
+#include "u_hid.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define HIDG_VENDOR_NUM		0x0525	/* XXX NetChip */
+#define HIDG_PRODUCT_NUM	0xa4ac	/* Linux-USB HID gadget */
+
+/*-------------------------------------------------------------------------*/
+
+struct hidg_func_node {
+	struct usb_function_instance *fi;
+	struct usb_function *f;
+	struct list_head node;
+	struct hidg_func_descriptor *func;
+};
+
+static LIST_HEAD(hidg_func_list);
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	/* .bDeviceClass =		USB_CLASS_COMM, */
+	/* .bDeviceSubClass =	0, */
+	/* .bDeviceProtocol =	0, */
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	.idVendor =		cpu_to_le16(HIDG_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(HIDG_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+
+
+/****************************** Configurations ******************************/
+
+static int do_config(struct usb_configuration *c)
+{
+	struct hidg_func_node *e, *n;
+	int status = 0;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	list_for_each_entry(e, &hidg_func_list, node) {
+		e->f = usb_get_function(e->fi);
+		if (IS_ERR(e->f))
+			goto put;
+		status = usb_add_function(c, e->f);
+		if (status < 0) {
+			usb_put_function(e->f);
+			goto put;
+		}
+	}
+
+	return 0;
+put:
+	list_for_each_entry(n, &hidg_func_list, node) {
+		if (n == e)
+			break;
+		usb_remove_function(c, n->f);
+		usb_put_function(n->f);
+	}
+	return status;
+}
+
+static struct usb_configuration config_driver = {
+	.label			= "HID Gadget",
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/****************************** Gadget Bind ******************************/
+
+static int hid_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+	struct list_head *tmp;
+	struct hidg_func_node *n, *m;
+	struct f_hid_opts *hid_opts;
+	int status, funcs = 0;
+
+	list_for_each(tmp, &hidg_func_list)
+		funcs++;
+
+	if (!funcs)
+		return -ENODEV;
+
+	list_for_each_entry(n, &hidg_func_list, node) {
+		n->fi = usb_get_function_instance("hid");
+		if (IS_ERR(n->fi)) {
+			status = PTR_ERR(n->fi);
+			goto put;
+		}
+		hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
+		hid_opts->subclass = n->func->subclass;
+		hid_opts->protocol = n->func->protocol;
+		hid_opts->report_length = n->func->report_length;
+		hid_opts->report_desc_length = n->func->report_desc_length;
+		hid_opts->report_desc = n->func->report_desc;
+	}
+
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto put;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto put;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* register our configuration */
+	status = usb_add_config(cdev, &config_driver, do_config);
+	if (status < 0)
+		goto free_otg_desc;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+
+	return 0;
+
+free_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+put:
+	list_for_each_entry(m, &hidg_func_list, node) {
+		if (m == n)
+			break;
+		usb_put_function_instance(m->fi);
+	}
+	return status;
+}
+
+static int hid_unbind(struct usb_composite_dev *cdev)
+{
+	struct hidg_func_node *n;
+
+	list_for_each_entry(n, &hidg_func_list, node) {
+		usb_put_function(n->f);
+		usb_put_function_instance(n->fi);
+	}
+
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static int hidg_plat_driver_probe(struct platform_device *pdev)
+{
+	struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev);
+	struct hidg_func_node *entry;
+
+	if (!func) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		return -ENODEV;
+	}
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->func = func;
+	list_add_tail(&entry->node, &hidg_func_list);
+
+	return 0;
+}
+
+static int hidg_plat_driver_remove(struct platform_device *pdev)
+{
+	struct hidg_func_node *e, *n;
+
+	list_for_each_entry_safe(e, n, &hidg_func_list, node) {
+		list_del(&e->node);
+		kfree(e);
+	}
+
+	return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver hidg_driver = {
+	.name		= "g_hid",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= hid_bind,
+	.unbind		= hid_unbind,
+};
+
+static struct platform_driver hidg_plat_driver = {
+	.remove		= hidg_plat_driver_remove,
+	.driver		= {
+		.name	= "hidg",
+	},
+};
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard");
+MODULE_LICENSE("GPL");
+
+static int __init hidg_init(void)
+{
+	int status;
+
+	status = platform_driver_probe(&hidg_plat_driver,
+				hidg_plat_driver_probe);
+	if (status < 0)
+		return status;
+
+	status = usb_composite_probe(&hidg_driver);
+	if (status < 0)
+		platform_driver_unregister(&hidg_plat_driver);
+
+	return status;
+}
+module_init(hidg_init);
+
+static void __exit hidg_cleanup(void)
+{
+	usb_composite_unregister(&hidg_driver);
+	platform_driver_unregister(&hidg_plat_driver);
+}
+module_exit(hidg_cleanup);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
new file mode 100644
index 0000000..37ca0e6
--- /dev/null
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -0,0 +1,2096 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * inode.c -- user mode filesystem api for usb gadget controllers
+ *
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ */
+
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/uts.h>
+#include <linux/wait.h>
+#include <linux/compiler.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/mmu_context.h>
+#include <linux/aio.h>
+#include <linux/uio.h>
+#include <linux/refcount.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <linux/usb/gadgetfs.h>
+#include <linux/usb/gadget.h>
+
+
+/*
+ * The gadgetfs API maps each endpoint to a file descriptor so that you
+ * can use standard synchronous read/write calls for I/O.  There's some
+ * O_NONBLOCK and O_ASYNC/FASYNC style i/o support.  Example usermode
+ * drivers show how this works in practice.  You can also use AIO to
+ * eliminate I/O gaps between requests, to help when streaming data.
+ *
+ * Key parts that must be USB-specific are protocols defining how the
+ * read/write operations relate to the hardware state machines.  There
+ * are two types of files.  One type is for the device, implementing ep0.
+ * The other type is for each IN or OUT endpoint.  In both cases, the
+ * user mode driver must configure the hardware before using it.
+ *
+ * - First, dev_config() is called when /dev/gadget/$CHIP is configured
+ *   (by writing configuration and device descriptors).  Afterwards it
+ *   may serve as a source of device events, used to handle all control
+ *   requests other than basic enumeration.
+ *
+ * - Then, after a SET_CONFIGURATION control request, ep_config() is
+ *   called when each /dev/gadget/ep* file is configured (by writing
+ *   endpoint descriptors).  Afterwards these files are used to write()
+ *   IN data or to read() OUT data.  To halt the endpoint, a "wrong
+ *   direction" request is issued (like reading an IN endpoint).
+ *
+ * Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe
+ * not possible on all hardware.  For example, precise fault handling with
+ * respect to data left in endpoint fifos after aborted operations; or
+ * selective clearing of endpoint halts, to implement SET_INTERFACE.
+ */
+
+#define	DRIVER_DESC	"USB Gadget filesystem"
+#define	DRIVER_VERSION	"24 Aug 2004"
+
+static const char driver_desc [] = DRIVER_DESC;
+static const char shortname [] = "gadgetfs";
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("GPL");
+
+static int ep_open(struct inode *, struct file *);
+
+
+/*----------------------------------------------------------------------*/
+
+#define GADGETFS_MAGIC		0xaee71ee7
+
+/* /dev/gadget/$CHIP represents ep0 and the whole device */
+enum ep0_state {
+	/* DISABLED is the initial state. */
+	STATE_DEV_DISABLED = 0,
+
+	/* Only one open() of /dev/gadget/$CHIP; only one file tracks
+	 * ep0/device i/o modes and binding to the controller.  Driver
+	 * must always write descriptors to initialize the device, then
+	 * the device becomes UNCONNECTED until enumeration.
+	 */
+	STATE_DEV_OPENED,
+
+	/* From then on, ep0 fd is in either of two basic modes:
+	 * - (UN)CONNECTED: read usb_gadgetfs_event(s) from it
+	 * - SETUP: read/write will transfer control data and succeed;
+	 *   or if "wrong direction", performs protocol stall
+	 */
+	STATE_DEV_UNCONNECTED,
+	STATE_DEV_CONNECTED,
+	STATE_DEV_SETUP,
+
+	/* UNBOUND means the driver closed ep0, so the device won't be
+	 * accessible again (DEV_DISABLED) until all fds are closed.
+	 */
+	STATE_DEV_UNBOUND,
+};
+
+/* enough for the whole queue: most events invalidate others */
+#define	N_EVENT			5
+
+struct dev_data {
+	spinlock_t			lock;
+	refcount_t			count;
+	int				udc_usage;
+	enum ep0_state			state;		/* P: lock */
+	struct usb_gadgetfs_event	event [N_EVENT];
+	unsigned			ev_next;
+	struct fasync_struct		*fasync;
+	u8				current_config;
+
+	/* drivers reading ep0 MUST handle control requests (SETUP)
+	 * reported that way; else the host will time out.
+	 */
+	unsigned			usermode_setup : 1,
+					setup_in : 1,
+					setup_can_stall : 1,
+					setup_out_ready : 1,
+					setup_out_error : 1,
+					setup_abort : 1,
+					gadget_registered : 1;
+	unsigned			setup_wLength;
+
+	/* the rest is basically write-once */
+	struct usb_config_descriptor	*config, *hs_config;
+	struct usb_device_descriptor	*dev;
+	struct usb_request		*req;
+	struct usb_gadget		*gadget;
+	struct list_head		epfiles;
+	void				*buf;
+	wait_queue_head_t		wait;
+	struct super_block		*sb;
+	struct dentry			*dentry;
+
+	/* except this scratch i/o buffer for ep0 */
+	u8				rbuf [256];
+};
+
+static inline void get_dev (struct dev_data *data)
+{
+	refcount_inc (&data->count);
+}
+
+static void put_dev (struct dev_data *data)
+{
+	if (likely (!refcount_dec_and_test (&data->count)))
+		return;
+	/* needs no more cleanup */
+	BUG_ON (waitqueue_active (&data->wait));
+	kfree (data);
+}
+
+static struct dev_data *dev_new (void)
+{
+	struct dev_data		*dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+	dev->state = STATE_DEV_DISABLED;
+	refcount_set (&dev->count, 1);
+	spin_lock_init (&dev->lock);
+	INIT_LIST_HEAD (&dev->epfiles);
+	init_waitqueue_head (&dev->wait);
+	return dev;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* other /dev/gadget/$ENDPOINT files represent endpoints */
+enum ep_state {
+	STATE_EP_DISABLED = 0,
+	STATE_EP_READY,
+	STATE_EP_ENABLED,
+	STATE_EP_UNBOUND,
+};
+
+struct ep_data {
+	struct mutex			lock;
+	enum ep_state			state;
+	refcount_t			count;
+	struct dev_data			*dev;
+	/* must hold dev->lock before accessing ep or req */
+	struct usb_ep			*ep;
+	struct usb_request		*req;
+	ssize_t				status;
+	char				name [16];
+	struct usb_endpoint_descriptor	desc, hs_desc;
+	struct list_head		epfiles;
+	wait_queue_head_t		wait;
+	struct dentry			*dentry;
+};
+
+static inline void get_ep (struct ep_data *data)
+{
+	refcount_inc (&data->count);
+}
+
+static void put_ep (struct ep_data *data)
+{
+	if (likely (!refcount_dec_and_test (&data->count)))
+		return;
+	put_dev (data->dev);
+	/* needs no more cleanup */
+	BUG_ON (!list_empty (&data->epfiles));
+	BUG_ON (waitqueue_active (&data->wait));
+	kfree (data);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* most "how to use the hardware" policy choices are in userspace:
+ * mapping endpoint roles (which the driver needs) to the capabilities
+ * which the usb controller has.  most of those capabilities are exposed
+ * implicitly, starting with the driver name and then endpoint names.
+ */
+
+static const char *CHIP;
+
+/*----------------------------------------------------------------------*/
+
+/* NOTE:  don't use dev_printk calls before binding to the gadget
+ * at the end of ep0 configuration, or after unbind.
+ */
+
+/* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */
+#define xprintk(d,level,fmt,args...) \
+	printk(level "%s: " fmt , shortname , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VDEBUG	DBG
+#else
+#define VDEBUG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+
+/*----------------------------------------------------------------------*/
+
+/* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso)
+ *
+ * After opening, configure non-control endpoints.  Then use normal
+ * stream read() and write() requests; and maybe ioctl() to get more
+ * precise FIFO status when recovering from cancellation.
+ */
+
+static void epio_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct ep_data	*epdata = ep->driver_data;
+
+	if (!req->context)
+		return;
+	if (req->status)
+		epdata->status = req->status;
+	else
+		epdata->status = req->actual;
+	complete ((struct completion *)req->context);
+}
+
+/* tasklock endpoint, returning when it's connected.
+ * still need dev->lock to use epdata->ep.
+ */
+static int
+get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write)
+{
+	int	val;
+
+	if (f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&epdata->lock))
+			goto nonblock;
+		if (epdata->state != STATE_EP_ENABLED &&
+		    (!is_write || epdata->state != STATE_EP_READY)) {
+			mutex_unlock(&epdata->lock);
+nonblock:
+			val = -EAGAIN;
+		} else
+			val = 0;
+		return val;
+	}
+
+	val = mutex_lock_interruptible(&epdata->lock);
+	if (val < 0)
+		return val;
+
+	switch (epdata->state) {
+	case STATE_EP_ENABLED:
+		return 0;
+	case STATE_EP_READY:			/* not configured yet */
+		if (is_write)
+			return 0;
+		// FALLTHRU
+	case STATE_EP_UNBOUND:			/* clean disconnect */
+		break;
+	// case STATE_EP_DISABLED:		/* "can't happen" */
+	default:				/* error! */
+		pr_debug ("%s: ep %p not available, state %d\n",
+				shortname, epdata, epdata->state);
+	}
+	mutex_unlock(&epdata->lock);
+	return -ENODEV;
+}
+
+static ssize_t
+ep_io (struct ep_data *epdata, void *buf, unsigned len)
+{
+	DECLARE_COMPLETION_ONSTACK (done);
+	int value;
+
+	spin_lock_irq (&epdata->dev->lock);
+	if (likely (epdata->ep != NULL)) {
+		struct usb_request	*req = epdata->req;
+
+		req->context = &done;
+		req->complete = epio_complete;
+		req->buf = buf;
+		req->length = len;
+		value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC);
+	} else
+		value = -ENODEV;
+	spin_unlock_irq (&epdata->dev->lock);
+
+	if (likely (value == 0)) {
+		value = wait_event_interruptible (done.wait, done.done);
+		if (value != 0) {
+			spin_lock_irq (&epdata->dev->lock);
+			if (likely (epdata->ep != NULL)) {
+				DBG (epdata->dev, "%s i/o interrupted\n",
+						epdata->name);
+				usb_ep_dequeue (epdata->ep, epdata->req);
+				spin_unlock_irq (&epdata->dev->lock);
+
+				wait_event (done.wait, done.done);
+				if (epdata->status == -ECONNRESET)
+					epdata->status = -EINTR;
+			} else {
+				spin_unlock_irq (&epdata->dev->lock);
+
+				DBG (epdata->dev, "endpoint gone\n");
+				epdata->status = -ENODEV;
+			}
+		}
+		return epdata->status;
+	}
+	return value;
+}
+
+static int
+ep_release (struct inode *inode, struct file *fd)
+{
+	struct ep_data		*data = fd->private_data;
+	int value;
+
+	value = mutex_lock_interruptible(&data->lock);
+	if (value < 0)
+		return value;
+
+	/* clean up if this can be reopened */
+	if (data->state != STATE_EP_UNBOUND) {
+		data->state = STATE_EP_DISABLED;
+		data->desc.bDescriptorType = 0;
+		data->hs_desc.bDescriptorType = 0;
+		usb_ep_disable(data->ep);
+	}
+	mutex_unlock(&data->lock);
+	put_ep (data);
+	return 0;
+}
+
+static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
+{
+	struct ep_data		*data = fd->private_data;
+	int			status;
+
+	if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
+		return status;
+
+	spin_lock_irq (&data->dev->lock);
+	if (likely (data->ep != NULL)) {
+		switch (code) {
+		case GADGETFS_FIFO_STATUS:
+			status = usb_ep_fifo_status (data->ep);
+			break;
+		case GADGETFS_FIFO_FLUSH:
+			usb_ep_fifo_flush (data->ep);
+			break;
+		case GADGETFS_CLEAR_HALT:
+			status = usb_ep_clear_halt (data->ep);
+			break;
+		default:
+			status = -ENOTTY;
+		}
+	} else
+		status = -ENODEV;
+	spin_unlock_irq (&data->dev->lock);
+	mutex_unlock(&data->lock);
+	return status;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
+
+struct kiocb_priv {
+	struct usb_request	*req;
+	struct ep_data		*epdata;
+	struct kiocb		*iocb;
+	struct mm_struct	*mm;
+	struct work_struct	work;
+	void			*buf;
+	struct iov_iter		to;
+	const void		*to_free;
+	unsigned		actual;
+};
+
+static int ep_aio_cancel(struct kiocb *iocb)
+{
+	struct kiocb_priv	*priv = iocb->private;
+	struct ep_data		*epdata;
+	int			value;
+
+	local_irq_disable();
+	epdata = priv->epdata;
+	// spin_lock(&epdata->dev->lock);
+	if (likely(epdata && epdata->ep && priv->req))
+		value = usb_ep_dequeue (epdata->ep, priv->req);
+	else
+		value = -EINVAL;
+	// spin_unlock(&epdata->dev->lock);
+	local_irq_enable();
+
+	return value;
+}
+
+static void ep_user_copy_worker(struct work_struct *work)
+{
+	struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
+	struct mm_struct *mm = priv->mm;
+	struct kiocb *iocb = priv->iocb;
+	size_t ret;
+
+	use_mm(mm);
+	ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
+	unuse_mm(mm);
+	if (!ret)
+		ret = -EFAULT;
+
+	/* completing the iocb can drop the ctx and mm, don't touch mm after */
+	iocb->ki_complete(iocb, ret, ret);
+
+	kfree(priv->buf);
+	kfree(priv->to_free);
+	kfree(priv);
+}
+
+static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct kiocb		*iocb = req->context;
+	struct kiocb_priv	*priv = iocb->private;
+	struct ep_data		*epdata = priv->epdata;
+
+	/* lock against disconnect (and ideally, cancel) */
+	spin_lock(&epdata->dev->lock);
+	priv->req = NULL;
+	priv->epdata = NULL;
+
+	/* if this was a write or a read returning no data then we
+	 * don't need to copy anything to userspace, so we can
+	 * complete the aio request immediately.
+	 */
+	if (priv->to_free == NULL || unlikely(req->actual == 0)) {
+		kfree(req->buf);
+		kfree(priv->to_free);
+		kfree(priv);
+		iocb->private = NULL;
+		/* aio_complete() reports bytes-transferred _and_ faults */
+
+		iocb->ki_complete(iocb, req->actual ? req->actual : req->status,
+				req->status);
+	} else {
+		/* ep_copy_to_user() won't report both; we hide some faults */
+		if (unlikely(0 != req->status))
+			DBG(epdata->dev, "%s fault %d len %d\n",
+				ep->name, req->status, req->actual);
+
+		priv->buf = req->buf;
+		priv->actual = req->actual;
+		INIT_WORK(&priv->work, ep_user_copy_worker);
+		schedule_work(&priv->work);
+	}
+
+	usb_ep_free_request(ep, req);
+	spin_unlock(&epdata->dev->lock);
+	put_ep(epdata);
+}
+
+static ssize_t ep_aio(struct kiocb *iocb,
+		      struct kiocb_priv *priv,
+		      struct ep_data *epdata,
+		      char *buf,
+		      size_t len)
+{
+	struct usb_request *req;
+	ssize_t value;
+
+	iocb->private = priv;
+	priv->iocb = iocb;
+
+	kiocb_set_cancel_fn(iocb, ep_aio_cancel);
+	get_ep(epdata);
+	priv->epdata = epdata;
+	priv->actual = 0;
+	priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
+
+	/* each kiocb is coupled to one usb_request, but we can't
+	 * allocate or submit those if the host disconnected.
+	 */
+	spin_lock_irq(&epdata->dev->lock);
+	value = -ENODEV;
+	if (unlikely(epdata->ep == NULL))
+		goto fail;
+
+	req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
+	value = -ENOMEM;
+	if (unlikely(!req))
+		goto fail;
+
+	priv->req = req;
+	req->buf = buf;
+	req->length = len;
+	req->complete = ep_aio_complete;
+	req->context = iocb;
+	value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
+	if (unlikely(0 != value)) {
+		usb_ep_free_request(epdata->ep, req);
+		goto fail;
+	}
+	spin_unlock_irq(&epdata->dev->lock);
+	return -EIOCBQUEUED;
+
+fail:
+	spin_unlock_irq(&epdata->dev->lock);
+	kfree(priv->to_free);
+	kfree(priv);
+	put_ep(epdata);
+	return value;
+}
+
+static ssize_t
+ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+	struct file *file = iocb->ki_filp;
+	struct ep_data *epdata = file->private_data;
+	size_t len = iov_iter_count(to);
+	ssize_t value;
+	char *buf;
+
+	if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
+		return value;
+
+	/* halt any endpoint by doing a "wrong direction" i/o call */
+	if (usb_endpoint_dir_in(&epdata->desc)) {
+		if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+		    !is_sync_kiocb(iocb)) {
+			mutex_unlock(&epdata->lock);
+			return -EINVAL;
+		}
+		DBG (epdata->dev, "%s halt\n", epdata->name);
+		spin_lock_irq(&epdata->dev->lock);
+		if (likely(epdata->ep != NULL))
+			usb_ep_set_halt(epdata->ep);
+		spin_unlock_irq(&epdata->dev->lock);
+		mutex_unlock(&epdata->lock);
+		return -EBADMSG;
+	}
+
+	buf = kmalloc(len, GFP_KERNEL);
+	if (unlikely(!buf)) {
+		mutex_unlock(&epdata->lock);
+		return -ENOMEM;
+	}
+	if (is_sync_kiocb(iocb)) {
+		value = ep_io(epdata, buf, len);
+		if (value >= 0 && (copy_to_iter(buf, value, to) != value))
+			value = -EFAULT;
+	} else {
+		struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+		value = -ENOMEM;
+		if (!priv)
+			goto fail;
+		priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
+		if (!priv->to_free) {
+			kfree(priv);
+			goto fail;
+		}
+		value = ep_aio(iocb, priv, epdata, buf, len);
+		if (value == -EIOCBQUEUED)
+			buf = NULL;
+	}
+fail:
+	kfree(buf);
+	mutex_unlock(&epdata->lock);
+	return value;
+}
+
+static ssize_t ep_config(struct ep_data *, const char *, size_t);
+
+static ssize_t
+ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *file = iocb->ki_filp;
+	struct ep_data *epdata = file->private_data;
+	size_t len = iov_iter_count(from);
+	bool configured;
+	ssize_t value;
+	char *buf;
+
+	if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
+		return value;
+
+	configured = epdata->state == STATE_EP_ENABLED;
+
+	/* halt any endpoint by doing a "wrong direction" i/o call */
+	if (configured && !usb_endpoint_dir_in(&epdata->desc)) {
+		if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+		    !is_sync_kiocb(iocb)) {
+			mutex_unlock(&epdata->lock);
+			return -EINVAL;
+		}
+		DBG (epdata->dev, "%s halt\n", epdata->name);
+		spin_lock_irq(&epdata->dev->lock);
+		if (likely(epdata->ep != NULL))
+			usb_ep_set_halt(epdata->ep);
+		spin_unlock_irq(&epdata->dev->lock);
+		mutex_unlock(&epdata->lock);
+		return -EBADMSG;
+	}
+
+	buf = kmalloc(len, GFP_KERNEL);
+	if (unlikely(!buf)) {
+		mutex_unlock(&epdata->lock);
+		return -ENOMEM;
+	}
+
+	if (unlikely(!copy_from_iter_full(buf, len, from))) {
+		value = -EFAULT;
+		goto out;
+	}
+
+	if (unlikely(!configured)) {
+		value = ep_config(epdata, buf, len);
+	} else if (is_sync_kiocb(iocb)) {
+		value = ep_io(epdata, buf, len);
+	} else {
+		struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+		value = -ENOMEM;
+		if (priv) {
+			value = ep_aio(iocb, priv, epdata, buf, len);
+			if (value == -EIOCBQUEUED)
+				buf = NULL;
+		}
+	}
+out:
+	kfree(buf);
+	mutex_unlock(&epdata->lock);
+	return value;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* used after endpoint configuration */
+static const struct file_operations ep_io_operations = {
+	.owner =	THIS_MODULE,
+
+	.open =		ep_open,
+	.release =	ep_release,
+	.llseek =	no_llseek,
+	.unlocked_ioctl = ep_ioctl,
+	.read_iter =	ep_read_iter,
+	.write_iter =	ep_write_iter,
+};
+
+/* ENDPOINT INITIALIZATION
+ *
+ *     fd = open ("/dev/gadget/$ENDPOINT", O_RDWR)
+ *     status = write (fd, descriptors, sizeof descriptors)
+ *
+ * That write establishes the endpoint configuration, configuring
+ * the controller to process bulk, interrupt, or isochronous transfers
+ * at the right maxpacket size, and so on.
+ *
+ * The descriptors are message type 1, identified by a host order u32
+ * at the beginning of what's written.  Descriptor order is: full/low
+ * speed descriptor, then optional high speed descriptor.
+ */
+static ssize_t
+ep_config (struct ep_data *data, const char *buf, size_t len)
+{
+	struct usb_ep		*ep;
+	u32			tag;
+	int			value, length = len;
+
+	if (data->state != STATE_EP_READY) {
+		value = -EL2HLT;
+		goto fail;
+	}
+
+	value = len;
+	if (len < USB_DT_ENDPOINT_SIZE + 4)
+		goto fail0;
+
+	/* we might need to change message format someday */
+	memcpy(&tag, buf, 4);
+	if (tag != 1) {
+		DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
+		goto fail0;
+	}
+	buf += 4;
+	len -= 4;
+
+	/* NOTE:  audio endpoint extensions not accepted here;
+	 * just don't include the extra bytes.
+	 */
+
+	/* full/low speed descriptor, then high speed */
+	memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
+	if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
+			|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
+		goto fail0;
+	if (len != USB_DT_ENDPOINT_SIZE) {
+		if (len != 2 * USB_DT_ENDPOINT_SIZE)
+			goto fail0;
+		memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
+			USB_DT_ENDPOINT_SIZE);
+		if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
+				|| data->hs_desc.bDescriptorType
+					!= USB_DT_ENDPOINT) {
+			DBG(data->dev, "config %s, bad hs length or type\n",
+					data->name);
+			goto fail0;
+		}
+	}
+
+	spin_lock_irq (&data->dev->lock);
+	if (data->dev->state == STATE_DEV_UNBOUND) {
+		value = -ENOENT;
+		goto gone;
+	} else {
+		ep = data->ep;
+		if (ep == NULL) {
+			value = -ENODEV;
+			goto gone;
+		}
+	}
+	switch (data->dev->gadget->speed) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+		ep->desc = &data->desc;
+		break;
+	case USB_SPEED_HIGH:
+		/* fails if caller didn't provide that descriptor... */
+		ep->desc = &data->hs_desc;
+		break;
+	default:
+		DBG(data->dev, "unconnected, %s init abandoned\n",
+				data->name);
+		value = -EINVAL;
+		goto gone;
+	}
+	value = usb_ep_enable(ep);
+	if (value == 0) {
+		data->state = STATE_EP_ENABLED;
+		value = length;
+	}
+gone:
+	spin_unlock_irq (&data->dev->lock);
+	if (value < 0) {
+fail:
+		data->desc.bDescriptorType = 0;
+		data->hs_desc.bDescriptorType = 0;
+	}
+	return value;
+fail0:
+	value = -EINVAL;
+	goto fail;
+}
+
+static int
+ep_open (struct inode *inode, struct file *fd)
+{
+	struct ep_data		*data = inode->i_private;
+	int			value = -EBUSY;
+
+	if (mutex_lock_interruptible(&data->lock) != 0)
+		return -EINTR;
+	spin_lock_irq (&data->dev->lock);
+	if (data->dev->state == STATE_DEV_UNBOUND)
+		value = -ENOENT;
+	else if (data->state == STATE_EP_DISABLED) {
+		value = 0;
+		data->state = STATE_EP_READY;
+		get_ep (data);
+		fd->private_data = data;
+		VDEBUG (data->dev, "%s ready\n", data->name);
+	} else
+		DBG (data->dev, "%s state %d\n",
+			data->name, data->state);
+	spin_unlock_irq (&data->dev->lock);
+	mutex_unlock(&data->lock);
+	return value;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* EP0 IMPLEMENTATION can be partly in userspace.
+ *
+ * Drivers that use this facility receive various events, including
+ * control requests the kernel doesn't handle.  Drivers that don't
+ * use this facility may be too simple-minded for real applications.
+ */
+
+static inline void ep0_readable (struct dev_data *dev)
+{
+	wake_up (&dev->wait);
+	kill_fasync (&dev->fasync, SIGIO, POLL_IN);
+}
+
+static void clean_req (struct usb_ep *ep, struct usb_request *req)
+{
+	struct dev_data		*dev = ep->driver_data;
+
+	if (req->buf != dev->rbuf) {
+		kfree(req->buf);
+		req->buf = dev->rbuf;
+	}
+	req->complete = epio_complete;
+	dev->setup_out_ready = 0;
+}
+
+static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct dev_data		*dev = ep->driver_data;
+	unsigned long		flags;
+	int			free = 1;
+
+	/* for control OUT, data must still get to userspace */
+	spin_lock_irqsave(&dev->lock, flags);
+	if (!dev->setup_in) {
+		dev->setup_out_error = (req->status != 0);
+		if (!dev->setup_out_error)
+			free = 0;
+		dev->setup_out_ready = 1;
+		ep0_readable (dev);
+	}
+
+	/* clean up as appropriate */
+	if (free && req->buf != &dev->rbuf)
+		clean_req (ep, req);
+	req->complete = epio_complete;
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
+{
+	struct dev_data	*dev = ep->driver_data;
+
+	if (dev->setup_out_ready) {
+		DBG (dev, "ep0 request busy!\n");
+		return -EBUSY;
+	}
+	if (len > sizeof (dev->rbuf))
+		req->buf = kmalloc(len, GFP_ATOMIC);
+	if (req->buf == NULL) {
+		req->buf = dev->rbuf;
+		return -ENOMEM;
+	}
+	req->complete = ep0_complete;
+	req->length = len;
+	req->zero = 0;
+	return 0;
+}
+
+static ssize_t
+ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
+{
+	struct dev_data			*dev = fd->private_data;
+	ssize_t				retval;
+	enum ep0_state			state;
+
+	spin_lock_irq (&dev->lock);
+	if (dev->state <= STATE_DEV_OPENED) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	/* report fd mode change before acting on it */
+	if (dev->setup_abort) {
+		dev->setup_abort = 0;
+		retval = -EIDRM;
+		goto done;
+	}
+
+	/* control DATA stage */
+	if ((state = dev->state) == STATE_DEV_SETUP) {
+
+		if (dev->setup_in) {		/* stall IN */
+			VDEBUG(dev, "ep0in stall\n");
+			(void) usb_ep_set_halt (dev->gadget->ep0);
+			retval = -EL2HLT;
+			dev->state = STATE_DEV_CONNECTED;
+
+		} else if (len == 0) {		/* ack SET_CONFIGURATION etc */
+			struct usb_ep		*ep = dev->gadget->ep0;
+			struct usb_request	*req = dev->req;
+
+			if ((retval = setup_req (ep, req, 0)) == 0) {
+				++dev->udc_usage;
+				spin_unlock_irq (&dev->lock);
+				retval = usb_ep_queue (ep, req, GFP_KERNEL);
+				spin_lock_irq (&dev->lock);
+				--dev->udc_usage;
+			}
+			dev->state = STATE_DEV_CONNECTED;
+
+			/* assume that was SET_CONFIGURATION */
+			if (dev->current_config) {
+				unsigned power;
+
+				if (gadget_is_dualspeed(dev->gadget)
+						&& (dev->gadget->speed
+							== USB_SPEED_HIGH))
+					power = dev->hs_config->bMaxPower;
+				else
+					power = dev->config->bMaxPower;
+				usb_gadget_vbus_draw(dev->gadget, 2 * power);
+			}
+
+		} else {			/* collect OUT data */
+			if ((fd->f_flags & O_NONBLOCK) != 0
+					&& !dev->setup_out_ready) {
+				retval = -EAGAIN;
+				goto done;
+			}
+			spin_unlock_irq (&dev->lock);
+			retval = wait_event_interruptible (dev->wait,
+					dev->setup_out_ready != 0);
+
+			/* FIXME state could change from under us */
+			spin_lock_irq (&dev->lock);
+			if (retval)
+				goto done;
+
+			if (dev->state != STATE_DEV_SETUP) {
+				retval = -ECANCELED;
+				goto done;
+			}
+			dev->state = STATE_DEV_CONNECTED;
+
+			if (dev->setup_out_error)
+				retval = -EIO;
+			else {
+				len = min (len, (size_t)dev->req->actual);
+				++dev->udc_usage;
+				spin_unlock_irq(&dev->lock);
+				if (copy_to_user (buf, dev->req->buf, len))
+					retval = -EFAULT;
+				else
+					retval = len;
+				spin_lock_irq(&dev->lock);
+				--dev->udc_usage;
+				clean_req (dev->gadget->ep0, dev->req);
+				/* NOTE userspace can't yet choose to stall */
+			}
+		}
+		goto done;
+	}
+
+	/* else normal: return event data */
+	if (len < sizeof dev->event [0]) {
+		retval = -EINVAL;
+		goto done;
+	}
+	len -= len % sizeof (struct usb_gadgetfs_event);
+	dev->usermode_setup = 1;
+
+scan:
+	/* return queued events right away */
+	if (dev->ev_next != 0) {
+		unsigned		i, n;
+
+		n = len / sizeof (struct usb_gadgetfs_event);
+		if (dev->ev_next < n)
+			n = dev->ev_next;
+
+		/* ep0 i/o has special semantics during STATE_DEV_SETUP */
+		for (i = 0; i < n; i++) {
+			if (dev->event [i].type == GADGETFS_SETUP) {
+				dev->state = STATE_DEV_SETUP;
+				n = i + 1;
+				break;
+			}
+		}
+		spin_unlock_irq (&dev->lock);
+		len = n * sizeof (struct usb_gadgetfs_event);
+		if (copy_to_user (buf, &dev->event, len))
+			retval = -EFAULT;
+		else
+			retval = len;
+		if (len > 0) {
+			/* NOTE this doesn't guard against broken drivers;
+			 * concurrent ep0 readers may lose events.
+			 */
+			spin_lock_irq (&dev->lock);
+			if (dev->ev_next > n) {
+				memmove(&dev->event[0], &dev->event[n],
+					sizeof (struct usb_gadgetfs_event)
+						* (dev->ev_next - n));
+			}
+			dev->ev_next -= n;
+			spin_unlock_irq (&dev->lock);
+		}
+		return retval;
+	}
+	if (fd->f_flags & O_NONBLOCK) {
+		retval = -EAGAIN;
+		goto done;
+	}
+
+	switch (state) {
+	default:
+		DBG (dev, "fail %s, state %d\n", __func__, state);
+		retval = -ESRCH;
+		break;
+	case STATE_DEV_UNCONNECTED:
+	case STATE_DEV_CONNECTED:
+		spin_unlock_irq (&dev->lock);
+		DBG (dev, "%s wait\n", __func__);
+
+		/* wait for events */
+		retval = wait_event_interruptible (dev->wait,
+				dev->ev_next != 0);
+		if (retval < 0)
+			return retval;
+		spin_lock_irq (&dev->lock);
+		goto scan;
+	}
+
+done:
+	spin_unlock_irq (&dev->lock);
+	return retval;
+}
+
+static struct usb_gadgetfs_event *
+next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type)
+{
+	struct usb_gadgetfs_event	*event;
+	unsigned			i;
+
+	switch (type) {
+	/* these events purge the queue */
+	case GADGETFS_DISCONNECT:
+		if (dev->state == STATE_DEV_SETUP)
+			dev->setup_abort = 1;
+		// FALL THROUGH
+	case GADGETFS_CONNECT:
+		dev->ev_next = 0;
+		break;
+	case GADGETFS_SETUP:		/* previous request timed out */
+	case GADGETFS_SUSPEND:		/* same effect */
+		/* these events can't be repeated */
+		for (i = 0; i != dev->ev_next; i++) {
+			if (dev->event [i].type != type)
+				continue;
+			DBG(dev, "discard old event[%d] %d\n", i, type);
+			dev->ev_next--;
+			if (i == dev->ev_next)
+				break;
+			/* indices start at zero, for simplicity */
+			memmove (&dev->event [i], &dev->event [i + 1],
+				sizeof (struct usb_gadgetfs_event)
+					* (dev->ev_next - i));
+		}
+		break;
+	default:
+		BUG ();
+	}
+	VDEBUG(dev, "event[%d] = %d\n", dev->ev_next, type);
+	event = &dev->event [dev->ev_next++];
+	BUG_ON (dev->ev_next > N_EVENT);
+	memset (event, 0, sizeof *event);
+	event->type = type;
+	return event;
+}
+
+static ssize_t
+ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+	struct dev_data		*dev = fd->private_data;
+	ssize_t			retval = -ESRCH;
+
+	/* report fd mode change before acting on it */
+	if (dev->setup_abort) {
+		dev->setup_abort = 0;
+		retval = -EIDRM;
+
+	/* data and/or status stage for control request */
+	} else if (dev->state == STATE_DEV_SETUP) {
+
+		len = min_t(size_t, len, dev->setup_wLength);
+		if (dev->setup_in) {
+			retval = setup_req (dev->gadget->ep0, dev->req, len);
+			if (retval == 0) {
+				dev->state = STATE_DEV_CONNECTED;
+				++dev->udc_usage;
+				spin_unlock_irq (&dev->lock);
+				if (copy_from_user (dev->req->buf, buf, len))
+					retval = -EFAULT;
+				else {
+					if (len < dev->setup_wLength)
+						dev->req->zero = 1;
+					retval = usb_ep_queue (
+						dev->gadget->ep0, dev->req,
+						GFP_KERNEL);
+				}
+				spin_lock_irq(&dev->lock);
+				--dev->udc_usage;
+				if (retval < 0) {
+					clean_req (dev->gadget->ep0, dev->req);
+				} else
+					retval = len;
+
+				return retval;
+			}
+
+		/* can stall some OUT transfers */
+		} else if (dev->setup_can_stall) {
+			VDEBUG(dev, "ep0out stall\n");
+			(void) usb_ep_set_halt (dev->gadget->ep0);
+			retval = -EL2HLT;
+			dev->state = STATE_DEV_CONNECTED;
+		} else {
+			DBG(dev, "bogus ep0out stall!\n");
+		}
+	} else
+		DBG (dev, "fail %s, state %d\n", __func__, dev->state);
+
+	return retval;
+}
+
+static int
+ep0_fasync (int f, struct file *fd, int on)
+{
+	struct dev_data		*dev = fd->private_data;
+	// caller must F_SETOWN before signal delivery happens
+	VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off");
+	return fasync_helper (f, fd, on, &dev->fasync);
+}
+
+static struct usb_gadget_driver gadgetfs_driver;
+
+static int
+dev_release (struct inode *inode, struct file *fd)
+{
+	struct dev_data		*dev = fd->private_data;
+
+	/* closing ep0 === shutdown all */
+
+	if (dev->gadget_registered) {
+		usb_gadget_unregister_driver (&gadgetfs_driver);
+		dev->gadget_registered = false;
+	}
+
+	/* at this point "good" hardware has disconnected the
+	 * device from USB; the host won't see it any more.
+	 * alternatively, all host requests will time out.
+	 */
+
+	kfree (dev->buf);
+	dev->buf = NULL;
+
+	/* other endpoints were all decoupled from this device */
+	spin_lock_irq(&dev->lock);
+	dev->state = STATE_DEV_DISABLED;
+	spin_unlock_irq(&dev->lock);
+
+	put_dev (dev);
+	return 0;
+}
+
+static __poll_t
+ep0_poll (struct file *fd, poll_table *wait)
+{
+       struct dev_data         *dev = fd->private_data;
+       __poll_t                mask = 0;
+
+	if (dev->state <= STATE_DEV_OPENED)
+		return DEFAULT_POLLMASK;
+
+       poll_wait(fd, &dev->wait, wait);
+
+       spin_lock_irq (&dev->lock);
+
+       /* report fd mode change before acting on it */
+       if (dev->setup_abort) {
+               dev->setup_abort = 0;
+               mask = EPOLLHUP;
+               goto out;
+       }
+
+       if (dev->state == STATE_DEV_SETUP) {
+               if (dev->setup_in || dev->setup_can_stall)
+                       mask = EPOLLOUT;
+       } else {
+               if (dev->ev_next != 0)
+                       mask = EPOLLIN;
+       }
+out:
+       spin_unlock_irq(&dev->lock);
+       return mask;
+}
+
+static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
+{
+	struct dev_data		*dev = fd->private_data;
+	struct usb_gadget	*gadget = dev->gadget;
+	long ret = -ENOTTY;
+
+	spin_lock_irq(&dev->lock);
+	if (dev->state == STATE_DEV_OPENED ||
+			dev->state == STATE_DEV_UNBOUND) {
+		/* Not bound to a UDC */
+	} else if (gadget->ops->ioctl) {
+		++dev->udc_usage;
+		spin_unlock_irq(&dev->lock);
+
+		ret = gadget->ops->ioctl (gadget, code, value);
+
+		spin_lock_irq(&dev->lock);
+		--dev->udc_usage;
+	}
+	spin_unlock_irq(&dev->lock);
+
+	return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* The in-kernel gadget driver handles most ep0 issues, in particular
+ * enumerating the single configuration (as provided from user space).
+ *
+ * Unrecognized ep0 requests may be handled in user space.
+ */
+
+static void make_qualifier (struct dev_data *dev)
+{
+	struct usb_qualifier_descriptor		qual;
+	struct usb_device_descriptor		*desc;
+
+	qual.bLength = sizeof qual;
+	qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+	qual.bcdUSB = cpu_to_le16 (0x0200);
+
+	desc = dev->dev;
+	qual.bDeviceClass = desc->bDeviceClass;
+	qual.bDeviceSubClass = desc->bDeviceSubClass;
+	qual.bDeviceProtocol = desc->bDeviceProtocol;
+
+	/* assumes ep0 uses the same value for both speeds ... */
+	qual.bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
+
+	qual.bNumConfigurations = 1;
+	qual.bRESERVED = 0;
+
+	memcpy (dev->rbuf, &qual, sizeof qual);
+}
+
+static int
+config_buf (struct dev_data *dev, u8 type, unsigned index)
+{
+	int		len;
+	int		hs = 0;
+
+	/* only one configuration */
+	if (index > 0)
+		return -EINVAL;
+
+	if (gadget_is_dualspeed(dev->gadget)) {
+		hs = (dev->gadget->speed == USB_SPEED_HIGH);
+		if (type == USB_DT_OTHER_SPEED_CONFIG)
+			hs = !hs;
+	}
+	if (hs) {
+		dev->req->buf = dev->hs_config;
+		len = le16_to_cpu(dev->hs_config->wTotalLength);
+	} else {
+		dev->req->buf = dev->config;
+		len = le16_to_cpu(dev->config->wTotalLength);
+	}
+	((u8 *)dev->req->buf) [1] = type;
+	return len;
+}
+
+static int
+gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct dev_data			*dev = get_gadget_data (gadget);
+	struct usb_request		*req = dev->req;
+	int				value = -EOPNOTSUPP;
+	struct usb_gadgetfs_event	*event;
+	u16				w_value = le16_to_cpu(ctrl->wValue);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+
+	spin_lock (&dev->lock);
+	dev->setup_abort = 0;
+	if (dev->state == STATE_DEV_UNCONNECTED) {
+		if (gadget_is_dualspeed(gadget)
+				&& gadget->speed == USB_SPEED_HIGH
+				&& dev->hs_config == NULL) {
+			spin_unlock(&dev->lock);
+			ERROR (dev, "no high speed config??\n");
+			return -EINVAL;
+		}
+
+		dev->state = STATE_DEV_CONNECTED;
+
+		INFO (dev, "connected\n");
+		event = next_event (dev, GADGETFS_CONNECT);
+		event->u.speed = gadget->speed;
+		ep0_readable (dev);
+
+	/* host may have given up waiting for response.  we can miss control
+	 * requests handled lower down (device/endpoint status and features);
+	 * then ep0_{read,write} will report the wrong status. controller
+	 * driver will have aborted pending i/o.
+	 */
+	} else if (dev->state == STATE_DEV_SETUP)
+		dev->setup_abort = 1;
+
+	req->buf = dev->rbuf;
+	req->context = NULL;
+	value = -EOPNOTSUPP;
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			goto unrecognized;
+		switch (w_value >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (w_length, (u16) sizeof *dev->dev);
+			dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
+			req->buf = dev->dev;
+			break;
+		case USB_DT_DEVICE_QUALIFIER:
+			if (!dev->hs_config)
+				break;
+			value = min (w_length, (u16)
+				sizeof (struct usb_qualifier_descriptor));
+			make_qualifier (dev);
+			break;
+		case USB_DT_OTHER_SPEED_CONFIG:
+			// FALLTHROUGH
+		case USB_DT_CONFIG:
+			value = config_buf (dev,
+					w_value >> 8,
+					w_value & 0xff);
+			if (value >= 0)
+				value = min (w_length, (u16) value);
+			break;
+		case USB_DT_STRING:
+			goto unrecognized;
+
+		default:		// all others are errors
+			break;
+		}
+		break;
+
+	/* currently one config, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			goto unrecognized;
+		if (0 == (u8) w_value) {
+			value = 0;
+			dev->current_config = 0;
+			usb_gadget_vbus_draw(gadget, 8 /* mA */ );
+			// user mode expected to disable endpoints
+		} else {
+			u8	config, power;
+
+			if (gadget_is_dualspeed(gadget)
+					&& gadget->speed == USB_SPEED_HIGH) {
+				config = dev->hs_config->bConfigurationValue;
+				power = dev->hs_config->bMaxPower;
+			} else {
+				config = dev->config->bConfigurationValue;
+				power = dev->config->bMaxPower;
+			}
+
+			if (config == (u8) w_value) {
+				value = 0;
+				dev->current_config = config;
+				usb_gadget_vbus_draw(gadget, 2 * power);
+			}
+		}
+
+		/* report SET_CONFIGURATION like any other control request,
+		 * except that usermode may not stall this.  the next
+		 * request mustn't be allowed start until this finishes:
+		 * endpoints and threads set up, etc.
+		 *
+		 * NOTE:  older PXA hardware (before PXA 255: without UDCCFR)
+		 * has bad/racey automagic that prevents synchronizing here.
+		 * even kernel mode drivers often miss them.
+		 */
+		if (value == 0) {
+			INFO (dev, "configuration #%d\n", dev->current_config);
+			usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
+			if (dev->usermode_setup) {
+				dev->setup_can_stall = 0;
+				goto delegate;
+			}
+		}
+		break;
+
+#ifndef	CONFIG_USB_PXA25X
+	/* PXA automagically handles this request too */
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != 0x80)
+			goto unrecognized;
+		*(u8 *)req->buf = dev->current_config;
+		value = min (w_length, (u16) 1);
+		break;
+#endif
+
+	default:
+unrecognized:
+		VDEBUG (dev, "%s req%02x.%02x v%04x i%04x l%d\n",
+			dev->usermode_setup ? "delegate" : "fail",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, le16_to_cpu(ctrl->wIndex), w_length);
+
+		/* if there's an ep0 reader, don't stall */
+		if (dev->usermode_setup) {
+			dev->setup_can_stall = 1;
+delegate:
+			dev->setup_in = (ctrl->bRequestType & USB_DIR_IN)
+						? 1 : 0;
+			dev->setup_wLength = w_length;
+			dev->setup_out_ready = 0;
+			dev->setup_out_error = 0;
+
+			/* read DATA stage for OUT right away */
+			if (unlikely (!dev->setup_in && w_length)) {
+				value = setup_req (gadget->ep0, dev->req,
+							w_length);
+				if (value < 0)
+					break;
+
+				++dev->udc_usage;
+				spin_unlock (&dev->lock);
+				value = usb_ep_queue (gadget->ep0, dev->req,
+							GFP_KERNEL);
+				spin_lock (&dev->lock);
+				--dev->udc_usage;
+				if (value < 0) {
+					clean_req (gadget->ep0, dev->req);
+					break;
+				}
+
+				/* we can't currently stall these */
+				dev->setup_can_stall = 0;
+			}
+
+			/* state changes when reader collects event */
+			event = next_event (dev, GADGETFS_SETUP);
+			event->u.setup = *ctrl;
+			ep0_readable (dev);
+			spin_unlock (&dev->lock);
+			return 0;
+		}
+	}
+
+	/* proceed with data transfer and status phases? */
+	if (value >= 0 && dev->state != STATE_DEV_SETUP) {
+		req->length = value;
+		req->zero = value < w_length;
+
+		++dev->udc_usage;
+		spin_unlock (&dev->lock);
+		value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
+		spin_lock(&dev->lock);
+		--dev->udc_usage;
+		spin_unlock(&dev->lock);
+		if (value < 0) {
+			DBG (dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+		}
+		return value;
+	}
+
+	/* device stalls when value < 0 */
+	spin_unlock (&dev->lock);
+	return value;
+}
+
+static void destroy_ep_files (struct dev_data *dev)
+{
+	DBG (dev, "%s %d\n", __func__, dev->state);
+
+	/* dev->state must prevent interference */
+	spin_lock_irq (&dev->lock);
+	while (!list_empty(&dev->epfiles)) {
+		struct ep_data	*ep;
+		struct inode	*parent;
+		struct dentry	*dentry;
+
+		/* break link to FS */
+		ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
+		list_del_init (&ep->epfiles);
+		spin_unlock_irq (&dev->lock);
+
+		dentry = ep->dentry;
+		ep->dentry = NULL;
+		parent = d_inode(dentry->d_parent);
+
+		/* break link to controller */
+		mutex_lock(&ep->lock);
+		if (ep->state == STATE_EP_ENABLED)
+			(void) usb_ep_disable (ep->ep);
+		ep->state = STATE_EP_UNBOUND;
+		usb_ep_free_request (ep->ep, ep->req);
+		ep->ep = NULL;
+		mutex_unlock(&ep->lock);
+
+		wake_up (&ep->wait);
+		put_ep (ep);
+
+		/* break link to dcache */
+		inode_lock(parent);
+		d_delete (dentry);
+		dput (dentry);
+		inode_unlock(parent);
+
+		spin_lock_irq (&dev->lock);
+	}
+	spin_unlock_irq (&dev->lock);
+}
+
+
+static struct dentry *
+gadgetfs_create_file (struct super_block *sb, char const *name,
+		void *data, const struct file_operations *fops);
+
+static int activate_ep_files (struct dev_data *dev)
+{
+	struct usb_ep	*ep;
+	struct ep_data	*data;
+
+	gadget_for_each_ep (ep, dev->gadget) {
+
+		data = kzalloc(sizeof(*data), GFP_KERNEL);
+		if (!data)
+			goto enomem0;
+		data->state = STATE_EP_DISABLED;
+		mutex_init(&data->lock);
+		init_waitqueue_head (&data->wait);
+
+		strncpy (data->name, ep->name, sizeof (data->name) - 1);
+		refcount_set (&data->count, 1);
+		data->dev = dev;
+		get_dev (dev);
+
+		data->ep = ep;
+		ep->driver_data = data;
+
+		data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
+		if (!data->req)
+			goto enomem1;
+
+		data->dentry = gadgetfs_create_file (dev->sb, data->name,
+				data, &ep_io_operations);
+		if (!data->dentry)
+			goto enomem2;
+		list_add_tail (&data->epfiles, &dev->epfiles);
+	}
+	return 0;
+
+enomem2:
+	usb_ep_free_request (ep, data->req);
+enomem1:
+	put_dev (dev);
+	kfree (data);
+enomem0:
+	DBG (dev, "%s enomem\n", __func__);
+	destroy_ep_files (dev);
+	return -ENOMEM;
+}
+
+static void
+gadgetfs_unbind (struct usb_gadget *gadget)
+{
+	struct dev_data		*dev = get_gadget_data (gadget);
+
+	DBG (dev, "%s\n", __func__);
+
+	spin_lock_irq (&dev->lock);
+	dev->state = STATE_DEV_UNBOUND;
+	while (dev->udc_usage > 0) {
+		spin_unlock_irq(&dev->lock);
+		usleep_range(1000, 2000);
+		spin_lock_irq(&dev->lock);
+	}
+	spin_unlock_irq (&dev->lock);
+
+	destroy_ep_files (dev);
+	gadget->ep0->driver_data = NULL;
+	set_gadget_data (gadget, NULL);
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req)
+		usb_ep_free_request (gadget->ep0, dev->req);
+	DBG (dev, "%s done\n", __func__);
+	put_dev (dev);
+}
+
+static struct dev_data		*the_device;
+
+static int gadgetfs_bind(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct dev_data		*dev = the_device;
+
+	if (!dev)
+		return -ESRCH;
+	if (0 != strcmp (CHIP, gadget->name)) {
+		pr_err("%s expected %s controller not %s\n",
+			shortname, CHIP, gadget->name);
+		return -ENODEV;
+	}
+
+	set_gadget_data (gadget, dev);
+	dev->gadget = gadget;
+	gadget->ep0->driver_data = dev;
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->context = NULL;
+	dev->req->complete = epio_complete;
+
+	if (activate_ep_files (dev) < 0)
+		goto enomem;
+
+	INFO (dev, "bound to %s driver\n", gadget->name);
+	spin_lock_irq(&dev->lock);
+	dev->state = STATE_DEV_UNCONNECTED;
+	spin_unlock_irq(&dev->lock);
+	get_dev (dev);
+	return 0;
+
+enomem:
+	gadgetfs_unbind (gadget);
+	return -ENOMEM;
+}
+
+static void
+gadgetfs_disconnect (struct usb_gadget *gadget)
+{
+	struct dev_data		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	if (dev->state == STATE_DEV_UNCONNECTED)
+		goto exit;
+	dev->state = STATE_DEV_UNCONNECTED;
+
+	INFO (dev, "disconnected\n");
+	next_event (dev, GADGETFS_DISCONNECT);
+	ep0_readable (dev);
+exit:
+	spin_unlock_irqrestore (&dev->lock, flags);
+}
+
+static void
+gadgetfs_suspend (struct usb_gadget *gadget)
+{
+	struct dev_data		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	INFO (dev, "suspended from state %d\n", dev->state);
+	spin_lock_irqsave(&dev->lock, flags);
+	switch (dev->state) {
+	case STATE_DEV_SETUP:		// VERY odd... host died??
+	case STATE_DEV_CONNECTED:
+	case STATE_DEV_UNCONNECTED:
+		next_event (dev, GADGETFS_SUSPEND);
+		ep0_readable (dev);
+		/* FALLTHROUGH */
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static struct usb_gadget_driver gadgetfs_driver = {
+	.function	= (char *) driver_desc,
+	.bind		= gadgetfs_bind,
+	.unbind		= gadgetfs_unbind,
+	.setup		= gadgetfs_setup,
+	.reset		= gadgetfs_disconnect,
+	.disconnect	= gadgetfs_disconnect,
+	.suspend	= gadgetfs_suspend,
+
+	.driver	= {
+		.name		= (char *) shortname,
+	},
+};
+
+/*----------------------------------------------------------------------*/
+/* DEVICE INITIALIZATION
+ *
+ *     fd = open ("/dev/gadget/$CHIP", O_RDWR)
+ *     status = write (fd, descriptors, sizeof descriptors)
+ *
+ * That write establishes the device configuration, so the kernel can
+ * bind to the controller ... guaranteeing it can handle enumeration
+ * at all necessary speeds.  Descriptor order is:
+ *
+ * . message tag (u32, host order) ... for now, must be zero; it
+ *	would change to support features like multi-config devices
+ * . full/low speed config ... all wTotalLength bytes (with interface,
+ *	class, altsetting, endpoint, and other descriptors)
+ * . high speed config ... all descriptors, for high speed operation;
+ *	this one's optional except for high-speed hardware
+ * . device descriptor
+ *
+ * Endpoints are not yet enabled. Drivers must wait until device
+ * configuration and interface altsetting changes create
+ * the need to configure (or unconfigure) them.
+ *
+ * After initialization, the device stays active for as long as that
+ * $CHIP file is open.  Events must then be read from that descriptor,
+ * such as configuration notifications.
+ */
+
+static int is_valid_config(struct usb_config_descriptor *config,
+		unsigned int total)
+{
+	return config->bDescriptorType == USB_DT_CONFIG
+		&& config->bLength == USB_DT_CONFIG_SIZE
+		&& total >= USB_DT_CONFIG_SIZE
+		&& config->bConfigurationValue != 0
+		&& (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
+		&& (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
+	/* FIXME if gadget->is_otg, _must_ include an otg descriptor */
+	/* FIXME check lengths: walk to end */
+}
+
+static ssize_t
+dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+	struct dev_data		*dev = fd->private_data;
+	ssize_t			value = len, length = len;
+	unsigned		total;
+	u32			tag;
+	char			*kbuf;
+
+	spin_lock_irq(&dev->lock);
+	if (dev->state > STATE_DEV_OPENED) {
+		value = ep0_write(fd, buf, len, ptr);
+		spin_unlock_irq(&dev->lock);
+		return value;
+	}
+	spin_unlock_irq(&dev->lock);
+
+	if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
+	    (len > PAGE_SIZE * 4))
+		return -EINVAL;
+
+	/* we might need to change message format someday */
+	if (copy_from_user (&tag, buf, 4))
+		return -EFAULT;
+	if (tag != 0)
+		return -EINVAL;
+	buf += 4;
+	length -= 4;
+
+	kbuf = memdup_user(buf, length);
+	if (IS_ERR(kbuf))
+		return PTR_ERR(kbuf);
+
+	spin_lock_irq (&dev->lock);
+	value = -EINVAL;
+	if (dev->buf) {
+		kfree(kbuf);
+		goto fail;
+	}
+	dev->buf = kbuf;
+
+	/* full or low speed config */
+	dev->config = (void *) kbuf;
+	total = le16_to_cpu(dev->config->wTotalLength);
+	if (!is_valid_config(dev->config, total) ||
+			total > length - USB_DT_DEVICE_SIZE)
+		goto fail;
+	kbuf += total;
+	length -= total;
+
+	/* optional high speed config */
+	if (kbuf [1] == USB_DT_CONFIG) {
+		dev->hs_config = (void *) kbuf;
+		total = le16_to_cpu(dev->hs_config->wTotalLength);
+		if (!is_valid_config(dev->hs_config, total) ||
+				total > length - USB_DT_DEVICE_SIZE)
+			goto fail;
+		kbuf += total;
+		length -= total;
+	} else {
+		dev->hs_config = NULL;
+	}
+
+	/* could support multiple configs, using another encoding! */
+
+	/* device descriptor (tweaked for paranoia) */
+	if (length != USB_DT_DEVICE_SIZE)
+		goto fail;
+	dev->dev = (void *)kbuf;
+	if (dev->dev->bLength != USB_DT_DEVICE_SIZE
+			|| dev->dev->bDescriptorType != USB_DT_DEVICE
+			|| dev->dev->bNumConfigurations != 1)
+		goto fail;
+	dev->dev->bcdUSB = cpu_to_le16 (0x0200);
+
+	/* triggers gadgetfs_bind(); then we can enumerate. */
+	spin_unlock_irq (&dev->lock);
+	if (dev->hs_config)
+		gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+	else
+		gadgetfs_driver.max_speed = USB_SPEED_FULL;
+
+	value = usb_gadget_probe_driver(&gadgetfs_driver);
+	if (value != 0) {
+		kfree (dev->buf);
+		dev->buf = NULL;
+	} else {
+		/* at this point "good" hardware has for the first time
+		 * let the USB the host see us.  alternatively, if users
+		 * unplug/replug that will clear all the error state.
+		 *
+		 * note:  everything running before here was guaranteed
+		 * to choke driver model style diagnostics.  from here
+		 * on, they can work ... except in cleanup paths that
+		 * kick in after the ep0 descriptor is closed.
+		 */
+		value = len;
+		dev->gadget_registered = true;
+	}
+	return value;
+
+fail:
+	spin_unlock_irq (&dev->lock);
+	pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
+	kfree (dev->buf);
+	dev->buf = NULL;
+	return value;
+}
+
+static int
+dev_open (struct inode *inode, struct file *fd)
+{
+	struct dev_data		*dev = inode->i_private;
+	int			value = -EBUSY;
+
+	spin_lock_irq(&dev->lock);
+	if (dev->state == STATE_DEV_DISABLED) {
+		dev->ev_next = 0;
+		dev->state = STATE_DEV_OPENED;
+		fd->private_data = dev;
+		get_dev (dev);
+		value = 0;
+	}
+	spin_unlock_irq(&dev->lock);
+	return value;
+}
+
+static const struct file_operations ep0_operations = {
+	.llseek =	no_llseek,
+
+	.open =		dev_open,
+	.read =		ep0_read,
+	.write =	dev_config,
+	.fasync =	ep0_fasync,
+	.poll =		ep0_poll,
+	.unlocked_ioctl = dev_ioctl,
+	.release =	dev_release,
+};
+
+/*----------------------------------------------------------------------*/
+
+/* FILESYSTEM AND SUPERBLOCK OPERATIONS
+ *
+ * Mounting the filesystem creates a controller file, used first for
+ * device configuration then later for event monitoring.
+ */
+
+
+/* FIXME PAM etc could set this security policy without mount options
+ * if epfiles inherited ownership and permissons from ep0 ...
+ */
+
+static unsigned default_uid;
+static unsigned default_gid;
+static unsigned default_perm = S_IRUSR | S_IWUSR;
+
+module_param (default_uid, uint, 0644);
+module_param (default_gid, uint, 0644);
+module_param (default_perm, uint, 0644);
+
+
+static struct inode *
+gadgetfs_make_inode (struct super_block *sb,
+		void *data, const struct file_operations *fops,
+		int mode)
+{
+	struct inode *inode = new_inode (sb);
+
+	if (inode) {
+		inode->i_ino = get_next_ino();
+		inode->i_mode = mode;
+		inode->i_uid = make_kuid(&init_user_ns, default_uid);
+		inode->i_gid = make_kgid(&init_user_ns, default_gid);
+		inode->i_atime = inode->i_mtime = inode->i_ctime
+				= current_time(inode);
+		inode->i_private = data;
+		inode->i_fop = fops;
+	}
+	return inode;
+}
+
+/* creates in fs root directory, so non-renamable and non-linkable.
+ * so inode and dentry are paired, until device reconfig.
+ */
+static struct dentry *
+gadgetfs_create_file (struct super_block *sb, char const *name,
+		void *data, const struct file_operations *fops)
+{
+	struct dentry	*dentry;
+	struct inode	*inode;
+
+	dentry = d_alloc_name(sb->s_root, name);
+	if (!dentry)
+		return NULL;
+
+	inode = gadgetfs_make_inode (sb, data, fops,
+			S_IFREG | (default_perm & S_IRWXUGO));
+	if (!inode) {
+		dput(dentry);
+		return NULL;
+	}
+	d_add (dentry, inode);
+	return dentry;
+}
+
+static const struct super_operations gadget_fs_operations = {
+	.statfs =	simple_statfs,
+	.drop_inode =	generic_delete_inode,
+};
+
+static int
+gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
+{
+	struct inode	*inode;
+	struct dev_data	*dev;
+
+	if (the_device)
+		return -ESRCH;
+
+	CHIP = usb_get_gadget_udc_name();
+	if (!CHIP)
+		return -ENODEV;
+
+	/* superblock */
+	sb->s_blocksize = PAGE_SIZE;
+	sb->s_blocksize_bits = PAGE_SHIFT;
+	sb->s_magic = GADGETFS_MAGIC;
+	sb->s_op = &gadget_fs_operations;
+	sb->s_time_gran = 1;
+
+	/* root inode */
+	inode = gadgetfs_make_inode (sb,
+			NULL, &simple_dir_operations,
+			S_IFDIR | S_IRUGO | S_IXUGO);
+	if (!inode)
+		goto Enomem;
+	inode->i_op = &simple_dir_inode_operations;
+	if (!(sb->s_root = d_make_root (inode)))
+		goto Enomem;
+
+	/* the ep0 file is named after the controller we expect;
+	 * user mode code can use it for sanity checks, like we do.
+	 */
+	dev = dev_new ();
+	if (!dev)
+		goto Enomem;
+
+	dev->sb = sb;
+	dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
+	if (!dev->dentry) {
+		put_dev(dev);
+		goto Enomem;
+	}
+
+	/* other endpoint files are available after hardware setup,
+	 * from binding to a controller.
+	 */
+	the_device = dev;
+	return 0;
+
+Enomem:
+	return -ENOMEM;
+}
+
+/* "mount -t gadgetfs path /dev/gadget" ends up here */
+static struct dentry *
+gadgetfs_mount (struct file_system_type *t, int flags,
+		const char *path, void *opts)
+{
+	return mount_single (t, flags, opts, gadgetfs_fill_super);
+}
+
+static void
+gadgetfs_kill_sb (struct super_block *sb)
+{
+	kill_litter_super (sb);
+	if (the_device) {
+		put_dev (the_device);
+		the_device = NULL;
+	}
+	kfree(CHIP);
+	CHIP = NULL;
+}
+
+/*----------------------------------------------------------------------*/
+
+static struct file_system_type gadgetfs_type = {
+	.owner		= THIS_MODULE,
+	.name		= shortname,
+	.mount		= gadgetfs_mount,
+	.kill_sb	= gadgetfs_kill_sb,
+};
+MODULE_ALIAS_FS("gadgetfs");
+
+/*----------------------------------------------------------------------*/
+
+static int __init init (void)
+{
+	int status;
+
+	status = register_filesystem (&gadgetfs_type);
+	if (status == 0)
+		pr_info ("%s: %s, version " DRIVER_VERSION "\n",
+			shortname, driver_desc);
+	return status;
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	pr_debug ("unregister %s\n", shortname);
+	unregister_filesystem (&gadgetfs_type);
+}
+module_exit (cleanup);
+
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
new file mode 100644
index 0000000..fd5595a
--- /dev/null
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * mass_storage.c -- Mass Storage USB Gadget
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ *                    Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ */
+
+
+/*
+ * The Mass Storage Gadget acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive.  In
+ * addition to providing an example of a genuinely useful gadget
+ * driver for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.  Last but not least, it
+ * gives an easy way to probe the behavior of the Mass Storage drivers
+ * in a USB host.
+ *
+ * Since this file serves only administrative purposes and all the
+ * business logic is implemented in f_mass_storage.* file.  Read
+ * comments in this file for more detailed description.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/usb/ch9.h>
+#include <linux/module.h>
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC		"Mass Storage Gadget"
+#define DRIVER_VERSION		"2009/09/11"
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID	0x0525	/* NetChip */
+#define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */
+
+#include "f_mass_storage.h"
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+static struct usb_device_descriptor msg_device_desc = {
+	.bLength =		sizeof msg_device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	.idVendor =		cpu_to_le16(FSG_VENDOR_ID),
+	.idProduct =		cpu_to_le16(FSG_PRODUCT_ID),
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language       = 0x0409,       /* en-us */
+	.strings        = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_function_instance *fi_msg;
+static struct usb_function *f_msg;
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters mod_data = {
+	.stall = 1
+};
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static int msg_do_config(struct usb_configuration *c)
+{
+	struct fsg_opts *opts;
+	int ret;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	opts = fsg_opts_from_func_inst(fi_msg);
+
+	f_msg = usb_get_function(fi_msg);
+	if (IS_ERR(f_msg))
+		return PTR_ERR(f_msg);
+
+	ret = usb_add_function(c, f_msg);
+	if (ret)
+		goto put_func;
+
+	return 0;
+
+put_func:
+	usb_put_function(f_msg);
+	return ret;
+}
+
+static struct usb_configuration msg_config_driver = {
+	.label			= "Linux File-Backed Storage",
+	.bConfigurationValue	= 1,
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+/****************************** Gadget Bind ******************************/
+
+static int msg_bind(struct usb_composite_dev *cdev)
+{
+	struct fsg_opts *opts;
+	struct fsg_config config;
+	int status;
+
+	fi_msg = usb_get_function_instance("mass_storage");
+	if (IS_ERR(fi_msg))
+		return PTR_ERR(fi_msg);
+
+	fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
+	opts = fsg_opts_from_func_inst(fi_msg);
+
+	opts->no_configfs = true;
+	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+	if (status)
+		goto fail;
+
+	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_sysfs(opts->common, true);
+	status = fsg_common_create_luns(opts->common, &config);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+				      config.product_name);
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail_string_ids;
+	msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+		if (!usb_desc)
+			goto fail_string_ids;
+		usb_otg_descriptor_init(cdev->gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
+	if (status < 0)
+		goto fail_otg_desc;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&cdev->gadget->dev,
+		 DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+	return 0;
+
+fail_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail_string_ids:
+	fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+	fsg_common_free_buffers(opts->common);
+fail:
+	usb_put_function_instance(fi_msg);
+	return status;
+}
+
+static int msg_unbind(struct usb_composite_dev *cdev)
+{
+	if (!IS_ERR(f_msg))
+		usb_put_function(f_msg);
+
+	if (!IS_ERR(fi_msg))
+		usb_put_function_instance(fi_msg);
+
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+/****************************** Some noise ******************************/
+
+static struct usb_composite_driver msg_driver = {
+	.name		= "g_mass_storage",
+	.dev		= &msg_device_desc,
+	.max_speed	= USB_SPEED_SUPER_PLUS,
+	.needs_serial	= 1,
+	.strings	= dev_strings,
+	.bind		= msg_bind,
+	.unbind		= msg_unbind,
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+static int __init msg_init(void)
+{
+	return usb_composite_probe(&msg_driver);
+}
+module_init(msg_init);
+
+static void __exit msg_cleanup(void)
+{
+	usb_composite_unregister(&msg_driver);
+}
+module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c
new file mode 100644
index 0000000..50515f9
--- /dev/null
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * multi.c -- Multifunction Composite driver
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "u_serial.h"
+#if defined USB_ETH_RNDIS
+#  undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+#  define USB_ETH_RNDIS y
+#endif
+
+
+#define DRIVER_DESC		"Multifunction Composite Gadget"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+
+#include "f_mass_storage.h"
+
+#include "u_ecm.h"
+#ifdef USB_ETH_RNDIS
+#  include "u_rndis.h"
+#  include "rndis.h"
+#endif
+#include "u_ether.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+/***************************** Device Descriptor ****************************/
+
+#define MULTI_VENDOR_NUM	0x1d6b	/* Linux Foundation */
+#define MULTI_PRODUCT_NUM	0x0104	/* Multifunction Composite Gadget */
+
+
+enum {
+	__MULTI_NO_CONFIG,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+	MULTI_RNDIS_CONFIG_NUM,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+	MULTI_CDC_CONFIG_NUM,
+#endif
+};
+
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	.bDeviceClass =		USB_CLASS_MISC /* 0xEF */,
+	.bDeviceSubClass =	2,
+	.bDeviceProtocol =	1,
+
+	/* Vendor and product id can be overridden by module parameters.  */
+	.idVendor =		cpu_to_le16(MULTI_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(MULTI_PRODUCT_NUM),
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+enum {
+	MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
+	MULTI_STRING_CDC_CONFIG_IDX,
+};
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	[MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
+	[MULTI_STRING_CDC_CONFIG_IDX].s   = "Multifunction with CDC ECM",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&(struct usb_gadget_strings){
+		.language	= 0x0409,	/* en-us */
+		.strings	= strings_dev,
+	},
+	NULL,
+};
+
+
+
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+static struct usb_function_instance *fi_acm;
+static struct usb_function_instance *fi_msg;
+
+/********** RNDIS **********/
+
+#ifdef USB_ETH_RNDIS
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_acm_rndis;
+static struct usb_function *f_rndis;
+static struct usb_function *f_msg_rndis;
+
+static int rndis_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_rndis = usb_get_function(fi_rndis);
+	if (IS_ERR(f_rndis))
+		return PTR_ERR(f_rndis);
+
+	ret = usb_add_function(c, f_rndis);
+	if (ret < 0)
+		goto err_func_rndis;
+
+	f_acm_rndis = usb_get_function(fi_acm);
+	if (IS_ERR(f_acm_rndis)) {
+		ret = PTR_ERR(f_acm_rndis);
+		goto err_func_acm;
+	}
+
+	ret = usb_add_function(c, f_acm_rndis);
+	if (ret)
+		goto err_conf;
+
+	f_msg_rndis = usb_get_function(fi_msg);
+	if (IS_ERR(f_msg_rndis)) {
+		ret = PTR_ERR(f_msg_rndis);
+		goto err_fsg;
+	}
+
+	ret = usb_add_function(c, f_msg_rndis);
+	if (ret)
+		goto err_run;
+
+	return 0;
+err_run:
+	usb_put_function(f_msg_rndis);
+err_fsg:
+	usb_remove_function(c, f_acm_rndis);
+err_conf:
+	usb_put_function(f_acm_rndis);
+err_func_acm:
+	usb_remove_function(c, f_rndis);
+err_func_rndis:
+	usb_put_function(f_rndis);
+	return ret;
+}
+
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
+{
+	static struct usb_configuration config = {
+		.bConfigurationValue	= MULTI_RNDIS_CONFIG_NUM,
+		.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+	};
+
+	config.label          = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
+	config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
+
+	return usb_add_config(cdev, &config, rndis_do_config);
+}
+
+#else
+
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
+{
+	return 0;
+}
+
+#endif
+
+
+/********** CDC ECM **********/
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_acm_multi;
+static struct usb_function *f_ecm;
+static struct usb_function *f_msg_multi;
+
+static int cdc_do_config(struct usb_configuration *c)
+{
+	int ret;
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_ecm = usb_get_function(fi_ecm);
+	if (IS_ERR(f_ecm))
+		return PTR_ERR(f_ecm);
+
+	ret = usb_add_function(c, f_ecm);
+	if (ret < 0)
+		goto err_func_ecm;
+
+	/* implicit port_num is zero */
+	f_acm_multi = usb_get_function(fi_acm);
+	if (IS_ERR(f_acm_multi)) {
+		ret = PTR_ERR(f_acm_multi);
+		goto err_func_acm;
+	}
+
+	ret = usb_add_function(c, f_acm_multi);
+	if (ret)
+		goto err_conf;
+
+	f_msg_multi = usb_get_function(fi_msg);
+	if (IS_ERR(f_msg_multi)) {
+		ret = PTR_ERR(f_msg_multi);
+		goto err_fsg;
+	}
+
+	ret = usb_add_function(c, f_msg_multi);
+	if (ret)
+		goto err_run;
+
+	return 0;
+err_run:
+	usb_put_function(f_msg_multi);
+err_fsg:
+	usb_remove_function(c, f_acm_multi);
+err_conf:
+	usb_put_function(f_acm_multi);
+err_func_acm:
+	usb_remove_function(c, f_ecm);
+err_func_ecm:
+	usb_put_function(f_ecm);
+	return ret;
+}
+
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
+{
+	static struct usb_configuration config = {
+		.bConfigurationValue	= MULTI_CDC_CONFIG_NUM,
+		.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+	};
+
+	config.label          = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
+	config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
+
+	return usb_add_config(cdev, &config, cdc_do_config);
+}
+
+#else
+
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
+{
+	return 0;
+}
+
+#endif
+
+
+
+/****************************** Gadget Bind ******************************/
+
+static int __ref multi_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget *gadget = cdev->gadget;
+#ifdef CONFIG_USB_G_MULTI_CDC
+	struct f_ecm_opts *ecm_opts;
+#endif
+#ifdef USB_ETH_RNDIS
+	struct f_rndis_opts *rndis_opts;
+#endif
+	struct fsg_opts *fsg_opts;
+	struct fsg_config config;
+	int status;
+
+	if (!can_support_ecm(cdev->gadget)) {
+		dev_err(&gadget->dev, "controller '%s' not usable\n",
+			gadget->name);
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+	fi_ecm = usb_get_function_instance("ecm");
+	if (IS_ERR(fi_ecm))
+		return PTR_ERR(fi_ecm);
+
+	ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+	gether_set_qmult(ecm_opts->net, qmult);
+	if (!gether_set_host_addr(ecm_opts->net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#ifdef USB_ETH_RNDIS
+	fi_rndis = usb_get_function_instance("rndis");
+	if (IS_ERR(fi_rndis)) {
+		status = PTR_ERR(fi_rndis);
+		goto fail;
+	}
+
+	rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
+
+	gether_set_qmult(rndis_opts->net, qmult);
+	if (!gether_set_host_addr(rndis_opts->net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
+	/*
+	 * If both ecm and rndis are selected then:
+	 *	1) rndis borrows the net interface from ecm
+	 *	2) since the interface is shared it must not be bound
+	 *	twice - in ecm's _and_ rndis' binds, so do it here.
+	 */
+	gether_set_gadget(ecm_opts->net, cdev->gadget);
+	status = gether_register_netdev(ecm_opts->net);
+	if (status)
+		goto fail0;
+
+	rndis_borrow_net(fi_rndis, ecm_opts->net);
+	ecm_opts->bound = true;
+#endif
+
+	/* set up serial link layer */
+	fi_acm = usb_get_function_instance("acm");
+	if (IS_ERR(fi_acm)) {
+		status = PTR_ERR(fi_acm);
+		goto fail0;
+	}
+
+	/* set up mass storage function */
+	fi_msg = usb_get_function_instance("mass_storage");
+	if (IS_ERR(fi_msg)) {
+		status = PTR_ERR(fi_msg);
+		goto fail1;
+	}
+	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+	fsg_opts = fsg_opts_from_func_inst(fi_msg);
+
+	fsg_opts->no_configfs = true;
+	status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
+	if (status)
+		goto fail2;
+
+	status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_sysfs(fsg_opts->common, true);
+	status = fsg_common_create_luns(fsg_opts->common, &config);
+	if (status)
+		goto fail_set_cdev;
+
+	fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
+				      config.product_name);
+
+	/* allocate string IDs */
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (unlikely(status < 0))
+		goto fail_string_ids;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto fail_string_ids;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	/* register configurations */
+	status = rndis_config_register(cdev);
+	if (unlikely(status < 0))
+		goto fail_otg_desc;
+
+	status = cdc_config_register(cdev);
+	if (unlikely(status < 0))
+		goto fail_otg_desc;
+	usb_composite_overwrite_options(cdev, &coverwrite);
+
+	/* we're done */
+	dev_info(&gadget->dev, DRIVER_DESC "\n");
+	return 0;
+
+
+	/* error recovery */
+fail_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail_string_ids:
+	fsg_common_remove_luns(fsg_opts->common);
+fail_set_cdev:
+	fsg_common_free_buffers(fsg_opts->common);
+fail2:
+	usb_put_function_instance(fi_msg);
+fail1:
+	usb_put_function_instance(fi_acm);
+fail0:
+#ifdef USB_ETH_RNDIS
+	usb_put_function_instance(fi_rndis);
+fail:
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+	usb_put_function_instance(fi_ecm);
+#endif
+	return status;
+}
+
+static int multi_unbind(struct usb_composite_dev *cdev)
+{
+#ifdef CONFIG_USB_G_MULTI_CDC
+	usb_put_function(f_msg_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+	usb_put_function(f_msg_rndis);
+#endif
+	usb_put_function_instance(fi_msg);
+#ifdef CONFIG_USB_G_MULTI_CDC
+	usb_put_function(f_acm_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+	usb_put_function(f_acm_rndis);
+#endif
+	usb_put_function_instance(fi_acm);
+#ifdef USB_ETH_RNDIS
+	usb_put_function(f_rndis);
+	usb_put_function_instance(fi_rndis);
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+	usb_put_function(f_ecm);
+	usb_put_function_instance(fi_ecm);
+#endif
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver multi_driver = {
+	.name		= "g_multi",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= multi_bind,
+	.unbind		= multi_unbind,
+	.needs_serial	= 1,
+};
+
+module_usb_composite_driver(multi_driver);
diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c
new file mode 100644
index 0000000..8465f08
--- /dev/null
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ncm.c -- NCM gadget driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from ether.c which is:
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+
+#include "u_ether.h"
+#include "u_ncm.h"
+
+#define DRIVER_DESC		"NCM Gadget"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM		0x0525	/* NetChip */
+#define CDC_PRODUCT_NUM		0xa4a1	/* Linux-USB Ethernet Gadget */
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+
+	.bDeviceClass =		USB_CLASS_COMM,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	/* .bMaxPacketSize0 = f(hardware) */
+
+	/* Vendor and product id defaults change according to what configs
+	 * we support.  (As does bNumConfigurations.)  These values can
+	 * also be overridden by module parameters.
+	 */
+	.idVendor =		cpu_to_le16 (CDC_VENDOR_NUM),
+	.idProduct =		cpu_to_le16 (CDC_PRODUCT_NUM),
+	/* .bcdDevice = f(hardware) */
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	/* NO SERIAL NUMBER */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/* string IDs are assigned dynamically */
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_function_instance *f_ncm_inst;
+static struct usb_function *f_ncm;
+
+/*-------------------------------------------------------------------------*/
+
+static int ncm_do_config(struct usb_configuration *c)
+{
+	int status;
+
+	/* FIXME alloc iConfiguration string, set it in c->strings */
+
+	if (gadget_is_otg(c->cdev->gadget)) {
+		c->descriptors = otg_desc;
+		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_ncm = usb_get_function(f_ncm_inst);
+	if (IS_ERR(f_ncm))
+		return PTR_ERR(f_ncm);
+
+	status = usb_add_function(c, f_ncm);
+	if (status < 0) {
+		usb_put_function(f_ncm);
+		return status;
+	}
+
+	return 0;
+}
+
+static struct usb_configuration ncm_config_driver = {
+	/* .label = f(hardware) */
+	.label			= "CDC Ethernet (NCM)",
+	.bConfigurationValue	= 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int gncm_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct f_ncm_opts	*ncm_opts;
+	int			status;
+
+	f_ncm_inst = usb_get_function_instance("ncm");
+	if (IS_ERR(f_ncm_inst))
+		return PTR_ERR(f_ncm_inst);
+
+	ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst);
+
+	gether_set_qmult(ncm_opts->net, qmult);
+	if (!gether_set_host_addr(ncm_opts->net, host_addr))
+		pr_info("using host ethernet address: %s", host_addr);
+	if (!gether_set_dev_addr(ncm_opts->net, dev_addr))
+		pr_info("using self ethernet address: %s", dev_addr);
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
+	if (gadget_is_otg(gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(gadget);
+		if (!usb_desc)
+			goto fail;
+		usb_otg_descriptor_init(gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	status = usb_add_config(cdev, &ncm_config_driver,
+				ncm_do_config);
+	if (status < 0)
+		goto fail1;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
+
+	return 0;
+
+fail1:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail:
+	usb_put_function_instance(f_ncm_inst);
+	return status;
+}
+
+static int gncm_unbind(struct usb_composite_dev *cdev)
+{
+	if (!IS_ERR_OR_NULL(f_ncm))
+		usb_put_function(f_ncm);
+	if (!IS_ERR_OR_NULL(f_ncm_inst))
+		usb_put_function_instance(f_ncm_inst);
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver ncm_driver = {
+	.name		= "g_ncm",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= gncm_bind,
+	.unbind		= gncm_unbind,
+};
+
+module_usb_composite_driver(ncm_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Yauheni Kaliuta");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
new file mode 100644
index 0000000..978c1a3
--- /dev/null
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * nokia.c -- Nokia Composite Gadget Driver
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This gadget driver borrows from serial.c which is:
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "u_ether.h"
+#include "u_phonet.h"
+#include "u_ecm.h"
+#include "f_mass_storage.h"
+
+/* Defines */
+
+#define NOKIA_VERSION_NUM		0x0211
+#define NOKIA_LONG_NAME			"N900 (PC-Suite Mode)"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
+
+static struct fsg_module_parameters fsg_mod_data = {
+	.stall = 0,
+	.luns = 2,
+	.removable_count = 2,
+	.removable = { 1, 1, },
+};
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_DEBUG */
+
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+#define NOKIA_VENDOR_ID			0x0421	/* Nokia */
+#define NOKIA_PRODUCT_ID		0x01c8	/* Nokia Gadget */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
+
+static char manufacturer_nokia[] = "Nokia";
+static const char product_nokia[] = NOKIA_LONG_NAME;
+static const char description_nokia[] = "PC-Suite Configuration";
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
+	[USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	[STRING_DESCRIPTION_IDX].s = description_nokia,
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+	.bLength		= USB_DT_DEVICE_SIZE,
+	.bDescriptorType	= USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass		= USB_CLASS_COMM,
+	.idVendor		= cpu_to_le16(NOKIA_VENDOR_ID),
+	.idProduct		= cpu_to_le16(NOKIA_PRODUCT_ID),
+	.bcdDevice		= cpu_to_le16(NOKIA_VERSION_NUM),
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	.bNumConfigurations =	1,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Module */
+MODULE_DESCRIPTION("Nokia composite gadget driver for N900");
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm_cfg1;
+static struct usb_function *f_acm_cfg2;
+static struct usb_function *f_ecm_cfg1;
+static struct usb_function *f_ecm_cfg2;
+static struct usb_function *f_obex1_cfg1;
+static struct usb_function *f_obex2_cfg1;
+static struct usb_function *f_obex1_cfg2;
+static struct usb_function *f_obex2_cfg2;
+static struct usb_function *f_phonet_cfg1;
+static struct usb_function *f_phonet_cfg2;
+static struct usb_function *f_msg_cfg1;
+static struct usb_function *f_msg_cfg2;
+
+
+static struct usb_configuration nokia_config_500ma_driver = {
+	.label		= "Bus Powered",
+	.bConfigurationValue = 1,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes	= USB_CONFIG_ATT_ONE,
+	.MaxPower	= 500,
+};
+
+static struct usb_configuration nokia_config_100ma_driver = {
+	.label		= "Self Powered",
+	.bConfigurationValue = 2,
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes	= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.MaxPower	= 100,
+};
+
+static struct usb_function_instance *fi_acm;
+static struct usb_function_instance *fi_ecm;
+static struct usb_function_instance *fi_obex1;
+static struct usb_function_instance *fi_obex2;
+static struct usb_function_instance *fi_phonet;
+static struct usb_function_instance *fi_msg;
+
+static int nokia_bind_config(struct usb_configuration *c)
+{
+	struct usb_function *f_acm;
+	struct usb_function *f_phonet = NULL;
+	struct usb_function *f_obex1 = NULL;
+	struct usb_function *f_ecm;
+	struct usb_function *f_obex2 = NULL;
+	struct usb_function *f_msg;
+	int status = 0;
+	int obex1_stat = -1;
+	int obex2_stat = -1;
+	int phonet_stat = -1;
+
+	if (!IS_ERR(fi_phonet)) {
+		f_phonet = usb_get_function(fi_phonet);
+		if (IS_ERR(f_phonet))
+			pr_debug("could not get phonet function\n");
+	}
+
+	if (!IS_ERR(fi_obex1)) {
+		f_obex1 = usb_get_function(fi_obex1);
+		if (IS_ERR(f_obex1))
+			pr_debug("could not get obex function 0\n");
+	}
+
+	if (!IS_ERR(fi_obex2)) {
+		f_obex2 = usb_get_function(fi_obex2);
+		if (IS_ERR(f_obex2))
+			pr_debug("could not get obex function 1\n");
+	}
+
+	f_acm = usb_get_function(fi_acm);
+	if (IS_ERR(f_acm)) {
+		status = PTR_ERR(f_acm);
+		goto err_get_acm;
+	}
+
+	f_ecm = usb_get_function(fi_ecm);
+	if (IS_ERR(f_ecm)) {
+		status = PTR_ERR(f_ecm);
+		goto err_get_ecm;
+	}
+
+	f_msg = usb_get_function(fi_msg);
+	if (IS_ERR(f_msg)) {
+		status = PTR_ERR(f_msg);
+		goto err_get_msg;
+	}
+
+	if (!IS_ERR_OR_NULL(f_phonet)) {
+		phonet_stat = usb_add_function(c, f_phonet);
+		if (phonet_stat)
+			pr_debug("could not add phonet function\n");
+	}
+
+	if (!IS_ERR_OR_NULL(f_obex1)) {
+		obex1_stat = usb_add_function(c, f_obex1);
+		if (obex1_stat)
+			pr_debug("could not add obex function 0\n");
+	}
+
+	if (!IS_ERR_OR_NULL(f_obex2)) {
+		obex2_stat = usb_add_function(c, f_obex2);
+		if (obex2_stat)
+			pr_debug("could not add obex function 1\n");
+	}
+
+	status = usb_add_function(c, f_acm);
+	if (status)
+		goto err_conf;
+
+	status = usb_add_function(c, f_ecm);
+	if (status) {
+		pr_debug("could not bind ecm config %d\n", status);
+		goto err_ecm;
+	}
+
+	status = usb_add_function(c, f_msg);
+	if (status)
+		goto err_msg;
+
+	if (c == &nokia_config_500ma_driver) {
+		f_acm_cfg1 = f_acm;
+		f_ecm_cfg1 = f_ecm;
+		f_phonet_cfg1 = f_phonet;
+		f_obex1_cfg1 = f_obex1;
+		f_obex2_cfg1 = f_obex2;
+		f_msg_cfg1 = f_msg;
+	} else {
+		f_acm_cfg2 = f_acm;
+		f_ecm_cfg2 = f_ecm;
+		f_phonet_cfg2 = f_phonet;
+		f_obex1_cfg2 = f_obex1;
+		f_obex2_cfg2 = f_obex2;
+		f_msg_cfg2 = f_msg;
+	}
+
+	return status;
+err_msg:
+	usb_remove_function(c, f_ecm);
+err_ecm:
+	usb_remove_function(c, f_acm);
+err_conf:
+	if (!obex2_stat)
+		usb_remove_function(c, f_obex2);
+	if (!obex1_stat)
+		usb_remove_function(c, f_obex1);
+	if (!phonet_stat)
+		usb_remove_function(c, f_phonet);
+	usb_put_function(f_msg);
+err_get_msg:
+	usb_put_function(f_ecm);
+err_get_ecm:
+	usb_put_function(f_acm);
+err_get_acm:
+	if (!IS_ERR_OR_NULL(f_obex2))
+		usb_put_function(f_obex2);
+	if (!IS_ERR_OR_NULL(f_obex1))
+		usb_put_function(f_obex1);
+	if (!IS_ERR_OR_NULL(f_phonet))
+		usb_put_function(f_phonet);
+	return status;
+}
+
+static int nokia_bind(struct usb_composite_dev *cdev)
+{
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct fsg_opts		*fsg_opts;
+	struct fsg_config	fsg_config;
+	int			status;
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto err_usb;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+	status = strings_dev[STRING_DESCRIPTION_IDX].id;
+	nokia_config_500ma_driver.iConfiguration = status;
+	nokia_config_100ma_driver.iConfiguration = status;
+
+	if (!gadget_is_altset_supported(gadget)) {
+		status = -ENODEV;
+		goto err_usb;
+	}
+
+	fi_phonet = usb_get_function_instance("phonet");
+	if (IS_ERR(fi_phonet))
+		pr_debug("could not find phonet function\n");
+
+	fi_obex1 = usb_get_function_instance("obex");
+	if (IS_ERR(fi_obex1))
+		pr_debug("could not find obex function 1\n");
+
+	fi_obex2 = usb_get_function_instance("obex");
+	if (IS_ERR(fi_obex2))
+		pr_debug("could not find obex function 2\n");
+
+	fi_acm = usb_get_function_instance("acm");
+	if (IS_ERR(fi_acm)) {
+		status = PTR_ERR(fi_acm);
+		goto err_obex2_inst;
+	}
+
+	fi_ecm = usb_get_function_instance("ecm");
+	if (IS_ERR(fi_ecm)) {
+		status = PTR_ERR(fi_ecm);
+		goto err_acm_inst;
+	}
+
+	fi_msg = usb_get_function_instance("mass_storage");
+	if (IS_ERR(fi_msg)) {
+		status = PTR_ERR(fi_msg);
+		goto err_ecm_inst;
+	}
+
+	/* set up mass storage function */
+	fsg_config_from_params(&fsg_config, &fsg_mod_data, fsg_num_buffers);
+	fsg_config.vendor_name = "Nokia";
+	fsg_config.product_name = "N900";
+
+	fsg_opts = fsg_opts_from_func_inst(fi_msg);
+	fsg_opts->no_configfs = true;
+
+	status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
+	if (status)
+		goto err_msg_inst;
+
+	status = fsg_common_set_cdev(fsg_opts->common, cdev, fsg_config.can_stall);
+	if (status)
+		goto err_msg_buf;
+
+	fsg_common_set_sysfs(fsg_opts->common, true);
+
+	status = fsg_common_create_luns(fsg_opts->common, &fsg_config);
+	if (status)
+		goto err_msg_buf;
+
+	fsg_common_set_inquiry_string(fsg_opts->common, fsg_config.vendor_name,
+				      fsg_config.product_name);
+
+	/* finally register the configuration */
+	status = usb_add_config(cdev, &nokia_config_500ma_driver,
+			nokia_bind_config);
+	if (status < 0)
+		goto err_msg_luns;
+
+	status = usb_add_config(cdev, &nokia_config_100ma_driver,
+			nokia_bind_config);
+	if (status < 0)
+		goto err_put_cfg1;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
+
+	return 0;
+
+err_put_cfg1:
+	usb_put_function(f_acm_cfg1);
+	if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+		usb_put_function(f_obex1_cfg1);
+	if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+		usb_put_function(f_obex2_cfg1);
+	if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+		usb_put_function(f_phonet_cfg1);
+	usb_put_function(f_ecm_cfg1);
+err_msg_luns:
+	fsg_common_remove_luns(fsg_opts->common);
+err_msg_buf:
+	fsg_common_free_buffers(fsg_opts->common);
+err_msg_inst:
+	usb_put_function_instance(fi_msg);
+err_ecm_inst:
+	usb_put_function_instance(fi_ecm);
+err_acm_inst:
+	usb_put_function_instance(fi_acm);
+err_obex2_inst:
+	if (!IS_ERR(fi_obex2))
+		usb_put_function_instance(fi_obex2);
+	if (!IS_ERR(fi_obex1))
+		usb_put_function_instance(fi_obex1);
+	if (!IS_ERR(fi_phonet))
+		usb_put_function_instance(fi_phonet);
+err_usb:
+	return status;
+}
+
+static int nokia_unbind(struct usb_composite_dev *cdev)
+{
+	if (!IS_ERR_OR_NULL(f_obex1_cfg2))
+		usb_put_function(f_obex1_cfg2);
+	if (!IS_ERR_OR_NULL(f_obex2_cfg2))
+		usb_put_function(f_obex2_cfg2);
+	if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+		usb_put_function(f_obex1_cfg1);
+	if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+		usb_put_function(f_obex2_cfg1);
+	if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+		usb_put_function(f_phonet_cfg1);
+	if (!IS_ERR_OR_NULL(f_phonet_cfg2))
+		usb_put_function(f_phonet_cfg2);
+	usb_put_function(f_acm_cfg1);
+	usb_put_function(f_acm_cfg2);
+	usb_put_function(f_ecm_cfg1);
+	usb_put_function(f_ecm_cfg2);
+	usb_put_function(f_msg_cfg1);
+	usb_put_function(f_msg_cfg2);
+
+	usb_put_function_instance(fi_msg);
+	usb_put_function_instance(fi_ecm);
+	if (!IS_ERR(fi_obex2))
+		usb_put_function_instance(fi_obex2);
+	if (!IS_ERR(fi_obex1))
+		usb_put_function_instance(fi_obex1);
+	if (!IS_ERR(fi_phonet))
+		usb_put_function_instance(fi_phonet);
+	usb_put_function_instance(fi_acm);
+
+	return 0;
+}
+
+static struct usb_composite_driver nokia_driver = {
+	.name		= "g_nokia",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_HIGH,
+	.bind		= nokia_bind,
+	.unbind		= nokia_unbind,
+};
+
+module_usb_composite_driver(nokia_driver);
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
new file mode 100644
index 0000000..57858f0
--- /dev/null
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * printer.c -- Printer gadget driver
+ *
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/byteorder.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#define DRIVER_DESC		"Printer Gadget"
+#define DRIVER_VERSION		"2015 FEB 17"
+
+static const char shortname [] = "printer";
+static const char driver_desc [] = DRIVER_DESC;
+
+#include "u_printer.h"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ */
+#define PRINTER_VENDOR_NUM	0x0525		/* NetChip */
+#define PRINTER_PRODUCT_NUM	0xa4a8		/* Linux-USB Printer Gadget */
+
+/* Some systems will want different product identifiers published in the
+ * device descriptor, either numbers or strings or both.  These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNum, "1");
+
+static char *iPNPstring;
+module_param(iPNPstring, charp, S_IRUGO);
+MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;");
+
+/* Number of requests to allocate per endpoint, not used for ep0. */
+static unsigned qlen = 10;
+module_param(qlen, uint, S_IRUGO|S_IWUSR);
+
+#define QLEN	qlen
+
+static struct usb_function_instance *fi_printer;
+static struct usb_function *f_printer;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	.idVendor =		cpu_to_le16(PRINTER_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(PRINTER_PRODUCT_NUM),
+	.bNumConfigurations =	1
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/*-------------------------------------------------------------------------*/
+
+/* descriptors that are built on-demand */
+
+static char				product_desc [40] = DRIVER_DESC;
+static char				serial_num [40] = "1";
+static char				*pnp_string =
+	"MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
+
+/* static strings, in UTF-8 */
+static struct usb_string		strings [] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = product_desc,
+	[USB_GADGET_SERIAL_IDX].s =	serial_num,
+	{  }		/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_configuration printer_cfg_driver = {
+	.label			= "printer",
+	.bConfigurationValue	= 1,
+	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+};
+
+static int printer_do_config(struct usb_configuration *c)
+{
+	struct usb_gadget	*gadget = c->cdev->gadget;
+	int			status = 0;
+
+	usb_ep_autoconfig_reset(gadget);
+
+	usb_gadget_set_selfpowered(gadget);
+
+	if (gadget_is_otg(gadget)) {
+		printer_cfg_driver.descriptors = otg_desc;
+		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	f_printer = usb_get_function(fi_printer);
+	if (IS_ERR(f_printer))
+		return PTR_ERR(f_printer);
+
+	status = usb_add_function(c, f_printer);
+	if (status < 0)
+		usb_put_function(f_printer);
+
+	return status;
+}
+
+static int printer_bind(struct usb_composite_dev *cdev)
+{
+	struct f_printer_opts *opts;
+	int ret;
+
+	fi_printer = usb_get_function_instance("printer");
+	if (IS_ERR(fi_printer))
+		return PTR_ERR(fi_printer);
+
+	opts = container_of(fi_printer, struct f_printer_opts, func_inst);
+	opts->minor = 0;
+	opts->q_len = QLEN;
+	if (iPNPstring) {
+		opts->pnp_string = kstrdup(iPNPstring, GFP_KERNEL);
+		if (!opts->pnp_string) {
+			ret = -ENOMEM;
+			goto fail_put_func_inst;
+		}
+		opts->pnp_string_allocated = true;
+		/*
+		 * we don't free this memory in case of error
+		 * as printer cleanup func will do this for us
+		 */
+	} else {
+		opts->pnp_string = pnp_string;
+	}
+
+	ret = usb_string_ids_tab(cdev, strings);
+	if (ret < 0)
+		goto fail_put_func_inst;
+
+	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
+	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
+
+	if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
+		struct usb_descriptor_header *usb_desc;
+
+		usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+		if (!usb_desc) {
+			ret = -ENOMEM;
+			goto fail_put_func_inst;
+		}
+		usb_otg_descriptor_init(cdev->gadget, usb_desc);
+		otg_desc[0] = usb_desc;
+		otg_desc[1] = NULL;
+	}
+
+	ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
+	if (ret)
+		goto fail_free_otg_desc;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	return ret;
+
+fail_free_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail_put_func_inst:
+	usb_put_function_instance(fi_printer);
+	return ret;
+}
+
+static int printer_unbind(struct usb_composite_dev *cdev)
+{
+	usb_put_function(f_printer);
+	usb_put_function_instance(fi_printer);
+
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver printer_driver = {
+	.name           = shortname,
+	.dev            = &device_desc,
+	.strings        = dev_strings,
+	.max_speed      = USB_SPEED_SUPER,
+	.bind		= printer_bind,
+	.unbind		= printer_unbind,
+};
+
+module_usb_composite_driver(printer_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Craig Nadler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c
new file mode 100644
index 0000000..de30d76
--- /dev/null
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * serial.c -- USB gadget serial driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "u_serial.h"
+
+
+/* Defines */
+
+#define GS_VERSION_STR			"v2.4"
+#define GS_VERSION_NUM			0x2400
+
+#define GS_LONG_NAME			"Gadget Serial"
+#define GS_VERSION_NAME			GS_LONG_NAME " " GS_VERSION_STR
+
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+/* Thanks to NetChip Technologies for donating this product ID.
+*
+* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+* Instead:  allocate your own, using normal USB-IF procedures.
+*/
+#define GS_VENDOR_ID			0x0525	/* NetChip */
+#define GS_PRODUCT_ID			0xa4a6	/* Linux-USB Serial Gadget */
+#define GS_CDC_PRODUCT_ID		0xa4a7	/* ... as CDC-ACM */
+#define GS_CDC_OBEX_PRODUCT_ID		0xa4a9	/* ... as CDC-OBEX */
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		USB_DT_DEVICE_SIZE,
+	.bDescriptorType =	USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	/* .bDeviceClass = f(use_acm) */
+	.bDeviceSubClass =	0,
+	.bDeviceProtocol =	0,
+	/* .bMaxPacketSize0 = f(hardware) */
+	.idVendor =		cpu_to_le16(GS_VENDOR_ID),
+	/* .idProduct =	f(use_acm) */
+	.bcdDevice = cpu_to_le16(GS_VERSION_NUM),
+	/* .iManufacturer = DYNAMIC */
+	/* .iProduct = DYNAMIC */
+	.bNumConfigurations =	1,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/*-------------------------------------------------------------------------*/
+
+/* Module */
+MODULE_DESCRIPTION(GS_VERSION_NAME);
+MODULE_AUTHOR("Al Borchers");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
+
+static bool use_acm = true;
+module_param(use_acm, bool, 0);
+MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
+
+static bool use_obex = false;
+module_param(use_obex, bool, 0);
+MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no");
+
+static unsigned n_ports = 1;
+module_param(n_ports, uint, 0);
+MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_configuration serial_config_driver = {
+	/* .label = f(use_acm) */
+	/* .bConfigurationValue = f(use_acm) */
+	/* .iConfiguration = DYNAMIC */
+	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER,
+};
+
+static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
+static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
+
+static int serial_register_ports(struct usb_composite_dev *cdev,
+		struct usb_configuration *c, const char *f_name)
+{
+	int i;
+	int ret;
+
+	ret = usb_add_config_only(cdev, c);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < n_ports; i++) {
+
+		fi_serial[i] = usb_get_function_instance(f_name);
+		if (IS_ERR(fi_serial[i])) {
+			ret = PTR_ERR(fi_serial[i]);
+			goto fail;
+		}
+
+		f_serial[i] = usb_get_function(fi_serial[i]);
+		if (IS_ERR(f_serial[i])) {
+			ret = PTR_ERR(f_serial[i]);
+			goto err_get_func;
+		}
+
+		ret = usb_add_function(c, f_serial[i]);
+		if (ret)
+			goto err_add_func;
+	}
+
+	return 0;
+
+err_add_func:
+	usb_put_function(f_serial[i]);
+err_get_func:
+	usb_put_function_instance(fi_serial[i]);
+
+fail:
+	i--;
+	while (i >= 0) {
+		usb_remove_function(c, f_serial[i]);
+		usb_put_function(f_serial[i]);
+		usb_put_function_instance(fi_serial[i]);
+		i--;
+	}
+out:
+	return ret;
+}
+
+static int gs_bind(struct usb_composite_dev *cdev)
+{
+	int			status;
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		goto fail;
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+	status = strings_dev[STRING_DESCRIPTION_IDX].id;
+	serial_config_driver.iConfiguration = status;
+
+	if (gadget_is_otg(cdev->gadget)) {
+		if (!otg_desc[0]) {
+			struct usb_descriptor_header *usb_desc;
+
+			usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+			if (!usb_desc) {
+				status = -ENOMEM;
+				goto fail;
+			}
+			usb_otg_descriptor_init(cdev->gadget, usb_desc);
+			otg_desc[0] = usb_desc;
+			otg_desc[1] = NULL;
+		}
+		serial_config_driver.descriptors = otg_desc;
+		serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	/* register our configuration */
+	if (use_acm) {
+		status  = serial_register_ports(cdev, &serial_config_driver,
+				"acm");
+		usb_ep_autoconfig_reset(cdev->gadget);
+	} else if (use_obex)
+		status = serial_register_ports(cdev, &serial_config_driver,
+				"obex");
+	else {
+		status = serial_register_ports(cdev, &serial_config_driver,
+				"gser");
+	}
+	if (status < 0)
+		goto fail1;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	INFO(cdev, "%s\n", GS_VERSION_NAME);
+
+	return 0;
+fail1:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+fail:
+	return status;
+}
+
+static int gs_unbind(struct usb_composite_dev *cdev)
+{
+	int i;
+
+	for (i = 0; i < n_ports; i++) {
+		usb_put_function(f_serial[i]);
+		usb_put_function_instance(fi_serial[i]);
+	}
+
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver gserial_driver = {
+	.name		= "g_serial",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_SUPER,
+	.bind		= gs_bind,
+	.unbind		= gs_unbind,
+};
+
+static int __init init(void)
+{
+	/* We *could* export two configs; that'd be much cleaner...
+	 * but neither of these product IDs was defined that way.
+	 */
+	if (use_acm) {
+		serial_config_driver.label = "CDC ACM config";
+		serial_config_driver.bConfigurationValue = 2;
+		device_desc.bDeviceClass = USB_CLASS_COMM;
+		device_desc.idProduct =
+				cpu_to_le16(GS_CDC_PRODUCT_ID);
+	} else if (use_obex) {
+		serial_config_driver.label = "CDC OBEX config";
+		serial_config_driver.bConfigurationValue = 3;
+		device_desc.bDeviceClass = USB_CLASS_COMM;
+		device_desc.idProduct =
+			cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
+	} else {
+		serial_config_driver.label = "Generic Serial config";
+		serial_config_driver.bConfigurationValue = 1;
+		device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+		device_desc.idProduct =
+				cpu_to_le16(GS_PRODUCT_ID);
+	}
+	strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
+
+	return usb_composite_probe(&gserial_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	usb_composite_unregister(&gserial_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
new file mode 100644
index 0000000..4087022
--- /dev/null
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Target based USB-Gadget
+ *
+ * UAS protocol handling, target callbacks, configfs handling,
+ * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <asm/unaligned.h>
+
+#include "u_tcm.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#define UAS_VENDOR_ID	0x0525	/* NetChip */
+#define UAS_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */
+
+static struct usb_device_descriptor usbg_device_desc = {
+	.bLength =		sizeof(usbg_device_desc),
+	.bDescriptorType =	USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.idVendor =		cpu_to_le16(UAS_VENDOR_ID),
+	.idProduct =		cpu_to_le16(UAS_PRODUCT_ID),
+	.bNumConfigurations =   1,
+};
+
+#define USB_G_STR_CONFIG USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_string	usbg_us_strings[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s	= "Target Manufacturer",
+	[USB_GADGET_PRODUCT_IDX].s	= "Target Product",
+	[USB_GADGET_SERIAL_IDX].s	= "000000000001",
+	[USB_G_STR_CONFIG].s		= "default config",
+	{ },
+};
+
+static struct usb_gadget_strings usbg_stringtab = {
+	.language = 0x0409,
+	.strings = usbg_us_strings,
+};
+
+static struct usb_gadget_strings *usbg_strings[] = {
+	&usbg_stringtab,
+	NULL,
+};
+
+static struct usb_function_instance *fi_tcm;
+static struct usb_function *f_tcm;
+
+static int guas_unbind(struct usb_composite_dev *cdev)
+{
+	if (!IS_ERR_OR_NULL(f_tcm))
+		usb_put_function(f_tcm);
+
+	return 0;
+}
+
+static int tcm_do_config(struct usb_configuration *c)
+{
+	int status;
+
+	f_tcm = usb_get_function(fi_tcm);
+	if (IS_ERR(f_tcm))
+		return PTR_ERR(f_tcm);
+
+	status = usb_add_function(c, f_tcm);
+	if (status < 0) {
+		usb_put_function(f_tcm);
+		return status;
+	}
+
+	return 0;
+}
+
+static struct usb_configuration usbg_config_driver = {
+	.label                  = "Linux Target",
+	.bConfigurationValue    = 1,
+	.bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+};
+
+static int usbg_attach(struct usb_function_instance *f);
+static void usbg_detach(struct usb_function_instance *f);
+
+static int usb_target_bind(struct usb_composite_dev *cdev)
+{
+	int ret;
+
+	ret = usb_string_ids_tab(cdev, usbg_us_strings);
+	if (ret)
+		return ret;
+
+	usbg_device_desc.iManufacturer =
+		usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id;
+	usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id;
+	usbg_device_desc.iSerialNumber =
+		usbg_us_strings[USB_GADGET_SERIAL_IDX].id;
+	usbg_config_driver.iConfiguration =
+		usbg_us_strings[USB_G_STR_CONFIG].id;
+
+	ret = usb_add_config(cdev, &usbg_config_driver, tcm_do_config);
+	if (ret)
+		return ret;
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	return 0;
+}
+
+static struct usb_composite_driver usbg_driver = {
+	.name           = "g_target",
+	.dev            = &usbg_device_desc,
+	.strings        = usbg_strings,
+	.max_speed      = USB_SPEED_SUPER,
+	.bind		= usb_target_bind,
+	.unbind         = guas_unbind,
+};
+
+static int usbg_attach(struct usb_function_instance *f)
+{
+	return usb_composite_probe(&usbg_driver);
+}
+
+static void usbg_detach(struct usb_function_instance *f)
+{
+	usb_composite_unregister(&usbg_driver);
+}
+
+static int __init usb_target_gadget_init(void)
+{
+	struct f_tcm_opts *tcm_opts;
+
+	fi_tcm = usb_get_function_instance("tcm");
+	if (IS_ERR(fi_tcm))
+		return PTR_ERR(fi_tcm);
+
+	tcm_opts = container_of(fi_tcm, struct f_tcm_opts, func_inst);
+	mutex_lock(&tcm_opts->dep_lock);
+	tcm_opts->tcm_register_callback = usbg_attach;
+	tcm_opts->tcm_unregister_callback = usbg_detach;
+	tcm_opts->dependent = THIS_MODULE;
+	tcm_opts->can_attach = true;
+	tcm_opts->has_dep = true;
+	mutex_unlock(&tcm_opts->dep_lock);
+
+	fi_tcm->set_inst_name(fi_tcm, "tcm-legacy");
+
+	return 0;
+}
+module_init(usb_target_gadget_init);
+
+static void __exit usb_target_gadget_exit(void)
+{
+	if (!IS_ERR_OR_NULL(fi_tcm))
+		usb_put_function_instance(fi_tcm);
+
+}
+module_exit(usb_target_gadget_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION("usb-gadget fabric");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
new file mode 100644
index 0000000..a9f8eb8
--- /dev/null
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *	webcam.c -- USB webcam gadget driver
+ *
+ *	Copyright (C) 2009-2010
+ *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb/video.h>
+
+#include "u_uvc.h"
+
+USB_GADGET_COMPOSITE_OPTIONS();
+
+/*-------------------------------------------------------------------------*/
+
+/* module parameters specific to the Video streaming endpoint */
+static unsigned int streaming_interval = 1;
+module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_interval, "1 - 16");
+
+static unsigned int streaming_maxpacket = 1024;
+module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
+
+static unsigned int streaming_maxburst;
+module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
+
+/* --------------------------------------------------------------------------
+ * Device descriptor
+ */
+
+#define WEBCAM_VENDOR_ID		0x1d6b	/* Linux Foundation */
+#define WEBCAM_PRODUCT_ID		0x0102	/* Webcam A/V gadget */
+#define WEBCAM_DEVICE_BCD		0x0010	/* 0.10 */
+
+static char webcam_vendor_label[] = "Linux Foundation";
+static char webcam_product_label[] = "Webcam gadget";
+static char webcam_config_label[] = "Video";
+
+/* string IDs are assigned dynamically */
+
+#define STRING_DESCRIPTION_IDX		USB_GADGET_FIRST_AVAIL_IDX
+
+static struct usb_string webcam_strings[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
+	[USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
+	[USB_GADGET_SERIAL_IDX].s = "",
+	[STRING_DESCRIPTION_IDX].s = webcam_config_label,
+	{  }
+};
+
+static struct usb_gadget_strings webcam_stringtab = {
+	.language = 0x0409,	/* en-us */
+	.strings = webcam_strings,
+};
+
+static struct usb_gadget_strings *webcam_device_strings[] = {
+	&webcam_stringtab,
+	NULL,
+};
+
+static struct usb_function_instance *fi_uvc;
+static struct usb_function *f_uvc;
+
+static struct usb_device_descriptor webcam_device_descriptor = {
+	.bLength		= USB_DT_DEVICE_SIZE,
+	.bDescriptorType	= USB_DT_DEVICE,
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass		= USB_CLASS_MISC,
+	.bDeviceSubClass	= 0x02,
+	.bDeviceProtocol	= 0x01,
+	.bMaxPacketSize0	= 0, /* dynamic */
+	.idVendor		= cpu_to_le16(WEBCAM_VENDOR_ID),
+	.idProduct		= cpu_to_le16(WEBCAM_PRODUCT_ID),
+	.bcdDevice		= cpu_to_le16(WEBCAM_DEVICE_BCD),
+	.iManufacturer		= 0, /* dynamic */
+	.iProduct		= 0, /* dynamic */
+	.iSerialNumber		= 0, /* dynamic */
+	.bNumConfigurations	= 0, /* dynamic */
+};
+
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
+	.bLength		= UVC_DT_HEADER_SIZE(1),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VC_HEADER,
+	.bcdUVC			= cpu_to_le16(0x0100),
+	.wTotalLength		= 0, /* dynamic */
+	.dwClockFrequency	= cpu_to_le32(48000000),
+	.bInCollection		= 0, /* dynamic */
+	.baInterfaceNr[0]	= 0, /* dynamic */
+};
+
+static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
+	.bLength		= UVC_DT_CAMERA_TERMINAL_SIZE(3),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VC_INPUT_TERMINAL,
+	.bTerminalID		= 1,
+	.wTerminalType		= cpu_to_le16(0x0201),
+	.bAssocTerminal		= 0,
+	.iTerminal		= 0,
+	.wObjectiveFocalLengthMin	= cpu_to_le16(0),
+	.wObjectiveFocalLengthMax	= cpu_to_le16(0),
+	.wOcularFocalLength		= cpu_to_le16(0),
+	.bControlSize		= 3,
+	.bmControls[0]		= 2,
+	.bmControls[1]		= 0,
+	.bmControls[2]		= 0,
+};
+
+static const struct uvc_processing_unit_descriptor uvc_processing = {
+	.bLength		= UVC_DT_PROCESSING_UNIT_SIZE(2),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VC_PROCESSING_UNIT,
+	.bUnitID		= 2,
+	.bSourceID		= 1,
+	.wMaxMultiplier		= cpu_to_le16(16*1024),
+	.bControlSize		= 2,
+	.bmControls[0]		= 1,
+	.bmControls[1]		= 0,
+	.iProcessing		= 0,
+};
+
+static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
+	.bLength		= UVC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VC_OUTPUT_TERMINAL,
+	.bTerminalID		= 3,
+	.wTerminalType		= cpu_to_le16(0x0101),
+	.bAssocTerminal		= 0,
+	.bSourceID		= 2,
+	.iTerminal		= 0,
+};
+
+DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
+
+static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
+	.bLength		= UVC_DT_INPUT_HEADER_SIZE(1, 2),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_INPUT_HEADER,
+	.bNumFormats		= 2,
+	.wTotalLength		= 0, /* dynamic */
+	.bEndpointAddress	= 0, /* dynamic */
+	.bmInfo			= 0,
+	.bTerminalLink		= 3,
+	.bStillCaptureMethod	= 0,
+	.bTriggerSupport	= 0,
+	.bTriggerUsage		= 0,
+	.bControlSize		= 1,
+	.bmaControls[0][0]	= 0,
+	.bmaControls[1][0]	= 4,
+};
+
+static const struct uvc_format_uncompressed uvc_format_yuv = {
+	.bLength		= UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FORMAT_UNCOMPRESSED,
+	.bFormatIndex		= 1,
+	.bNumFrameDescriptors	= 2,
+	.guidFormat		=
+		{ 'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
+		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
+	.bBitsPerPixel		= 16,
+	.bDefaultFrameIndex	= 1,
+	.bAspectRatioX		= 0,
+	.bAspectRatioY		= 0,
+	.bmInterfaceFlags	= 0,
+	.bCopyProtect		= 0,
+};
+
+DECLARE_UVC_FRAME_UNCOMPRESSED(1);
+DECLARE_UVC_FRAME_UNCOMPRESSED(3);
+
+static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
+	.bLength		= UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FRAME_UNCOMPRESSED,
+	.bFrameIndex		= 1,
+	.bmCapabilities		= 0,
+	.wWidth			= cpu_to_le16(640),
+	.wHeight		= cpu_to_le16(360),
+	.dwMinBitRate		= cpu_to_le32(18432000),
+	.dwMaxBitRate		= cpu_to_le32(55296000),
+	.dwMaxVideoFrameBufferSize	= cpu_to_le32(460800),
+	.dwDefaultFrameInterval	= cpu_to_le32(666666),
+	.bFrameIntervalType	= 3,
+	.dwFrameInterval[0]	= cpu_to_le32(666666),
+	.dwFrameInterval[1]	= cpu_to_le32(1000000),
+	.dwFrameInterval[2]	= cpu_to_le32(5000000),
+};
+
+static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
+	.bLength		= UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FRAME_UNCOMPRESSED,
+	.bFrameIndex		= 2,
+	.bmCapabilities		= 0,
+	.wWidth			= cpu_to_le16(1280),
+	.wHeight		= cpu_to_le16(720),
+	.dwMinBitRate		= cpu_to_le32(29491200),
+	.dwMaxBitRate		= cpu_to_le32(29491200),
+	.dwMaxVideoFrameBufferSize	= cpu_to_le32(1843200),
+	.dwDefaultFrameInterval	= cpu_to_le32(5000000),
+	.bFrameIntervalType	= 1,
+	.dwFrameInterval[0]	= cpu_to_le32(5000000),
+};
+
+static const struct uvc_format_mjpeg uvc_format_mjpg = {
+	.bLength		= UVC_DT_FORMAT_MJPEG_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FORMAT_MJPEG,
+	.bFormatIndex		= 2,
+	.bNumFrameDescriptors	= 2,
+	.bmFlags		= 0,
+	.bDefaultFrameIndex	= 1,
+	.bAspectRatioX		= 0,
+	.bAspectRatioY		= 0,
+	.bmInterfaceFlags	= 0,
+	.bCopyProtect		= 0,
+};
+
+DECLARE_UVC_FRAME_MJPEG(1);
+DECLARE_UVC_FRAME_MJPEG(3);
+
+static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
+	.bLength		= UVC_DT_FRAME_MJPEG_SIZE(3),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FRAME_MJPEG,
+	.bFrameIndex		= 1,
+	.bmCapabilities		= 0,
+	.wWidth			= cpu_to_le16(640),
+	.wHeight		= cpu_to_le16(360),
+	.dwMinBitRate		= cpu_to_le32(18432000),
+	.dwMaxBitRate		= cpu_to_le32(55296000),
+	.dwMaxVideoFrameBufferSize	= cpu_to_le32(460800),
+	.dwDefaultFrameInterval	= cpu_to_le32(666666),
+	.bFrameIntervalType	= 3,
+	.dwFrameInterval[0]	= cpu_to_le32(666666),
+	.dwFrameInterval[1]	= cpu_to_le32(1000000),
+	.dwFrameInterval[2]	= cpu_to_le32(5000000),
+};
+
+static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
+	.bLength		= UVC_DT_FRAME_MJPEG_SIZE(1),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_FRAME_MJPEG,
+	.bFrameIndex		= 2,
+	.bmCapabilities		= 0,
+	.wWidth			= cpu_to_le16(1280),
+	.wHeight		= cpu_to_le16(720),
+	.dwMinBitRate		= cpu_to_le32(29491200),
+	.dwMaxBitRate		= cpu_to_le32(29491200),
+	.dwMaxVideoFrameBufferSize	= cpu_to_le32(1843200),
+	.dwDefaultFrameInterval	= cpu_to_le32(5000000),
+	.bFrameIntervalType	= 1,
+	.dwFrameInterval[0]	= cpu_to_le32(5000000),
+};
+
+static const struct uvc_color_matching_descriptor uvc_color_matching = {
+	.bLength		= UVC_DT_COLOR_MATCHING_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubType	= UVC_VS_COLORFORMAT,
+	.bColorPrimaries	= 1,
+	.bTransferCharacteristics	= 1,
+	.bMatrixCoefficients	= 4,
+};
+
+static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
+	(const struct uvc_descriptor_header *) &uvc_control_header,
+	(const struct uvc_descriptor_header *) &uvc_camera_terminal,
+	(const struct uvc_descriptor_header *) &uvc_processing,
+	(const struct uvc_descriptor_header *) &uvc_output_terminal,
+	NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
+	(const struct uvc_descriptor_header *) &uvc_control_header,
+	(const struct uvc_descriptor_header *) &uvc_camera_terminal,
+	(const struct uvc_descriptor_header *) &uvc_processing,
+	(const struct uvc_descriptor_header *) &uvc_output_terminal,
+	NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
+	(const struct uvc_descriptor_header *) &uvc_input_header,
+	(const struct uvc_descriptor_header *) &uvc_format_yuv,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+	(const struct uvc_descriptor_header *) &uvc_format_mjpg,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+	(const struct uvc_descriptor_header *) &uvc_color_matching,
+	NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
+	(const struct uvc_descriptor_header *) &uvc_input_header,
+	(const struct uvc_descriptor_header *) &uvc_format_yuv,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+	(const struct uvc_descriptor_header *) &uvc_format_mjpg,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+	(const struct uvc_descriptor_header *) &uvc_color_matching,
+	NULL,
+};
+
+static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
+	(const struct uvc_descriptor_header *) &uvc_input_header,
+	(const struct uvc_descriptor_header *) &uvc_format_yuv,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+	(const struct uvc_descriptor_header *) &uvc_format_mjpg,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
+	(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
+	(const struct uvc_descriptor_header *) &uvc_color_matching,
+	NULL,
+};
+
+/* --------------------------------------------------------------------------
+ * USB configuration
+ */
+
+static int
+webcam_config_bind(struct usb_configuration *c)
+{
+	int status = 0;
+
+	f_uvc = usb_get_function(fi_uvc);
+	if (IS_ERR(f_uvc))
+		return PTR_ERR(f_uvc);
+
+	status = usb_add_function(c, f_uvc);
+	if (status < 0)
+		usb_put_function(f_uvc);
+
+	return status;
+}
+
+static struct usb_configuration webcam_config_driver = {
+	.label			= webcam_config_label,
+	.bConfigurationValue	= 1,
+	.iConfiguration		= 0, /* dynamic */
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+	.MaxPower		= CONFIG_USB_GADGET_VBUS_DRAW,
+};
+
+static int
+webcam_unbind(struct usb_composite_dev *cdev)
+{
+	if (!IS_ERR_OR_NULL(f_uvc))
+		usb_put_function(f_uvc);
+	if (!IS_ERR_OR_NULL(fi_uvc))
+		usb_put_function_instance(fi_uvc);
+	return 0;
+}
+
+static int
+webcam_bind(struct usb_composite_dev *cdev)
+{
+	struct f_uvc_opts *uvc_opts;
+	int ret;
+
+	fi_uvc = usb_get_function_instance("uvc");
+	if (IS_ERR(fi_uvc))
+		return PTR_ERR(fi_uvc);
+
+	uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
+
+	uvc_opts->streaming_interval = streaming_interval;
+	uvc_opts->streaming_maxpacket = streaming_maxpacket;
+	uvc_opts->streaming_maxburst = streaming_maxburst;
+
+	uvc_opts->fs_control = uvc_fs_control_cls;
+	uvc_opts->ss_control = uvc_ss_control_cls;
+	uvc_opts->fs_streaming = uvc_fs_streaming_cls;
+	uvc_opts->hs_streaming = uvc_hs_streaming_cls;
+	uvc_opts->ss_streaming = uvc_ss_streaming_cls;
+
+	/* Allocate string descriptor numbers ... note that string contents
+	 * can be overridden by the composite_dev glue.
+	 */
+	ret = usb_string_ids_tab(cdev, webcam_strings);
+	if (ret < 0)
+		goto error;
+	webcam_device_descriptor.iManufacturer =
+		webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
+	webcam_device_descriptor.iProduct =
+		webcam_strings[USB_GADGET_PRODUCT_IDX].id;
+	webcam_config_driver.iConfiguration =
+		webcam_strings[STRING_DESCRIPTION_IDX].id;
+
+	/* Register our configuration. */
+	if ((ret = usb_add_config(cdev, &webcam_config_driver,
+					webcam_config_bind)) < 0)
+		goto error;
+
+	usb_composite_overwrite_options(cdev, &coverwrite);
+	INFO(cdev, "Webcam Video Gadget\n");
+	return 0;
+
+error:
+	usb_put_function_instance(fi_uvc);
+	return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Driver
+ */
+
+static struct usb_composite_driver webcam_driver = {
+	.name		= "g_webcam",
+	.dev		= &webcam_device_descriptor,
+	.strings	= webcam_device_strings,
+	.max_speed	= USB_SPEED_SUPER,
+	.bind		= webcam_bind,
+	.unbind		= webcam_unbind,
+};
+
+module_usb_composite_driver(webcam_driver);
+
+MODULE_AUTHOR("Laurent Pinchart");
+MODULE_DESCRIPTION("Webcam Video Gadget");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
new file mode 100644
index 0000000..6e84b44
--- /dev/null
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * zero.c -- Gadget Zero, for USB development
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ */
+
+/*
+ * Gadget Zero only needs two bulk endpoints, and is an example of how you
+ * can write a hardware-agnostic gadget driver running inside a USB device.
+ * Some hardware details are visible, but don't affect most of the driver.
+ *
+ * Use it with the Linux host/master side "usbtest" driver to get a basic
+ * functional test of your device-side usb stack, or with "usb-skeleton".
+ *
+ * It supports two similar configurations.  One sinks whatever the usb host
+ * writes, and in return sources zeroes.  The other loops whatever the host
+ * writes back, so the host can read it.
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+ * driver does).
+ *
+ * Why is *this* driver using two configurations, rather than setting up
+ * two interfaces with different functions?  To help verify that multiple
+ * configuration infrastructure is working correctly; also, so that it can
+ * work with low capability USB controllers without four bulk endpoints.
+ */
+
+/*
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/usb/composite.h>
+
+#include "g_zero.h"
+/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+#define DRIVER_VERSION		"Cinco de Mayo 2008"
+
+static const char longname[] = "Gadget Zero";
+
+/*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default.  Here's where to change that order, to
+ * work better with hosts where config changes are problematic or
+ * controllers (like original superh) that only support one config.
+ */
+static bool loopdefault = 0;
+module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
+
+static struct usb_zero_options gzero_options = {
+	.isoc_interval = GZERO_ISOC_INTERVAL,
+	.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
+	.bulk_buflen = GZERO_BULK_BUFLEN,
+	.qlen = GZERO_QLEN,
+	.ss_bulk_qlen = GZERO_SS_BULK_QLEN,
+	.ss_iso_qlen = GZERO_SS_ISO_QLEN,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#ifndef	CONFIG_USB_ZERO_HNPTEST
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+#define DEFAULT_AUTORESUME	0
+#else
+#define DRIVER_VENDOR_NUM	0x1a0a		/* OTG test device IDs */
+#define DRIVER_PRODUCT_NUM	0xbadd
+#define DEFAULT_AUTORESUME	5
+#endif
+
+/* If the optional "autoresume" mode is enabled, it provides good
+ * functional coverage for the "USBCV" test harness from USB-IF.
+ * It's always set if OTG mode is enabled.
+ */
+static unsigned autoresume = DEFAULT_AUTORESUME;
+module_param(autoresume, uint, S_IRUGO);
+MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
+
+/* Maximum Autoresume time */
+static unsigned max_autoresume;
+module_param(max_autoresume, uint, S_IRUGO);
+MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup");
+
+/* Interval between two remote wakeups */
+static unsigned autoresume_interval_ms;
+module_param(autoresume_interval_ms, uint, S_IRUGO);
+MODULE_PARM_DESC(autoresume_interval_ms,
+		"milliseconds to increase successive wakeup delays");
+
+static unsigned autoresume_step_ms;
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	/* .bcdUSB = DYNAMIC */
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	.idVendor =		cpu_to_le16(DRIVER_VENDOR_NUM),
+	.idProduct =		cpu_to_le16(DRIVER_PRODUCT_NUM),
+	.bNumConfigurations =	2,
+};
+
+static const struct usb_descriptor_header *otg_desc[2];
+
+/* string IDs are assigned dynamically */
+/* default serial number takes at least two packets */
+static char serial[] = "0123456789.0123456789.0123456789";
+
+#define USB_GZERO_SS_DESC	(USB_GADGET_FIRST_AVAIL_IDX + 0)
+#define USB_GZERO_LB_DESC	(USB_GADGET_FIRST_AVAIL_IDX + 1)
+
+static struct usb_string strings_dev[] = {
+	[USB_GADGET_MANUFACTURER_IDX].s = "",
+	[USB_GADGET_PRODUCT_IDX].s = longname,
+	[USB_GADGET_SERIAL_IDX].s = serial,
+	[USB_GZERO_SS_DESC].s	= "source and sink data",
+	[USB_GZERO_LB_DESC].s	= "loop input to output",
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct timer_list	autoresume_timer;
+static struct usb_composite_dev *autoresume_cdev;
+
+static void zero_autoresume(struct timer_list *unused)
+{
+	struct usb_composite_dev	*cdev = autoresume_cdev;
+	struct usb_gadget		*g = cdev->gadget;
+
+	/* unconfigured devices can't issue wakeups */
+	if (!cdev->config)
+		return;
+
+	/* Normally the host would be woken up for something
+	 * more significant than just a timer firing; likely
+	 * because of some direct user request.
+	 */
+	if (g->speed != USB_SPEED_UNKNOWN) {
+		int status = usb_gadget_wakeup(g);
+		INFO(cdev, "%s --> %d\n", __func__, status);
+	}
+}
+
+static void zero_suspend(struct usb_composite_dev *cdev)
+{
+	if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
+		return;
+
+	if (autoresume) {
+		if (max_autoresume &&
+			(autoresume_step_ms > max_autoresume * 1000))
+				autoresume_step_ms = autoresume * 1000;
+
+		mod_timer(&autoresume_timer, jiffies +
+			msecs_to_jiffies(autoresume_step_ms));
+		DBG(cdev, "suspend, wakeup in %d milliseconds\n",
+			autoresume_step_ms);
+
+		autoresume_step_ms += autoresume_interval_ms;
+	} else
+		DBG(cdev, "%s\n", __func__);
+}
+
+static void zero_resume(struct usb_composite_dev *cdev)
+{
+	DBG(cdev, "%s\n", __func__);
+	del_timer(&autoresume_timer);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_configuration loopback_driver = {
+	.label          = "loopback",
+	.bConfigurationValue = 2,
+	.bmAttributes   = USB_CONFIG_ATT_SELFPOWER,
+	/* .iConfiguration = DYNAMIC */
+};
+
+static struct usb_function *func_ss;
+static struct usb_function_instance *func_inst_ss;
+
+static int ss_config_setup(struct usb_configuration *c,
+		const struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequest) {
+	case 0x5b:
+	case 0x5c:
+		return func_ss->setup(func_ss, ctrl);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static struct usb_configuration sourcesink_driver = {
+	.label                  = "source/sink",
+	.setup                  = ss_config_setup,
+	.bConfigurationValue    = 3,
+	.bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+	/* .iConfiguration      = DYNAMIC */
+};
+
+module_param_named(buflen, gzero_options.bulk_buflen, uint, 0);
+module_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
+
+module_param_named(isoc_interval, gzero_options.isoc_interval, uint,
+		S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_interval, "1 - 16");
+
+module_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint,
+		S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+module_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
+
+module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
+		S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
+
+static struct usb_function *func_lb;
+static struct usb_function_instance *func_inst_lb;
+
+module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(qlen, "depth of loopback queue");
+
+module_param_named(ss_bulk_qlen, gzero_options.ss_bulk_qlen, uint,
+		S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(bulk_qlen, "depth of sourcesink queue for bulk transfer");
+
+module_param_named(ss_iso_qlen, gzero_options.ss_iso_qlen, uint,
+		S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(iso_qlen, "depth of sourcesink queue for iso transfer");
+
+static int zero_bind(struct usb_composite_dev *cdev)
+{
+	struct f_ss_opts	*ss_opts;
+	struct f_lb_opts	*lb_opts;
+	int			status;
+
+	/* Allocate string descriptor numbers ... note that string
+	 * contents can be overridden by the composite_dev glue.
+	 */
+	status = usb_string_ids_tab(cdev, strings_dev);
+	if (status < 0)
+		return status;
+
+	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+	device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;
+
+	autoresume_cdev = cdev;
+	timer_setup(&autoresume_timer, zero_autoresume, 0);
+
+	func_inst_ss = usb_get_function_instance("SourceSink");
+	if (IS_ERR(func_inst_ss))
+		return PTR_ERR(func_inst_ss);
+
+	ss_opts =  container_of(func_inst_ss, struct f_ss_opts, func_inst);
+	ss_opts->pattern = gzero_options.pattern;
+	ss_opts->isoc_interval = gzero_options.isoc_interval;
+	ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
+	ss_opts->isoc_mult = gzero_options.isoc_mult;
+	ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
+	ss_opts->bulk_buflen = gzero_options.bulk_buflen;
+	ss_opts->bulk_qlen = gzero_options.ss_bulk_qlen;
+	ss_opts->iso_qlen = gzero_options.ss_iso_qlen;
+
+	func_ss = usb_get_function(func_inst_ss);
+	if (IS_ERR(func_ss)) {
+		status = PTR_ERR(func_ss);
+		goto err_put_func_inst_ss;
+	}
+
+	func_inst_lb = usb_get_function_instance("Loopback");
+	if (IS_ERR(func_inst_lb)) {
+		status = PTR_ERR(func_inst_lb);
+		goto err_put_func_ss;
+	}
+
+	lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst);
+	lb_opts->bulk_buflen = gzero_options.bulk_buflen;
+	lb_opts->qlen = gzero_options.qlen;
+
+	func_lb = usb_get_function(func_inst_lb);
+	if (IS_ERR(func_lb)) {
+		status = PTR_ERR(func_lb);
+		goto err_put_func_inst_lb;
+	}
+
+	sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id;
+	loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id;
+
+	/* support autoresume for remote wakeup testing */
+	sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+	loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+	sourcesink_driver.descriptors = NULL;
+	loopback_driver.descriptors = NULL;
+	if (autoresume) {
+		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		autoresume_step_ms = autoresume * 1000;
+	}
+
+	/* support OTG systems */
+	if (gadget_is_otg(cdev->gadget)) {
+		if (!otg_desc[0]) {
+			struct usb_descriptor_header *usb_desc;
+
+			usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
+			if (!usb_desc) {
+				status = -ENOMEM;
+				goto err_conf_flb;
+			}
+			usb_otg_descriptor_init(cdev->gadget, usb_desc);
+			otg_desc[0] = usb_desc;
+			otg_desc[1] = NULL;
+		}
+		sourcesink_driver.descriptors = otg_desc;
+		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		loopback_driver.descriptors = otg_desc;
+		loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	/* Register primary, then secondary configuration.  Note that
+	 * SH3 only allows one config...
+	 */
+	if (loopdefault) {
+		usb_add_config_only(cdev, &loopback_driver);
+		usb_add_config_only(cdev, &sourcesink_driver);
+	} else {
+		usb_add_config_only(cdev, &sourcesink_driver);
+		usb_add_config_only(cdev, &loopback_driver);
+	}
+	status = usb_add_function(&sourcesink_driver, func_ss);
+	if (status)
+		goto err_free_otg_desc;
+
+	usb_ep_autoconfig_reset(cdev->gadget);
+	status = usb_add_function(&loopback_driver, func_lb);
+	if (status)
+		goto err_free_otg_desc;
+
+	usb_ep_autoconfig_reset(cdev->gadget);
+	usb_composite_overwrite_options(cdev, &coverwrite);
+
+	INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
+
+	return 0;
+
+err_free_otg_desc:
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+err_conf_flb:
+	usb_put_function(func_lb);
+	func_lb = NULL;
+err_put_func_inst_lb:
+	usb_put_function_instance(func_inst_lb);
+	func_inst_lb = NULL;
+err_put_func_ss:
+	usb_put_function(func_ss);
+	func_ss = NULL;
+err_put_func_inst_ss:
+	usb_put_function_instance(func_inst_ss);
+	func_inst_ss = NULL;
+	return status;
+}
+
+static int zero_unbind(struct usb_composite_dev *cdev)
+{
+	del_timer_sync(&autoresume_timer);
+	if (!IS_ERR_OR_NULL(func_ss))
+		usb_put_function(func_ss);
+	usb_put_function_instance(func_inst_ss);
+	if (!IS_ERR_OR_NULL(func_lb))
+		usb_put_function(func_lb);
+	usb_put_function_instance(func_inst_lb);
+	kfree(otg_desc[0]);
+	otg_desc[0] = NULL;
+
+	return 0;
+}
+
+static struct usb_composite_driver zero_driver = {
+	.name		= "zero",
+	.dev		= &device_desc,
+	.strings	= dev_strings,
+	.max_speed	= USB_SPEED_SUPER,
+	.bind		= zero_bind,
+	.unbind		= zero_unbind,
+	.suspend	= zero_suspend,
+	.resume		= zero_resume,
+};
+
+module_usb_composite_driver(zero_driver);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c
new file mode 100644
index 0000000..dbaa46e
--- /dev/null
+++ b/drivers/usb/gadget/u_f.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_f.c -- USB function utilities for Gadget stack
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#include "u_f.h"
+#include <linux/usb/ch9.h>
+
+struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
+{
+	struct usb_request      *req;
+
+	req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (req) {
+		req->length = usb_endpoint_dir_out(ep->desc) ?
+			usb_ep_align(ep, len) : len;
+		req->buf = kmalloc(req->length, GFP_ATOMIC);
+		if (!req->buf) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+	return req;
+}
+EXPORT_SYMBOL_GPL(alloc_ep_req);
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
new file mode 100644
index 0000000..09f9044
--- /dev/null
+++ b/drivers/usb/gadget/u_f.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_f.h
+ *
+ * Utility definitions for USB functions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef __U_F_H__
+#define __U_F_H__
+
+#include <linux/usb/gadget.h>
+
+/* Variable Length Array Macros **********************************************/
+#define vla_group(groupname) size_t groupname##__next = 0
+#define vla_group_size(groupname) groupname##__next
+
+#define vla_item(groupname, type, name, n) \
+	size_t groupname##_##name##__offset = ({			       \
+		size_t align_mask = __alignof__(type) - 1;		       \
+		size_t offset = (groupname##__next + align_mask) & ~align_mask;\
+		size_t size = (n) * sizeof(type);			       \
+		groupname##__next = offset + size;			       \
+		offset;							       \
+	})
+
+#define vla_item_with_sz(groupname, type, name, n) \
+	size_t groupname##_##name##__sz = (n) * sizeof(type);		       \
+	size_t groupname##_##name##__offset = ({			       \
+		size_t align_mask = __alignof__(type) - 1;		       \
+		size_t offset = (groupname##__next + align_mask) & ~align_mask;\
+		size_t size = groupname##_##name##__sz;			       \
+		groupname##__next = offset + size;			       \
+		offset;							       \
+	})
+
+#define vla_ptr(ptr, groupname, name) \
+	((void *) ((char *)ptr + groupname##_##name##__offset))
+
+struct usb_ep;
+struct usb_request;
+
+/**
+ * alloc_ep_req - returns a usb_request allocated by the gadget driver and
+ * allocates the request's buffer.
+ *
+ * @ep: the endpoint to allocate a usb_request
+ * @len: usb_requests's buffer suggested size
+ *
+ * In case @ep direction is OUT, the @len will be aligned to ep's
+ * wMaxPacketSize. In order to avoid memory leaks or drops, *always* use
+ * usb_requests's length (req->length) to refer to the allocated buffer size.
+ * Requests allocated via alloc_ep_req() *must* be freed by free_ep_req().
+ */
+struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len);
+
+/* Frees a usb_request previously allocated by alloc_ep_req() */
+static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+	WARN_ON(req->buf == NULL);
+	kfree(req->buf);
+	req->buf = NULL;
+	usb_ep_free_request(ep, req);
+}
+
+#endif /* __U_F_H__ */
diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h
new file mode 100644
index 0000000..8acd217
--- /dev/null
+++ b/drivers/usb/gadget/u_os_desc.h
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * u_os_desc.h
+ *
+ * Utility definitions for "OS Descriptors" support
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ */
+
+#ifndef __U_OS_DESC_H__
+#define __U_OS_DESC_H__
+
+#include <asm/unaligned.h>
+#include <linux/nls.h>
+
+#define USB_EXT_PROP_DW_SIZE			0
+#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE	4
+#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH	8
+#define USB_EXT_PROP_B_PROPERTY_NAME		10
+#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH	10
+#define USB_EXT_PROP_B_PROPERTY_DATA		14
+
+#define USB_EXT_PROP_RESERVED			0
+#define USB_EXT_PROP_UNICODE			1
+#define USB_EXT_PROP_UNICODE_ENV		2
+#define USB_EXT_PROP_BINARY			3
+#define USB_EXT_PROP_LE32			4
+#define USB_EXT_PROP_BE32			5
+#define USB_EXT_PROP_UNICODE_LINK		6
+#define USB_EXT_PROP_UNICODE_MULTI		7
+
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+	return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+	return __usb_ext_prop_ptr(buf,
+				  USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+	return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
+static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
+{
+	put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
+}
+
+static inline void usb_ext_prop_put_type(u8 *buf, int type)
+{
+	put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
+}
+
+static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
+{
+	int result;
+
+	put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
+	result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
+		(wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2);
+	if (result < 0)
+		return result;
+
+	put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
+
+	return pnl;
+}
+
+static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
+					   int data_len)
+{
+	put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+	memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
+}
+
+static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
+					   int data_len)
+{
+	int result;
+	put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+	result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
+			(wchar_t *) usb_ext_prop_data_ptr(buf, pnl),
+			data_len - 2);
+	if (result < 0)
+		return result;
+
+	put_unaligned_le16(0,
+		&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
+
+	return data_len;
+}
+
+#endif /* __U_OS_DESC_H__ */
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
new file mode 100644
index 0000000..0a16cbd
--- /dev/null
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -0,0 +1,473 @@
+#
+# USB Gadget support on a system involves
+#    (a) a peripheral controller, and
+#    (b) the gadget driver using it.
+#
+# NOTE:  Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+#  - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+#  - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+#  - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+#
+# USB Peripheral Controller Support
+#
+# The order here is alphabetical, except that integrated controllers go
+# before discrete ones so they will be the initial/default value:
+#   - integrated/SOC controllers first
+#   - licensed IP used in both SOC and discrete versions
+#   - discrete ones (including all PCI-only controllers)
+#   - debug/dummy gadget+hcd is last.
+#
+menu "USB Peripheral Controller"
+
+#
+# Integrated controllers
+#
+
+config USB_AT91
+	tristate "Atmel AT91 USB Device Port"
+	depends on ARCH_AT91
+	depends on OF || COMPILE_TEST
+	help
+	   Many Atmel AT91 processors (such as the AT91RM2000) have a
+	   full speed USB Device Port with support for five configurable
+	   endpoints (plus endpoint zero).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "at91_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_LPC32XX
+	tristate "LPC32XX USB Peripheral Controller"
+	depends on ARCH_LPC32XX && I2C
+	select USB_ISP1301
+	help
+	   This option selects the USB device controller in the LPC32xx SoC.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "lpc32xx_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_ATMEL_USBA
+	tristate "Atmel USBA"
+	depends on ARCH_AT91
+	help
+	  USBA is the integrated high-speed USB Device controller on
+	  the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+
+	  The fifo_mode parameter is used to select endpoint allocation mode.
+	  fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
+	  In this case, for ep1 2 banks are allocated if it works in isochronous
+	  mode and only 1 bank otherwise. For the rest of the endpoints
+	  only 1 bank is allocated.
+
+	  fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
+	  allowing the usage of ep1 - ep6
+
+	  fifo_mode = 2 is a generic performance maximum fifo size (1024 bytes)
+	  configuration allowing the usage of ep1 - ep3
+
+	  fifo_mode = 3 is a balanced performance configuration allowing the
+	  the usage of ep1 - ep8
+
+config USB_BCM63XX_UDC
+	tristate "Broadcom BCM63xx Peripheral Controller"
+	depends on BCM63XX
+	help
+	   Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
+	   high speed USB Device Port with support for four fixed endpoints
+	   (plus endpoint zero).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "bcm63xx_udc".
+
+config USB_FSL_USB2
+	tristate "Freescale Highspeed USB DR Peripheral Controller"
+	depends on FSL_SOC || ARCH_MXC
+	help
+	   Some of Freescale PowerPC and i.MX processors have a High Speed
+	   Dual-Role(DR) USB controller, which supports device mode.
+
+	   The number of programmable endpoints is different through
+	   SOC revisions.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "fsl_usb2_udc" and force
+	   all gadget drivers to also be dynamically linked.
+
+config USB_FUSB300
+	tristate "Faraday FUSB300 USB Peripheral Controller"
+	depends on !PHYS_ADDR_T_64BIT && HAS_DMA
+	help
+	   Faraday usb device controller FUSB300 driver
+
+config USB_FOTG210_UDC
+	depends on HAS_DMA
+	tristate "Faraday FOTG210 USB Peripheral Controller"
+	help
+	   Faraday USB2.0 OTG controller which can be configured as
+	   high speed or full speed USB device. This driver supppors
+	   Bulk Transfer so far.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "fotg210_udc".
+
+config USB_GR_UDC
+       tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
+       depends on HAS_DMA
+       help
+          Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
+	  VHDL IP core library.
+
+config USB_OMAP
+	tristate "OMAP USB Device Controller"
+	depends on ARCH_OMAP1
+	depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
+	help
+	   Many Texas Instruments OMAP processors have flexible full
+	   speed USB device controllers, with support for up to 30
+	   endpoints (plus endpoint zero).  This driver supports the
+	   controller in the OMAP 1611, and should work with controllers
+	   in other OMAP processors too, given minor tweaks.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "omap_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_PXA25X
+	tristate "PXA 25x or IXP 4xx"
+	depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
+	depends on HAS_IOMEM
+	help
+	   Intel's PXA 25x series XScale ARM-5TE processors include
+	   an integrated full speed USB 1.1 device controller.  The
+	   controller in the IXP 4xx series is register-compatible.
+
+	   It has fifteen fixed-function endpoints, as well as endpoint
+	   zero (for control transfers).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "pxa25x_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+# 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_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_R8A66597
+	tristate "Renesas R8A66597 USB Peripheral Controller"
+	depends on HAS_DMA
+	help
+	   R8A66597 is a discrete USB host and peripheral controller chip that
+	   supports both full and high speed USB 2.0 data transfers.
+	   It has nine configurable endpoints, and endpoint zero.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "r8a66597_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_RENESAS_USBHS_UDC
+	tristate 'Renesas USBHS controller'
+	depends on USB_RENESAS_USBHS
+	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.
+
+	   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_RENESAS_USB3
+	tristate 'Renesas USB3.0 Peripheral controller'
+	depends on ARCH_RENESAS || COMPILE_TEST
+	depends on EXTCON
+	select USB_ROLE_SWITCH
+	help
+	   Renesas USB3.0 Peripheral controller is a USB peripheral controller
+	   that supports super, high, and full speed USB 3.0 data transfers.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "renesas_usb3" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_PXA27X
+	tristate "PXA 27x"
+	depends on HAS_IOMEM
+	help
+	   Intel's PXA 27x series XScale ARM v5TE processors include
+	   an integrated full speed USB 1.1 device controller.
+
+	   It has up to 23 endpoints, as well as endpoint zero (for
+	   control transfers).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "pxa27x_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_S3C2410
+	tristate "S3C2410 USB Device Controller"
+	depends on ARCH_S3C24XX
+	help
+	  Samsung's S3C2410 is an ARM-4 processor with an integrated
+	  full speed USB 1.1 device controller.  It has 4 configurable
+	  endpoints, as well as endpoint zero (for control transfers).
+
+	  This driver has been tested on the S3C2410, S3C2412, and
+	  S3C2440 processors.
+
+config USB_S3C2410_DEBUG
+	bool "S3C2410 udc debug messages"
+	depends on USB_S3C2410
+
+config USB_S3C_HSUDC
+	tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C24XX
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_MV_UDC
+	tristate "Marvell USB2.0 Device Controller"
+	depends on HAS_DMA
+	help
+	  Marvell Socs (including PXA and MMP series) include a high speed
+	  USB2.0 OTG controller, which can be configured as high speed or
+	  full speed USB peripheral.
+
+config USB_MV_U3D
+	depends on HAS_DMA
+	tristate "MARVELL PXA2128 USB 3.0 controller"
+	help
+	  MARVELL PXA2128 Processor series include a super speed USB3.0 device
+	  controller, which support super speed USB peripheral.
+
+config USB_SNP_CORE
+	depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
+	depends on HAS_DMA
+	tristate
+	help
+	  This enables core driver support for Synopsys USB 2.0 Device
+	  controller.
+
+	  This will be enabled when PCI or Platform driver for this UDC is
+	  selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
+	  USB_AMD5536UDC options.
+
+	  This IP is different to the High Speed OTG IP that can be enabled
+	  by selecting USB_DWC2 or USB_DWC3 options.
+
+config USB_SNP_UDC_PLAT
+	tristate "Synopsys USB 2.0 Device controller"
+	depends on USB_GADGET && OF && HAS_DMA
+	depends on EXTCON || EXTCON=n
+	select USB_SNP_CORE
+	default ARCH_BCM_IPROC
+	help
+	  This adds Platform Device support for Synopsys Designware core
+	  AHB subsystem USB2.0 Device Controller (UDC).
+
+	  This driver works with UDCs integrated into Broadcom's Northstar2
+	  and Cygnus SoCs.
+
+	  If unsure, say N.
+#
+# Controllers available in both integrated and discrete versions
+#
+
+config USB_M66592
+	tristate "Renesas M66592 USB Peripheral Controller"
+	depends on HAS_IOMEM
+	help
+	   M66592 is a discrete USB peripheral controller chip that
+	   supports both full and high speed USB 2.0 data transfers.
+	   It has seven configurable endpoints, and endpoint zero.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "m66592_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+source "drivers/usb/gadget/udc/bdc/Kconfig"
+
+#
+# Controllers available only in discrete form (and all PCI controllers)
+#
+
+config USB_AMD5536UDC
+	tristate "AMD5536 UDC"
+	depends on USB_PCI
+	select USB_SNP_CORE
+	help
+	   The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
+	   It is a USB Highspeed DMA capable USB device controller. Beside ep0
+	   it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
+	   The UDC port supports OTG operation, and may be used as a host port
+	   if it's not being used to implement peripheral or OTG roles.
+
+	   This UDC is based on Synopsys USB device controller IP and selects
+	   CONFIG_USB_SNP_CORE option to build the core driver.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "amd5536udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+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
+	   QE/CPM2 USB controller, which support device mode with 4
+	   programmable endpoints. This driver supports the
+	   controller in the MPC8360 and MPC8272, and should work with
+	   controllers having QE or CPM2, given minor tweaks.
+
+	   Set CONFIG_USB_GADGET to "m" to build this driver as a
+	   dynamically linked module called "fsl_qe_udc".
+
+config USB_NET2272
+	depends on HAS_IOMEM
+	tristate "PLX NET2272"
+	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
+	bool "Support external DMA controller"
+	depends on USB_NET2272 && HAS_DMA
+	help
+	  The NET2272 part can optionally support an external DMA
+	  controller, but your board has to have support in the
+	  driver itself.
+
+	  If unsure, say "N" here.  The driver works fine in PIO mode.
+
+config USB_NET2280
+	tristate "NetChip NET228x / PLX USB3x8x"
+	depends on USB_PCI
+	help
+	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which
+	   supports both full and high speed USB 2.0 data transfers.
+
+	   It has six configurable endpoints, as well as endpoint zero
+	   (for control transfers) and several endpoints with dedicated
+	   functions.
+
+	   PLX 2380 is a PCIe version of the PLX 2380.
+
+	   PLX 3380 / 3382 is a PCIe based USB peripheral controller which
+	   supports full, high speed USB 2.0 and super speed USB 3.0
+	   data transfers.
+
+	   It has eight configurable endpoints, as well as endpoint zero
+	   (for control transfers) and several endpoints with dedicated
+	   functions.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "net2280" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_GOKU
+	tristate "Toshiba TC86C001 'Goku-S'"
+	depends on USB_PCI
+	help
+	   The Toshiba TC86C001 is a PCI device which includes controllers
+	   for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
+
+	   The device controller has three configurable (bulk or interrupt)
+	   endpoints, plus endpoint zero (for control transfers).
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "goku_udc" and to force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_EG20T
+	tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
+	depends on USB_PCI
+	help
+	  This is a USB device driver for EG20T PCH.
+	  EG20T PCH is the platform controller hub that is used in Intel's
+	  general embedded platform. EG20T PCH has USB device interface.
+	  Using this interface, it is able to access system devices connected
+	  to USB device.
+	  This driver enables USB device function.
+	  USB device is a USB peripheral controller which
+	  supports both full and high speed USB 2.0 data transfers.
+	  This driver supports both control transfer and bulk transfer modes.
+	  This driver dose not support interrupt transfer or isochronous
+	  transfer modes.
+
+	  This driver also can be used for LAPIS Semiconductor's ML7213 which is
+	  for IVI(In-Vehicle Infotainment) use.
+	  ML7831 is for general purpose use.
+	  ML7213/ML7831 is companion chip for Intel Atom E6xx series.
+	  ML7213/ML7831 is completely compatible for Intel EG20T PCH.
+
+	  This driver can be used with Intel's Quark X1000 SOC platform
+
+config USB_GADGET_XILINX
+	tristate "Xilinx USB Driver"
+	depends on HAS_DMA
+	depends on OF || COMPILE_TEST
+	help
+	  USB peripheral controller driver for Xilinx USB2 device.
+	  Xilinx USB2 device is a soft IP which supports both full
+	  and high speed USB 2.0 data transfers. It has seven configurable
+	  endpoints(bulk or interrupt or isochronous), as well as
+	  endpoint zero(for control transfers).
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "udc-xilinx" and force all
+	  gadget drivers to also be dynamically linked.
+
+source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
+
+#
+# LAST -- dummy/emulated controller
+#
+
+config USB_DUMMY_HCD
+	tristate "Dummy HCD (DEVELOPMENT)"
+	depends on USB=y || (USB=m && USB_GADGET=m)
+	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.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "dummy_hcd" and force all
+	  gadget drivers to also be dynamically linked.
+
+# NOTE:  Please keep dummy_hcd LAST so that "real hardware" appears
+# first and will be selected by default.
+
+endmenu
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
new file mode 100644
index 0000000..897f648
--- /dev/null
+++ b/drivers/usb/gadget/udc/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o			:= -I$(src)
+
+udc-core-y			:= core.o trace.o
+
+#
+# USB peripheral controller drivers
+#
+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_SNP_CORE)	+= snps_udc_core.o
+obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc_pci.o
+obj-$(CONFIG_USB_PXA25X)	+= pxa25x_udc.o
+obj-$(CONFIG_USB_PXA27X)	+= pxa27x_udc.o
+obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
+obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
+obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
+obj-$(CONFIG_USB_AT91)		+= at91_udc.o
+obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o
+obj-$(CONFIG_USB_BCM63XX_UDC)	+= bcm63xx_udc.o
+obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
+fsl_usb2_udc-y			:= fsl_udc_core.o
+fsl_usb2_udc-$(CONFIG_ARCH_MXC)	+= fsl_mxc_udc.o
+obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
+obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
+obj-$(CONFIG_USB_RENESAS_USB3)	+= renesas_usb3.o
+obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
+obj-$(CONFIG_USB_LPC32XX)	+= lpc32xx_udc.o
+obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
+obj-$(CONFIG_USB_MV_UDC)	+= mv_udc.o
+mv_udc-y			:= mv_udc_core.o
+obj-$(CONFIG_USB_FUSB300)	+= fusb300_udc.o
+obj-$(CONFIG_USB_FOTG210_UDC)	+= fotg210-udc.o
+obj-$(CONFIG_USB_MV_U3D)	+= mv_u3d_core.o
+obj-$(CONFIG_USB_GR_UDC)	+= gr_udc.o
+obj-$(CONFIG_USB_GADGET_XILINX)	+= udc-xilinx.o
+obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
+obj-$(CONFIG_USB_ASPEED_VHUB)	+= aspeed-vhub/
+obj-$(CONFIG_USB_BDC_UDC)	+= bdc/
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
new file mode 100644
index 0000000..dfdef6a
--- /dev/null
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -0,0 +1,662 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * amd5536.h -- header for AMD 5536 UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ */
+
+#ifndef AMD5536UDC_H
+#define AMD5536UDC_H
+
+/* debug control */
+/* #define UDC_VERBOSE */
+
+#include <linux/extcon.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* various constants */
+#define UDC_RDE_TIMER_SECONDS		1
+#define UDC_RDE_TIMER_DIV		10
+#define UDC_POLLSTALL_TIMER_USECONDS	500
+
+/* Hs AMD5536 chip rev. */
+#define UDC_HSA0_REV 1
+#define UDC_HSB1_REV 2
+
+/* Broadcom chip rev. */
+#define UDC_BCM_REV 10
+
+/*
+ * SETUP usb commands
+ * needed, because some SETUP's are handled in hw, but must be passed to
+ * gadget driver above
+ * SET_CONFIG
+ */
+#define UDC_SETCONFIG_DWORD0			0x00000900
+#define UDC_SETCONFIG_DWORD0_VALUE_MASK		0xffff0000
+#define UDC_SETCONFIG_DWORD0_VALUE_OFS		16
+
+#define UDC_SETCONFIG_DWORD1			0x00000000
+
+/* SET_INTERFACE */
+#define UDC_SETINTF_DWORD0			0x00000b00
+#define UDC_SETINTF_DWORD0_ALT_MASK		0xffff0000
+#define UDC_SETINTF_DWORD0_ALT_OFS		16
+
+#define UDC_SETINTF_DWORD1			0x00000000
+#define UDC_SETINTF_DWORD1_INTF_MASK		0x0000ffff
+#define UDC_SETINTF_DWORD1_INTF_OFS		0
+
+/* Mass storage reset */
+#define UDC_MSCRES_DWORD0			0x0000ff21
+#define UDC_MSCRES_DWORD1			0x00000000
+
+/* Global CSR's -------------------------------------------------------------*/
+#define UDC_CSR_ADDR				0x500
+
+/* EP NE bits */
+/* EP number */
+#define UDC_CSR_NE_NUM_MASK			0x0000000f
+#define UDC_CSR_NE_NUM_OFS			0
+/* EP direction */
+#define UDC_CSR_NE_DIR_MASK			0x00000010
+#define UDC_CSR_NE_DIR_OFS			4
+/* EP type */
+#define UDC_CSR_NE_TYPE_MASK			0x00000060
+#define UDC_CSR_NE_TYPE_OFS			5
+/* EP config number */
+#define UDC_CSR_NE_CFG_MASK			0x00000780
+#define UDC_CSR_NE_CFG_OFS			7
+/* EP interface number */
+#define UDC_CSR_NE_INTF_MASK			0x00007800
+#define UDC_CSR_NE_INTF_OFS			11
+/* EP alt setting */
+#define UDC_CSR_NE_ALT_MASK			0x00078000
+#define UDC_CSR_NE_ALT_OFS			15
+
+/* max pkt */
+#define UDC_CSR_NE_MAX_PKT_MASK			0x3ff80000
+#define UDC_CSR_NE_MAX_PKT_OFS			19
+
+/* Device Config Register ---------------------------------------------------*/
+#define UDC_DEVCFG_ADDR				0x400
+
+#define UDC_DEVCFG_SOFTRESET			31
+#define UDC_DEVCFG_HNPSFEN			30
+#define UDC_DEVCFG_DMARST			29
+#define UDC_DEVCFG_SET_DESC			18
+#define UDC_DEVCFG_CSR_PRG			17
+#define UDC_DEVCFG_STATUS			7
+#define UDC_DEVCFG_DIR				6
+#define UDC_DEVCFG_PI				5
+#define UDC_DEVCFG_SS				4
+#define UDC_DEVCFG_SP				3
+#define UDC_DEVCFG_RWKP				2
+
+#define UDC_DEVCFG_SPD_MASK			0x3
+#define UDC_DEVCFG_SPD_OFS			0
+#define UDC_DEVCFG_SPD_HS			0x0
+#define UDC_DEVCFG_SPD_FS			0x1
+#define UDC_DEVCFG_SPD_LS			0x2
+/*#define UDC_DEVCFG_SPD_FS			0x3*/
+
+
+/* Device Control Register --------------------------------------------------*/
+#define UDC_DEVCTL_ADDR				0x404
+
+#define UDC_DEVCTL_THLEN_MASK			0xff000000
+#define UDC_DEVCTL_THLEN_OFS			24
+
+#define UDC_DEVCTL_BRLEN_MASK			0x00ff0000
+#define UDC_DEVCTL_BRLEN_OFS			16
+
+#define UDC_DEVCTL_SRX_FLUSH			14
+#define UDC_DEVCTL_CSR_DONE			13
+#define UDC_DEVCTL_DEVNAK			12
+#define UDC_DEVCTL_SD				10
+#define UDC_DEVCTL_MODE				9
+#define UDC_DEVCTL_BREN				8
+#define UDC_DEVCTL_THE				7
+#define UDC_DEVCTL_BF				6
+#define UDC_DEVCTL_BE				5
+#define UDC_DEVCTL_DU				4
+#define UDC_DEVCTL_TDE				3
+#define UDC_DEVCTL_RDE				2
+#define UDC_DEVCTL_RES				0
+
+
+/* Device Status Register ---------------------------------------------------*/
+#define UDC_DEVSTS_ADDR				0x408
+
+#define UDC_DEVSTS_TS_MASK			0xfffc0000
+#define UDC_DEVSTS_TS_OFS			18
+
+#define UDC_DEVSTS_SESSVLD			17
+#define UDC_DEVSTS_PHY_ERROR			16
+#define UDC_DEVSTS_RXFIFO_EMPTY			15
+
+#define UDC_DEVSTS_ENUM_SPEED_MASK		0x00006000
+#define UDC_DEVSTS_ENUM_SPEED_OFS		13
+#define UDC_DEVSTS_ENUM_SPEED_FULL		1
+#define UDC_DEVSTS_ENUM_SPEED_HIGH		0
+
+#define UDC_DEVSTS_SUSP				12
+
+#define UDC_DEVSTS_ALT_MASK			0x00000f00
+#define UDC_DEVSTS_ALT_OFS			8
+
+#define UDC_DEVSTS_INTF_MASK			0x000000f0
+#define UDC_DEVSTS_INTF_OFS			4
+
+#define UDC_DEVSTS_CFG_MASK			0x0000000f
+#define UDC_DEVSTS_CFG_OFS			0
+
+
+/* Device Interrupt Register ------------------------------------------------*/
+#define UDC_DEVINT_ADDR				0x40c
+
+#define UDC_DEVINT_SVC				7
+#define UDC_DEVINT_ENUM				6
+#define UDC_DEVINT_SOF				5
+#define UDC_DEVINT_US				4
+#define UDC_DEVINT_UR				3
+#define UDC_DEVINT_ES				2
+#define UDC_DEVINT_SI				1
+#define UDC_DEVINT_SC				0
+
+/* Device Interrupt Mask Register -------------------------------------------*/
+#define UDC_DEVINT_MSK_ADDR			0x410
+
+#define UDC_DEVINT_MSK				0x7f
+
+/* Endpoint Interrupt Register ----------------------------------------------*/
+#define UDC_EPINT_ADDR				0x414
+
+#define UDC_EPINT_OUT_MASK			0xffff0000
+#define UDC_EPINT_OUT_OFS			16
+#define UDC_EPINT_IN_MASK			0x0000ffff
+#define UDC_EPINT_IN_OFS			0
+
+#define UDC_EPINT_IN_EP0			0
+#define UDC_EPINT_IN_EP1			1
+#define UDC_EPINT_IN_EP2			2
+#define UDC_EPINT_IN_EP3			3
+#define UDC_EPINT_OUT_EP0			16
+#define UDC_EPINT_OUT_EP1			17
+#define UDC_EPINT_OUT_EP2			18
+#define UDC_EPINT_OUT_EP3			19
+
+#define UDC_EPINT_EP0_ENABLE_MSK		0x001e001e
+
+/* Endpoint Interrupt Mask Register -----------------------------------------*/
+#define UDC_EPINT_MSK_ADDR			0x418
+
+#define UDC_EPINT_OUT_MSK_MASK			0xffff0000
+#define UDC_EPINT_OUT_MSK_OFS			16
+#define UDC_EPINT_IN_MSK_MASK			0x0000ffff
+#define UDC_EPINT_IN_MSK_OFS			0
+
+#define UDC_EPINT_MSK_DISABLE_ALL		0xffffffff
+/* mask non-EP0 endpoints */
+#define UDC_EPDATAINT_MSK_DISABLE		0xfffefffe
+/* mask all dev interrupts */
+#define UDC_DEV_MSK_DISABLE			0x7f
+
+/* Endpoint-specific CSR's --------------------------------------------------*/
+#define UDC_EPREGS_ADDR				0x0
+#define UDC_EPIN_REGS_ADDR			0x0
+#define UDC_EPOUT_REGS_ADDR			0x200
+
+#define UDC_EPCTL_ADDR				0x0
+
+#define UDC_EPCTL_RRDY				9
+#define UDC_EPCTL_CNAK				8
+#define UDC_EPCTL_SNAK				7
+#define UDC_EPCTL_NAK				6
+
+#define UDC_EPCTL_ET_MASK			0x00000030
+#define UDC_EPCTL_ET_OFS			4
+#define UDC_EPCTL_ET_CONTROL			0
+#define UDC_EPCTL_ET_ISO			1
+#define UDC_EPCTL_ET_BULK			2
+#define UDC_EPCTL_ET_INTERRUPT			3
+
+#define UDC_EPCTL_P				3
+#define UDC_EPCTL_SN				2
+#define UDC_EPCTL_F				1
+#define UDC_EPCTL_S				0
+
+/* Endpoint Status Registers ------------------------------------------------*/
+#define UDC_EPSTS_ADDR				0x4
+
+#define UDC_EPSTS_RX_PKT_SIZE_MASK		0x007ff800
+#define UDC_EPSTS_RX_PKT_SIZE_OFS		11
+
+#define UDC_EPSTS_TDC				10
+#define UDC_EPSTS_HE				9
+#define UDC_EPSTS_BNA				7
+#define UDC_EPSTS_IN				6
+
+#define UDC_EPSTS_OUT_MASK			0x00000030
+#define UDC_EPSTS_OUT_OFS			4
+#define UDC_EPSTS_OUT_DATA			1
+#define UDC_EPSTS_OUT_DATA_CLEAR		0x10
+#define UDC_EPSTS_OUT_SETUP			2
+#define UDC_EPSTS_OUT_SETUP_CLEAR		0x20
+#define UDC_EPSTS_OUT_CLEAR			0x30
+
+/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers ------*/
+#define UDC_EPIN_BUFF_SIZE_ADDR			0x8
+#define UDC_EPOUT_FRAME_NUMBER_ADDR		0x8
+
+#define UDC_EPIN_BUFF_SIZE_MASK			0x0000ffff
+#define UDC_EPIN_BUFF_SIZE_OFS			0
+/* EP0in txfifo = 128 bytes*/
+#define UDC_EPIN0_BUFF_SIZE			32
+/* EP0in fullspeed txfifo = 128 bytes*/
+#define UDC_FS_EPIN0_BUFF_SIZE			32
+
+/* fifo size mult = fifo size / max packet */
+#define UDC_EPIN_BUFF_SIZE_MULT			2
+
+/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */
+#define UDC_EPIN_BUFF_SIZE			256
+/* EPin small INT data fifo size = 128 bytes */
+#define UDC_EPIN_SMALLINT_BUFF_SIZE		32
+
+/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
+#define UDC_FS_EPIN_BUFF_SIZE			32
+
+#define UDC_EPOUT_FRAME_NUMBER_MASK		0x0000ffff
+#define UDC_EPOUT_FRAME_NUMBER_OFS		0
+
+/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/
+#define UDC_EPOUT_BUFF_SIZE_ADDR		0x0c
+#define UDC_EP_MAX_PKT_SIZE_ADDR		0x0c
+
+#define UDC_EPOUT_BUFF_SIZE_MASK		0xffff0000
+#define UDC_EPOUT_BUFF_SIZE_OFS			16
+#define UDC_EP_MAX_PKT_SIZE_MASK		0x0000ffff
+#define UDC_EP_MAX_PKT_SIZE_OFS			0
+/* EP0in max packet size = 64 bytes */
+#define UDC_EP0IN_MAX_PKT_SIZE			64
+/* EP0out max packet size = 64 bytes */
+#define UDC_EP0OUT_MAX_PKT_SIZE			64
+/* EP0in fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0IN_MAX_PKT_SIZE		64
+/* EP0out fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0OUT_MAX_PKT_SIZE		64
+
+/*
+ * Endpoint dma descriptors ------------------------------------------------
+ *
+ * Setup data, Status dword
+ */
+#define UDC_DMA_STP_STS_CFG_MASK		0x0fff0000
+#define UDC_DMA_STP_STS_CFG_OFS			16
+#define UDC_DMA_STP_STS_CFG_ALT_MASK		0x000f0000
+#define UDC_DMA_STP_STS_CFG_ALT_OFS		16
+#define UDC_DMA_STP_STS_CFG_INTF_MASK		0x00f00000
+#define UDC_DMA_STP_STS_CFG_INTF_OFS		20
+#define UDC_DMA_STP_STS_CFG_NUM_MASK		0x0f000000
+#define UDC_DMA_STP_STS_CFG_NUM_OFS		24
+#define UDC_DMA_STP_STS_RX_MASK			0x30000000
+#define UDC_DMA_STP_STS_RX_OFS			28
+#define UDC_DMA_STP_STS_BS_MASK			0xc0000000
+#define UDC_DMA_STP_STS_BS_OFS			30
+#define UDC_DMA_STP_STS_BS_HOST_READY		0
+#define UDC_DMA_STP_STS_BS_DMA_BUSY		1
+#define UDC_DMA_STP_STS_BS_DMA_DONE		2
+#define UDC_DMA_STP_STS_BS_HOST_BUSY		3
+/* IN data, Status dword */
+#define UDC_DMA_IN_STS_TXBYTES_MASK		0x0000ffff
+#define UDC_DMA_IN_STS_TXBYTES_OFS		0
+#define	UDC_DMA_IN_STS_FRAMENUM_MASK		0x07ff0000
+#define UDC_DMA_IN_STS_FRAMENUM_OFS		0
+#define UDC_DMA_IN_STS_L			27
+#define UDC_DMA_IN_STS_TX_MASK			0x30000000
+#define UDC_DMA_IN_STS_TX_OFS			28
+#define UDC_DMA_IN_STS_BS_MASK			0xc0000000
+#define UDC_DMA_IN_STS_BS_OFS			30
+#define UDC_DMA_IN_STS_BS_HOST_READY		0
+#define UDC_DMA_IN_STS_BS_DMA_BUSY		1
+#define UDC_DMA_IN_STS_BS_DMA_DONE		2
+#define UDC_DMA_IN_STS_BS_HOST_BUSY		3
+/* OUT data, Status dword */
+#define UDC_DMA_OUT_STS_RXBYTES_MASK		0x0000ffff
+#define UDC_DMA_OUT_STS_RXBYTES_OFS		0
+#define UDC_DMA_OUT_STS_FRAMENUM_MASK		0x07ff0000
+#define UDC_DMA_OUT_STS_FRAMENUM_OFS		0
+#define UDC_DMA_OUT_STS_L			27
+#define UDC_DMA_OUT_STS_RX_MASK			0x30000000
+#define UDC_DMA_OUT_STS_RX_OFS			28
+#define UDC_DMA_OUT_STS_BS_MASK			0xc0000000
+#define UDC_DMA_OUT_STS_BS_OFS			30
+#define UDC_DMA_OUT_STS_BS_HOST_READY		0
+#define UDC_DMA_OUT_STS_BS_DMA_BUSY		1
+#define UDC_DMA_OUT_STS_BS_DMA_DONE		2
+#define UDC_DMA_OUT_STS_BS_HOST_BUSY		3
+/* max ep0in packet */
+#define UDC_EP0IN_MAXPACKET			1000
+/* max dma packet */
+#define UDC_DMA_MAXPACKET			65536
+
+/* un-usable DMA address */
+#define DMA_DONT_USE				(~(dma_addr_t) 0 )
+
+/* other Endpoint register addresses and values-----------------------------*/
+#define UDC_EP_SUBPTR_ADDR			0x10
+#define UDC_EP_DESPTR_ADDR			0x14
+#define UDC_EP_WRITE_CONFIRM_ADDR		0x1c
+
+/* EP number as layouted in AHB space */
+#define UDC_EP_NUM				32
+#define UDC_EPIN_NUM				16
+#define UDC_EPIN_NUM_USED			5
+#define UDC_EPOUT_NUM				16
+/* EP number of EP's really used = EP0 + 8 data EP's */
+#define UDC_USED_EP_NUM				9
+/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */
+#define UDC_CSR_EP_OUT_IX_OFS			12
+
+#define UDC_EP0OUT_IX				16
+#define UDC_EP0IN_IX				0
+
+/* Rx fifo address and size = 1k -------------------------------------------*/
+#define UDC_RXFIFO_ADDR				0x800
+#define UDC_RXFIFO_SIZE				0x400
+
+/* Tx fifo address and size = 1.5k -----------------------------------------*/
+#define UDC_TXFIFO_ADDR				0xc00
+#define UDC_TXFIFO_SIZE				0x600
+
+/* default data endpoints --------------------------------------------------*/
+#define UDC_EPIN_STATUS_IX			1
+#define UDC_EPIN_IX				2
+#define UDC_EPOUT_IX				18
+
+/* general constants -------------------------------------------------------*/
+#define UDC_DWORD_BYTES				4
+#define UDC_BITS_PER_BYTE_SHIFT			3
+#define UDC_BYTE_MASK				0xff
+#define UDC_BITS_PER_BYTE			8
+
+/*---------------------------------------------------------------------------*/
+/* UDC CSR's */
+struct udc_csrs {
+
+	/* sca - setup command address */
+	u32 sca;
+
+	/* ep ne's */
+	u32 ne[UDC_USED_EP_NUM];
+} __attribute__ ((packed));
+
+/* AHB subsystem CSR registers */
+struct udc_regs {
+
+	/* device configuration */
+	u32 cfg;
+
+	/* device control */
+	u32 ctl;
+
+	/* device status */
+	u32 sts;
+
+	/* device interrupt */
+	u32 irqsts;
+
+	/* device interrupt mask */
+	u32 irqmsk;
+
+	/* endpoint interrupt */
+	u32 ep_irqsts;
+
+	/* endpoint interrupt mask */
+	u32 ep_irqmsk;
+} __attribute__ ((packed));
+
+/* endpoint specific registers */
+struct udc_ep_regs {
+
+	/* endpoint control */
+	u32 ctl;
+
+	/* endpoint status */
+	u32 sts;
+
+	/* endpoint buffer size in/ receive packet frame number out */
+	u32 bufin_framenum;
+
+	/* endpoint buffer size out/max packet size */
+	u32 bufout_maxpkt;
+
+	/* endpoint setup buffer pointer */
+	u32 subptr;
+
+	/* endpoint data descriptor pointer */
+	u32 desptr;
+
+	/* reserverd */
+	u32 reserved;
+
+	/* write/read confirmation */
+	u32 confirm;
+
+} __attribute__ ((packed));
+
+/* control data DMA desc */
+struct udc_stp_dma {
+	/* status quadlet */
+	u32	status;
+	/* reserved */
+	u32	_reserved;
+	/* first setup word */
+	u32	data12;
+	/* second setup word */
+	u32	data34;
+} __attribute__ ((aligned (16)));
+
+/* normal data DMA desc */
+struct udc_data_dma {
+	/* status quadlet */
+	u32	status;
+	/* reserved */
+	u32	_reserved;
+	/* buffer pointer */
+	u32	bufptr;
+	/* next descriptor pointer */
+	u32	next;
+} __attribute__ ((aligned (16)));
+
+/* request packet */
+struct udc_request {
+	/* embedded gadget ep */
+	struct usb_request		req;
+
+	/* flags */
+	unsigned			dma_going : 1,
+					dma_done : 1;
+	/* phys. address */
+	dma_addr_t			td_phys;
+	/* first dma desc. of chain */
+	struct udc_data_dma		*td_data;
+	/* last dma desc. of chain */
+	struct udc_data_dma		*td_data_last;
+	struct list_head		queue;
+
+	/* chain length */
+	unsigned			chain_len;
+
+};
+
+/* UDC specific endpoint parameters */
+struct udc_ep {
+	struct usb_ep			ep;
+	struct udc_ep_regs __iomem	*regs;
+	u32 __iomem			*txfifo;
+	u32 __iomem			*dma;
+	dma_addr_t			td_phys;
+	dma_addr_t			td_stp_dma;
+	struct udc_stp_dma		*td_stp;
+	struct udc_data_dma		*td;
+	/* temp request */
+	struct udc_request		*req;
+	unsigned			req_used;
+	unsigned			req_completed;
+	/* dummy DMA desc for BNA dummy */
+	struct udc_request		*bna_dummy_req;
+	unsigned			bna_occurred;
+
+	/* NAK state */
+	unsigned			naking;
+
+	struct udc			*dev;
+
+	/* queue for requests */
+	struct list_head		queue;
+	unsigned			halted;
+	unsigned			cancel_transfer;
+	unsigned			num : 5,
+					fifo_depth : 14,
+					in : 1;
+};
+
+/* device struct */
+struct udc {
+	struct usb_gadget		gadget;
+	spinlock_t			lock;	/* protects all state */
+	/* all endpoints */
+	struct udc_ep			ep[UDC_EP_NUM];
+	struct usb_gadget_driver	*driver;
+	/* operational flags */
+	unsigned			stall_ep0in : 1,
+					waiting_zlp_ack_ep0in : 1,
+					set_cfg_not_acked : 1,
+					data_ep_enabled : 1,
+					data_ep_queued : 1,
+					sys_suspended : 1,
+					connected;
+
+	u16				chiprev;
+
+	/* registers */
+	struct pci_dev			*pdev;
+	struct udc_csrs __iomem		*csr;
+	struct udc_regs __iomem		*regs;
+	struct udc_ep_regs __iomem	*ep_regs;
+	u32 __iomem			*rxfifo;
+	u32 __iomem			*txfifo;
+
+	/* DMA desc pools */
+	struct dma_pool			*data_requests;
+	struct dma_pool			*stp_requests;
+
+	/* device data */
+	unsigned long			phys_addr;
+	void __iomem			*virt_addr;
+	unsigned			irq;
+
+	/* states */
+	u16				cur_config;
+	u16				cur_intf;
+	u16				cur_alt;
+
+	/* for platform device and extcon support */
+	struct device			*dev;
+	struct phy			*udc_phy;
+	struct extcon_dev		*edev;
+	struct extcon_specific_cable_nb	extcon_nb;
+	struct notifier_block		nb;
+	struct delayed_work		drd_work;
+	struct workqueue_struct		*drd_wq;
+	u32				conn_type;
+};
+
+#define to_amd5536_udc(g)	(container_of((g), struct udc, gadget))
+
+/* setup request data */
+union udc_setup_data {
+	u32			data[2];
+	struct usb_ctrlrequest	request;
+};
+
+/* Function declarations */
+int udc_enable_dev_setup_interrupts(struct udc *dev);
+int udc_mask_unused_interrupts(struct udc *dev);
+irqreturn_t udc_irq(int irq, void *pdev);
+void gadget_release(struct device *pdev);
+void empty_req_queue(struct udc_ep *ep);
+void udc_basic_init(struct udc *dev);
+void free_dma_pools(struct udc *dev);
+int init_dma_pools(struct udc *dev);
+void udc_remove(struct udc *dev);
+int udc_probe(struct udc *dev);
+
+/* DMA usage flag */
+static bool use_dma = 1;
+/* packet per buffer dma */
+static bool use_dma_ppb = 1;
+/* with per descr. update */
+static bool use_dma_ppb_du;
+/* full speed only mode */
+static bool use_fullspeed;
+
+/* module parameters */
+module_param(use_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma, "true for DMA");
+module_param(use_dma_ppb, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
+module_param(use_dma_ppb_du, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb_du,
+	"true for DMA in packet per buffer mode with descriptor update");
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
+/*
+ *---------------------------------------------------------------------------
+ * SET and GET bitfields in u32 values
+ * via constants for mask/offset:
+ * <bit_field_stub_name> is the text between
+ * UDC_ and _MASK|_OFS of appropriate
+ * constant
+ *
+ * set bitfield value in u32 u32Val
+ */
+#define AMD_ADDBITS(u32Val, bitfield_val, bitfield_stub_name)		\
+	(((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK))))	\
+	| (((bitfield_val) << ((u32) bitfield_stub_name##_OFS))		\
+		& ((u32) bitfield_stub_name##_MASK)))
+
+/*
+ * set bitfield value in zero-initialized u32 u32Val
+ * => bitfield bits in u32Val are all zero
+ */
+#define AMD_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name)	\
+	((u32Val)							\
+	| (((bitfield_val) << ((u32) bitfield_stub_name##_OFS))		\
+		& ((u32) bitfield_stub_name##_MASK)))
+
+/* get bitfield value from u32 u32Val */
+#define AMD_GETBITS(u32Val, bitfield_stub_name)				\
+	((u32Val & ((u32) bitfield_stub_name##_MASK))			\
+		>> ((u32) bitfield_stub_name##_OFS))
+
+/* SET and GET bits in u32 values ------------------------------------------*/
+#define AMD_BIT(bit_stub_name) (1 << bit_stub_name)
+#define AMD_UNMASK_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
+#define AMD_CLEAR_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
+
+/* debug macros ------------------------------------------------------------*/
+
+#define DBG(udc , args...)	dev_dbg(udc->dev, args)
+
+#ifdef UDC_VERBOSE
+#define VDBG			DBG
+#else
+#define VDBG(udc , args...)	do {} while (0)
+#endif
+
+#endif /* #ifdef AMD5536UDC_H */
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
new file mode 100644
index 0000000..57b6f66
--- /dev/null
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ */
+
+/*
+ * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
+ * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
+ * provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
+ *
+ * Make sure that UDC is assigned to port 4 by BIOS settings (port can also
+ * be used as host port) and UOC bits PAD_EN and APU are set (should be done
+ * by BIOS init).
+ *
+ * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
+ * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
+ * can be used with gadget ether.
+ *
+ * This file does pci device registration, and the core driver implementation
+ * is done in amd5536udc.c
+ *
+ * The driver is split so as to use the core UDC driver which is based on
+ * Synopsys device controller IP (different than HS OTG IP) in UDCs
+ * integrated to SoC platforms.
+ *
+ */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION		"AMD 5536 UDC - USB Device Controller"
+
+/* system */
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/prefetch.h>
+#include <linux/pci.h>
+
+/* udc specific */
+#include "amd5536udc.h"
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "amd5536udc-pci";
+
+/* Reset all pci context */
+static void udc_pci_remove(struct pci_dev *pdev)
+{
+	struct udc		*dev;
+
+	dev = pci_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&udc->gadget);
+	/* gadget driver must not be registered */
+	if (WARN_ON(dev->driver))
+		return;
+
+	/* dma pool cleanup */
+	free_dma_pools(dev);
+
+	/* reset controller */
+	writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+	free_irq(pdev->irq, dev);
+	iounmap(dev->virt_addr);
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+	pci_disable_device(pdev);
+
+	udc_remove(dev);
+}
+
+/* Called by pci bus driver to init pci context */
+static int udc_pci_probe(
+	struct pci_dev *pdev,
+	const struct pci_device_id *id
+)
+{
+	struct udc		*dev;
+	unsigned long		resource;
+	unsigned long		len;
+	int			retval = 0;
+
+	/* one udc only */
+	if (udc) {
+		dev_dbg(&pdev->dev, "already probed\n");
+		return -EBUSY;
+	}
+
+	/* init */
+	dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	/* pci setup */
+	if (pci_enable_device(pdev) < 0) {
+		retval = -ENODEV;
+		goto err_pcidev;
+	}
+
+	/* PCI resource allocation */
+	resource = pci_resource_start(pdev, 0);
+	len = pci_resource_len(pdev, 0);
+
+	if (!request_mem_region(resource, len, name)) {
+		dev_dbg(&pdev->dev, "pci device used already\n");
+		retval = -EBUSY;
+		goto err_memreg;
+	}
+
+	dev->virt_addr = ioremap_nocache(resource, len);
+	if (!dev->virt_addr) {
+		dev_dbg(&pdev->dev, "start address cannot be mapped\n");
+		retval = -EFAULT;
+		goto err_ioremap;
+	}
+
+	if (!pdev->irq) {
+		dev_err(&pdev->dev, "irq not set\n");
+		retval = -ENODEV;
+		goto err_irq;
+	}
+
+	spin_lock_init(&dev->lock);
+	/* udc csr registers base */
+	dev->csr = dev->virt_addr + UDC_CSR_ADDR;
+	/* dev registers base */
+	dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
+	/* ep registers base */
+	dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
+	/* fifo's base */
+	dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
+	dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
+
+	if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
+		dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
+		retval = -EBUSY;
+		goto err_irq;
+	}
+
+	pci_set_drvdata(pdev, dev);
+
+	/* chip revision for Hs AMD5536 */
+	dev->chiprev = pdev->revision;
+
+	pci_set_master(pdev);
+	pci_try_set_mwi(pdev);
+
+	/* init dma pools */
+	if (use_dma) {
+		retval = init_dma_pools(dev);
+		if (retval != 0)
+			goto err_dma;
+	}
+
+	dev->phys_addr = resource;
+	dev->irq = pdev->irq;
+	dev->pdev = pdev;
+	dev->dev = &pdev->dev;
+
+	/* general probing */
+	if (udc_probe(dev)) {
+		retval = -ENODEV;
+		goto err_probe;
+	}
+	return 0;
+
+err_probe:
+	if (use_dma)
+		free_dma_pools(dev);
+err_dma:
+	free_irq(pdev->irq, dev);
+err_irq:
+	iounmap(dev->virt_addr);
+err_ioremap:
+	release_mem_region(resource, len);
+err_memreg:
+	pci_disable_device(pdev);
+err_pcidev:
+	kfree(dev);
+	return retval;
+}
+
+/* PCI device parameters */
+static const struct pci_device_id pci_id[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
+		.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+		.class_mask =	0xffffffff,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(pci, pci_id);
+
+/* PCI functions */
+static struct pci_driver udc_pci_driver = {
+	.name =		(char *) name,
+	.id_table =	pci_id,
+	.probe =	udc_pci_probe,
+	.remove =	udc_pci_remove,
+};
+module_pci_driver(udc_pci_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
new file mode 100644
index 0000000..83ba8a2
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+config USB_ASPEED_VHUB
+	tristate "Aspeed vHub UDC driver"
+	depends on ARCH_ASPEED || COMPILE_TEST
+	depends on USB_LIBCOMPOSITE
+	help
+	  USB peripheral controller for the Aspeed AST2500 family
+	  SoCs supporting the "vHub" functionality and USB2.0
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Makefile b/drivers/usb/gadget/udc/aspeed-vhub/Makefile
new file mode 100644
index 0000000..9f3add6
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+obj-$(CONFIG_USB_ASPEED_VHUB)	+= aspeed-vhub.o
+aspeed-vhub-y	:= core.o ep0.o epn.o dev.o hub.o
+
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
new file mode 100644
index 0000000..db3628b
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * core.c - Top level support
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+		   int status)
+{
+	bool internal = req->internal;
+
+	EPVDBG(ep, "completing request @%p, status %d\n", req, status);
+
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+
+	if (req->req.dma) {
+		if (!WARN_ON(!ep->dev))
+			usb_gadget_unmap_request(&ep->dev->gadget,
+						 &req->req, ep->epn.is_in);
+		req->req.dma = 0;
+	}
+
+	/*
+	 * If this isn't an internal EP0 request, call the core
+	 * to call the gadget completion.
+	 */
+	if (!internal) {
+		spin_unlock(&ep->vhub->lock);
+		usb_gadget_giveback_request(&ep->ep, &req->req);
+		spin_lock(&ep->vhub->lock);
+	}
+}
+
+void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
+{
+	struct ast_vhub_req *req;
+
+	EPDBG(ep, "Nuking\n");
+
+	/* Beware, lock will be dropped & req-acquired by done() */
+	while (!list_empty(&ep->queue)) {
+		req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
+		ast_vhub_done(ep, req, status);
+	}
+}
+
+struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
+					   gfp_t gfp_flags)
+{
+	struct ast_vhub_req *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+	return &req->req;
+}
+
+void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req)
+{
+	struct ast_vhub_req *req = to_ast_req(u_req);
+
+	kfree(req);
+}
+
+static irqreturn_t ast_vhub_irq(int irq, void *data)
+{
+	struct ast_vhub *vhub = data;
+	irqreturn_t iret = IRQ_NONE;
+	u32 istat;
+
+	/* Stale interrupt while tearing down */
+	if (!vhub->ep0_bufs)
+		return IRQ_NONE;
+
+	spin_lock(&vhub->lock);
+
+	/* Read and ACK interrupts */
+	istat = readl(vhub->regs + AST_VHUB_ISR);
+	if (!istat)
+		goto bail;
+	writel(istat, vhub->regs + AST_VHUB_ISR);
+	iret = IRQ_HANDLED;
+
+	UDCVDBG(vhub, "irq status=%08x, ep_acks=%08x ep_nacks=%08x\n",
+	       istat,
+	       readl(vhub->regs + AST_VHUB_EP_ACK_ISR),
+	       readl(vhub->regs + AST_VHUB_EP_NACK_ISR));
+
+	/* Handle generic EPs first */
+	if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
+		u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
+		writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
+
+		for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) {
+			u32 mask = VHUB_EP_IRQ(i);
+			if (ep_acks & mask) {
+				ast_vhub_epn_ack_irq(&vhub->epns[i]);
+				ep_acks &= ~mask;
+			}
+		}
+	}
+
+	/* Handle device interrupts */
+	if (istat & (VHUB_IRQ_DEVICE1 |
+		     VHUB_IRQ_DEVICE2 |
+		     VHUB_IRQ_DEVICE3 |
+		     VHUB_IRQ_DEVICE4 |
+		     VHUB_IRQ_DEVICE5)) {
+		if (istat & VHUB_IRQ_DEVICE1)
+			ast_vhub_dev_irq(&vhub->ports[0].dev);
+		if (istat & VHUB_IRQ_DEVICE2)
+			ast_vhub_dev_irq(&vhub->ports[1].dev);
+		if (istat & VHUB_IRQ_DEVICE3)
+			ast_vhub_dev_irq(&vhub->ports[2].dev);
+		if (istat & VHUB_IRQ_DEVICE4)
+			ast_vhub_dev_irq(&vhub->ports[3].dev);
+		if (istat & VHUB_IRQ_DEVICE5)
+			ast_vhub_dev_irq(&vhub->ports[4].dev);
+	}
+
+	/* Handle top-level vHub EP0 interrupts */
+	if (istat & (VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
+		     VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
+		     VHUB_IRQ_HUB_EP0_SETUP)) {
+		if (istat & VHUB_IRQ_HUB_EP0_IN_ACK_STALL)
+			ast_vhub_ep0_handle_ack(&vhub->ep0, true);
+		if (istat & VHUB_IRQ_HUB_EP0_OUT_ACK_STALL)
+			ast_vhub_ep0_handle_ack(&vhub->ep0, false);
+		if (istat & VHUB_IRQ_HUB_EP0_SETUP)
+			ast_vhub_ep0_handle_setup(&vhub->ep0);
+	}
+
+	/* Various top level bus events */
+	if (istat & (VHUB_IRQ_BUS_RESUME |
+		     VHUB_IRQ_BUS_SUSPEND |
+		     VHUB_IRQ_BUS_RESET)) {
+		if (istat & VHUB_IRQ_BUS_RESUME)
+			ast_vhub_hub_resume(vhub);
+		if (istat & VHUB_IRQ_BUS_SUSPEND)
+			ast_vhub_hub_suspend(vhub);
+		if (istat & VHUB_IRQ_BUS_RESET)
+			ast_vhub_hub_reset(vhub);
+	}
+
+ bail:
+	spin_unlock(&vhub->lock);
+	return iret;
+}
+
+void ast_vhub_init_hw(struct ast_vhub *vhub)
+{
+	u32 ctrl;
+
+	UDCDBG(vhub,"(Re)Starting HW ...\n");
+
+	/* Enable PHY */
+	ctrl = VHUB_CTRL_PHY_CLK |
+		VHUB_CTRL_PHY_RESET_DIS;
+
+       /*
+	* We do *NOT* set the VHUB_CTRL_CLK_STOP_SUSPEND bit
+	* to stop the logic clock during suspend because
+	* it causes the registers to become inaccessible and
+	* we haven't yet figured out a good wayt to bring the
+	* controller back into life to issue a wakeup.
+	*/
+
+	/*
+	 * Set some ISO & split control bits according to Aspeed
+	 * recommendation
+	 *
+	 * VHUB_CTRL_ISO_RSP_CTRL: When set tells the HW to respond
+	 * with 0 bytes data packet to ISO IN endpoints when no data
+	 * is available.
+	 *
+	 * VHUB_CTRL_SPLIT_IN: This makes a SOF complete a split IN
+	 * transaction.
+	 */
+	ctrl |= VHUB_CTRL_ISO_RSP_CTRL | VHUB_CTRL_SPLIT_IN;
+	writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+	udelay(1);
+
+	/* Set descriptor ring size */
+	if (AST_VHUB_DESCS_COUNT == 256) {
+		ctrl |= VHUB_CTRL_LONG_DESC;
+		writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+	} else {
+		BUILD_BUG_ON(AST_VHUB_DESCS_COUNT != 32);
+	}
+
+	/* Reset all devices */
+	writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET);
+	udelay(1);
+	writel(0, vhub->regs + AST_VHUB_SW_RESET);
+
+	/* Disable and cleanup EP ACK/NACK interrupts */
+	writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
+	writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
+	writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR);
+	writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR);
+
+	/* Default settings for EP0, enable HW hub EP1 */
+	writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
+	writel(VHUB_EP1_CTRL_RESET_TOGGLE |
+	       VHUB_EP1_CTRL_ENABLE,
+	       vhub->regs + AST_VHUB_EP1_CTRL);
+	writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
+
+	/* Configure EP0 DMA buffer */
+	writel(vhub->ep0.buf_dma, vhub->regs + AST_VHUB_EP0_DATA);
+
+	/* Clear address */
+	writel(0, vhub->regs + AST_VHUB_CONF);
+
+	/* Pullup hub (activate on host) */
+	if (vhub->force_usb1)
+		ctrl |= VHUB_CTRL_FULL_SPEED_ONLY;
+
+	ctrl |= VHUB_CTRL_UPSTREAM_CONNECT;
+	writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+
+	/* Enable some interrupts */
+	writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
+	       VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
+	       VHUB_IRQ_HUB_EP0_SETUP |
+	       VHUB_IRQ_EP_POOL_ACK_STALL |
+	       VHUB_IRQ_BUS_RESUME |
+	       VHUB_IRQ_BUS_SUSPEND |
+	       VHUB_IRQ_BUS_RESET,
+	       vhub->regs + AST_VHUB_IER);
+}
+
+static int ast_vhub_remove(struct platform_device *pdev)
+{
+	struct ast_vhub *vhub = platform_get_drvdata(pdev);
+	unsigned long flags;
+	int i;
+
+	if (!vhub || !vhub->regs)
+		return 0;
+
+	/* Remove devices */
+	for (i = 0; i < AST_VHUB_NUM_PORTS; i++)
+		ast_vhub_del_dev(&vhub->ports[i].dev);
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* Mask & ack all interrupts  */
+	writel(0, vhub->regs + AST_VHUB_IER);
+	writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
+
+	/* Pull device, leave PHY enabled */
+	writel(VHUB_CTRL_PHY_CLK |
+	       VHUB_CTRL_PHY_RESET_DIS,
+	       vhub->regs + AST_VHUB_CTRL);
+
+	if (vhub->clk)
+		clk_disable_unprepare(vhub->clk);
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	if (vhub->ep0_bufs)
+		dma_free_coherent(&pdev->dev,
+				  AST_VHUB_EP0_MAX_PACKET *
+				  (AST_VHUB_NUM_PORTS + 1),
+				  vhub->ep0_bufs,
+				  vhub->ep0_bufs_dma);
+	vhub->ep0_bufs = NULL;
+
+	return 0;
+}
+
+static int ast_vhub_probe(struct platform_device *pdev)
+{
+	enum usb_device_speed max_speed;
+	struct ast_vhub *vhub;
+	struct resource *res;
+	int i, rc = 0;
+
+	vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
+	if (!vhub)
+		return -ENOMEM;
+
+	spin_lock_init(&vhub->lock);
+	vhub->pdev = pdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	vhub->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(vhub->regs)) {
+		dev_err(&pdev->dev, "Failed to map resources\n");
+		return PTR_ERR(vhub->regs);
+	}
+	UDCDBG(vhub, "vHub@%pR mapped @%p\n", res, vhub->regs);
+
+	platform_set_drvdata(pdev, vhub);
+
+	vhub->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(vhub->clk)) {
+		rc = PTR_ERR(vhub->clk);
+		goto err;
+	}
+	rc = clk_prepare_enable(vhub->clk);
+	if (rc) {
+		dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", rc);
+		goto err;
+	}
+
+	/* Check if we need to limit the HW to USB1 */
+	max_speed = usb_get_maximum_speed(&pdev->dev);
+	if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
+		vhub->force_usb1 = true;
+
+	/* Mask & ack all interrupts before installing the handler */
+	writel(0, vhub->regs + AST_VHUB_IER);
+	writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
+
+	/* Find interrupt and install handler */
+	vhub->irq = platform_get_irq(pdev, 0);
+	if (vhub->irq < 0) {
+		dev_err(&pdev->dev, "Failed to get interrupt\n");
+		rc = vhub->irq;
+		goto err;
+	}
+	rc = devm_request_irq(&pdev->dev, vhub->irq, ast_vhub_irq, 0,
+			      KBUILD_MODNAME, vhub);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to request interrupt\n");
+		goto err;
+	}
+
+	/*
+	 * Allocate DMA buffers for all EP0s in one chunk,
+	 * one per port and one for the vHub itself
+	 */
+	vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
+					    AST_VHUB_EP0_MAX_PACKET *
+					    (AST_VHUB_NUM_PORTS + 1),
+					    &vhub->ep0_bufs_dma, GFP_KERNEL);
+	if (!vhub->ep0_bufs) {
+		dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+	UDCVDBG(vhub, "EP0 DMA buffers @%p (DMA 0x%08x)\n",
+		vhub->ep0_bufs, (u32)vhub->ep0_bufs_dma);
+
+	/* Init vHub EP0 */
+	ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
+
+	/* Init devices */
+	for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++)
+		rc = ast_vhub_init_dev(vhub, i);
+	if (rc)
+		goto err;
+
+	/* Init hub emulation */
+	ast_vhub_init_hub(vhub);
+
+	/* Initialize HW */
+	ast_vhub_init_hw(vhub);
+
+	dev_info(&pdev->dev, "Initialized virtual hub in USB%d mode\n",
+		 vhub->force_usb1 ? 1 : 2);
+
+	return 0;
+ err:
+	ast_vhub_remove(pdev);
+	return rc;
+}
+
+static const struct of_device_id ast_vhub_dt_ids[] = {
+	{
+		.compatible = "aspeed,ast2400-usb-vhub",
+	},
+	{
+		.compatible = "aspeed,ast2500-usb-vhub",
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
+
+static struct platform_driver ast_vhub_driver = {
+	.probe		= ast_vhub_probe,
+	.remove		= ast_vhub_remove,
+	.driver		= {
+		.name	= KBUILD_MODNAME,
+		.of_match_table	= ast_vhub_dt_ids,
+	},
+};
+module_platform_driver(ast_vhub_driver);
+
+MODULE_DESCRIPTION("Aspeed vHub udc driver");
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
new file mode 100644
index 0000000..f023391
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * dev.c - Individual device/gadget management (ie, a port = a gadget)
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "vhub.h"
+
+void ast_vhub_dev_irq(struct ast_vhub_dev *d)
+{
+	u32 istat = readl(d->regs + AST_VHUB_DEV_ISR);
+
+	writel(istat, d->regs + AST_VHUB_DEV_ISR);
+
+	if (istat & VHUV_DEV_IRQ_EP0_IN_ACK_STALL)
+		ast_vhub_ep0_handle_ack(&d->ep0, true);
+	if (istat & VHUV_DEV_IRQ_EP0_OUT_ACK_STALL)
+		ast_vhub_ep0_handle_ack(&d->ep0, false);
+	if (istat & VHUV_DEV_IRQ_EP0_SETUP)
+		ast_vhub_ep0_handle_setup(&d->ep0);
+}
+
+static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
+{
+	u32 reg, hmsk;
+
+	if (d->enabled)
+		return;
+
+	/* Enable device and its EP0 interrupts */
+	reg = VHUB_DEV_EN_ENABLE_PORT |
+		VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
+		VHUB_DEV_EN_EP0_OUT_ACK_IRQEN |
+		VHUB_DEV_EN_EP0_SETUP_IRQEN;
+	if (d->gadget.speed == USB_SPEED_HIGH)
+		reg |= VHUB_DEV_EN_SPEED_SEL_HIGH;
+	writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
+
+	/* Enable device interrupt in the hub as well */
+	hmsk = VHUB_IRQ_DEVICE1 << d->index;
+	reg = readl(d->vhub->regs + AST_VHUB_IER);
+	reg |= hmsk;
+	writel(reg, d->vhub->regs + AST_VHUB_IER);
+
+	/* Set EP0 DMA buffer address */
+	writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
+
+	d->enabled = true;
+}
+
+static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
+{
+	u32 reg, hmsk;
+
+	if (!d->enabled)
+		return;
+
+	/* Disable device interrupt in the hub */
+	hmsk = VHUB_IRQ_DEVICE1 << d->index;
+	reg = readl(d->vhub->regs + AST_VHUB_IER);
+	reg &= ~hmsk;
+	writel(reg, d->vhub->regs + AST_VHUB_IER);
+
+	/* Then disable device */
+	writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
+	d->gadget.speed = USB_SPEED_UNKNOWN;
+	d->enabled = false;
+	d->suspended = false;
+}
+
+static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
+				u16 wIndex, u16 wValue,
+				bool is_set)
+{
+	DDBG(d, "%s_FEATURE(dev val=%02x)\n",
+	     is_set ? "SET" : "CLEAR", wValue);
+
+	if (wValue != USB_DEVICE_REMOTE_WAKEUP)
+		return std_req_driver;
+
+	d->wakeup_en = is_set;
+
+	return std_req_complete;
+}
+
+static int ast_vhub_ep_feature(struct ast_vhub_dev *d,
+			       u16 wIndex, u16 wValue, bool is_set)
+{
+	struct ast_vhub_ep *ep;
+	int ep_num;
+
+	ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+	DDBG(d, "%s_FEATURE(ep%d val=%02x)\n",
+	     is_set ? "SET" : "CLEAR", ep_num, wValue);
+	if (ep_num == 0)
+		return std_req_complete;
+	if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1])
+		return std_req_stall;
+	if (wValue != USB_ENDPOINT_HALT)
+		return std_req_driver;
+
+	ep = d->epns[ep_num - 1];
+	if (WARN_ON(!ep))
+		return std_req_stall;
+
+	if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
+	    ep->epn.is_in != !!(wIndex & USB_DIR_IN))
+		return std_req_stall;
+
+	DDBG(d, "%s stall on EP %d\n",
+	     is_set ? "setting" : "clearing", ep_num);
+	ep->epn.stalled = is_set;
+	ast_vhub_update_epn_stall(ep);
+
+	return std_req_complete;
+}
+
+static int ast_vhub_dev_status(struct ast_vhub_dev *d,
+			       u16 wIndex, u16 wValue)
+{
+	u8 st0;
+
+	DDBG(d, "GET_STATUS(dev)\n");
+
+	st0 = d->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
+	if (d->wakeup_en)
+		st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+
+	return ast_vhub_simple_reply(&d->ep0, st0, 0);
+}
+
+static int ast_vhub_ep_status(struct ast_vhub_dev *d,
+			      u16 wIndex, u16 wValue)
+{
+	int ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+	struct ast_vhub_ep *ep;
+	u8 st0 = 0;
+
+	DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
+
+	if (ep_num >= AST_VHUB_NUM_GEN_EPs)
+		return std_req_stall;
+	if (ep_num != 0) {
+		ep = d->epns[ep_num - 1];
+		if (!ep)
+			return std_req_stall;
+		if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
+		    ep->epn.is_in != !!(wIndex & USB_DIR_IN))
+			return std_req_stall;
+		if (ep->epn.stalled)
+			st0 |= 1 << USB_ENDPOINT_HALT;
+	}
+
+	return ast_vhub_simple_reply(&d->ep0, st0, 0);
+}
+
+static void ast_vhub_dev_set_address(struct ast_vhub_dev *d, u8 addr)
+{
+	u32 reg;
+
+	DDBG(d, "SET_ADDRESS: Got address %x\n", addr);
+
+	reg = readl(d->regs + AST_VHUB_DEV_EN_CTRL);
+	reg &= ~VHUB_DEV_EN_ADDR_MASK;
+	reg |= VHUB_DEV_EN_SET_ADDR(addr);
+	writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
+}
+
+int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
+			     struct usb_ctrlrequest *crq)
+{
+	struct ast_vhub_dev *d = ep->dev;
+	u16 wValue, wIndex;
+
+	/* No driver, we shouldn't be enabled ... */
+	if (!d->driver || !d->enabled || d->suspended) {
+		EPDBG(ep,
+		      "Device is wrong state driver=%p enabled=%d"
+		      " suspended=%d\n",
+		      d->driver, d->enabled, d->suspended);
+		return std_req_stall;
+	}
+
+	/* First packet, grab speed */
+	if (d->gadget.speed == USB_SPEED_UNKNOWN) {
+		d->gadget.speed = ep->vhub->speed;
+		if (d->gadget.speed > d->driver->max_speed)
+			d->gadget.speed = d->driver->max_speed;
+		DDBG(d, "fist packet, captured speed %d\n",
+		     d->gadget.speed);
+	}
+
+	wValue = le16_to_cpu(crq->wValue);
+	wIndex = le16_to_cpu(crq->wIndex);
+
+	switch ((crq->bRequestType << 8) | crq->bRequest) {
+		/* SET_ADDRESS */
+	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+		ast_vhub_dev_set_address(d, wValue);
+		return std_req_complete;
+
+		/* GET_STATUS */
+	case DeviceRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_dev_status(d, wIndex, wValue);
+	case InterfaceRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_simple_reply(ep, 0, 0);
+	case EndpointRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_ep_status(d, wIndex, wValue);
+
+		/* SET/CLEAR_FEATURE */
+	case DeviceOutRequest | USB_REQ_SET_FEATURE:
+		return ast_vhub_dev_feature(d, wIndex, wValue, true);
+	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+		return ast_vhub_dev_feature(d, wIndex, wValue, false);
+	case EndpointOutRequest | USB_REQ_SET_FEATURE:
+		return ast_vhub_ep_feature(d, wIndex, wValue, true);
+	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+		return ast_vhub_ep_feature(d, wIndex, wValue, false);
+	}
+	return std_req_driver;
+}
+
+static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+	unsigned long flags;
+	int rc = -EINVAL;
+
+	spin_lock_irqsave(&d->vhub->lock, flags);
+	if (!d->wakeup_en)
+		goto err;
+
+	DDBG(d, "Device initiated wakeup\n");
+
+	/* Wakeup the host */
+	ast_vhub_hub_wake_all(d->vhub);
+	rc = 0;
+ err:
+	spin_unlock_irqrestore(&d->vhub->lock, flags);
+	return rc;
+}
+
+static int ast_vhub_udc_get_frame(struct usb_gadget* gadget)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+
+	return (readl(d->vhub->regs + AST_VHUB_USBSTS) >> 16) & 0x7ff;
+}
+
+static void ast_vhub_dev_nuke(struct ast_vhub_dev *d)
+{
+	unsigned int i;
+
+	for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+		if (!d->epns[i])
+			continue;
+		ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
+	}
+}
+
+static int ast_vhub_udc_pullup(struct usb_gadget* gadget, int on)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->vhub->lock, flags);
+
+	DDBG(d, "pullup(%d)\n", on);
+
+	/* Mark disconnected in the hub */
+	ast_vhub_device_connect(d->vhub, d->index, on);
+
+	/*
+	 * If enabled, nuke all requests if any (there shouldn't be)
+	 * and disable the port. This will clear the address too.
+	 */
+	if (d->enabled) {
+		ast_vhub_dev_nuke(d);
+		ast_vhub_dev_disable(d);
+	}
+
+	spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+	return 0;
+}
+
+static int ast_vhub_udc_start(struct usb_gadget *gadget,
+			      struct usb_gadget_driver *driver)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->vhub->lock, flags);
+
+	DDBG(d, "start\n");
+
+	/* We don't do much more until the hub enables us */
+	d->driver = driver;
+	d->gadget.is_selfpowered = 1;
+
+	spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+	return 0;
+}
+
+static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget,
+					    struct usb_endpoint_descriptor *desc,
+					    struct usb_ss_ep_comp_descriptor *ss)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+	struct ast_vhub_ep *ep;
+	struct usb_ep *u_ep;
+	unsigned int max, addr, i;
+
+	DDBG(d, "Match EP type %d\n", usb_endpoint_type(desc));
+
+	/*
+	 * First we need to look for an existing unclaimed EP as another
+	 * configuration may have already associated a bunch of EPs with
+	 * this gadget. This duplicates the code in usb_ep_autoconfig_ss()
+	 * unfortunately.
+	 */
+	list_for_each_entry(u_ep, &gadget->ep_list, ep_list) {
+		if (usb_gadget_ep_match_desc(gadget, u_ep, desc, ss)) {
+			DDBG(d, " -> using existing EP%d\n",
+			     to_ast_ep(u_ep)->d_idx);
+			return u_ep;
+		}
+	}
+
+	/*
+	 * We didn't find one, we need to grab one from the pool.
+	 *
+	 * First let's do some sanity checking
+	 */
+	switch(usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		/* Only EP0 can be a control endpoint */
+		return NULL;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* ISO:	 limit 1023 bytes full speed, 1024 high/super speed */
+		if (gadget_is_dualspeed(gadget))
+			max = 1024;
+		else
+			max = 1023;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		if (gadget_is_dualspeed(gadget))
+			max = 512;
+		else
+			max = 64;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (gadget_is_dualspeed(gadget))
+			max = 1024;
+		else
+			max = 64;
+		break;
+	}
+	if (usb_endpoint_maxp(desc) > max)
+		return NULL;
+
+	/*
+	 * Find a free EP address for that device. We can't
+	 * let the generic code assign these as it would
+	 * create overlapping numbers for IN and OUT which
+	 * we don't support, so also create a suitable name
+	 * that will allow the generic code to use our
+	 * assigned address.
+	 */
+	for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+		if (d->epns[i] == NULL)
+			break;
+	if (i >= AST_VHUB_NUM_GEN_EPs)
+		return NULL;
+	addr = i + 1;
+
+	/*
+	 * Now grab an EP from the shared pool and associate
+	 * it with our device
+	 */
+	ep = ast_vhub_alloc_epn(d, addr);
+	if (!ep)
+		return NULL;
+	DDBG(d, "Allocated epn#%d for port EP%d\n",
+	     ep->epn.g_idx, addr);
+
+	return &ep->ep;
+}
+
+static int ast_vhub_udc_stop(struct usb_gadget *gadget)
+{
+	struct ast_vhub_dev *d = to_ast_dev(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->vhub->lock, flags);
+
+	DDBG(d, "stop\n");
+
+	d->driver = NULL;
+	d->gadget.speed = USB_SPEED_UNKNOWN;
+
+	ast_vhub_dev_nuke(d);
+
+	if (d->enabled)
+		ast_vhub_dev_disable(d);
+
+	spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+	return 0;
+}
+
+static struct usb_gadget_ops ast_vhub_udc_ops = {
+	.get_frame	= ast_vhub_udc_get_frame,
+	.wakeup		= ast_vhub_udc_wakeup,
+	.pullup		= ast_vhub_udc_pullup,
+	.udc_start	= ast_vhub_udc_start,
+	.udc_stop	= ast_vhub_udc_stop,
+	.match_ep	= ast_vhub_udc_match_ep,
+};
+
+void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
+{
+	d->suspended = true;
+	if (d->driver) {
+		spin_unlock(&d->vhub->lock);
+		d->driver->suspend(&d->gadget);
+		spin_lock(&d->vhub->lock);
+	}
+}
+
+void ast_vhub_dev_resume(struct ast_vhub_dev *d)
+{
+	d->suspended = false;
+	if (d->driver) {
+		spin_unlock(&d->vhub->lock);
+		d->driver->resume(&d->gadget);
+		spin_lock(&d->vhub->lock);
+	}
+}
+
+void ast_vhub_dev_reset(struct ast_vhub_dev *d)
+{
+	/*
+	 * If speed is not set, we enable the port. If it is,
+	 * send reset to the gadget and reset "speed".
+	 *
+	 * Speed is an indication that we have got the first
+	 * setup packet to the device.
+	 */
+	if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) {
+		DDBG(d, "Reset at unknown speed of disabled device, enabling...\n");
+		ast_vhub_dev_enable(d);
+		d->suspended = false;
+	}
+	if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) {
+		unsigned int i;
+
+		DDBG(d, "Reset at known speed of bound device, resetting...\n");
+		spin_unlock(&d->vhub->lock);
+		d->driver->reset(&d->gadget);
+		spin_lock(&d->vhub->lock);
+
+		/*
+		 * Disable/re-enable HW, this will clear the address
+		 * and speed setting.
+		 */
+		ast_vhub_dev_disable(d);
+		ast_vhub_dev_enable(d);
+
+		/* Clear stall on all EPs */
+		for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+			struct ast_vhub_ep *ep = d->epns[i];
+
+			if (ep && ep->epn.stalled) {
+				ep->epn.stalled = false;
+				ast_vhub_update_epn_stall(ep);
+			}
+		}
+
+		/* Additional cleanups */
+		d->wakeup_en = false;
+		d->suspended = false;
+	}
+}
+
+void ast_vhub_del_dev(struct ast_vhub_dev *d)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->vhub->lock, flags);
+	if (!d->registered) {
+		spin_unlock_irqrestore(&d->vhub->lock, flags);
+		return;
+	}
+	d->registered = false;
+	spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+	usb_del_gadget_udc(&d->gadget);
+	device_unregister(d->port_dev);
+}
+
+static void ast_vhub_dev_release(struct device *dev)
+{
+	kfree(dev);
+}
+
+int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
+{
+	struct ast_vhub_dev *d = &vhub->ports[idx].dev;
+	struct device *parent = &vhub->pdev->dev;
+	int rc;
+
+	d->vhub = vhub;
+	d->index = idx;
+	d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
+	d->regs = vhub->regs + 0x100 + 0x10 * idx;
+
+	ast_vhub_init_ep0(vhub, &d->ep0, d);
+
+	/*
+	 * The UDC core really needs us to have separate and uniquely
+	 * named "parent" devices for each port so we create a sub device
+	 * here for that purpose
+	 */
+	d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!d->port_dev)
+		return -ENOMEM;
+	device_initialize(d->port_dev);
+	d->port_dev->release = ast_vhub_dev_release;
+	d->port_dev->parent = parent;
+	dev_set_name(d->port_dev, "%s:p%d", dev_name(parent), idx + 1);
+	rc = device_add(d->port_dev);
+	if (rc)
+		goto fail_add;
+
+	/* Populate gadget */
+	INIT_LIST_HEAD(&d->gadget.ep_list);
+	d->gadget.ops = &ast_vhub_udc_ops;
+	d->gadget.ep0 = &d->ep0.ep;
+	d->gadget.name = KBUILD_MODNAME;
+	if (vhub->force_usb1)
+		d->gadget.max_speed = USB_SPEED_FULL;
+	else
+		d->gadget.max_speed = USB_SPEED_HIGH;
+	d->gadget.speed = USB_SPEED_UNKNOWN;
+	d->gadget.dev.of_node = vhub->pdev->dev.of_node;
+
+	rc = usb_add_gadget_udc(d->port_dev, &d->gadget);
+	if (rc != 0)
+		goto fail_udc;
+	d->registered = true;
+
+	return 0;
+ fail_udc:
+	device_del(d->port_dev);
+ fail_add:
+	put_device(d->port_dev);
+
+	return rc;
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
new file mode 100644
index 0000000..e2927fb
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * ep0.c - Endpoint 0 handling
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len)
+{
+	struct usb_request *req = &ep->ep0.req.req;
+	int rc;
+
+	if (WARN_ON(ep->d_idx != 0))
+		return std_req_stall;
+	if (WARN_ON(!ep->ep0.dir_in))
+		return std_req_stall;
+	if (WARN_ON(len > AST_VHUB_EP0_MAX_PACKET))
+		return std_req_stall;
+	if (WARN_ON(req->status == -EINPROGRESS))
+		return std_req_stall;
+
+	req->buf = ptr;
+	req->length = len;
+	req->complete = NULL;
+	req->zero = true;
+
+	/*
+	 * Call internal queue directly after dropping the lock. This is
+	 * safe to do as the reply is always the last thing done when
+	 * processing a SETUP packet, usually as a tail call
+	 */
+	spin_unlock(&ep->vhub->lock);
+	if (ep->ep.ops->queue(&ep->ep, req, GFP_ATOMIC))
+		rc = std_req_stall;
+	else
+		rc = std_req_data;
+	spin_lock(&ep->vhub->lock);
+	return rc;
+}
+
+int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...)
+{
+	u8 *buffer = ep->buf;
+	unsigned int i;
+	va_list args;
+
+	va_start(args, len);
+
+	/* Copy data directly into EP buffer */
+	for (i = 0; i < len; i++)
+		buffer[i] = va_arg(args, int);
+	va_end(args);
+
+	/* req->buf NULL means data is already there */
+	return ast_vhub_reply(ep, NULL, len);
+}
+
+void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
+{
+	struct usb_ctrlrequest crq;
+	enum std_req_rc std_req_rc;
+	int rc = -ENODEV;
+
+	if (WARN_ON(ep->d_idx != 0))
+		return;
+
+	/*
+	 * Grab the setup packet from the chip and byteswap
+	 * interesting fields
+	 */
+	memcpy_fromio(&crq, ep->ep0.setup, sizeof(crq));
+
+	EPDBG(ep, "SETUP packet %02x/%02x/%04x/%04x/%04x [%s] st=%d\n",
+	      crq.bRequestType, crq.bRequest,
+	       le16_to_cpu(crq.wValue),
+	       le16_to_cpu(crq.wIndex),
+	       le16_to_cpu(crq.wLength),
+	       (crq.bRequestType & USB_DIR_IN) ? "in" : "out",
+	       ep->ep0.state);
+
+	/* Check our state, cancel pending requests if needed */
+	if (ep->ep0.state != ep0_state_token) {
+		EPDBG(ep, "wrong state\n");
+		ast_vhub_nuke(ep, -EIO);
+
+		/*
+		 * Accept the packet regardless, this seems to happen
+		 * when stalling a SETUP packet that has an OUT data
+		 * phase.
+		 */
+		ast_vhub_nuke(ep, 0);
+		goto stall;
+	}
+
+	/* Calculate next state for EP0 */
+	ep->ep0.state = ep0_state_data;
+	ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN);
+
+	/* If this is the vHub, we handle requests differently */
+	std_req_rc = std_req_driver;
+	if (ep->dev == NULL) {
+		if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+			std_req_rc = ast_vhub_std_hub_request(ep, &crq);
+		else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
+			std_req_rc = ast_vhub_class_hub_request(ep, &crq);
+		else
+			std_req_rc = std_req_stall;
+	} else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		std_req_rc = ast_vhub_std_dev_request(ep, &crq);
+
+	/* Act upon result */
+	switch(std_req_rc) {
+	case std_req_complete:
+		goto complete;
+	case std_req_stall:
+		goto stall;
+	case std_req_driver:
+		break;
+	case std_req_data:
+		return;
+	}
+
+	/* Pass request up to the gadget driver */
+	if (WARN_ON(!ep->dev))
+		goto stall;
+	if (ep->dev->driver) {
+		EPDBG(ep, "forwarding to gadget...\n");
+		spin_unlock(&ep->vhub->lock);
+		rc = ep->dev->driver->setup(&ep->dev->gadget, &crq);
+		spin_lock(&ep->vhub->lock);
+		EPDBG(ep, "driver returned %d\n", rc);
+	} else {
+		EPDBG(ep, "no gadget for request !\n");
+	}
+	if (rc >= 0)
+		return;
+
+ stall:
+	EPDBG(ep, "stalling\n");
+	writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+	ep->ep0.state = ep0_state_status;
+	ep->ep0.dir_in = false;
+	return;
+
+ complete:
+	EPVDBG(ep, "sending [in] status with no data\n");
+	writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+	ep->ep0.state = ep0_state_status;
+	ep->ep0.dir_in = false;
+}
+
+
+static void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep,
+				 struct ast_vhub_req *req)
+{
+	unsigned int chunk;
+	u32 reg;
+
+	/* If this is a 0-length request, it's the gadget trying to
+	 * send a status on our behalf. We take it from here.
+	 */
+	if (req->req.length == 0)
+		req->last_desc = 1;
+
+	/* Are we done ? Complete request, otherwise wait for next interrupt */
+	if (req->last_desc >= 0) {
+		EPVDBG(ep, "complete send %d/%d\n",
+		       req->req.actual, req->req.length);
+		ep->ep0.state = ep0_state_status;
+		writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
+		ast_vhub_done(ep, req, 0);
+		return;
+	}
+
+	/*
+	 * Next chunk cropped to max packet size. Also check if this
+	 * is the last packet
+	 */
+	chunk = req->req.length - req->req.actual;
+	if (chunk > ep->ep.maxpacket)
+		chunk = ep->ep.maxpacket;
+	else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
+		req->last_desc = 1;
+
+	EPVDBG(ep, "send chunk=%d last=%d, req->act=%d mp=%d\n",
+	       chunk, req->last_desc, req->req.actual, ep->ep.maxpacket);
+
+	/*
+	 * Copy data if any (internal requests already have data
+	 * in the EP buffer)
+	 */
+	if (chunk && req->req.buf)
+		memcpy(ep->buf, req->req.buf + req->req.actual, chunk);
+
+	vhub_dma_workaround(ep->buf);
+
+	/* Remember chunk size and trigger send */
+	reg = VHUB_EP0_SET_TX_LEN(chunk);
+	writel(reg, ep->ep0.ctlstat);
+	writel(reg | VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+	req->req.actual += chunk;
+}
+
+static void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep)
+{
+	EPVDBG(ep, "rx prime\n");
+
+	/* Prime endpoint for receiving data */
+	writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
+}
+
+static void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+				    unsigned int len)
+{
+	unsigned int remain;
+	int rc = 0;
+
+	/* We are receiving... grab request */
+	remain = req->req.length - req->req.actual;
+
+	EPVDBG(ep, "receive got=%d remain=%d\n", len, remain);
+
+	/* Are we getting more than asked ? */
+	if (len > remain) {
+		EPDBG(ep, "receiving too much (ovf: %d) !\n",
+		      len - remain);
+		len = remain;
+		rc = -EOVERFLOW;
+	}
+	if (len && req->req.buf)
+		memcpy(req->req.buf + req->req.actual, ep->buf, len);
+	req->req.actual += len;
+
+	/* Done ? */
+	if (len < ep->ep.maxpacket || len == remain) {
+		ep->ep0.state = ep0_state_status;
+		writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+		ast_vhub_done(ep, req, rc);
+	} else
+		ast_vhub_ep0_rx_prime(ep);
+}
+
+void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
+{
+	struct ast_vhub_req *req;
+	struct ast_vhub *vhub = ep->vhub;
+	struct device *dev = &vhub->pdev->dev;
+	bool stall = false;
+	u32 stat;
+
+	/* Read EP0 status */
+	stat = readl(ep->ep0.ctlstat);
+
+	/* Grab current request if any */
+	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+	EPVDBG(ep, "ACK status=%08x,state=%d is_in=%d in_ack=%d req=%p\n",
+		stat, ep->ep0.state, ep->ep0.dir_in, in_ack, req);
+
+	switch(ep->ep0.state) {
+	case ep0_state_token:
+		/* There should be no request queued in that state... */
+		if (req) {
+			dev_warn(dev, "request present while in TOKEN state\n");
+			ast_vhub_nuke(ep, -EINVAL);
+		}
+		dev_warn(dev, "ack while in TOKEN state\n");
+		stall = true;
+		break;
+	case ep0_state_data:
+		/* Check the state bits corresponding to our direction */
+		if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
+		    (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
+		    (ep->ep0.dir_in != in_ack)) {
+			dev_warn(dev, "irq state mismatch");
+			stall = true;
+			break;
+		}
+		/*
+		 * We are in data phase and there's no request, something is
+		 * wrong, stall
+		 */
+		if (!req) {
+			dev_warn(dev, "data phase, no request\n");
+			stall = true;
+			break;
+		}
+
+		/* We have a request, handle data transfers */
+		if (ep->ep0.dir_in)
+			ast_vhub_ep0_do_send(ep, req);
+		else
+			ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat));
+		return;
+	case ep0_state_status:
+		/* Nuke stale requests */
+		if (req) {
+			dev_warn(dev, "request present while in STATUS state\n");
+			ast_vhub_nuke(ep, -EINVAL);
+		}
+
+		/*
+		 * If the status phase completes with the wrong ack, stall
+		 * the endpoint just in case, to abort whatever the host
+		 * was doing.
+		 */
+		if (ep->ep0.dir_in == in_ack) {
+			dev_warn(dev, "status direction mismatch\n");
+			stall = true;
+		}
+	}
+
+	/* Reset to token state */
+	ep->ep0.state = ep0_state_token;
+	if (stall)
+		writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+}
+
+static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
+			      gfp_t gfp_flags)
+{
+	struct ast_vhub_req *req = to_ast_req(u_req);
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	struct device *dev = &vhub->pdev->dev;
+	unsigned long flags;
+
+	/* Paranoid cheks */
+	if (!u_req || (!u_req->complete && !req->internal)) {
+		dev_warn(dev, "Bogus EP0 request ! u_req=%p\n", u_req);
+		if (u_req) {
+			dev_warn(dev, "complete=%p internal=%d\n",
+				 u_req->complete, req->internal);
+		}
+		return -EINVAL;
+	}
+
+	/* Not endpoint 0 ? */
+	if (WARN_ON(ep->d_idx != 0))
+		return -EINVAL;
+
+	/* Disabled device */
+	if (ep->dev && (!ep->dev->enabled || ep->dev->suspended))
+		return -ESHUTDOWN;
+
+	/* Data, no buffer and not internal ? */
+	if (u_req->length && !u_req->buf && !req->internal) {
+		dev_warn(dev, "Request with no buffer !\n");
+		return -EINVAL;
+	}
+
+	EPVDBG(ep, "enqueue req @%p\n", req);
+	EPVDBG(ep, "  l=%d zero=%d noshort=%d is_in=%d\n",
+	       u_req->length, u_req->zero,
+	       u_req->short_not_ok, ep->ep0.dir_in);
+
+	/* Initialize request progress fields */
+	u_req->status = -EINPROGRESS;
+	u_req->actual = 0;
+	req->last_desc = -1;
+	req->active = false;
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* EP0 can only support a single request at a time */
+	if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) {
+		dev_warn(dev, "EP0: Request in wrong state\n");
+		spin_unlock_irqrestore(&vhub->lock, flags);
+		return -EBUSY;
+	}
+
+	/* Add request to list and kick processing if empty */
+	list_add_tail(&req->queue, &ep->queue);
+
+	if (ep->ep0.dir_in) {
+		/* IN request, send data */
+		ast_vhub_ep0_do_send(ep, req);
+	} else if (u_req->length == 0) {
+		/* 0-len request, send completion as rx */
+		EPVDBG(ep, "0-length rx completion\n");
+		ep->ep0.state = ep0_state_status;
+		writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+		ast_vhub_done(ep, req, 0);
+	} else {
+		/* OUT request, start receiver */
+		ast_vhub_ep0_rx_prime(ep);
+	}
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	return 0;
+}
+
+static int ast_vhub_ep0_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
+{
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	struct ast_vhub_req *req;
+	unsigned long flags;
+	int rc = -EINVAL;
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* Only one request can be in the queue */
+	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+	/* Is it ours ? */
+	if (req && u_req == &req->req) {
+		EPVDBG(ep, "dequeue req @%p\n", req);
+
+		/*
+		 * We don't have to deal with "active" as all
+		 * DMAs go to the EP buffers, not the request.
+		 */
+		ast_vhub_done(ep, req, -ECONNRESET);
+
+		/* We do stall the EP to clean things up in HW */
+		writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+		ep->ep0.state = ep0_state_status;
+		ep->ep0.dir_in = false;
+		rc = 0;
+	}
+	spin_unlock_irqrestore(&vhub->lock, flags);
+	return rc;
+}
+
+
+static const struct usb_ep_ops ast_vhub_ep0_ops = {
+	.queue		= ast_vhub_ep0_queue,
+	.dequeue	= ast_vhub_ep0_dequeue,
+	.alloc_request	= ast_vhub_alloc_request,
+	.free_request	= ast_vhub_free_request,
+};
+
+void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
+		       struct ast_vhub_dev *dev)
+{
+	memset(ep, 0, sizeof(*ep));
+
+	INIT_LIST_HEAD(&ep->ep.ep_list);
+	INIT_LIST_HEAD(&ep->queue);
+	ep->ep.ops = &ast_vhub_ep0_ops;
+	ep->ep.name = "ep0";
+	ep->ep.caps.type_control = true;
+	usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EP0_MAX_PACKET);
+	ep->d_idx = 0;
+	ep->dev = dev;
+	ep->vhub = vhub;
+	ep->ep0.state = ep0_state_token;
+	INIT_LIST_HEAD(&ep->ep0.req.queue);
+	ep->ep0.req.internal = true;
+
+	/* Small difference between vHub and devices */
+	if (dev) {
+		ep->ep0.ctlstat = dev->regs + AST_VHUB_DEV_EP0_CTRL;
+		ep->ep0.setup = vhub->regs +
+			AST_VHUB_SETUP0 + 8 * (dev->index + 1);
+		ep->buf = vhub->ep0_bufs +
+			AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
+		ep->buf_dma = vhub->ep0_bufs_dma +
+			AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
+	} else {
+		ep->ep0.ctlstat = vhub->regs + AST_VHUB_EP0_CTRL;
+		ep->ep0.setup = vhub->regs + AST_VHUB_SETUP0;
+		ep->buf = vhub->ep0_bufs;
+		ep->buf_dma = vhub->ep0_bufs_dma;
+	}
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
new file mode 100644
index 0000000..5939eb1
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * epn.c - Generic endpoints management
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+#define EXTRA_CHECKS
+
+#ifdef EXTRA_CHECKS
+#define CHECK(ep, expr, fmt...)					\
+	do {							\
+		if (!(expr)) EPDBG(ep, "CHECK:" fmt);		\
+	} while(0)
+#else
+#define CHECK(ep, expr, fmt...)	do { } while(0)
+#endif
+
+static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
+{
+	unsigned int act = req->req.actual;
+	unsigned int len = req->req.length;
+	unsigned int chunk;
+
+	/* There should be no DMA ongoing */
+	WARN_ON(req->active);
+
+	/* Calculate next chunk size */
+	chunk = len - act;
+	if (chunk > ep->ep.maxpacket)
+		chunk = ep->ep.maxpacket;
+	else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
+		req->last_desc = 1;
+
+	EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n",
+	       req, act, len, chunk, req->last_desc);
+
+	/* If DMA unavailable, using staging EP buffer */
+	if (!req->req.dma) {
+
+		/* For IN transfers, copy data over first */
+		if (ep->epn.is_in) {
+			memcpy(ep->buf, req->req.buf + act, chunk);
+			vhub_dma_workaround(ep->buf);
+		}
+		writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+	} else {
+		if (ep->epn.is_in)
+			vhub_dma_workaround(req->req.buf);
+		writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+	}
+
+	/* Start DMA */
+	req->active = true;
+	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
+	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
+	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+}
+
+static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
+{
+	struct ast_vhub_req *req;
+	unsigned int len;
+	u32 stat;
+
+	/* Read EP status */
+	stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+	/* Grab current request if any */
+	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+	EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
+	       stat, ep->epn.is_in, req, req ? req->active : 0);
+
+	/* In absence of a request, bail out, must have been dequeued */
+	if (!req)
+		return;
+
+	/*
+	 * Request not active, move on to processing queue, active request
+	 * was probably dequeued
+	 */
+	if (!req->active)
+		goto next_chunk;
+
+	/* Check if HW has moved on */
+	if (VHUB_EP_DMA_RPTR(stat) != 0) {
+		EPDBG(ep, "DMA read pointer not 0 !\n");
+		return;
+	}
+
+	/* No current DMA ongoing */
+	req->active = false;
+
+	/* Grab lenght out of HW */
+	len = VHUB_EP_DMA_TX_SIZE(stat);
+
+	/* If not using DMA, copy data out if needed */
+	if (!req->req.dma && !ep->epn.is_in && len)
+		memcpy(req->req.buf + req->req.actual, ep->buf, len);
+
+	/* Adjust size */
+	req->req.actual += len;
+
+	/* Check for short packet */
+	if (len < ep->ep.maxpacket)
+		req->last_desc = 1;
+
+	/* That's it ? complete the request and pick a new one */
+	if (req->last_desc >= 0) {
+		ast_vhub_done(ep, req, 0);
+		req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
+					       queue);
+
+		/*
+		 * Due to lock dropping inside "done" the next request could
+		 * already be active, so check for that and bail if needed.
+		 */
+		if (!req || req->active)
+			return;
+	}
+
+ next_chunk:
+	ast_vhub_epn_kick(ep, req);
+}
+
+static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
+{
+	/*
+	 * d_next == d_last means descriptor list empty to HW,
+	 * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors
+	 * in the list
+	 */
+	return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) &
+		(AST_VHUB_DESCS_COUNT - 1);
+}
+
+static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
+				   struct ast_vhub_req *req)
+{
+	struct ast_vhub_desc *desc = NULL;
+	unsigned int act = req->act_count;
+	unsigned int len = req->req.length;
+	unsigned int chunk;
+
+	/* Mark request active if not already */
+	req->active = true;
+
+	/* If the request was already completely written, do nothing */
+	if (req->last_desc >= 0)
+		return;
+
+	EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n",
+	       act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep));
+
+	/* While we can create descriptors */
+	while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
+		unsigned int d_num;
+
+		/* Grab next free descriptor */
+		d_num = ep->epn.d_next;
+		desc = &ep->epn.descs[d_num];
+		ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
+
+		/* Calculate next chunk size */
+		chunk = len - act;
+		if (chunk <= ep->epn.chunk_max) {
+			/*
+			 * Is this the last packet ? Because of having up to 8
+			 * packets in a descriptor we can't just compare "chunk"
+			 * with ep.maxpacket. We have to see if it's a multiple
+			 * of it to know if we have to send a zero packet.
+			 * Sadly that involves a modulo which is a bit expensive
+			 * but probably still better than not doing it.
+			 */
+			if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0)
+				req->last_desc = d_num;
+		} else {
+			chunk = ep->epn.chunk_max;
+		}
+
+		EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n",
+		       act, len, chunk, req->last_desc, d_num,
+		       ast_vhub_count_free_descs(ep));
+
+		/* Populate descriptor */
+		desc->w0 = cpu_to_le32(req->req.dma + act);
+
+		/* Interrupt if end of request or no more descriptors */
+
+		/*
+		 * TODO: Be smarter about it, if we don't have enough
+		 * descriptors request an interrupt before queue empty
+		 * or so in order to be able to populate more before
+		 * the HW runs out. This isn't a problem at the moment
+		 * as we use 256 descriptors and only put at most one
+		 * request in the ring.
+		 */
+		desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk));
+		if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep))
+			desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT);
+
+		/* Account packet */
+		req->act_count = act = act + chunk;
+	}
+
+	if (likely(desc))
+		vhub_dma_workaround(desc);
+
+	/* Tell HW about new descriptors */
+	writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
+	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+	EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n",
+	       ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS));
+}
+
+static void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep)
+{
+	struct ast_vhub_req *req;
+	unsigned int len, d_last;
+	u32 stat, stat1;
+
+	/* Read EP status, workaround HW race */
+	do {
+		stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+		stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+	} while(stat != stat1);
+
+	/* Extract RPTR */
+	d_last = VHUB_EP_DMA_RPTR(stat);
+
+	/* Grab current request if any */
+	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+	EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n",
+	       stat, ep->epn.is_in, ep->epn.d_last, d_last);
+
+	/* Check all completed descriptors */
+	while (ep->epn.d_last != d_last) {
+		struct ast_vhub_desc *desc;
+		unsigned int d_num;
+		bool is_last_desc;
+
+		/* Grab next completed descriptor */
+		d_num = ep->epn.d_last;
+		desc = &ep->epn.descs[d_num];
+		ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
+
+		/* Grab len out of descriptor */
+		len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1));
+
+		EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n",
+		       d_num, len, req, req ? req->active : 0);
+
+		/* If no active request pending, move on */
+		if (!req || !req->active)
+			continue;
+
+		/* Adjust size */
+		req->req.actual += len;
+
+		/* Is that the last chunk ? */
+		is_last_desc = req->last_desc == d_num;
+		CHECK(ep, is_last_desc == (len < ep->ep.maxpacket ||
+					   (req->req.actual >= req->req.length &&
+					    !req->req.zero)),
+		      "Last packet discrepancy: last_desc=%d len=%d r.act=%d "
+		      "r.len=%d r.zero=%d mp=%d\n",
+		      is_last_desc, len, req->req.actual, req->req.length,
+		      req->req.zero, ep->ep.maxpacket);
+
+		if (is_last_desc) {
+			/*
+			 * Because we can only have one request at a time
+			 * in our descriptor list in this implementation,
+			 * d_last and ep->d_last should now be equal
+			 */
+			CHECK(ep, d_last == ep->epn.d_last,
+			      "DMA read ptr mismatch %d vs %d\n",
+			      d_last, ep->epn.d_last);
+
+			/* Note: done will drop and re-acquire the lock */
+			ast_vhub_done(ep, req, 0);
+			req = list_first_entry_or_null(&ep->queue,
+						       struct ast_vhub_req,
+						       queue);
+			break;
+		}
+	}
+
+	/* More work ? */
+	if (req)
+		ast_vhub_epn_kick_desc(ep, req);
+}
+
+void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep)
+{
+	if (ep->epn.desc_mode)
+		ast_vhub_epn_handle_ack_desc(ep);
+	else
+		ast_vhub_epn_handle_ack(ep);
+}
+
+static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
+			      gfp_t gfp_flags)
+{
+	struct ast_vhub_req *req = to_ast_req(u_req);
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	unsigned long flags;
+	bool empty;
+	int rc;
+
+	/* Paranoid checks */
+	if (!u_req || !u_req->complete || !u_req->buf) {
+		dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req);
+		if (u_req) {
+			dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n",
+				 u_req->complete, req->internal);
+		}
+		return -EINVAL;
+	}
+
+	/* Endpoint enabled ? */
+	if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
+	    !ep->dev->enabled || ep->dev->suspended) {
+		EPDBG(ep,"Enqueing request on wrong or disabled EP\n");
+		return -ESHUTDOWN;
+	}
+
+	/* Map request for DMA if possible. For now, the rule for DMA is
+	 * that:
+	 *
+	 *  * For single stage mode (no descriptors):
+	 *
+	 *   - The buffer is aligned to a 8 bytes boundary (HW requirement)
+	 *   - For a OUT endpoint, the request size is a multiple of the EP
+	 *     packet size (otherwise the controller will DMA past the end
+	 *     of the buffer if the host is sending a too long packet).
+	 *
+	 *  * For descriptor mode (tx only for now), always.
+	 *
+	 * We could relax the latter by making the decision to use the bounce
+	 * buffer based on the size of a given *segment* of the request rather
+	 * than the whole request.
+	 */
+	if (ep->epn.desc_mode ||
+	    ((((unsigned long)u_req->buf & 7) == 0) &&
+	     (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) {
+		rc = usb_gadget_map_request(&ep->dev->gadget, u_req,
+					    ep->epn.is_in);
+		if (rc) {
+			dev_warn(&vhub->pdev->dev,
+				 "Request mapping failure %d\n", rc);
+			return rc;
+		}
+	} else
+		u_req->dma = 0;
+
+	EPVDBG(ep, "enqueue req @%p\n", req);
+	EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n",
+	       u_req->length, (u32)u_req->dma, u_req->zero,
+	       u_req->short_not_ok, u_req->no_interrupt,
+	       ep->epn.is_in);
+
+	/* Initialize request progress fields */
+	u_req->status = -EINPROGRESS;
+	u_req->actual = 0;
+	req->act_count = 0;
+	req->active = false;
+	req->last_desc = -1;
+	spin_lock_irqsave(&vhub->lock, flags);
+	empty = list_empty(&ep->queue);
+
+	/* Add request to list and kick processing if empty */
+	list_add_tail(&req->queue, &ep->queue);
+	if (empty) {
+		if (ep->epn.desc_mode)
+			ast_vhub_epn_kick_desc(ep, req);
+		else
+			ast_vhub_epn_kick(ep, req);
+	}
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	return 0;
+}
+
+static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
+				     bool restart_ep)
+{
+	u32 state, reg, loops;
+
+	/* Stop DMA activity */
+	writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+	/* Wait for it to complete */
+	for (loops = 0; loops < 1000; loops++) {
+		state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+		state = VHUB_EP_DMA_PROC_STATUS(state);
+		if (state == EP_DMA_PROC_RX_IDLE ||
+		    state == EP_DMA_PROC_TX_IDLE)
+			break;
+		udelay(1);
+	}
+	if (loops >= 1000)
+		dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n");
+
+	/* If we don't have to restart the endpoint, that's it */
+	if (!restart_ep)
+		return;
+
+	/* Restart the endpoint */
+	if (ep->epn.desc_mode) {
+		/*
+		 * Take out descriptors by resetting the DMA read
+		 * pointer to be equal to the CPU write pointer.
+		 *
+		 * Note: If we ever support creating descriptors for
+		 * requests that aren't the head of the queue, we
+		 * may have to do something more complex here,
+		 * especially if the request being taken out is
+		 * not the current head descriptors.
+		 */
+		reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) |
+			VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next);
+		writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+		/* Then turn it back on */
+		writel(ep->epn.dma_conf,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+	} else {
+		/* Single mode: just turn it back on */
+		writel(ep->epn.dma_conf,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+	}
+}
+
+static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
+{
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	struct ast_vhub_req *req;
+	unsigned long flags;
+	int rc = -EINVAL;
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* Make sure it's actually queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == u_req)
+			break;
+	}
+
+	if (&req->req == u_req) {
+		EPVDBG(ep, "dequeue req @%p active=%d\n",
+		       req, req->active);
+		if (req->active)
+			ast_vhub_stop_active_req(ep, true);
+		ast_vhub_done(ep, req, -ECONNRESET);
+		rc = 0;
+	}
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+	return rc;
+}
+
+void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep)
+{
+	u32 reg;
+
+	if (WARN_ON(ep->d_idx == 0))
+		return;
+	reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG);
+	if (ep->epn.stalled || ep->epn.wedged)
+		reg |= VHUB_EP_CFG_STALL_CTRL;
+	else
+		reg &= ~VHUB_EP_CFG_STALL_CTRL;
+	writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+	if (!ep->epn.stalled && !ep->epn.wedged)
+		writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
+		       ep->vhub->regs + AST_VHUB_EP_TOGGLE);
+}
+
+static int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt,
+				      bool wedge)
+{
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	unsigned long flags;
+
+	EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge);
+
+	if (!u_ep || !u_ep->desc)
+		return -EINVAL;
+	if (ep->d_idx == 0)
+		return 0;
+	if (ep->epn.is_iso)
+		return -EOPNOTSUPP;
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* Fail with still-busy IN endpoints */
+	if (halt && ep->epn.is_in && !list_empty(&ep->queue)) {
+		spin_unlock_irqrestore(&vhub->lock, flags);
+		return -EAGAIN;
+	}
+	ep->epn.stalled = halt;
+	ep->epn.wedged = wedge;
+	ast_vhub_update_epn_stall(ep);
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	return 0;
+}
+
+static int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value)
+{
+	return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false);
+}
+
+static int ast_vhub_epn_set_wedge(struct usb_ep *u_ep)
+{
+	return ast_vhub_set_halt_and_wedge(u_ep, true, true);
+}
+
+static int ast_vhub_epn_disable(struct usb_ep* u_ep)
+{
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub *vhub = ep->vhub;
+	unsigned long flags;
+	u32 imask, ep_ier;
+
+	EPDBG(ep, "Disabling !\n");
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	ep->epn.enabled = false;
+
+	/* Stop active DMA if any */
+	ast_vhub_stop_active_req(ep, false);
+
+	/* Disable endpoint */
+	writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+	/* Disable ACK interrupt */
+	imask = VHUB_EP_IRQ(ep->epn.g_idx);
+	ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
+	ep_ier &= ~imask;
+	writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
+	writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+
+	/* Nuke all pending requests */
+	ast_vhub_nuke(ep, -ESHUTDOWN);
+
+	/* No more descriptor associated with request */
+	ep->ep.desc = NULL;
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	return 0;
+}
+
+static int ast_vhub_epn_enable(struct usb_ep* u_ep,
+			       const struct usb_endpoint_descriptor *desc)
+{
+	static const char *ep_type_string[] __maybe_unused = { "ctrl",
+							       "isoc",
+							       "bulk",
+							       "intr" };
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+	struct ast_vhub_dev *dev;
+	struct ast_vhub *vhub;
+	u16 maxpacket, type;
+	unsigned long flags;
+	u32 ep_conf, ep_ier, imask;
+
+	/* Check arguments */
+	if (!u_ep || !desc)
+		return -EINVAL;
+
+	maxpacket = usb_endpoint_maxp(desc);
+	if (!ep->d_idx || !ep->dev ||
+	    desc->bDescriptorType != USB_DT_ENDPOINT ||
+	    maxpacket == 0 || maxpacket > ep->ep.maxpacket) {
+		EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n",
+		      ep->d_idx, ep->dev, desc->bDescriptorType,
+		      maxpacket, ep->ep.maxpacket);
+		return -EINVAL;
+	}
+	if (ep->d_idx != usb_endpoint_num(desc)) {
+		EPDBG(ep, "EP number mismatch !\n");
+		return -EINVAL;
+	}
+
+	if (ep->epn.enabled) {
+		EPDBG(ep, "Already enabled\n");
+		return -EBUSY;
+	}
+	dev = ep->dev;
+	vhub = ep->vhub;
+
+	/* Check device state */
+	if (!dev->driver) {
+		EPDBG(ep, "Bogus device state: driver=%p speed=%d\n",
+		       dev->driver, dev->gadget.speed);
+		return -ESHUTDOWN;
+	}
+
+	/* Grab some info from the descriptor */
+	ep->epn.is_in = usb_endpoint_dir_in(desc);
+	ep->ep.maxpacket = maxpacket;
+	type = usb_endpoint_type(desc);
+	ep->epn.d_next = ep->epn.d_last = 0;
+	ep->epn.is_iso = false;
+	ep->epn.stalled = false;
+	ep->epn.wedged = false;
+
+	EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
+	      ep->epn.is_in ? "in" : "out", ep_type_string[type],
+	      usb_endpoint_num(desc), maxpacket);
+
+	/* Can we use DMA descriptor mode ? */
+	ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in;
+	if (ep->epn.desc_mode)
+		memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT);
+
+	/*
+	 * Large send function can send up to 8 packets from
+	 * one descriptor with a limit of 4095 bytes.
+	 */
+	ep->epn.chunk_max = ep->ep.maxpacket;
+	if (ep->epn.is_in) {
+		ep->epn.chunk_max <<= 3;
+		while (ep->epn.chunk_max > 4095)
+			ep->epn.chunk_max -= ep->ep.maxpacket;
+	}
+
+	switch(type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		EPDBG(ep, "Only one control endpoint\n");
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_INT:
+		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK);
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO);
+		ep->epn.is_iso = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Encode the rest of the EP config register */
+	if (maxpacket < 1024)
+		ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket);
+	if (!ep->epn.is_in)
+		ep_conf |= VHUB_EP_CFG_DIR_OUT;
+	ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc));
+	ep_conf |= VHUB_EP_CFG_ENABLE;
+	ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1);
+	EPVDBG(ep, "config=%08x\n", ep_conf);
+
+	spin_lock_irqsave(&vhub->lock, flags);
+
+	/* Disable HW and reset DMA */
+	writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
+	writel(VHUB_EP_DMA_CTRL_RESET,
+	       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+	/* Configure and enable */
+	writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+	if (ep->epn.desc_mode) {
+		/* Clear DMA status, including the DMA read ptr */
+		writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+		/* Set descriptor base */
+		writel(ep->epn.descs_dma,
+		       ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+
+		/* Set base DMA config value */
+		ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE;
+		if (ep->epn.is_in)
+			ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE;
+
+		/* First reset and disable all operations */
+		writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+		/* Enable descriptor mode */
+		writel(ep->epn.dma_conf,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+	} else {
+		/* Set base DMA config value */
+		ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE;
+
+		/* Reset and switch to single stage mode */
+		writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+		writel(ep->epn.dma_conf,
+		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+		writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+	}
+
+	/* Cleanup data toggle just in case */
+	writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
+	       vhub->regs + AST_VHUB_EP_TOGGLE);
+
+	/* Cleanup and enable ACK interrupt */
+	imask = VHUB_EP_IRQ(ep->epn.g_idx);
+	writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+	ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
+	ep_ier |= imask;
+	writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
+
+	/* Woot, we are online ! */
+	ep->epn.enabled = true;
+
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	return 0;
+}
+
+static void ast_vhub_epn_dispose(struct usb_ep *u_ep)
+{
+	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+
+	if (WARN_ON(!ep->dev || !ep->d_idx))
+		return;
+
+	EPDBG(ep, "Releasing endpoint\n");
+
+	/* Take it out of the EP list */
+	list_del_init(&ep->ep.ep_list);
+
+	/* Mark the address free in the device */
+	ep->dev->epns[ep->d_idx - 1] = NULL;
+
+	/* Free name & DMA buffers */
+	kfree(ep->ep.name);
+	ep->ep.name = NULL;
+	dma_free_coherent(&ep->vhub->pdev->dev,
+			  AST_VHUB_EPn_MAX_PACKET +
+			  8 * AST_VHUB_DESCS_COUNT,
+			  ep->buf, ep->buf_dma);
+	ep->buf = NULL;
+	ep->epn.descs = NULL;
+
+	/* Mark free */
+	ep->dev = NULL;
+}
+
+static const struct usb_ep_ops ast_vhub_epn_ops = {
+	.enable		= ast_vhub_epn_enable,
+	.disable	= ast_vhub_epn_disable,
+	.dispose	= ast_vhub_epn_dispose,
+	.queue		= ast_vhub_epn_queue,
+	.dequeue	= ast_vhub_epn_dequeue,
+	.set_halt	= ast_vhub_epn_set_halt,
+	.set_wedge	= ast_vhub_epn_set_wedge,
+	.alloc_request	= ast_vhub_alloc_request,
+	.free_request	= ast_vhub_free_request,
+};
+
+struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
+{
+	struct ast_vhub *vhub = d->vhub;
+	struct ast_vhub_ep *ep;
+	unsigned long flags;
+	int i;
+
+	/* Find a free one (no device) */
+	spin_lock_irqsave(&vhub->lock, flags);
+	for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+		if (vhub->epns[i].dev == NULL)
+			break;
+	if (i >= AST_VHUB_NUM_GEN_EPs) {
+		spin_unlock_irqrestore(&vhub->lock, flags);
+		return NULL;
+	}
+
+	/* Set it up */
+	ep = &vhub->epns[i];
+	ep->dev = d;
+	spin_unlock_irqrestore(&vhub->lock, flags);
+
+	DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr);
+	INIT_LIST_HEAD(&ep->queue);
+	ep->d_idx = addr;
+	ep->vhub = vhub;
+	ep->ep.ops = &ast_vhub_epn_ops;
+	ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr);
+	d->epns[addr-1] = ep;
+	ep->epn.g_idx = i;
+	ep->epn.regs = vhub->regs + 0x200 + (i * 0x10);
+
+	ep->buf = dma_alloc_coherent(&vhub->pdev->dev,
+				     AST_VHUB_EPn_MAX_PACKET +
+				     8 * AST_VHUB_DESCS_COUNT,
+				     &ep->buf_dma, GFP_KERNEL);
+	if (!ep->buf) {
+		kfree(ep->ep.name);
+		ep->ep.name = NULL;
+		return NULL;
+	}
+	ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET;
+	ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET;
+
+	usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET);
+	list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list);
+	ep->ep.caps.type_iso = true;
+	ep->ep.caps.type_bulk = true;
+	ep->ep.caps.type_int = true;
+	ep->ep.caps.dir_in = true;
+	ep->ep.caps.dir_out = true;
+
+	return ep;
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
new file mode 100644
index 0000000..35ba0e5
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * hub.c - virtual hub handling
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/bcd.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "vhub.h"
+
+/* usb 2.0 hub device descriptor
+ *
+ * A few things we may want to improve here:
+ *
+ *    - We may need to indicate TT support
+ *    - We may need a device qualifier descriptor
+ *	as devices can pretend to be usb1 or 2
+ *    - Make vid/did overridable
+ *    - make it look like usb1 if usb1 mode forced
+ */
+#define KERNEL_REL	bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
+#define KERNEL_VER	bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
+
+enum {
+	AST_VHUB_STR_MANUF = 3,
+	AST_VHUB_STR_PRODUCT = 2,
+	AST_VHUB_STR_SERIAL = 1,
+};
+
+static const struct usb_device_descriptor ast_vhub_dev_desc = {
+	.bLength		= USB_DT_DEVICE_SIZE,
+	.bDescriptorType	= USB_DT_DEVICE,
+	.bcdUSB			= cpu_to_le16(0x0200),
+	.bDeviceClass		= USB_CLASS_HUB,
+	.bDeviceSubClass	= 0,
+	.bDeviceProtocol	= 1,
+	.bMaxPacketSize0	= 64,
+	.idVendor		= cpu_to_le16(0x1d6b),
+	.idProduct		= cpu_to_le16(0x0107),
+	.bcdDevice		= cpu_to_le16(0x0100),
+	.iManufacturer		= AST_VHUB_STR_MANUF,
+	.iProduct		= AST_VHUB_STR_PRODUCT,
+	.iSerialNumber		= AST_VHUB_STR_SERIAL,
+	.bNumConfigurations	= 1,
+};
+
+/* Patches to the above when forcing USB1 mode */
+static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
+{
+	desc->bcdUSB = cpu_to_le16(0x0100);
+	desc->bDeviceProtocol = 0;
+}
+
+/*
+ * Configuration descriptor: same comments as above
+ * regarding handling USB1 mode.
+ */
+
+/*
+ * We don't use sizeof() as Linux definition of
+ * struct usb_endpoint_descriptor contains 2
+ * extra bytes
+ */
+#define AST_VHUB_CONF_DESC_SIZE	(USB_DT_CONFIG_SIZE + \
+				 USB_DT_INTERFACE_SIZE + \
+				 USB_DT_ENDPOINT_SIZE)
+
+static const struct ast_vhub_full_cdesc {
+	struct usb_config_descriptor	cfg;
+	struct usb_interface_descriptor intf;
+	struct usb_endpoint_descriptor	ep;
+} __attribute__ ((packed)) ast_vhub_conf_desc = {
+	.cfg = {
+		.bLength		= USB_DT_CONFIG_SIZE,
+		.bDescriptorType	= USB_DT_CONFIG,
+		.wTotalLength		= cpu_to_le16(AST_VHUB_CONF_DESC_SIZE),
+		.bNumInterfaces		= 1,
+		.bConfigurationValue	= 1,
+		.iConfiguration		= 0,
+		.bmAttributes		= USB_CONFIG_ATT_ONE |
+					  USB_CONFIG_ATT_SELFPOWER |
+					  USB_CONFIG_ATT_WAKEUP,
+		.bMaxPower		= 0,
+	},
+	.intf = {
+		.bLength		= USB_DT_INTERFACE_SIZE,
+		.bDescriptorType	= USB_DT_INTERFACE,
+		.bInterfaceNumber	= 0,
+		.bAlternateSetting	= 0,
+		.bNumEndpoints		= 1,
+		.bInterfaceClass	= USB_CLASS_HUB,
+		.bInterfaceSubClass	= 0,
+		.bInterfaceProtocol	= 0,
+		.iInterface		= 0,
+	},
+	.ep = {
+		.bLength		= USB_DT_ENDPOINT_SIZE,
+		.bDescriptorType	= USB_DT_ENDPOINT,
+		.bEndpointAddress	= 0x81,
+		.bmAttributes		= USB_ENDPOINT_XFER_INT,
+		.wMaxPacketSize		= cpu_to_le16(1),
+		.bInterval		= 0x0c,
+	},
+};
+
+#define AST_VHUB_HUB_DESC_SIZE	(USB_DT_HUB_NONVAR_SIZE + 2)
+
+static const struct usb_hub_descriptor ast_vhub_hub_desc = {
+	.bDescLength			= AST_VHUB_HUB_DESC_SIZE,
+	.bDescriptorType		= USB_DT_HUB,
+	.bNbrPorts			= AST_VHUB_NUM_PORTS,
+	.wHubCharacteristics		= cpu_to_le16(HUB_CHAR_NO_LPSM),
+	.bPwrOn2PwrGood			= 10,
+	.bHubContrCurrent		= 0,
+	.u.hs.DeviceRemovable[0]	= 0,
+	.u.hs.DeviceRemovable[1]	= 0xff,
+};
+
+/*
+ * These strings converted to UTF-16 must be smaller than
+ * our EP0 buffer.
+ */
+static const struct usb_string ast_vhub_str_array[] = {
+	{
+		.id = AST_VHUB_STR_SERIAL,
+		.s = "00000000"
+	},
+	{
+		.id = AST_VHUB_STR_PRODUCT,
+		.s = "USB Virtual Hub"
+	},
+	{
+		.id = AST_VHUB_STR_MANUF,
+		.s = "Aspeed"
+	},
+	{ }
+};
+
+static const struct usb_gadget_strings ast_vhub_strings = {
+	.language = 0x0409,
+	.strings = (struct usb_string *)ast_vhub_str_array
+};
+
+static int ast_vhub_hub_dev_status(struct ast_vhub_ep *ep,
+				   u16 wIndex, u16 wValue)
+{
+	u8 st0;
+
+	EPDBG(ep, "GET_STATUS(dev)\n");
+
+	/*
+	 * Mark it as self-powered, I doubt the BMC is powered off
+	 * the USB bus ...
+	 */
+	st0 = 1 << USB_DEVICE_SELF_POWERED;
+
+	/*
+	 * Need to double check how remote wakeup actually works
+	 * on that chip and what triggers it.
+	 */
+	if (ep->vhub->wakeup_en)
+		st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+
+	return ast_vhub_simple_reply(ep, st0, 0);
+}
+
+static int ast_vhub_hub_ep_status(struct ast_vhub_ep *ep,
+				  u16 wIndex, u16 wValue)
+{
+	int ep_num;
+	u8 st0 = 0;
+
+	ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+	EPDBG(ep, "GET_STATUS(ep%d)\n", ep_num);
+
+	/* On the hub we have only EP 0 and 1 */
+	if (ep_num == 1) {
+		if (ep->vhub->ep1_stalled)
+			st0 |= 1 << USB_ENDPOINT_HALT;
+	} else if (ep_num != 0)
+		return std_req_stall;
+
+	return ast_vhub_simple_reply(ep, st0, 0);
+}
+
+static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
+				    u16 wIndex, u16 wValue,
+				    bool is_set)
+{
+	EPDBG(ep, "%s_FEATURE(dev val=%02x)\n",
+	      is_set ? "SET" : "CLEAR", wValue);
+
+	if (wValue != USB_DEVICE_REMOTE_WAKEUP)
+		return std_req_stall;
+
+	ep->vhub->wakeup_en = is_set;
+	EPDBG(ep, "Hub remote wakeup %s\n",
+	      is_set ? "enabled" : "disabled");
+
+	return std_req_complete;
+}
+
+static int ast_vhub_hub_ep_feature(struct ast_vhub_ep *ep,
+				   u16 wIndex, u16 wValue,
+				   bool is_set)
+{
+	int ep_num;
+	u32 reg;
+
+	ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+	EPDBG(ep, "%s_FEATURE(ep%d val=%02x)\n",
+	      is_set ? "SET" : "CLEAR", ep_num, wValue);
+
+	if (ep_num > 1)
+		return std_req_stall;
+	if (wValue != USB_ENDPOINT_HALT)
+		return std_req_stall;
+	if (ep_num == 0)
+		return std_req_complete;
+
+	EPDBG(ep, "%s stall on EP 1\n",
+	      is_set ? "setting" : "clearing");
+
+	ep->vhub->ep1_stalled = is_set;
+	reg = readl(ep->vhub->regs + AST_VHUB_EP1_CTRL);
+	if (is_set) {
+		reg |= VHUB_EP1_CTRL_STALL;
+	} else {
+		reg &= ~VHUB_EP1_CTRL_STALL;
+		reg |= VHUB_EP1_CTRL_RESET_TOGGLE;
+	}
+	writel(reg, ep->vhub->regs + AST_VHUB_EP1_CTRL);
+
+	return std_req_complete;
+}
+
+static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
+			     u8 desc_type, u16 len)
+{
+	size_t dsize;
+
+	EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
+
+	/*
+	 * Copy first to EP buffer and send from there, so
+	 * we can do some in-place patching if needed. We know
+	 * the EP buffer is big enough but ensure that doesn't
+	 * change. We do that now rather than later after we
+	 * have checked sizes etc... to avoid a gcc bug where
+	 * it thinks len is constant and barfs about read
+	 * overflows in memcpy.
+	 */
+	switch(desc_type) {
+	case USB_DT_DEVICE:
+		dsize = USB_DT_DEVICE_SIZE;
+		memcpy(ep->buf, &ast_vhub_dev_desc, dsize);
+		BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc));
+		BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+		break;
+	case USB_DT_CONFIG:
+		dsize = AST_VHUB_CONF_DESC_SIZE;
+		memcpy(ep->buf, &ast_vhub_conf_desc, dsize);
+		BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc));
+		BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+		break;
+	case USB_DT_HUB:
+		dsize = AST_VHUB_HUB_DESC_SIZE;
+		memcpy(ep->buf, &ast_vhub_hub_desc, dsize);
+		BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc));
+	BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+		break;
+	default:
+		return std_req_stall;
+	}
+
+	/* Crop requested length */
+	if (len > dsize)
+		len = dsize;
+
+	/* Patch it if forcing USB1 */
+	if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
+		ast_vhub_patch_dev_desc_usb1(ep->buf);
+
+	/* Shoot it from the EP buffer */
+	return ast_vhub_reply(ep, NULL, len);
+}
+
+static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
+			       u8 string_id, u16 lang_id,
+			       u16 len)
+{
+	int rc = usb_gadget_get_string (&ast_vhub_strings, string_id, ep->buf);
+
+	/*
+	 * This should never happen unless we put too big strings in
+	 * the array above
+	 */
+	BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+
+	if (rc < 0)
+		return std_req_stall;
+
+	/* Shoot it from the EP buffer */
+	return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
+}
+
+enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
+					 struct usb_ctrlrequest *crq)
+{
+	struct ast_vhub *vhub = ep->vhub;
+	u16 wValue, wIndex, wLength;
+
+	wValue = le16_to_cpu(crq->wValue);
+	wIndex = le16_to_cpu(crq->wIndex);
+	wLength = le16_to_cpu(crq->wLength);
+
+	/* First packet, grab speed */
+	if (vhub->speed == USB_SPEED_UNKNOWN) {
+		u32 ustat = readl(vhub->regs + AST_VHUB_USBSTS);
+		if (ustat & VHUB_USBSTS_HISPEED)
+			vhub->speed = USB_SPEED_HIGH;
+		else
+			vhub->speed = USB_SPEED_FULL;
+		UDCDBG(vhub, "USB status=%08x speed=%s\n", ustat,
+		       vhub->speed == USB_SPEED_HIGH ? "high" : "full");
+	}
+
+	switch ((crq->bRequestType << 8) | crq->bRequest) {
+		/* SET_ADDRESS */
+	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+		EPDBG(ep, "SET_ADDRESS: Got address %x\n", wValue);
+		writel(wValue, vhub->regs + AST_VHUB_CONF);
+		return std_req_complete;
+
+		/* GET_STATUS */
+	case DeviceRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_hub_dev_status(ep, wIndex, wValue);
+	case InterfaceRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_simple_reply(ep, 0, 0);
+	case EndpointRequest | USB_REQ_GET_STATUS:
+		return ast_vhub_hub_ep_status(ep, wIndex, wValue);
+
+		/* SET/CLEAR_FEATURE */
+	case DeviceOutRequest | USB_REQ_SET_FEATURE:
+		return ast_vhub_hub_dev_feature(ep, wIndex, wValue, true);
+	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+		return ast_vhub_hub_dev_feature(ep, wIndex, wValue, false);
+	case EndpointOutRequest | USB_REQ_SET_FEATURE:
+		return ast_vhub_hub_ep_feature(ep, wIndex, wValue, true);
+	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+		return ast_vhub_hub_ep_feature(ep, wIndex, wValue, false);
+
+		/* GET/SET_CONFIGURATION */
+	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+		return ast_vhub_simple_reply(ep, 1);
+	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+		if (wValue != 1)
+			return std_req_stall;
+		return std_req_complete;
+
+		/* GET_DESCRIPTOR */
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		switch (wValue >> 8) {
+		case USB_DT_DEVICE:
+		case USB_DT_CONFIG:
+			return ast_vhub_rep_desc(ep, wValue >> 8,
+						 wLength);
+		case USB_DT_STRING:
+			return ast_vhub_rep_string(ep, wValue & 0xff,
+						   wIndex, wLength);
+		}
+		return std_req_stall;
+
+		/* GET/SET_INTERFACE */
+	case DeviceRequest | USB_REQ_GET_INTERFACE:
+		return ast_vhub_simple_reply(ep, 0);
+	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+		if (wValue != 0 || wIndex != 0)
+			return std_req_stall;
+		return std_req_complete;
+	}
+	return std_req_stall;
+}
+
+static void ast_vhub_update_hub_ep1(struct ast_vhub *vhub,
+				    unsigned int port)
+{
+	/* Update HW EP1 response */
+	u32 reg = readl(vhub->regs + AST_VHUB_EP1_STS_CHG);
+	u32 pmask = (1 << (port + 1));
+	if (vhub->ports[port].change)
+		reg |= pmask;
+	else
+		reg &= ~pmask;
+	writel(reg, vhub->regs + AST_VHUB_EP1_STS_CHG);
+}
+
+static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
+				      unsigned int port,
+				      u16 clr_flags,
+				      u16 set_flags,
+				      bool set_c)
+{
+	struct ast_vhub_port *p = &vhub->ports[port];
+	u16 prev;
+
+	/* Update port status */
+	prev = p->status;
+	p->status = (prev & ~clr_flags) | set_flags;
+	DDBG(&p->dev, "port %d status %04x -> %04x (C=%d)\n",
+	     port + 1, prev, p->status, set_c);
+
+	/* Update change bits if needed */
+	if (set_c) {
+		u16 chg = p->status ^ prev;
+
+		/* Only these are relevant for change */
+		chg &= USB_PORT_STAT_C_CONNECTION |
+		       USB_PORT_STAT_C_ENABLE |
+		       USB_PORT_STAT_C_SUSPEND |
+		       USB_PORT_STAT_C_OVERCURRENT |
+		       USB_PORT_STAT_C_RESET |
+		       USB_PORT_STAT_C_L1;
+		p->change |= chg;
+
+		ast_vhub_update_hub_ep1(vhub, port);
+	}
+}
+
+static void ast_vhub_send_host_wakeup(struct ast_vhub *vhub)
+{
+	u32 reg = readl(vhub->regs + AST_VHUB_CTRL);
+	UDCDBG(vhub, "Waking up host !\n");
+	reg |= VHUB_CTRL_MANUAL_REMOTE_WAKEUP;
+	writel(reg, vhub->regs + AST_VHUB_CTRL);
+}
+
+void ast_vhub_device_connect(struct ast_vhub *vhub,
+			     unsigned int port, bool on)
+{
+	if (on)
+		ast_vhub_change_port_stat(vhub, port, 0,
+					  USB_PORT_STAT_CONNECTION, true);
+	else
+		ast_vhub_change_port_stat(vhub, port,
+					  USB_PORT_STAT_CONNECTION |
+					  USB_PORT_STAT_ENABLE,
+					  0, true);
+
+	/*
+	 * If the hub is set to wakup the host on connection events
+	 * then send a wakeup.
+	 */
+	if (vhub->wakeup_en)
+		ast_vhub_send_host_wakeup(vhub);
+}
+
+static void ast_vhub_wake_work(struct work_struct *work)
+{
+	struct ast_vhub *vhub = container_of(work,
+					     struct ast_vhub,
+					     wake_work);
+	unsigned long flags;
+	unsigned int i;
+
+	/*
+	 * Wake all sleeping ports. If a port is suspended by
+	 * the host suspend (without explicit state suspend),
+	 * we let the normal host wake path deal with it later.
+	 */
+	spin_lock_irqsave(&vhub->lock, flags);
+	for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+		struct ast_vhub_port *p = &vhub->ports[i];
+
+		if (!(p->status & USB_PORT_STAT_SUSPEND))
+			continue;
+		ast_vhub_change_port_stat(vhub, i,
+					  USB_PORT_STAT_SUSPEND,
+					  0, true);
+		ast_vhub_dev_resume(&p->dev);
+	}
+	ast_vhub_send_host_wakeup(vhub);
+	spin_unlock_irqrestore(&vhub->lock, flags);
+}
+
+void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
+{
+	/*
+	 * A device is trying to wake the world, because this
+	 * can recurse into the device, we break the call chain
+	 * using a work queue
+	 */
+	schedule_work(&vhub->wake_work);
+}
+
+static void ast_vhub_port_reset(struct ast_vhub *vhub, u8 port)
+{
+	struct ast_vhub_port *p = &vhub->ports[port];
+	u16 set, clr, speed;
+
+	/* First mark disabled */
+	ast_vhub_change_port_stat(vhub, port,
+				  USB_PORT_STAT_ENABLE |
+				  USB_PORT_STAT_SUSPEND,
+				  USB_PORT_STAT_RESET,
+				  false);
+
+	if (!p->dev.driver)
+		return;
+
+	/*
+	 * This will either "start" the port or reset the
+	 * device if already started...
+	 */
+	ast_vhub_dev_reset(&p->dev);
+
+	/* Grab the right speed */
+	speed = p->dev.driver->max_speed;
+	if (speed == USB_SPEED_UNKNOWN || speed > vhub->speed)
+		speed = vhub->speed;
+
+	switch (speed) {
+	case USB_SPEED_LOW:
+		set = USB_PORT_STAT_LOW_SPEED;
+		clr = USB_PORT_STAT_HIGH_SPEED;
+		break;
+	case USB_SPEED_FULL:
+		set = 0;
+		clr = USB_PORT_STAT_LOW_SPEED |
+			USB_PORT_STAT_HIGH_SPEED;
+		break;
+	case USB_SPEED_HIGH:
+		set = USB_PORT_STAT_HIGH_SPEED;
+		clr = USB_PORT_STAT_LOW_SPEED;
+		break;
+	default:
+		UDCDBG(vhub, "Unsupported speed %d when"
+		       " connecting device\n",
+		       speed);
+		return;
+	}
+	clr |= USB_PORT_STAT_RESET;
+	set |= USB_PORT_STAT_ENABLE;
+
+	/* This should ideally be delayed ... */
+	ast_vhub_change_port_stat(vhub, port, clr, set, true);
+}
+
+static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
+						 u8 port, u16 feat)
+{
+	struct ast_vhub *vhub = ep->vhub;
+	struct ast_vhub_port *p;
+
+	if (port == 0 || port > AST_VHUB_NUM_PORTS)
+		return std_req_stall;
+	port--;
+	p = &vhub->ports[port];
+
+	switch(feat) {
+	case USB_PORT_FEAT_SUSPEND:
+		if (!(p->status & USB_PORT_STAT_ENABLE))
+			return std_req_complete;
+		ast_vhub_change_port_stat(vhub, port,
+					  0, USB_PORT_STAT_SUSPEND,
+					  false);
+		ast_vhub_dev_suspend(&p->dev);
+		return std_req_complete;
+	case USB_PORT_FEAT_RESET:
+		EPDBG(ep, "Port reset !\n");
+		ast_vhub_port_reset(vhub, port);
+		return std_req_complete;
+	case USB_PORT_FEAT_POWER:
+		/*
+		 * On Power-on, we mark the connected flag changed,
+		 * if there's a connected device, some hosts will
+		 * otherwise fail to detect it.
+		 */
+		if (p->status & USB_PORT_STAT_CONNECTION) {
+			p->change |= USB_PORT_STAT_C_CONNECTION;
+			ast_vhub_update_hub_ep1(vhub, port);
+		}
+		return std_req_complete;
+	case USB_PORT_FEAT_TEST:
+	case USB_PORT_FEAT_INDICATOR:
+		/* We don't do anything with these */
+		return std_req_complete;
+	}
+	return std_req_stall;
+}
+
+static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
+						 u8 port, u16 feat)
+{
+	struct ast_vhub *vhub = ep->vhub;
+	struct ast_vhub_port *p;
+
+	if (port == 0 || port > AST_VHUB_NUM_PORTS)
+		return std_req_stall;
+	port--;
+	p = &vhub->ports[port];
+
+	switch(feat) {
+	case USB_PORT_FEAT_ENABLE:
+		ast_vhub_change_port_stat(vhub, port,
+					  USB_PORT_STAT_ENABLE |
+					  USB_PORT_STAT_SUSPEND, 0,
+					  false);
+		ast_vhub_dev_suspend(&p->dev);
+		return std_req_complete;
+	case USB_PORT_FEAT_SUSPEND:
+		if (!(p->status & USB_PORT_STAT_SUSPEND))
+			return std_req_complete;
+		ast_vhub_change_port_stat(vhub, port,
+					  USB_PORT_STAT_SUSPEND, 0,
+					  false);
+		ast_vhub_dev_resume(&p->dev);
+		return std_req_complete;
+	case USB_PORT_FEAT_POWER:
+		/* We don't do power control */
+		return std_req_complete;
+	case USB_PORT_FEAT_INDICATOR:
+		/* We don't have indicators */
+		return std_req_complete;
+	case USB_PORT_FEAT_C_CONNECTION:
+	case USB_PORT_FEAT_C_ENABLE:
+	case USB_PORT_FEAT_C_SUSPEND:
+	case USB_PORT_FEAT_C_OVER_CURRENT:
+	case USB_PORT_FEAT_C_RESET:
+		/* Clear state-change feature */
+		p->change &= ~(1u << (feat - 16));
+		ast_vhub_update_hub_ep1(vhub, port);
+		return std_req_complete;
+	}
+	return std_req_stall;
+}
+
+static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
+					      u8 port)
+{
+	struct ast_vhub *vhub = ep->vhub;
+	u16 stat, chg;
+
+	if (port == 0 || port > AST_VHUB_NUM_PORTS)
+		return std_req_stall;
+	port--;
+
+	stat = vhub->ports[port].status;
+	chg = vhub->ports[port].change;
+
+	/* We always have power */
+	stat |= USB_PORT_STAT_POWER;
+
+	EPDBG(ep, " port status=%04x change=%04x\n", stat, chg);
+
+	return ast_vhub_simple_reply(ep,
+				     stat & 0xff,
+				     stat >> 8,
+				     chg & 0xff,
+				     chg >> 8);
+}
+
+enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
+					   struct usb_ctrlrequest *crq)
+{
+	u16 wValue, wIndex, wLength;
+
+	wValue = le16_to_cpu(crq->wValue);
+	wIndex = le16_to_cpu(crq->wIndex);
+	wLength = le16_to_cpu(crq->wLength);
+
+	switch ((crq->bRequestType << 8) | crq->bRequest) {
+	case GetHubStatus:
+		EPDBG(ep, "GetHubStatus\n");
+		return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
+	case GetPortStatus:
+		EPDBG(ep, "GetPortStatus(%d)\n", wIndex & 0xff);
+		return ast_vhub_get_port_stat(ep, wIndex & 0xf);
+	case GetHubDescriptor:
+		if (wValue != (USB_DT_HUB << 8))
+			return std_req_stall;
+		EPDBG(ep, "GetHubDescriptor(%d)\n", wIndex & 0xff);
+		return ast_vhub_rep_desc(ep, USB_DT_HUB, wLength);
+	case SetHubFeature:
+	case ClearHubFeature:
+		EPDBG(ep, "Get/SetHubFeature(%d)\n", wValue);
+		/* No feature, just complete the requests */
+		if (wValue == C_HUB_LOCAL_POWER ||
+		    wValue == C_HUB_OVER_CURRENT)
+			return std_req_complete;
+		return std_req_stall;
+	case SetPortFeature:
+		EPDBG(ep, "SetPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
+		return ast_vhub_set_port_feature(ep, wIndex & 0xf, wValue);
+	case ClearPortFeature:
+		EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
+		return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
+	default:
+		EPDBG(ep, "Unknown class request\n");
+	}
+	return std_req_stall;
+}
+
+void ast_vhub_hub_suspend(struct ast_vhub *vhub)
+{
+	unsigned int i;
+
+	UDCDBG(vhub, "USB bus suspend\n");
+
+	if (vhub->suspended)
+		return;
+
+	vhub->suspended = true;
+
+	/*
+	 * Forward to unsuspended ports without changing
+	 * their connection status.
+	 */
+	for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+		struct ast_vhub_port *p = &vhub->ports[i];
+
+		if (!(p->status & USB_PORT_STAT_SUSPEND))
+			ast_vhub_dev_suspend(&p->dev);
+	}
+}
+
+void ast_vhub_hub_resume(struct ast_vhub *vhub)
+{
+	unsigned int i;
+
+	UDCDBG(vhub, "USB bus resume\n");
+
+	if (!vhub->suspended)
+		return;
+
+	vhub->suspended = false;
+
+	/*
+	 * Forward to unsuspended ports without changing
+	 * their connection status.
+	 */
+	for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+		struct ast_vhub_port *p = &vhub->ports[i];
+
+		if (!(p->status & USB_PORT_STAT_SUSPEND))
+			ast_vhub_dev_resume(&p->dev);
+	}
+}
+
+void ast_vhub_hub_reset(struct ast_vhub *vhub)
+{
+	unsigned int i;
+
+	UDCDBG(vhub, "USB bus reset\n");
+
+	/*
+	 * Is the speed known ? If not we don't care, we aren't
+	 * initialized yet and ports haven't been enabled.
+	 */
+	if (vhub->speed == USB_SPEED_UNKNOWN)
+		return;
+
+	/* We aren't suspended anymore obviously */
+	vhub->suspended = false;
+
+	/* No speed set */
+	vhub->speed = USB_SPEED_UNKNOWN;
+
+	/* Wakeup not enabled anymore */
+	vhub->wakeup_en = false;
+
+	/*
+	 * Clear all port status, disable gadgets and "suspend"
+	 * them. They will be woken up by a port reset.
+	 */
+	for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+		struct ast_vhub_port *p = &vhub->ports[i];
+
+		/* Only keep the connected flag */
+		p->status &= USB_PORT_STAT_CONNECTION;
+		p->change = 0;
+
+		/* Suspend the gadget if any */
+		ast_vhub_dev_suspend(&p->dev);
+	}
+
+	/* Cleanup HW */
+	writel(0, vhub->regs + AST_VHUB_CONF);
+	writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
+	writel(VHUB_EP1_CTRL_RESET_TOGGLE |
+	       VHUB_EP1_CTRL_ENABLE,
+	       vhub->regs + AST_VHUB_EP1_CTRL);
+	writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
+}
+
+void ast_vhub_init_hub(struct ast_vhub *vhub)
+{
+	vhub->speed = USB_SPEED_UNKNOWN;
+	INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
+}
+
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
new file mode 100644
index 0000000..4ed03d3
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -0,0 +1,547 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __ASPEED_VHUB_H
+#define __ASPEED_VHUB_H
+
+/*****************************
+ *                           *
+ * VHUB register definitions *
+ *                           *
+ *****************************/
+
+#define	AST_VHUB_CTRL		0x00	/* Root Function Control & Status Register */
+#define	AST_VHUB_CONF		0x04	/* Root Configuration Setting Register */
+#define	AST_VHUB_IER		0x08	/* Interrupt Ctrl Register */
+#define	AST_VHUB_ISR		0x0C	/* Interrupt Status Register */
+#define	AST_VHUB_EP_ACK_IER	0x10	/* Programmable Endpoint Pool ACK Interrupt Enable Register */
+#define	AST_VHUB_EP_NACK_IER	0x14	/* Programmable Endpoint Pool NACK Interrupt Enable Register  */
+#define AST_VHUB_EP_ACK_ISR	0x18	/* Programmable Endpoint Pool ACK Interrupt Status Register  */
+#define AST_VHUB_EP_NACK_ISR	0x1C	/* Programmable Endpoint Pool NACK Interrupt Status Register  */
+#define AST_VHUB_SW_RESET	0x20	/* Device Controller Soft Reset Enable Register */
+#define AST_VHUB_USBSTS		0x24	/* USB Status Register */
+#define AST_VHUB_EP_TOGGLE	0x28	/* Programmable Endpoint Pool Data Toggle Value Set */
+#define AST_VHUB_ISO_FAIL_ACC	0x2C	/* Isochronous Transaction Fail Accumulator */
+#define AST_VHUB_EP0_CTRL	0x30	/* Endpoint 0 Contrl/Status Register */
+#define AST_VHUB_EP0_DATA	0x34	/* Base Address of Endpoint 0 In/OUT Data Buffer Register */
+#define AST_VHUB_EP1_CTRL	0x38	/* Endpoint 1 Contrl/Status Register */
+#define AST_VHUB_EP1_STS_CHG	0x3C	/* Endpoint 1 Status Change Bitmap Data */
+#define AST_VHUB_SETUP0		0x80	/* Root Device Setup Data Buffer0 */
+#define AST_VHUB_SETUP1		0x84	/* Root Device Setup Data Buffer1 */
+
+/* Main control reg */
+#define VHUB_CTRL_PHY_CLK			(1 << 31)
+#define VHUB_CTRL_PHY_LOOP_TEST			(1 << 25)
+#define VHUB_CTRL_DN_PWN			(1 << 24)
+#define VHUB_CTRL_DP_PWN			(1 << 23)
+#define VHUB_CTRL_LONG_DESC			(1 << 18)
+#define VHUB_CTRL_ISO_RSP_CTRL			(1 << 17)
+#define VHUB_CTRL_SPLIT_IN			(1 << 16)
+#define VHUB_CTRL_LOOP_T_RESULT			(1 << 15)
+#define VHUB_CTRL_LOOP_T_STS			(1 << 14)
+#define VHUB_CTRL_PHY_BIST_RESULT		(1 << 13)
+#define VHUB_CTRL_PHY_BIST_CTRL			(1 << 12)
+#define VHUB_CTRL_PHY_RESET_DIS			(1 << 11)
+#define VHUB_CTRL_SET_TEST_MODE(x)		((x) << 8)
+#define VHUB_CTRL_MANUAL_REMOTE_WAKEUP		(1 << 4)
+#define VHUB_CTRL_AUTO_REMOTE_WAKEUP		(1 << 3)
+#define VHUB_CTRL_CLK_STOP_SUSPEND		(1 << 2)
+#define VHUB_CTRL_FULL_SPEED_ONLY		(1 << 1)
+#define VHUB_CTRL_UPSTREAM_CONNECT		(1 << 0)
+
+/* IER & ISR */
+#define VHUB_IRQ_USB_CMD_DEADLOCK		(1 << 18)
+#define VHUB_IRQ_EP_POOL_NAK			(1 << 17)
+#define VHUB_IRQ_EP_POOL_ACK_STALL		(1 << 16)
+#define VHUB_IRQ_DEVICE5			(1 << 13)
+#define VHUB_IRQ_DEVICE4			(1 << 12)
+#define VHUB_IRQ_DEVICE3			(1 << 11)
+#define VHUB_IRQ_DEVICE2			(1 << 10)
+#define VHUB_IRQ_DEVICE1			(1 << 9)
+#define VHUB_IRQ_BUS_RESUME			(1 << 8)
+#define VHUB_IRQ_BUS_SUSPEND 			(1 << 7)
+#define VHUB_IRQ_BUS_RESET 			(1 << 6)
+#define VHUB_IRQ_HUB_EP1_IN_DATA_ACK		(1 << 5)
+#define VHUB_IRQ_HUB_EP0_IN_DATA_NAK		(1 << 4)
+#define VHUB_IRQ_HUB_EP0_IN_ACK_STALL		(1 << 3)
+#define VHUB_IRQ_HUB_EP0_OUT_NAK		(1 << 2)
+#define VHUB_IRQ_HUB_EP0_OUT_ACK_STALL		(1 << 1)
+#define VHUB_IRQ_HUB_EP0_SETUP			(1 << 0)
+#define VHUB_IRQ_ACK_ALL			0x1ff
+
+/* SW reset reg */
+#define VHUB_SW_RESET_EP_POOL			(1 << 9)
+#define VHUB_SW_RESET_DMA_CONTROLLER		(1 << 8)
+#define VHUB_SW_RESET_DEVICE5			(1 << 5)
+#define VHUB_SW_RESET_DEVICE4			(1 << 4)
+#define VHUB_SW_RESET_DEVICE3			(1 << 3)
+#define VHUB_SW_RESET_DEVICE2			(1 << 2)
+#define VHUB_SW_RESET_DEVICE1			(1 << 1)
+#define VHUB_SW_RESET_ROOT_HUB			(1 << 0)
+#define VHUB_SW_RESET_ALL			(VHUB_SW_RESET_EP_POOL | \
+						 VHUB_SW_RESET_DMA_CONTROLLER | \
+						 VHUB_SW_RESET_DEVICE5 | \
+						 VHUB_SW_RESET_DEVICE4 | \
+						 VHUB_SW_RESET_DEVICE3 | \
+						 VHUB_SW_RESET_DEVICE2 | \
+						 VHUB_SW_RESET_DEVICE1 | \
+						 VHUB_SW_RESET_ROOT_HUB)
+/* EP ACK/NACK IRQ masks */
+#define VHUB_EP_IRQ(n)				(1 << (n))
+#define VHUB_EP_IRQ_ALL				0x7fff	/* 15 EPs */
+
+/* USB status reg */
+#define VHUB_USBSTS_HISPEED			(1 << 27)
+
+/* EP toggle */
+#define VHUB_EP_TOGGLE_VALUE			(1 << 8)
+#define VHUB_EP_TOGGLE_SET_EPNUM(x)		((x) & 0x1f)
+
+/* HUB EP0 control */
+#define VHUB_EP0_CTRL_STALL			(1 << 0)
+#define VHUB_EP0_TX_BUFF_RDY			(1 << 1)
+#define VHUB_EP0_RX_BUFF_RDY			(1 << 2)
+#define VHUB_EP0_RX_LEN(x)			(((x) >> 16) & 0x7f)
+#define VHUB_EP0_SET_TX_LEN(x)			(((x) & 0x7f) << 8)
+
+/* HUB EP1 control */
+#define VHUB_EP1_CTRL_RESET_TOGGLE		(1 << 2)
+#define VHUB_EP1_CTRL_STALL			(1 << 1)
+#define VHUB_EP1_CTRL_ENABLE			(1 << 0)
+
+/***********************************
+ *                                 *
+ * per-device register definitions *
+ *                                 *
+ ***********************************/
+#define AST_VHUB_DEV_EN_CTRL		0x00
+#define AST_VHUB_DEV_ISR		0x04
+#define AST_VHUB_DEV_EP0_CTRL		0x08
+#define AST_VHUB_DEV_EP0_DATA		0x0c
+
+/* Device enable control */
+#define VHUB_DEV_EN_SET_ADDR(x)			((x) << 8)
+#define VHUB_DEV_EN_ADDR_MASK			((0xff) << 8)
+#define VHUB_DEV_EN_EP0_NAK_IRQEN		(1 << 6)
+#define VHUB_DEV_EN_EP0_IN_ACK_IRQEN		(1 << 5)
+#define VHUB_DEV_EN_EP0_OUT_NAK_IRQEN		(1 << 4)
+#define VHUB_DEV_EN_EP0_OUT_ACK_IRQEN		(1 << 3)
+#define VHUB_DEV_EN_EP0_SETUP_IRQEN		(1 << 2)
+#define VHUB_DEV_EN_SPEED_SEL_HIGH		(1 << 1)
+#define VHUB_DEV_EN_ENABLE_PORT			(1 << 0)
+
+/* Interrupt status */
+#define VHUV_DEV_IRQ_EP0_IN_DATA_NACK		(1 << 4)
+#define VHUV_DEV_IRQ_EP0_IN_ACK_STALL		(1 << 3)
+#define VHUV_DEV_IRQ_EP0_OUT_DATA_NACK		(1 << 2)
+#define VHUV_DEV_IRQ_EP0_OUT_ACK_STALL		(1 << 1)
+#define VHUV_DEV_IRQ_EP0_SETUP			(1 << 0)
+
+/* Control bits.
+ *
+ * Note: The driver relies on the bulk of those bits
+ *       matching corresponding vHub EP0 control bits
+ */
+#define VHUB_DEV_EP0_CTRL_STALL			VHUB_EP0_CTRL_STALL
+#define VHUB_DEV_EP0_TX_BUFF_RDY		VHUB_EP0_TX_BUFF_RDY
+#define VHUB_DEV_EP0_RX_BUFF_RDY		VHUB_EP0_RX_BUFF_RDY
+#define VHUB_DEV_EP0_RX_LEN(x)			VHUB_EP0_RX_LEN(x)
+#define VHUB_DEV_EP0_SET_TX_LEN(x)		VHUB_EP0_SET_TX_LEN(x)
+
+/*************************************
+ *                                   *
+ * per-endpoint register definitions *
+ *                                   *
+ *************************************/
+
+#define AST_VHUB_EP_CONFIG		0x00
+#define AST_VHUB_EP_DMA_CTLSTAT		0x04
+#define AST_VHUB_EP_DESC_BASE		0x08
+#define AST_VHUB_EP_DESC_STATUS		0x0C
+
+/* EP config reg */
+#define VHUB_EP_CFG_SET_MAX_PKT(x)	(((x) & 0x3ff) << 16)
+#define VHUB_EP_CFG_AUTO_DATA_DISABLE	(1 << 13)
+#define VHUB_EP_CFG_STALL_CTRL		(1 << 12)
+#define VHUB_EP_CFG_SET_EP_NUM(x)	(((x) & 0xf) << 8)
+#define VHUB_EP_CFG_SET_TYPE(x)		((x) << 5)
+#define   EP_TYPE_OFF			0
+#define   EP_TYPE_BULK			1
+#define   EP_TYPE_INT			2
+#define   EP_TYPE_ISO			3
+#define VHUB_EP_CFG_DIR_OUT		(1 << 4)
+#define VHUB_EP_CFG_SET_DEV(x)		((x) << 1)
+#define VHUB_EP_CFG_ENABLE		(1 << 0)
+
+/* EP DMA control */
+#define VHUB_EP_DMA_PROC_STATUS(x)	(((x) >> 4) & 0xf)
+#define   EP_DMA_PROC_RX_IDLE		0
+#define   EP_DMA_PROC_TX_IDLE		8
+#define VHUB_EP_DMA_IN_LONG_MODE	(1 << 3)
+#define VHUB_EP_DMA_OUT_CONTIG_MODE	(1 << 3)
+#define VHUB_EP_DMA_CTRL_RESET		(1 << 2)
+#define VHUB_EP_DMA_SINGLE_STAGE	(1 << 1)
+#define VHUB_EP_DMA_DESC_MODE		(1 << 0)
+
+/* EP DMA status */
+#define VHUB_EP_DMA_SET_TX_SIZE(x)	((x) << 16)
+#define VHUB_EP_DMA_TX_SIZE(x)		(((x) >> 16) & 0x7ff)
+#define VHUB_EP_DMA_RPTR(x)		(((x) >> 8) & 0xff)
+#define VHUB_EP_DMA_SET_RPTR(x)		(((x) & 0xff) << 8)
+#define VHUB_EP_DMA_SET_CPU_WPTR(x)	(x)
+#define VHUB_EP_DMA_SINGLE_KICK		(1 << 0) /* WPTR = 1 for single mode */
+
+/*******************************
+ *                             *
+ * DMA descriptors definitions *
+ *                             *
+ *******************************/
+
+/* Desc W1 IN */
+#define VHUB_DSC1_IN_INTERRUPT		(1 << 31)
+#define VHUB_DSC1_IN_SPID_DATA0		(0 << 14)
+#define VHUB_DSC1_IN_SPID_DATA2		(1 << 14)
+#define VHUB_DSC1_IN_SPID_DATA1		(2 << 14)
+#define VHUB_DSC1_IN_SPID_MDATA		(3 << 14)
+#define VHUB_DSC1_IN_SET_LEN(x)		((x) & 0xfff)
+#define VHUB_DSC1_IN_LEN(x)		((x) & 0xfff)
+
+/****************************************
+ *                                      *
+ * Data structures and misc definitions *
+ *                                      *
+ ****************************************/
+
+#define AST_VHUB_NUM_GEN_EPs	15	/* Generic non-0 EPs */
+#define AST_VHUB_NUM_PORTS	5	/* vHub ports */
+#define AST_VHUB_EP0_MAX_PACKET	64	/* EP0's max packet size */
+#define AST_VHUB_EPn_MAX_PACKET	1024	/* Generic EPs max packet size */
+#define AST_VHUB_DESCS_COUNT	256	/* Use 256 descriptor mode (valid
+					 * values are 256 and 32)
+					 */
+
+struct ast_vhub;
+struct ast_vhub_dev;
+
+/*
+ * DMA descriptor (generic EPs only, currently only used
+ * for IN endpoints
+ */
+struct ast_vhub_desc {
+	__le32	w0;
+	__le32	w1;
+};
+
+/* A transfer request, either core-originated or internal */
+struct ast_vhub_req {
+	struct usb_request	req;
+	struct list_head	queue;
+
+	/* Actual count written to descriptors (desc mode only) */
+	unsigned int		act_count;
+
+	/*
+	 * Desc number of the final packet or -1. For non-desc
+	 * mode (or ep0), any >= 0 value means "last packet"
+	 */
+	int			last_desc;
+
+	/* Request active (pending DMAs) */
+	bool			active  : 1;
+
+	/* Internal request (don't call back core) */
+	bool			internal : 1;
+};
+#define to_ast_req(__ureq) container_of(__ureq, struct ast_vhub_req, req)
+
+/* Current state of an EP0 */
+enum ep0_state {
+	ep0_state_token,
+	ep0_state_data,
+	ep0_state_status,
+};
+
+/*
+ * An endpoint, either generic, ep0, actual gadget EP
+ * or internal use vhub EP0. vhub EP1 doesn't have an
+ * associated structure as it's mostly HW managed.
+ */
+struct ast_vhub_ep {
+	struct usb_ep		ep;
+
+	/* Request queue */
+	struct list_head	queue;
+
+	/* EP index in the device, 0 means this is an EP0 */
+	unsigned int		d_idx;
+
+	/* Dev pointer or NULL for vHub EP0 */
+	struct ast_vhub_dev	*dev;
+
+	/* vHub itself */
+	struct ast_vhub		*vhub;
+
+	/*
+	 * DMA buffer for EP0, fallback DMA buffer for misaligned
+	 * OUT transfers for generic EPs
+	 */
+	void			*buf;
+	dma_addr_t		buf_dma;
+
+	/* The rest depends on the EP type */
+	union {
+		/* EP0 (either device or vhub) */
+		struct {
+			/*
+			 * EP0 registers are "similar" for
+			 * vHub and devices but located in
+			 * different places.
+			 */
+			void __iomem		*ctlstat;
+			void __iomem		*setup;
+
+			/* Current state & direction */
+			enum ep0_state		state;
+			bool			dir_in;
+
+			/* Internal use request */
+			struct ast_vhub_req	req;
+		} ep0;
+
+		/* Generic endpoint (aka EPn) */
+		struct {
+			/* Registers */
+			void __iomem   		*regs;
+
+			/* Index in global pool (0..14) */
+			unsigned int		g_idx;
+
+			/* DMA Descriptors */
+			struct ast_vhub_desc	*descs;
+			dma_addr_t		descs_dma;
+			unsigned int		d_next;
+			unsigned int		d_last;
+			unsigned int		dma_conf;
+
+			/* Max chunk size for IN EPs */
+			unsigned int		chunk_max;
+
+			/* State flags */
+			bool			is_in :  1;
+			bool			is_iso : 1;
+			bool			stalled : 1;
+			bool			wedged : 1;
+			bool			enabled : 1;
+			bool			desc_mode : 1;
+		} epn;
+	};
+};
+#define to_ast_ep(__uep) container_of(__uep, struct ast_vhub_ep, ep)
+
+/* A device attached to a vHub port */
+struct ast_vhub_dev {
+	struct ast_vhub			*vhub;
+	void __iomem			*regs;
+
+	/* Device index (0...4) and name string */
+	unsigned int			index;
+	const char			*name;
+
+	/* sysfs enclosure for the gadget gunk */
+	struct device			*port_dev;
+
+	/* Link to gadget core */
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	bool				registered : 1;
+	bool				wakeup_en : 1;
+	bool				suspended : 1;
+	bool				enabled : 1;
+
+	/* Endpoint structures */
+	struct ast_vhub_ep		ep0;
+	struct ast_vhub_ep		*epns[AST_VHUB_NUM_GEN_EPs];
+
+};
+#define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
+
+/* Per vhub port stateinfo structure */
+struct ast_vhub_port {
+	/* Port status & status change registers */
+	u16			status;
+	u16			change;
+
+	/* Associated device slot */
+	struct ast_vhub_dev	dev;
+};
+
+/* Global vhub structure */
+struct ast_vhub {
+	struct platform_device		*pdev;
+	void __iomem			*regs;
+	int				irq;
+	spinlock_t			lock;
+	struct work_struct		wake_work;
+	struct clk			*clk;
+
+	/* EP0 DMA buffers allocated in one chunk */
+	void				*ep0_bufs;
+	dma_addr_t			ep0_bufs_dma;
+
+	/* EP0 of the vhub itself */
+	struct ast_vhub_ep		ep0;
+
+	/* State of vhub ep1 */
+	bool				ep1_stalled : 1;
+
+	/* Per-port info */
+	struct ast_vhub_port		ports[AST_VHUB_NUM_PORTS];
+
+	/* Generic EP data structures */
+	struct ast_vhub_ep		epns[AST_VHUB_NUM_GEN_EPs];
+
+	/* Upstream bus is suspended ? */
+	bool				suspended : 1;
+
+	/* Hub itself can signal remote wakeup */
+	bool				wakeup_en : 1;
+
+	/* Force full speed only */
+	bool				force_usb1 : 1;
+
+	/* Upstream bus speed captured at bus reset */
+	unsigned int			speed;
+};
+
+/* Standard request handlers result codes */
+enum std_req_rc {
+	std_req_stall = -1,	/* Stall requested */
+	std_req_complete = 0,	/* Request completed with no data */
+	std_req_data = 1,	/* Request completed with data */
+	std_req_driver = 2,	/* Pass to driver pls */
+};
+
+#ifdef CONFIG_USB_GADGET_VERBOSE
+#define UDCVDBG(u, fmt...)	dev_dbg(&(u)->pdev->dev, fmt)
+
+#define EPVDBG(ep, fmt, ...)	do {			\
+	dev_dbg(&(ep)->vhub->pdev->dev,			\
+		"%s:EP%d " fmt,				\
+		(ep)->dev ? (ep)->dev->name : "hub",	\
+		(ep)->d_idx, ##__VA_ARGS__);		\
+	} while(0)
+
+#define DVDBG(d, fmt, ...)	do {			\
+	dev_dbg(&(d)->vhub->pdev->dev,			\
+		"%s " fmt, (d)->name,			\
+		##__VA_ARGS__);				\
+	} while(0)
+
+#else
+#define UDCVDBG(u, fmt...)	do { } while(0)
+#define EPVDBG(ep, fmt, ...)	do { } while(0)
+#define DVDBG(d, fmt, ...)	do { } while(0)
+#endif
+
+#ifdef CONFIG_USB_GADGET_DEBUG
+#define UDCDBG(u, fmt...)	dev_dbg(&(u)->pdev->dev, fmt)
+
+#define EPDBG(ep, fmt, ...)	do {			\
+	dev_dbg(&(ep)->vhub->pdev->dev,			\
+		"%s:EP%d " fmt,				\
+		(ep)->dev ? (ep)->dev->name : "hub",	\
+		(ep)->d_idx, ##__VA_ARGS__);		\
+	} while(0)
+
+#define DDBG(d, fmt, ...)	do {			\
+	dev_dbg(&(d)->vhub->pdev->dev,			\
+		"%s " fmt, (d)->name,			\
+		##__VA_ARGS__);				\
+	} while(0)
+#else
+#define UDCDBG(u, fmt...)	do { } while(0)
+#define EPDBG(ep, fmt, ...)	do { } while(0)
+#define DDBG(d, fmt, ...)	do { } while(0)
+#endif
+
+static inline void vhub_dma_workaround(void *addr)
+{
+	/*
+	 * This works around a confirmed HW issue with the Aspeed chip.
+	 *
+	 * The core uses a different bus to memory than the AHB going to
+	 * the USB device controller. Due to the latter having a higher
+	 * priority than the core for arbitration on that bus, it's
+	 * possible for an MMIO to the device, followed by a DMA by the
+	 * device from memory to all be performed and services before
+	 * a previous store to memory gets completed.
+	 *
+	 * This the following scenario can happen:
+	 *
+	 *    - Driver writes to a DMA descriptor (Mbus)
+	 *    - Driver writes to the MMIO register to start the DMA (AHB)
+	 *    - The gadget sees the second write and sends a read of the
+	 *      descriptor to the memory controller (Mbus)
+	 *    - The gadget hits memory before the descriptor write
+	 *      causing it to read an obsolete value.
+	 *
+	 * Thankfully the problem is limited to the USB gadget device, other
+	 * masters in the SoC all have a lower priority than the core, thus
+	 * ensuring that the store by the core arrives first.
+	 *
+	 * The workaround consists of using a dummy read of the memory before
+	 * doing the MMIO writes. This will ensure that the previous writes
+	 * have been "pushed out".
+	 */
+	mb();
+	(void)__raw_readl((void __iomem *)addr);
+}
+
+/* core.c */
+void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+		   int status);
+void ast_vhub_nuke(struct ast_vhub_ep *ep, int status);
+struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
+					   gfp_t gfp_flags);
+void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req);
+void ast_vhub_init_hw(struct ast_vhub *vhub);
+
+/* ep0.c */
+void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
+void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
+void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
+		       struct ast_vhub_dev *dev);
+int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);
+int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
+#define ast_vhub_simple_reply(udc, ...)					       \
+	__ast_vhub_simple_reply((udc),					       \
+			       sizeof((u8[]) { __VA_ARGS__ })/sizeof(u8),      \
+			       __VA_ARGS__)
+
+/* hub.c */
+void ast_vhub_init_hub(struct ast_vhub *vhub);
+enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
+					 struct usb_ctrlrequest *crq);
+enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
+					   struct usb_ctrlrequest *crq);
+void ast_vhub_device_connect(struct ast_vhub *vhub, unsigned int port,
+			     bool on);
+void ast_vhub_hub_suspend(struct ast_vhub *vhub);
+void ast_vhub_hub_resume(struct ast_vhub *vhub);
+void ast_vhub_hub_reset(struct ast_vhub *vhub);
+void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
+
+/* dev.c */
+int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
+void ast_vhub_del_dev(struct ast_vhub_dev *d);
+void ast_vhub_dev_irq(struct ast_vhub_dev *d);
+int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
+			     struct usb_ctrlrequest *crq);
+
+/* epn.c */
+void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep);
+void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep);
+struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr);
+void ast_vhub_dev_suspend(struct ast_vhub_dev *d);
+void ast_vhub_dev_resume(struct ast_vhub_dev *d);
+void ast_vhub_dev_reset(struct ast_vhub_dev *d);
+
+#endif /* __ASPEED_VHUB_H */
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
new file mode 100644
index 0000000..03959dc
--- /dev/null
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -0,0 +1,2037 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * at91_udc -- driver for at91-series USB peripheral controller
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ */
+
+#undef	VERBOSE_DEBUG
+#undef	PACKET_TRACE
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/atmel.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+
+#include "at91_udc.h"
+
+
+/*
+ * This controller is simple and PIO-only.  It's used in many AT91-series
+ * full speed USB controllers, including the at91rm9200 (arm920T, with MMU),
+ * at91sam926x (arm926ejs, with MMU), and several no-mmu versions.
+ *
+ * This driver expects the board has been wired with two GPIOs supporting
+ * a VBUS sensing IRQ, and a D+ pullup.  (They may be omitted, but the
+ * testing hasn't covered such cases.)
+ *
+ * The pullup is most important (so it's integrated on sam926x parts).  It
+ * provides software control over whether the host enumerates the device.
+ *
+ * The VBUS sensing helps during enumeration, and allows both USB clocks
+ * (and the transceiver) to stay gated off until they're necessary, saving
+ * power.  During USB suspend, the 48 MHz clock is gated off in hardware;
+ * it may also be gated off by software during some Linux sleep states.
+ */
+
+#define	DRIVER_VERSION	"3 May 2006"
+
+static const char driver_name [] = "at91_udc";
+
+static const struct {
+	const char *name;
+	const struct usb_ep_caps caps;
+} ep_info[] = {
+#define EP_INFO(_name, _caps) \
+	{ \
+		.name = _name, \
+		.caps = _caps, \
+	}
+
+	EP_INFO("ep0",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep1",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep2",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep3-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep4",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep5",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+
+#undef EP_INFO
+};
+
+#define ep0name		ep_info[0].name
+
+#define VBUS_POLL_TIMEOUT	msecs_to_jiffies(1000)
+
+#define at91_udp_read(udc, reg) \
+	__raw_readl((udc)->udp_baseaddr + (reg))
+#define at91_udp_write(udc, reg, val) \
+	__raw_writel((val), (udc)->udp_baseaddr + (reg))
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char debug_filename[] = "driver/udc";
+
+#define FOURBITS "%s%s%s%s"
+#define EIGHTBITS FOURBITS FOURBITS
+
+static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
+{
+	static char		*types[] = {
+		"control", "out-iso", "out-bulk", "out-int",
+		"BOGUS",   "in-iso",  "in-bulk",  "in-int"};
+
+	u32			csr;
+	struct at91_request	*req;
+	unsigned long	flags;
+	struct at91_udc	*udc = ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	csr = __raw_readl(ep->creg);
+
+	/* NOTE:  not collecting per-endpoint irq statistics... */
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%s, maxpacket %d %s%s %s%s\n",
+			ep->ep.name, ep->ep.maxpacket,
+			ep->is_in ? "in" : "out",
+			ep->is_iso ? " iso" : "",
+			ep->is_pingpong
+				? (ep->fifo_bank ? "pong" : "ping")
+				: "",
+			ep->stopped ? " stopped" : "");
+	seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n",
+		csr,
+		(csr & 0x07ff0000) >> 16,
+		(csr & (1 << 15)) ? "enabled" : "disabled",
+		(csr & (1 << 11)) ? "DATA1" : "DATA0",
+		types[(csr & 0x700) >> 8],
+
+		/* iff type is control then print current direction */
+		(!(csr & 0x700))
+			? ((csr & (1 << 7)) ? " IN" : " OUT")
+			: "",
+		(csr & (1 << 6)) ? " rxdatabk1" : "",
+		(csr & (1 << 5)) ? " forcestall" : "",
+		(csr & (1 << 4)) ? " txpktrdy" : "",
+
+		(csr & (1 << 3)) ? " stallsent" : "",
+		(csr & (1 << 2)) ? " rxsetup" : "",
+		(csr & (1 << 1)) ? " rxdatabk0" : "",
+		(csr & (1 << 0)) ? " txcomp" : "");
+	if (list_empty (&ep->queue))
+		seq_printf(s, "\t(queue empty)\n");
+
+	else list_for_each_entry (req, &ep->queue, queue) {
+		unsigned	length = req->req.actual;
+
+		seq_printf(s, "\treq %p len %d/%d buf %p\n",
+				&req->req, length,
+				req->req.length, req->req.buf);
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void proc_irq_show(struct seq_file *s, const char *label, u32 mask)
+{
+	int i;
+
+	seq_printf(s, "%s %04x:%s%s" FOURBITS, label, mask,
+		(mask & (1 << 13)) ? " wakeup" : "",
+		(mask & (1 << 12)) ? " endbusres" : "",
+
+		(mask & (1 << 11)) ? " sofint" : "",
+		(mask & (1 << 10)) ? " extrsm" : "",
+		(mask & (1 << 9)) ? " rxrsm" : "",
+		(mask & (1 << 8)) ? " rxsusp" : "");
+	for (i = 0; i < 8; i++) {
+		if (mask & (1 << i))
+			seq_printf(s, " ep%d", i);
+	}
+	seq_printf(s, "\n");
+}
+
+static int proc_udc_show(struct seq_file *s, void *unused)
+{
+	struct at91_udc	*udc = s->private;
+	struct at91_ep	*ep;
+	u32		tmp;
+
+	seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION);
+
+	seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n",
+		udc->vbus ? "present" : "off",
+		udc->enabled
+			? (udc->vbus ? "active" : "enabled")
+			: "disabled",
+		udc->gadget.is_selfpowered ? "self" : "VBUS",
+		udc->suspended ? ", suspended" : "",
+		udc->driver ? udc->driver->driver.name : "(none)");
+
+	/* don't access registers when interface isn't clocked */
+	if (!udc->clocked) {
+		seq_printf(s, "(not clocked)\n");
+		return 0;
+	}
+
+	tmp = at91_udp_read(udc, AT91_UDP_FRM_NUM);
+	seq_printf(s, "frame %05x:%s%s frame=%d\n", tmp,
+		(tmp & AT91_UDP_FRM_OK) ? " ok" : "",
+		(tmp & AT91_UDP_FRM_ERR) ? " err" : "",
+		(tmp & AT91_UDP_NUM));
+
+	tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+	seq_printf(s, "glbstate %02x:%s" FOURBITS "\n", tmp,
+		(tmp & AT91_UDP_RMWUPE) ? " rmwupe" : "",
+		(tmp & AT91_UDP_RSMINPR) ? " rsminpr" : "",
+		(tmp & AT91_UDP_ESR) ? " esr" : "",
+		(tmp & AT91_UDP_CONFG) ? " confg" : "",
+		(tmp & AT91_UDP_FADDEN) ? " fadden" : "");
+
+	tmp = at91_udp_read(udc, AT91_UDP_FADDR);
+	seq_printf(s, "faddr   %03x:%s fadd=%d\n", tmp,
+		(tmp & AT91_UDP_FEN) ? " fen" : "",
+		(tmp & AT91_UDP_FADD));
+
+	proc_irq_show(s, "imr   ", at91_udp_read(udc, AT91_UDP_IMR));
+	proc_irq_show(s, "isr   ", at91_udp_read(udc, AT91_UDP_ISR));
+
+	if (udc->enabled && udc->vbus) {
+		proc_ep_show(s, &udc->ep[0]);
+		list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
+			if (ep->ep.desc)
+				proc_ep_show(s, ep);
+		}
+	}
+	return 0;
+}
+
+static void create_debug_file(struct at91_udc *udc)
+{
+	udc->pde = proc_create_single_data(debug_filename, 0, NULL,
+			proc_udc_show, udc);
+}
+
+static void remove_debug_file(struct at91_udc *udc)
+{
+	if (udc->pde)
+		remove_proc_entry(debug_filename, NULL);
+}
+
+#else
+
+static inline void create_debug_file(struct at91_udc *udc) {}
+static inline void remove_debug_file(struct at91_udc *udc) {}
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+static void done(struct at91_ep *ep, struct at91_request *req, int status)
+{
+	unsigned	stopped = ep->stopped;
+	struct at91_udc	*udc = ep->udc;
+
+	list_del_init(&req->queue);
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+	if (status && status != -ESHUTDOWN)
+		VDBG("%s done %p, status %d\n", ep->ep.name, req, status);
+
+	ep->stopped = 1;
+	spin_unlock(&udc->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&udc->lock);
+	ep->stopped = stopped;
+
+	/* ep0 is always ready; other endpoints need a non-empty queue */
+	if (list_empty(&ep->queue) && ep->int_mask != (1 << 0))
+		at91_udp_write(udc, AT91_UDP_IDR, ep->int_mask);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* bits indicating OUT fifo has data ready */
+#define	RX_DATA_READY	(AT91_UDP_RX_DATA_BK0 | AT91_UDP_RX_DATA_BK1)
+
+/*
+ * Endpoint FIFO CSR bits have a mix of bits, making it unsafe to just write
+ * back most of the value you just read (because of side effects, including
+ * bits that may change after reading and before writing).
+ *
+ * Except when changing a specific bit, always write values which:
+ *  - clear SET_FX bits (setting them could change something)
+ *  - set CLR_FX bits (clearing them could change something)
+ *
+ * There are also state bits like FORCESTALL, EPEDS, DIR, and EPTYPE
+ * that shouldn't normally be changed.
+ *
+ * NOTE at91sam9260 docs mention synch between UDPCK and MCK clock domains,
+ * implying a need to wait for one write to complete (test relevant bits)
+ * before starting the next write.  This shouldn't be an issue given how
+ * infrequently we write, except maybe for write-then-read idioms.
+ */
+#define	SET_FX	(AT91_UDP_TXPKTRDY)
+#define	CLR_FX	(RX_DATA_READY | AT91_UDP_RXSETUP \
+		| AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
+
+/* pull OUT packet data from the endpoint's fifo */
+static int read_fifo (struct at91_ep *ep, struct at91_request *req)
+{
+	u32 __iomem	*creg = ep->creg;
+	u8 __iomem	*dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+	u32		csr;
+	u8		*buf;
+	unsigned int	count, bufferspace, is_done;
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+
+	/*
+	 * there might be nothing to read if ep_queue() calls us,
+	 * or if we already emptied both pingpong buffers
+	 */
+rescan:
+	csr = __raw_readl(creg);
+	if ((csr & RX_DATA_READY) == 0)
+		return 0;
+
+	count = (csr & AT91_UDP_RXBYTECNT) >> 16;
+	if (count > ep->ep.maxpacket)
+		count = ep->ep.maxpacket;
+	if (count > bufferspace) {
+		DBG("%s buffer overflow\n", ep->ep.name);
+		req->req.status = -EOVERFLOW;
+		count = bufferspace;
+	}
+	__raw_readsb(dreg, buf, count);
+
+	/* release and swap pingpong mem bank */
+	csr |= CLR_FX;
+	if (ep->is_pingpong) {
+		if (ep->fifo_bank == 0) {
+			csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+			ep->fifo_bank = 1;
+		} else {
+			csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK1);
+			ep->fifo_bank = 0;
+		}
+	} else
+		csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+	__raw_writel(csr, creg);
+
+	req->req.actual += count;
+	is_done = (count < ep->ep.maxpacket);
+	if (count == bufferspace)
+		is_done = 1;
+
+	PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count,
+			is_done ? " (done)" : "");
+
+	/*
+	 * avoid extra trips through IRQ logic for packets already in
+	 * the fifo ... maybe preventing an extra (expensive) OUT-NAK
+	 */
+	if (is_done)
+		done(ep, req, 0);
+	else if (ep->is_pingpong) {
+		/*
+		 * One dummy read to delay the code because of a HW glitch:
+		 * CSR returns bad RXCOUNT when read too soon after updating
+		 * RX_DATA_BK flags.
+		 */
+		csr = __raw_readl(creg);
+
+		bufferspace -= count;
+		buf += count;
+		goto rescan;
+	}
+
+	return is_done;
+}
+
+/* load fifo for an IN packet */
+static int write_fifo(struct at91_ep *ep, struct at91_request *req)
+{
+	u32 __iomem	*creg = ep->creg;
+	u32		csr = __raw_readl(creg);
+	u8 __iomem	*dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+	unsigned	total, count, is_last;
+	u8		*buf;
+
+	/*
+	 * TODO: allow for writing two packets to the fifo ... that'll
+	 * reduce the amount of IN-NAKing, but probably won't affect
+	 * throughput much.  (Unlike preventing OUT-NAKing!)
+	 */
+
+	/*
+	 * If ep_queue() calls us, the queue is empty and possibly in
+	 * odd states like TXCOMP not yet cleared (we do it, saving at
+	 * least one IRQ) or the fifo not yet being free.  Those aren't
+	 * issues normally (IRQ handler fast path).
+	 */
+	if (unlikely(csr & (AT91_UDP_TXCOMP | AT91_UDP_TXPKTRDY))) {
+		if (csr & AT91_UDP_TXCOMP) {
+			csr |= CLR_FX;
+			csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+			__raw_writel(csr, creg);
+			csr = __raw_readl(creg);
+		}
+		if (csr & AT91_UDP_TXPKTRDY)
+			return 0;
+	}
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+	total = req->req.length - req->req.actual;
+	if (ep->ep.maxpacket < total) {
+		count = ep->ep.maxpacket;
+		is_last = 0;
+	} else {
+		count = total;
+		is_last = (count < ep->ep.maxpacket) || !req->req.zero;
+	}
+
+	/*
+	 * Write the packet, maybe it's a ZLP.
+	 *
+	 * NOTE:  incrementing req->actual before we receive the ACK means
+	 * gadget driver IN bytecounts can be wrong in fault cases.  That's
+	 * fixable with PIO drivers like this one (save "count" here, and
+	 * do the increment later on TX irq), but not for most DMA hardware.
+	 *
+	 * So all gadget drivers must accept that potential error.  Some
+	 * hardware supports precise fifo status reporting, letting them
+	 * recover when the actual bytecount matters (e.g. for USB Test
+	 * and Measurement Class devices).
+	 */
+	__raw_writesb(dreg, buf, count);
+	csr &= ~SET_FX;
+	csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+	__raw_writel(csr, creg);
+	req->req.actual += count;
+
+	PACKET("%s %p in/%d%s\n", ep->ep.name, &req->req, count,
+			is_last ? " (done)" : "");
+	if (is_last)
+		done(ep, req, 0);
+	return is_last;
+}
+
+static void nuke(struct at91_ep *ep, int status)
+{
+	struct at91_request *req;
+
+	/* terminate any request in the queue */
+	ep->stopped = 1;
+	if (list_empty(&ep->queue))
+		return;
+
+	VDBG("%s %s\n", __func__, ep->ep.name);
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct at91_request, queue);
+		done(ep, req, status);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);
+	struct at91_udc *udc;
+	u16		maxpacket;
+	u32		tmp;
+	unsigned long	flags;
+
+	if (!_ep || !ep
+			|| !desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| (maxpacket = usb_endpoint_maxp(desc)) == 0
+			|| maxpacket > ep->maxpacket) {
+		DBG("bad ep or descriptor\n");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		DBG("bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	tmp = usb_endpoint_type(desc);
+	switch (tmp) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		DBG("only one control endpoint\n");
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_INT:
+		if (maxpacket > 64)
+			goto bogus_max;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		switch (maxpacket) {
+		case 8:
+		case 16:
+		case 32:
+		case 64:
+			goto ok;
+		}
+bogus_max:
+		DBG("bogus maxpacket %d\n", maxpacket);
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (!ep->is_pingpong) {
+			DBG("iso requires double buffering\n");
+			return -EINVAL;
+		}
+		break;
+	}
+
+ok:
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* initialize endpoint to match this descriptor */
+	ep->is_in = usb_endpoint_dir_in(desc);
+	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+	ep->stopped = 0;
+	if (ep->is_in)
+		tmp |= 0x04;
+	tmp <<= 8;
+	tmp |= AT91_UDP_EPEDS;
+	__raw_writel(tmp, ep->creg);
+
+	ep->ep.maxpacket = maxpacket;
+
+	/*
+	 * reset/init endpoint fifo.  NOTE:  leaves fifo_bank alone,
+	 * since endpoint resets don't reset hw pingpong state.
+	 */
+	at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+	at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int at91_ep_disable (struct usb_ep * _ep)
+{
+	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);
+	struct at91_udc	*udc = ep->udc;
+	unsigned long	flags;
+
+	if (ep == &ep->udc->ep[0])
+		return -EINVAL;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	nuke(ep, -ESHUTDOWN);
+
+	/* restore the endpoint's pristine config */
+	ep->ep.desc = NULL;
+	ep->ep.maxpacket = ep->maxpacket;
+
+	/* reset fifos and endpoint */
+	if (ep->udc->clocked) {
+		at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+		at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+		__raw_writel(0, ep->creg);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/*
+ * this is a PIO-only driver, so there's nothing
+ * interesting for request or buffer allocation.
+ */
+
+static struct usb_request *
+at91_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct at91_request *req;
+
+	req = kzalloc(sizeof (struct at91_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct at91_request *req;
+
+	req = container_of(_req, struct at91_request, req);
+	BUG_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static int at91_ep_queue(struct usb_ep *_ep,
+			struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct at91_request	*req;
+	struct at91_ep		*ep;
+	struct at91_udc		*udc;
+	int			status;
+	unsigned long		flags;
+
+	req = container_of(_req, struct at91_request, req);
+	ep = container_of(_ep, struct at91_ep, ep);
+
+	if (!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue)) {
+		DBG("invalid request\n");
+		return -EINVAL;
+	}
+
+	if (!_ep || (!ep->ep.desc && ep->ep.name != ep0name)) {
+		DBG("invalid ep\n");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+
+	if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		DBG("invalid device\n");
+		return -EINVAL;
+	}
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* try to kickstart any empty and idle queue */
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		int	is_ep0;
+
+		/*
+		 * If this control request has a non-empty DATA stage, this
+		 * will start that stage.  It works just like a non-control
+		 * request (until the status stage starts, maybe early).
+		 *
+		 * If the data stage is empty, then this starts a successful
+		 * IN/STATUS stage.  (Unsuccessful ones use set_halt.)
+		 */
+		is_ep0 = (ep->ep.name == ep0name);
+		if (is_ep0) {
+			u32	tmp;
+
+			if (!udc->req_pending) {
+				status = -EINVAL;
+				goto done;
+			}
+
+			/*
+			 * defer changing CONFG until after the gadget driver
+			 * reconfigures the endpoints.
+			 */
+			if (udc->wait_for_config_ack) {
+				tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+				tmp ^= AT91_UDP_CONFG;
+				VDBG("toggle config\n");
+				at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+			}
+			if (req->req.length == 0) {
+ep0_in_status:
+				PACKET("ep0 in/status\n");
+				status = 0;
+				tmp = __raw_readl(ep->creg);
+				tmp &= ~SET_FX;
+				tmp |= CLR_FX | AT91_UDP_TXPKTRDY;
+				__raw_writel(tmp, ep->creg);
+				udc->req_pending = 0;
+				goto done;
+			}
+		}
+
+		if (ep->is_in)
+			status = write_fifo(ep, req);
+		else {
+			status = read_fifo(ep, req);
+
+			/* IN/STATUS stage is otherwise triggered by irq */
+			if (status && is_ep0)
+				goto ep0_in_status;
+		}
+	} else
+		status = 0;
+
+	if (req && !status) {
+		list_add_tail (&req->queue, &ep->queue);
+		at91_udp_write(udc, AT91_UDP_IER, ep->int_mask);
+	}
+done:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return (status < 0) ? status : 0;
+}
+
+static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct at91_ep		*ep;
+	struct at91_request	*req;
+	unsigned long		flags;
+	struct at91_udc		*udc;
+
+	ep = container_of(_ep, struct at91_ep, ep);
+	if (!_ep || ep->ep.name == ep0name)
+		return -EINVAL;
+
+	udc = ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int at91_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);
+	struct at91_udc	*udc = ep->udc;
+	u32 __iomem	*creg;
+	u32		csr;
+	unsigned long	flags;
+	int		status = 0;
+
+	if (!_ep || ep->is_iso || !ep->udc->clocked)
+		return -EINVAL;
+
+	creg = ep->creg;
+	spin_lock_irqsave(&udc->lock, flags);
+
+	csr = __raw_readl(creg);
+
+	/*
+	 * fail with still-busy IN endpoints, ensuring correct sequencing
+	 * of data tx then stall.  note that the fifo rx bytecount isn't
+	 * completely accurate as a tx bytecount.
+	 */
+	if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0))
+		status = -EAGAIN;
+	else {
+		csr |= CLR_FX;
+		csr &= ~SET_FX;
+		if (value) {
+			csr |= AT91_UDP_FORCESTALL;
+			VDBG("halt %s\n", ep->ep.name);
+		} else {
+			at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+			at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+			csr &= ~AT91_UDP_FORCESTALL;
+		}
+		__raw_writel(csr, creg);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+static const struct usb_ep_ops at91_ep_ops = {
+	.enable		= at91_ep_enable,
+	.disable	= at91_ep_disable,
+	.alloc_request	= at91_ep_alloc_request,
+	.free_request	= at91_ep_free_request,
+	.queue		= at91_ep_queue,
+	.dequeue	= at91_ep_dequeue,
+	.set_halt	= at91_ep_set_halt,
+	/* there's only imprecise fifo status reporting */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int at91_get_frame(struct usb_gadget *gadget)
+{
+	struct at91_udc *udc = to_udc(gadget);
+
+	if (!to_udc(gadget)->clocked)
+		return -EINVAL;
+	return at91_udp_read(udc, AT91_UDP_FRM_NUM) & AT91_UDP_NUM;
+}
+
+static int at91_wakeup(struct usb_gadget *gadget)
+{
+	struct at91_udc	*udc = to_udc(gadget);
+	u32		glbstate;
+	int		status = -EINVAL;
+	unsigned long	flags;
+
+	DBG("%s\n", __func__ );
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (!udc->clocked || !udc->suspended)
+		goto done;
+
+	/* NOTE:  some "early versions" handle ESR differently ... */
+
+	glbstate = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+	if (!(glbstate & AT91_UDP_ESR))
+		goto done;
+	glbstate |= AT91_UDP_ESR;
+	at91_udp_write(udc, AT91_UDP_GLB_STAT, glbstate);
+
+done:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+/* reinit == restore initial software state */
+static void udc_reinit(struct at91_udc *udc)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+	udc->gadget.quirk_stall_not_supp = 1;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct at91_ep *ep = &udc->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+		ep->ep.desc = NULL;
+		ep->stopped = 0;
+		ep->fifo_bank = 0;
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
+		ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i);
+		/* initialize one queue per endpoint */
+		INIT_LIST_HEAD(&ep->queue);
+	}
+}
+
+static void reset_gadget(struct at91_udc *udc)
+{
+	struct usb_gadget_driver *driver = udc->driver;
+	int i;
+
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->suspended = 0;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct at91_ep *ep = &udc->ep[i];
+
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+	if (driver) {
+		spin_unlock(&udc->lock);
+		usb_gadget_udc_reset(&udc->gadget, driver);
+		spin_lock(&udc->lock);
+	}
+
+	udc_reinit(udc);
+}
+
+static void stop_activity(struct at91_udc *udc)
+{
+	struct usb_gadget_driver *driver = udc->driver;
+	int i;
+
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->suspended = 0;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct at91_ep *ep = &udc->ep[i];
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+	if (driver) {
+		spin_unlock(&udc->lock);
+		driver->disconnect(&udc->gadget);
+		spin_lock(&udc->lock);
+	}
+
+	udc_reinit(udc);
+}
+
+static void clk_on(struct at91_udc *udc)
+{
+	if (udc->clocked)
+		return;
+	udc->clocked = 1;
+
+	clk_enable(udc->iclk);
+	clk_enable(udc->fclk);
+}
+
+static void clk_off(struct at91_udc *udc)
+{
+	if (!udc->clocked)
+		return;
+	udc->clocked = 0;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	clk_disable(udc->fclk);
+	clk_disable(udc->iclk);
+}
+
+/*
+ * activate/deactivate link with host; minimize power usage for
+ * inactive links by cutting clocks and transceiver power.
+ */
+static void pullup(struct at91_udc *udc, int is_on)
+{
+	if (!udc->enabled || !udc->vbus)
+		is_on = 0;
+	DBG("%sactive\n", is_on ? "" : "in");
+
+	if (is_on) {
+		clk_on(udc);
+		at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
+		at91_udp_write(udc, AT91_UDP_TXVC, 0);
+	} else {
+		stop_activity(udc);
+		at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
+		at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+		clk_off(udc);
+	}
+
+	if (udc->caps && udc->caps->pullup)
+		udc->caps->pullup(udc, is_on);
+}
+
+/* vbus is here!  turn everything on that's ready */
+static int at91_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct at91_udc	*udc = to_udc(gadget);
+	unsigned long	flags;
+
+	/* VDBG("vbus %s\n", is_active ? "on" : "off"); */
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->vbus = (is_active != 0);
+	if (udc->driver)
+		pullup(udc, is_active);
+	else
+		pullup(udc, 0);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int at91_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct at91_udc	*udc = to_udc(gadget);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->enabled = is_on = !!is_on;
+	pullup(udc, is_on);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
+{
+	struct at91_udc	*udc = to_udc(gadget);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	gadget->is_selfpowered = (is_on != 0);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int at91_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver);
+static int at91_stop(struct usb_gadget *gadget);
+
+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,
+	.udc_start		= at91_start,
+	.udc_stop		= at91_stop,
+
+	/*
+	 * VBUS-powered devices may also also want to support bigger
+	 * power budgets after an appropriate SET_CONFIGURATION.
+	 */
+	/* .vbus_power		= at91_vbus_power, */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int handle_ep(struct at91_ep *ep)
+{
+	struct at91_request	*req;
+	u32 __iomem		*creg = ep->creg;
+	u32			csr = __raw_readl(creg);
+
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next,
+			struct at91_request, queue);
+	else
+		req = NULL;
+
+	if (ep->is_in) {
+		if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) {
+			csr |= CLR_FX;
+			csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP);
+			__raw_writel(csr, creg);
+		}
+		if (req)
+			return write_fifo(ep, req);
+
+	} else {
+		if (csr & AT91_UDP_STALLSENT) {
+			/* STALLSENT bit == ISOERR */
+			if (ep->is_iso && req)
+				req->req.status = -EILSEQ;
+			csr |= CLR_FX;
+			csr &= ~(SET_FX | AT91_UDP_STALLSENT);
+			__raw_writel(csr, creg);
+			csr = __raw_readl(creg);
+		}
+		if (req && (csr & RX_DATA_READY))
+			return read_fifo(ep, req);
+	}
+	return 0;
+}
+
+union setup {
+	u8			raw[8];
+	struct usb_ctrlrequest	r;
+};
+
+static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
+{
+	u32 __iomem	*creg = ep->creg;
+	u8 __iomem	*dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));
+	unsigned	rxcount, i = 0;
+	u32		tmp;
+	union setup	pkt;
+	int		status = 0;
+
+	/* read and ack SETUP; hard-fail for bogus packets */
+	rxcount = (csr & AT91_UDP_RXBYTECNT) >> 16;
+	if (likely(rxcount == 8)) {
+		while (rxcount--)
+			pkt.raw[i++] = __raw_readb(dreg);
+		if (pkt.r.bRequestType & USB_DIR_IN) {
+			csr |= AT91_UDP_DIR;
+			ep->is_in = 1;
+		} else {
+			csr &= ~AT91_UDP_DIR;
+			ep->is_in = 0;
+		}
+	} else {
+		/* REVISIT this happens sometimes under load; why?? */
+		ERR("SETUP len %d, csr %08x\n", rxcount, csr);
+		status = -EINVAL;
+	}
+	csr |= CLR_FX;
+	csr &= ~(SET_FX | AT91_UDP_RXSETUP);
+	__raw_writel(csr, creg);
+	udc->wait_for_addr_ack = 0;
+	udc->wait_for_config_ack = 0;
+	ep->stopped = 0;
+	if (unlikely(status != 0))
+		goto stall;
+
+#define w_index		le16_to_cpu(pkt.r.wIndex)
+#define w_value		le16_to_cpu(pkt.r.wValue)
+#define w_length	le16_to_cpu(pkt.r.wLength)
+
+	VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
+			pkt.r.bRequestType, pkt.r.bRequest,
+			w_value, w_index, w_length);
+
+	/*
+	 * A few standard requests get handled here, ones that touch
+	 * hardware ... notably for device and endpoint features.
+	 */
+	udc->req_pending = 1;
+	csr = __raw_readl(creg);
+	csr |= CLR_FX;
+	csr &= ~SET_FX;
+	switch ((pkt.r.bRequestType << 8) | pkt.r.bRequest) {
+
+	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+			| USB_REQ_SET_ADDRESS:
+		__raw_writel(csr | AT91_UDP_TXPKTRDY, creg);
+		udc->addr = w_value;
+		udc->wait_for_addr_ack = 1;
+		udc->req_pending = 0;
+		/* FADDR is set later, when we ack host STATUS */
+		return;
+
+	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+			| USB_REQ_SET_CONFIGURATION:
+		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_CONFG;
+		if (pkt.r.wValue)
+			udc->wait_for_config_ack = (tmp == 0);
+		else
+			udc->wait_for_config_ack = (tmp != 0);
+		if (udc->wait_for_config_ack)
+			VDBG("wait for config\n");
+		/* CONFG is toggled later, if gadget driver succeeds */
+		break;
+
+	/*
+	 * Hosts may set or clear remote wakeup status, and
+	 * devices may report they're VBUS powered.
+	 */
+	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+			| USB_REQ_GET_STATUS:
+		tmp = (udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED);
+		if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
+			tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+		PACKET("get device status\n");
+		__raw_writeb(tmp, dreg);
+		__raw_writeb(0, dreg);
+		goto write_in;
+		/* then STATUS starts later, automatically */
+	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+			| USB_REQ_SET_FEATURE:
+		if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+			goto stall;
+		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+		tmp |= AT91_UDP_ESR;
+		at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+		goto succeed;
+	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
+			| USB_REQ_CLEAR_FEATURE:
+		if (w_value != USB_DEVICE_REMOTE_WAKEUP)
+			goto stall;
+		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+		tmp &= ~AT91_UDP_ESR;
+		at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+		goto succeed;
+
+	/*
+	 * Interfaces have no feature settings; this is pretty useless.
+	 * we won't even insist the interface exists...
+	 */
+	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+			| USB_REQ_GET_STATUS:
+		PACKET("get interface status\n");
+		__raw_writeb(0, dreg);
+		__raw_writeb(0, dreg);
+		goto write_in;
+		/* then STATUS starts later, automatically */
+	case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+			| USB_REQ_SET_FEATURE:
+	case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)
+			| USB_REQ_CLEAR_FEATURE:
+		goto stall;
+
+	/*
+	 * Hosts may clear bulk/intr endpoint halt after the gadget
+	 * driver sets it (not widely used); or set it (for testing)
+	 */
+	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+			| USB_REQ_GET_STATUS:
+		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+		ep = &udc->ep[tmp];
+		if (tmp >= NUM_ENDPOINTS || (tmp && !ep->ep.desc))
+			goto stall;
+
+		if (tmp) {
+			if ((w_index & USB_DIR_IN)) {
+				if (!ep->is_in)
+					goto stall;
+			} else if (ep->is_in)
+				goto stall;
+		}
+		PACKET("get %s status\n", ep->ep.name);
+		if (__raw_readl(ep->creg) & AT91_UDP_FORCESTALL)
+			tmp = (1 << USB_ENDPOINT_HALT);
+		else
+			tmp = 0;
+		__raw_writeb(tmp, dreg);
+		__raw_writeb(0, dreg);
+		goto write_in;
+		/* then STATUS starts later, automatically */
+	case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+			| USB_REQ_SET_FEATURE:
+		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+		ep = &udc->ep[tmp];
+		if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
+			goto stall;
+		if (!ep->ep.desc || ep->is_iso)
+			goto stall;
+		if ((w_index & USB_DIR_IN)) {
+			if (!ep->is_in)
+				goto stall;
+		} else if (ep->is_in)
+			goto stall;
+
+		tmp = __raw_readl(ep->creg);
+		tmp &= ~SET_FX;
+		tmp |= CLR_FX | AT91_UDP_FORCESTALL;
+		__raw_writel(tmp, ep->creg);
+		goto succeed;
+	case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)
+			| USB_REQ_CLEAR_FEATURE:
+		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;
+		ep = &udc->ep[tmp];
+		if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS)
+			goto stall;
+		if (tmp == 0)
+			goto succeed;
+		if (!ep->ep.desc || ep->is_iso)
+			goto stall;
+		if ((w_index & USB_DIR_IN)) {
+			if (!ep->is_in)
+				goto stall;
+		} else if (ep->is_in)
+			goto stall;
+
+		at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+		at91_udp_write(udc, AT91_UDP_RST_EP, 0);
+		tmp = __raw_readl(ep->creg);
+		tmp |= CLR_FX;
+		tmp &= ~(SET_FX | AT91_UDP_FORCESTALL);
+		__raw_writel(tmp, ep->creg);
+		if (!list_empty(&ep->queue))
+			handle_ep(ep);
+		goto succeed;
+	}
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+	/* pass request up to the gadget driver */
+	if (udc->driver) {
+		spin_unlock(&udc->lock);
+		status = udc->driver->setup(&udc->gadget, &pkt.r);
+		spin_lock(&udc->lock);
+	}
+	else
+		status = -ENODEV;
+	if (status < 0) {
+stall:
+		VDBG("req %02x.%02x protocol STALL; stat %d\n",
+				pkt.r.bRequestType, pkt.r.bRequest, status);
+		csr |= AT91_UDP_FORCESTALL;
+		__raw_writel(csr, creg);
+		udc->req_pending = 0;
+	}
+	return;
+
+succeed:
+	/* immediate successful (IN) STATUS after zero length DATA */
+	PACKET("ep0 in/status\n");
+write_in:
+	csr |= AT91_UDP_TXPKTRDY;
+	__raw_writel(csr, creg);
+	udc->req_pending = 0;
+}
+
+static void handle_ep0(struct at91_udc *udc)
+{
+	struct at91_ep		*ep0 = &udc->ep[0];
+	u32 __iomem		*creg = ep0->creg;
+	u32			csr = __raw_readl(creg);
+	struct at91_request	*req;
+
+	if (unlikely(csr & AT91_UDP_STALLSENT)) {
+		nuke(ep0, -EPROTO);
+		udc->req_pending = 0;
+		csr |= CLR_FX;
+		csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL);
+		__raw_writel(csr, creg);
+		VDBG("ep0 stalled\n");
+		csr = __raw_readl(creg);
+	}
+	if (csr & AT91_UDP_RXSETUP) {
+		nuke(ep0, 0);
+		udc->req_pending = 0;
+		handle_setup(udc, ep0, csr);
+		return;
+	}
+
+	if (list_empty(&ep0->queue))
+		req = NULL;
+	else
+		req = list_entry(ep0->queue.next, struct at91_request, queue);
+
+	/* host ACKed an IN packet that we sent */
+	if (csr & AT91_UDP_TXCOMP) {
+		csr |= CLR_FX;
+		csr &= ~(SET_FX | AT91_UDP_TXCOMP);
+
+		/* write more IN DATA? */
+		if (req && ep0->is_in) {
+			if (handle_ep(ep0))
+				udc->req_pending = 0;
+
+		/*
+		 * Ack after:
+		 *  - last IN DATA packet (including GET_STATUS)
+		 *  - IN/STATUS for OUT DATA
+		 *  - IN/STATUS for any zero-length DATA stage
+		 * except for the IN DATA case, the host should send
+		 * an OUT status later, which we'll ack.
+		 */
+		} else {
+			udc->req_pending = 0;
+			__raw_writel(csr, creg);
+
+			/*
+			 * SET_ADDRESS takes effect only after the STATUS
+			 * (to the original address) gets acked.
+			 */
+			if (udc->wait_for_addr_ack) {
+				u32	tmp;
+
+				at91_udp_write(udc, AT91_UDP_FADDR,
+						AT91_UDP_FEN | udc->addr);
+				tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
+				tmp &= ~AT91_UDP_FADDEN;
+				if (udc->addr)
+					tmp |= AT91_UDP_FADDEN;
+				at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
+
+				udc->wait_for_addr_ack = 0;
+				VDBG("address %d\n", udc->addr);
+			}
+		}
+	}
+
+	/* OUT packet arrived ... */
+	else if (csr & AT91_UDP_RX_DATA_BK0) {
+		csr |= CLR_FX;
+		csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);
+
+		/* OUT DATA stage */
+		if (!ep0->is_in) {
+			if (req) {
+				if (handle_ep(ep0)) {
+					/* send IN/STATUS */
+					PACKET("ep0 in/status\n");
+					csr = __raw_readl(creg);
+					csr &= ~SET_FX;
+					csr |= CLR_FX | AT91_UDP_TXPKTRDY;
+					__raw_writel(csr, creg);
+					udc->req_pending = 0;
+				}
+			} else if (udc->req_pending) {
+				/*
+				 * AT91 hardware has a hard time with this
+				 * "deferred response" mode for control-OUT
+				 * transfers.  (For control-IN it's fine.)
+				 *
+				 * The normal solution leaves OUT data in the
+				 * fifo until the gadget driver is ready.
+				 * We couldn't do that here without disabling
+				 * the IRQ that tells about SETUP packets,
+				 * e.g. when the host gets impatient...
+				 *
+				 * Working around it by copying into a buffer
+				 * would almost be a non-deferred response,
+				 * except that it wouldn't permit reliable
+				 * stalling of the request.  Instead, demand
+				 * that gadget drivers not use this mode.
+				 */
+				DBG("no control-OUT deferred responses!\n");
+				__raw_writel(csr | AT91_UDP_FORCESTALL, creg);
+				udc->req_pending = 0;
+			}
+
+		/* STATUS stage for control-IN; ack.  */
+		} else {
+			PACKET("ep0 out/status ACK\n");
+			__raw_writel(csr, creg);
+
+			/* "early" status stage */
+			if (req)
+				done(ep0, req, 0);
+		}
+	}
+}
+
+static irqreturn_t at91_udc_irq (int irq, void *_udc)
+{
+	struct at91_udc		*udc = _udc;
+	u32			rescans = 5;
+	int			disable_clock = 0;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (!udc->clocked) {
+		clk_on(udc);
+		disable_clock = 1;
+	}
+
+	while (rescans--) {
+		u32 status;
+
+		status = at91_udp_read(udc, AT91_UDP_ISR)
+			& at91_udp_read(udc, AT91_UDP_IMR);
+		if (!status)
+			break;
+
+		/* USB reset irq:  not maskable */
+		if (status & AT91_UDP_ENDBUSRES) {
+			at91_udp_write(udc, AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS);
+			at91_udp_write(udc, AT91_UDP_IER, MINIMUS_INTERRUPTUS);
+			/* Atmel code clears this irq twice */
+			at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+			at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
+			VDBG("end bus reset\n");
+			udc->addr = 0;
+			reset_gadget(udc);
+
+			/* enable ep0 */
+			at91_udp_write(udc, AT91_UDP_CSR(0),
+					AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
+			udc->gadget.speed = USB_SPEED_FULL;
+			udc->suspended = 0;
+			at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_EP(0));
+
+			/*
+			 * NOTE:  this driver keeps clocks off unless the
+			 * USB host is present.  That saves power, but for
+			 * boards that don't support VBUS detection, both
+			 * clocks need to be active most of the time.
+			 */
+
+		/* host initiated suspend (3+ms bus idle) */
+		} else if (status & AT91_UDP_RXSUSP) {
+			at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP);
+			at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM);
+			at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP);
+			/* VDBG("bus suspend\n"); */
+			if (udc->suspended)
+				continue;
+			udc->suspended = 1;
+
+			/*
+			 * NOTE:  when suspending a VBUS-powered device, the
+			 * gadget driver should switch into slow clock mode
+			 * and then into standby to avoid drawing more than
+			 * 500uA power (2500uA for some high-power configs).
+			 */
+			if (udc->driver && udc->driver->suspend) {
+				spin_unlock(&udc->lock);
+				udc->driver->suspend(&udc->gadget);
+				spin_lock(&udc->lock);
+			}
+
+		/* host initiated resume */
+		} else if (status & AT91_UDP_RXRSM) {
+			at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM);
+			at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP);
+			at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM);
+			/* VDBG("bus resume\n"); */
+			if (!udc->suspended)
+				continue;
+			udc->suspended = 0;
+
+			/*
+			 * NOTE:  for a VBUS-powered device, the gadget driver
+			 * would normally want to switch out of slow clock
+			 * mode into normal mode.
+			 */
+			if (udc->driver && udc->driver->resume) {
+				spin_unlock(&udc->lock);
+				udc->driver->resume(&udc->gadget);
+				spin_lock(&udc->lock);
+			}
+
+		/* endpoint IRQs are cleared by handling them */
+		} else {
+			int		i;
+			unsigned	mask = 1;
+			struct at91_ep	*ep = &udc->ep[1];
+
+			if (status & mask)
+				handle_ep0(udc);
+			for (i = 1; i < NUM_ENDPOINTS; i++) {
+				mask <<= 1;
+				if (status & mask)
+					handle_ep(ep);
+				ep++;
+			}
+		}
+	}
+
+	if (disable_clock)
+		clk_off(udc);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void at91_vbus_update(struct at91_udc *udc, unsigned value)
+{
+	value ^= udc->board.vbus_active_low;
+	if (value != udc->vbus)
+		at91_vbus_session(&udc->gadget, value);
+}
+
+static irqreturn_t at91_vbus_irq(int irq, void *_udc)
+{
+	struct at91_udc	*udc = _udc;
+
+	/* vbus needs at least brief debouncing */
+	udelay(10);
+	at91_vbus_update(udc, gpio_get_value(udc->board.vbus_pin));
+
+	return IRQ_HANDLED;
+}
+
+static void at91_vbus_timer_work(struct work_struct *work)
+{
+	struct at91_udc *udc = container_of(work, struct at91_udc,
+					    vbus_timer_work);
+
+	at91_vbus_update(udc, gpio_get_value_cansleep(udc->board.vbus_pin));
+
+	if (!timer_pending(&udc->vbus_timer))
+		mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
+}
+
+static void at91_vbus_timer(struct timer_list *t)
+{
+	struct at91_udc *udc = from_timer(udc, t, vbus_timer);
+
+	/*
+	 * If we are polling vbus it is likely that the gpio is on an
+	 * bus such as i2c or spi which may sleep, so schedule some work
+	 * to read the vbus gpio
+	 */
+	schedule_work(&udc->vbus_timer_work);
+}
+
+static int at91_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct at91_udc	*udc;
+
+	udc = container_of(gadget, struct at91_udc, gadget);
+	udc->driver = driver;
+	udc->gadget.dev.of_node = udc->pdev->dev.of_node;
+	udc->enabled = 1;
+	udc->gadget.is_selfpowered = 1;
+
+	return 0;
+}
+
+static int at91_stop(struct usb_gadget *gadget)
+{
+	struct at91_udc *udc;
+	unsigned long	flags;
+
+	udc = container_of(gadget, struct at91_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->enabled = 0;
+	at91_udp_write(udc, AT91_UDP_IDR, ~0);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	udc->driver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void at91udc_shutdown(struct platform_device *dev)
+{
+	struct at91_udc *udc = platform_get_drvdata(dev);
+	unsigned long	flags;
+
+	/* force disconnect on reboot */
+	spin_lock_irqsave(&udc->lock, flags);
+	pullup(platform_get_drvdata(dev), 0);
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static int at91rm9200_udc_init(struct at91_udc *udc)
+{
+	struct at91_ep *ep;
+	int ret;
+	int i;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+
+		switch (i) {
+		case 0:
+		case 3:
+			ep->maxpacket = 8;
+			break;
+		case 1 ... 2:
+			ep->maxpacket = 64;
+			break;
+		case 4 ... 5:
+			ep->maxpacket = 256;
+			break;
+		}
+	}
+
+	if (!gpio_is_valid(udc->board.pullup_pin)) {
+		DBG("no D+ pullup?\n");
+		return -ENODEV;
+	}
+
+	ret = devm_gpio_request(&udc->pdev->dev, udc->board.pullup_pin,
+				"udc_pullup");
+	if (ret) {
+		DBG("D+ pullup is busy\n");
+		return ret;
+	}
+
+	gpio_direction_output(udc->board.pullup_pin,
+			      udc->board.pullup_active_low);
+
+	return 0;
+}
+
+static void at91rm9200_udc_pullup(struct at91_udc *udc, int is_on)
+{
+	int active = !udc->board.pullup_active_low;
+
+	if (is_on)
+		gpio_set_value(udc->board.pullup_pin, active);
+	else
+		gpio_set_value(udc->board.pullup_pin, !active);
+}
+
+static const struct at91_udc_caps at91rm9200_udc_caps = {
+	.init = at91rm9200_udc_init,
+	.pullup = at91rm9200_udc_pullup,
+};
+
+static int at91sam9260_udc_init(struct at91_udc *udc)
+{
+	struct at91_ep *ep;
+	int i;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+
+		switch (i) {
+		case 0 ... 3:
+			ep->maxpacket = 64;
+			break;
+		case 4 ... 5:
+			ep->maxpacket = 512;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void at91sam9260_udc_pullup(struct at91_udc *udc, int is_on)
+{
+	u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
+
+	if (is_on)
+		txvc |= AT91_UDP_TXVC_PUON;
+	else
+		txvc &= ~AT91_UDP_TXVC_PUON;
+
+	at91_udp_write(udc, AT91_UDP_TXVC, txvc);
+}
+
+static const struct at91_udc_caps at91sam9260_udc_caps = {
+	.init = at91sam9260_udc_init,
+	.pullup = at91sam9260_udc_pullup,
+};
+
+static int at91sam9261_udc_init(struct at91_udc *udc)
+{
+	struct at91_ep *ep;
+	int i;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+
+		switch (i) {
+		case 0:
+			ep->maxpacket = 8;
+			break;
+		case 1 ... 3:
+			ep->maxpacket = 64;
+			break;
+		case 4 ... 5:
+			ep->maxpacket = 256;
+			break;
+		}
+	}
+
+	udc->matrix = syscon_regmap_lookup_by_phandle(udc->pdev->dev.of_node,
+						      "atmel,matrix");
+	return PTR_ERR_OR_ZERO(udc->matrix);
+}
+
+static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on)
+{
+	u32 usbpucr = 0;
+
+	if (is_on)
+		usbpucr = AT91_MATRIX_USBPUCR_PUON;
+
+	regmap_update_bits(udc->matrix, AT91SAM9261_MATRIX_USBPUCR,
+			   AT91_MATRIX_USBPUCR_PUON, usbpucr);
+}
+
+static const struct at91_udc_caps at91sam9261_udc_caps = {
+	.init = at91sam9261_udc_init,
+	.pullup = at91sam9261_udc_pullup,
+};
+
+static int at91sam9263_udc_init(struct at91_udc *udc)
+{
+	struct at91_ep *ep;
+	int i;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+
+		switch (i) {
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+			ep->maxpacket = 64;
+			break;
+		case 4:
+		case 5:
+			ep->maxpacket = 256;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static const struct at91_udc_caps at91sam9263_udc_caps = {
+	.init = at91sam9263_udc_init,
+	.pullup = at91sam9260_udc_pullup,
+};
+
+static const struct of_device_id at91_udc_dt_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-udc",
+		.data = &at91rm9200_udc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9260-udc",
+		.data = &at91sam9260_udc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9261-udc",
+		.data = &at91sam9261_udc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9263-udc",
+		.data = &at91sam9263_udc_caps,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_udc_dt_ids);
+
+static void at91udc_of_init(struct at91_udc *udc, struct device_node *np)
+{
+	struct at91_udc_data *board = &udc->board;
+	const struct of_device_id *match;
+	enum of_gpio_flags flags;
+	u32 val;
+
+	if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0)
+		board->vbus_polled = 1;
+
+	board->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
+						  &flags);
+	board->vbus_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+
+	board->pullup_pin = of_get_named_gpio_flags(np, "atmel,pullup-gpio", 0,
+						  &flags);
+
+	board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+
+	match = of_match_node(at91_udc_dt_ids, np);
+	if (match)
+		udc->caps = match->data;
+}
+
+static int at91udc_probe(struct platform_device *pdev)
+{
+	struct device	*dev = &pdev->dev;
+	struct at91_udc	*udc;
+	int		retval;
+	struct resource	*res;
+	struct at91_ep	*ep;
+	int		i;
+
+	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	/* init software state */
+	udc->gadget.dev.parent = dev;
+	at91udc_of_init(udc, pdev->dev.of_node);
+	udc->pdev = pdev;
+	udc->enabled = 0;
+	spin_lock_init(&udc->lock);
+
+	udc->gadget.ops = &at91_udc_ops;
+	udc->gadget.ep0 = &udc->ep[0].ep;
+	udc->gadget.name = driver_name;
+	udc->gadget.dev.init_name = "gadget";
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+		ep->ep.name = ep_info[i].name;
+		ep->ep.caps = ep_info[i].caps;
+		ep->ep.ops = &at91_ep_ops;
+		ep->udc = udc;
+		ep->int_mask = BIT(i);
+		if (i != 0 && i != 3)
+			ep->is_pingpong = 1;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->udp_baseaddr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(udc->udp_baseaddr))
+		return PTR_ERR(udc->udp_baseaddr);
+
+	if (udc->caps && udc->caps->init) {
+		retval = udc->caps->init(udc);
+		if (retval)
+			return retval;
+	}
+
+	udc_reinit(udc);
+
+	/* get interface and function clocks */
+	udc->iclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(udc->iclk))
+		return PTR_ERR(udc->iclk);
+
+	udc->fclk = devm_clk_get(dev, "hclk");
+	if (IS_ERR(udc->fclk))
+		return PTR_ERR(udc->fclk);
+
+	/* don't do anything until we have both gadget driver and VBUS */
+	clk_set_rate(udc->fclk, 48000000);
+	retval = clk_prepare(udc->fclk);
+	if (retval)
+		return retval;
+
+	retval = clk_prepare_enable(udc->iclk);
+	if (retval)
+		goto err_unprepare_fclk;
+
+	at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+	at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
+	/* Clear all pending interrupts - UDP may be used by bootloader. */
+	at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff);
+	clk_disable(udc->iclk);
+
+	/* request UDC and maybe VBUS irqs */
+	udc->udp_irq = platform_get_irq(pdev, 0);
+	retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0,
+				  driver_name, udc);
+	if (retval) {
+		DBG("request irq %d failed\n", udc->udp_irq);
+		goto err_unprepare_iclk;
+	}
+
+	if (gpio_is_valid(udc->board.vbus_pin)) {
+		retval = devm_gpio_request(dev, udc->board.vbus_pin,
+					   "udc_vbus");
+		if (retval) {
+			DBG("request vbus pin failed\n");
+			goto err_unprepare_iclk;
+		}
+
+		gpio_direction_input(udc->board.vbus_pin);
+
+		/*
+		 * Get the initial state of VBUS - we cannot expect
+		 * a pending interrupt.
+		 */
+		udc->vbus = gpio_get_value_cansleep(udc->board.vbus_pin) ^
+			udc->board.vbus_active_low;
+
+		if (udc->board.vbus_polled) {
+			INIT_WORK(&udc->vbus_timer_work, at91_vbus_timer_work);
+			timer_setup(&udc->vbus_timer, at91_vbus_timer, 0);
+			mod_timer(&udc->vbus_timer,
+				  jiffies + VBUS_POLL_TIMEOUT);
+		} else {
+			retval = devm_request_irq(dev,
+					gpio_to_irq(udc->board.vbus_pin),
+					at91_vbus_irq, 0, driver_name, udc);
+			if (retval) {
+				DBG("request vbus irq %d failed\n",
+				    udc->board.vbus_pin);
+				goto err_unprepare_iclk;
+			}
+		}
+	} else {
+		DBG("no VBUS detection, assuming always-on\n");
+		udc->vbus = 1;
+	}
+	retval = usb_add_gadget_udc(dev, &udc->gadget);
+	if (retval)
+		goto err_unprepare_iclk;
+	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;
+
+err_unprepare_iclk:
+	clk_unprepare(udc->iclk);
+err_unprepare_fclk:
+	clk_unprepare(udc->fclk);
+
+	DBG("%s probe failed, %d\n", driver_name, retval);
+
+	return retval;
+}
+
+static int at91udc_remove(struct platform_device *pdev)
+{
+	struct at91_udc *udc = platform_get_drvdata(pdev);
+	unsigned long	flags;
+
+	DBG("remove\n");
+
+	usb_del_gadget_udc(&udc->gadget);
+	if (udc->driver)
+		return -EBUSY;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	pullup(udc, 0);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	device_init_wakeup(&pdev->dev, 0);
+	remove_debug_file(udc);
+	clk_unprepare(udc->fclk);
+	clk_unprepare(udc->iclk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+	struct at91_udc *udc = platform_get_drvdata(pdev);
+	int		wake = udc->driver && device_may_wakeup(&pdev->dev);
+	unsigned long	flags;
+
+	/* Unless we can act normally to the host (letting it wake us up
+	 * whenever it has work for us) force disconnect.  Wakeup requires
+	 * PLLB for USB events (signaling for reset, wakeup, or incoming
+	 * tokens) and VBUS irqs (on systems which support them).
+	 */
+	if ((!udc->suspended && udc->addr)
+			|| !wake
+			|| at91_suspend_entering_slow_clock()) {
+		spin_lock_irqsave(&udc->lock, flags);
+		pullup(udc, 0);
+		wake = 0;
+		spin_unlock_irqrestore(&udc->lock, flags);
+	} else
+		enable_irq_wake(udc->udp_irq);
+
+	udc->active_suspend = wake;
+	if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled && wake)
+		enable_irq_wake(udc->board.vbus_pin);
+	return 0;
+}
+
+static int at91udc_resume(struct platform_device *pdev)
+{
+	struct at91_udc *udc = platform_get_drvdata(pdev);
+	unsigned long	flags;
+
+	if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled &&
+	    udc->active_suspend)
+		disable_irq_wake(udc->board.vbus_pin);
+
+	/* maybe reconnect to host; if so, clocks on */
+	if (udc->active_suspend)
+		disable_irq_wake(udc->udp_irq);
+	else {
+		spin_lock_irqsave(&udc->lock, flags);
+		pullup(udc, 1);
+		spin_unlock_irqrestore(&udc->lock, flags);
+	}
+	return 0;
+}
+#else
+#define	at91udc_suspend	NULL
+#define	at91udc_resume	NULL
+#endif
+
+static struct platform_driver at91_udc_driver = {
+	.remove		= at91udc_remove,
+	.shutdown	= at91udc_shutdown,
+	.suspend	= at91udc_suspend,
+	.resume		= at91udc_resume,
+	.driver		= {
+		.name	= (char *) driver_name,
+		.of_match_table	= at91_udc_dt_ids,
+	},
+};
+
+module_platform_driver_probe(at91_udc_driver, at91udc_probe);
+
+MODULE_DESCRIPTION("AT91 udc driver");
+MODULE_AUTHOR("Thomas Rathbone, David Brownell");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_udc");
diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
new file mode 100644
index 0000000..fd58c5b
--- /dev/null
+++ b/drivers/usb/gadget/udc/at91_udc.h
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004 by Thomas Rathbone, HP Labs
+ * Copyright (C) 2005 by Ivan Kokshaysky
+ * Copyright (C) 2006 by SAN People
+ */
+
+#ifndef AT91_UDC_H
+#define AT91_UDC_H
+
+/*
+ * USB Device Port (UDP) registers.
+ * Based on AT91RM9200 datasheet revision E.
+ */
+
+#define AT91_UDP_FRM_NUM	0x00		/* Frame Number Register */
+#define     AT91_UDP_NUM	(0x7ff <<  0)	/* Frame Number */
+#define     AT91_UDP_FRM_ERR	(1     << 16)	/* Frame Error */
+#define     AT91_UDP_FRM_OK	(1     << 17)	/* Frame OK */
+
+#define AT91_UDP_GLB_STAT	0x04		/* Global State Register */
+#define     AT91_UDP_FADDEN	(1 <<  0)	/* Function Address Enable */
+#define     AT91_UDP_CONFG	(1 <<  1)	/* Configured */
+#define     AT91_UDP_ESR	(1 <<  2)	/* Enable Send Resume */
+#define     AT91_UDP_RSMINPR	(1 <<  3)	/* Resume has been sent */
+#define     AT91_UDP_RMWUPE	(1 <<  4)	/* Remote Wake Up Enable */
+
+#define AT91_UDP_FADDR		0x08		/* Function Address Register */
+#define     AT91_UDP_FADD	(0x7f << 0)	/* Function Address Value */
+#define     AT91_UDP_FEN	(1    << 8)	/* Function Enable */
+
+#define AT91_UDP_IER		0x10		/* Interrupt Enable Register */
+#define AT91_UDP_IDR		0x14		/* Interrupt Disable Register */
+#define AT91_UDP_IMR		0x18		/* Interrupt Mask Register */
+
+#define AT91_UDP_ISR		0x1c		/* Interrupt Status Register */
+#define     AT91_UDP_EP(n)	(1 << (n))	/* Endpoint Interrupt Status */
+#define     AT91_UDP_RXSUSP	(1 <<  8) 	/* USB Suspend Interrupt Status */
+#define     AT91_UDP_RXRSM	(1 <<  9)	/* USB Resume Interrupt Status */
+#define     AT91_UDP_EXTRSM	(1 << 10)	/* External Resume Interrupt Status [AT91RM9200 only] */
+#define     AT91_UDP_SOFINT	(1 << 11)	/* Start of Frame Interrupt Status */
+#define     AT91_UDP_ENDBUSRES	(1 << 12)	/* End of Bus Reset Interrupt Status */
+#define     AT91_UDP_WAKEUP	(1 << 13)	/* USB Wakeup Interrupt Status [AT91RM9200 only] */
+
+#define AT91_UDP_ICR		0x20		/* Interrupt Clear Register */
+#define AT91_UDP_RST_EP		0x28		/* Reset Endpoint Register */
+
+#define AT91_UDP_CSR(n)		(0x30+((n)*4))	/* Endpoint Control/Status Registers 0-7 */
+#define     AT91_UDP_TXCOMP	(1 <<  0)	/* Generates IN packet with data previously written in DPR */
+#define     AT91_UDP_RX_DATA_BK0 (1 <<  1)	/* Receive Data Bank 0 */
+#define     AT91_UDP_RXSETUP	(1 <<  2)	/* Send STALL to the host */
+#define     AT91_UDP_STALLSENT	(1 <<  3)	/* Stall Sent / Isochronous error (Isochronous endpoints) */
+#define     AT91_UDP_TXPKTRDY	(1 <<  4)	/* Transmit Packet Ready */
+#define     AT91_UDP_FORCESTALL	(1 <<  5)	/* Force Stall */
+#define     AT91_UDP_RX_DATA_BK1 (1 <<  6)	/* Receive Data Bank 1 */
+#define     AT91_UDP_DIR	(1 <<  7)	/* Transfer Direction */
+#define     AT91_UDP_EPTYPE	(7 <<  8)	/* Endpoint Type */
+#define		AT91_UDP_EPTYPE_CTRL		(0 <<  8)
+#define		AT91_UDP_EPTYPE_ISO_OUT		(1 <<  8)
+#define		AT91_UDP_EPTYPE_BULK_OUT	(2 <<  8)
+#define		AT91_UDP_EPTYPE_INT_OUT		(3 <<  8)
+#define		AT91_UDP_EPTYPE_ISO_IN		(5 <<  8)
+#define		AT91_UDP_EPTYPE_BULK_IN		(6 <<  8)
+#define		AT91_UDP_EPTYPE_INT_IN		(7 <<  8)
+#define     AT91_UDP_DTGLE	(1 << 11)	/* Data Toggle */
+#define     AT91_UDP_EPEDS	(1 << 15)	/* Endpoint Enable/Disable */
+#define     AT91_UDP_RXBYTECNT	(0x7ff << 16)	/* Number of bytes in FIFO */
+
+#define AT91_UDP_FDR(n)		(0x50+((n)*4))	/* Endpoint FIFO Data Registers 0-7 */
+
+#define AT91_UDP_TXVC		0x74		/* Transceiver Control Register */
+#define     AT91_UDP_TXVC_TXVDIS (1 << 8)	/* Transceiver Disable */
+#define     AT91_UDP_TXVC_PUON   (1 << 9)	/* PullUp On [AT91SAM9260 only] */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * controller driver data structures
+ */
+
+#define	NUM_ENDPOINTS	6
+
+/*
+ * hardware won't disable bus reset, or resume while the controller
+ * is suspended ... watching suspend helps keep the logic symmetric.
+ */
+#define	MINIMUS_INTERRUPTUS \
+	(AT91_UDP_ENDBUSRES | AT91_UDP_RXRSM | AT91_UDP_RXSUSP)
+
+struct at91_ep {
+	struct usb_ep			ep;
+	struct list_head		queue;
+	struct at91_udc			*udc;
+	void __iomem			*creg;
+
+	unsigned			maxpacket:16;
+	u8				int_mask;
+	unsigned			is_pingpong:1;
+
+	unsigned			stopped:1;
+	unsigned			is_in:1;
+	unsigned			is_iso:1;
+	unsigned			fifo_bank:1;
+};
+
+struct at91_udc_caps {
+	int (*init)(struct at91_udc *udc);
+	void (*pullup)(struct at91_udc *udc, int is_on);
+};
+
+struct at91_udc_data {
+	int	vbus_pin;		/* high == host powering us */
+	u8	vbus_active_low;	/* vbus polarity */
+	u8	vbus_polled;		/* Use polling, not interrupt */
+	int	pullup_pin;		/* active == D+ pulled up */
+	u8	pullup_active_low;	/* true == pullup_pin is active low */
+};
+
+/*
+ * driver is non-SMP, and just blocks IRQs whenever it needs
+ * access protection for chip registers or driver state
+ */
+struct at91_udc {
+	struct usb_gadget		gadget;
+	struct at91_ep			ep[NUM_ENDPOINTS];
+	struct usb_gadget_driver	*driver;
+	const struct at91_udc_caps	*caps;
+	unsigned			vbus:1;
+	unsigned			enabled:1;
+	unsigned			clocked:1;
+	unsigned			suspended:1;
+	unsigned			req_pending:1;
+	unsigned			wait_for_addr_ack:1;
+	unsigned			wait_for_config_ack:1;
+	unsigned			active_suspend:1;
+	u8				addr;
+	struct at91_udc_data		board;
+	struct clk			*iclk, *fclk;
+	struct platform_device		*pdev;
+	struct proc_dir_entry		*pde;
+	void __iomem			*udp_baseaddr;
+	int				udp_irq;
+	spinlock_t			lock;
+	struct timer_list		vbus_timer;
+	struct work_struct		vbus_timer_work;
+	struct regmap			*matrix;
+};
+
+static inline struct at91_udc *to_udc(struct usb_gadget *g)
+{
+	return container_of(g, struct at91_udc, gadget);
+}
+
+struct at91_request {
+	struct usb_request		req;
+	struct list_head		queue;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef VERBOSE_DEBUG
+#    define VDBG		DBG
+#else
+#    define VDBG(stuff...)	do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+#    define PACKET		VDBG
+#else
+#    define PACKET(stuff...)	do{}while(0)
+#endif
+
+#define ERR(stuff...)		pr_err("udc: " stuff)
+#define WARNING(stuff...)	pr_warn("udc: " stuff)
+#define INFO(stuff...)		pr_info("udc: " stuff)
+#define DBG(stuff...)		pr_debug("udc: " stuff)
+
+#endif
+
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
new file mode 100644
index 0000000..8f267be
--- /dev/null
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -0,0 +1,2341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2005-2007 Atmel Corporation
+ */
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/ctype.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+
+#include "atmel_usba_udc.h"
+#define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \
+			   | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+static int queue_dbg_open(struct inode *inode, struct file *file)
+{
+	struct usba_ep *ep = inode->i_private;
+	struct usba_request *req, *req_copy;
+	struct list_head *queue_data;
+
+	queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL);
+	if (!queue_data)
+		return -ENOMEM;
+	INIT_LIST_HEAD(queue_data);
+
+	spin_lock_irq(&ep->udc->lock);
+	list_for_each_entry(req, &ep->queue, queue) {
+		req_copy = kmemdup(req, sizeof(*req_copy), GFP_ATOMIC);
+		if (!req_copy)
+			goto fail;
+		list_add_tail(&req_copy->queue, queue_data);
+	}
+	spin_unlock_irq(&ep->udc->lock);
+
+	file->private_data = queue_data;
+	return 0;
+
+fail:
+	spin_unlock_irq(&ep->udc->lock);
+	list_for_each_entry_safe(req, req_copy, queue_data, queue) {
+		list_del(&req->queue);
+		kfree(req);
+	}
+	kfree(queue_data);
+	return -ENOMEM;
+}
+
+/*
+ * bbbbbbbb llllllll IZS sssss nnnn FDL\n\0
+ *
+ * b: buffer address
+ * l: buffer length
+ * I/i: interrupt/no interrupt
+ * Z/z: zero/no zero
+ * S/s: short ok/short not ok
+ * s: status
+ * n: nr_packets
+ * F/f: submitted/not submitted to FIFO
+ * D/d: using/not using DMA
+ * L/l: last transaction/not last transaction
+ */
+static ssize_t queue_dbg_read(struct file *file, char __user *buf,
+		size_t nbytes, loff_t *ppos)
+{
+	struct list_head *queue = file->private_data;
+	struct usba_request *req, *tmp_req;
+	size_t len, remaining, actual = 0;
+	char tmpbuf[38];
+
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+
+	inode_lock(file_inode(file));
+	list_for_each_entry_safe(req, tmp_req, queue, queue) {
+		len = snprintf(tmpbuf, sizeof(tmpbuf),
+				"%8p %08x %c%c%c %5d %c%c%c\n",
+				req->req.buf, req->req.length,
+				req->req.no_interrupt ? 'i' : 'I',
+				req->req.zero ? 'Z' : 'z',
+				req->req.short_not_ok ? 's' : 'S',
+				req->req.status,
+				req->submitted ? 'F' : 'f',
+				req->using_dma ? 'D' : 'd',
+				req->last_transaction ? 'L' : 'l');
+		len = min(len, sizeof(tmpbuf));
+		if (len > nbytes)
+			break;
+
+		list_del(&req->queue);
+		kfree(req);
+
+		remaining = __copy_to_user(buf, tmpbuf, len);
+		actual += len - remaining;
+		if (remaining)
+			break;
+
+		nbytes -= len;
+		buf += len;
+	}
+	inode_unlock(file_inode(file));
+
+	return actual;
+}
+
+static int queue_dbg_release(struct inode *inode, struct file *file)
+{
+	struct list_head *queue_data = file->private_data;
+	struct usba_request *req, *tmp_req;
+
+	list_for_each_entry_safe(req, tmp_req, queue_data, queue) {
+		list_del(&req->queue);
+		kfree(req);
+	}
+	kfree(queue_data);
+	return 0;
+}
+
+static int regs_dbg_open(struct inode *inode, struct file *file)
+{
+	struct usba_udc *udc;
+	unsigned int i;
+	u32 *data;
+	int ret = -ENOMEM;
+
+	inode_lock(inode);
+	udc = inode->i_private;
+	data = kmalloc(inode->i_size, GFP_KERNEL);
+	if (!data)
+		goto out;
+
+	spin_lock_irq(&udc->lock);
+	for (i = 0; i < inode->i_size / 4; i++)
+		data[i] = readl_relaxed(udc->regs + i * 4);
+	spin_unlock_irq(&udc->lock);
+
+	file->private_data = data;
+	ret = 0;
+
+out:
+	inode_unlock(inode);
+
+	return ret;
+}
+
+static ssize_t regs_dbg_read(struct file *file, char __user *buf,
+		size_t nbytes, loff_t *ppos)
+{
+	struct inode *inode = file_inode(file);
+	int ret;
+
+	inode_lock(inode);
+	ret = simple_read_from_buffer(buf, nbytes, ppos,
+			file->private_data,
+			file_inode(file)->i_size);
+	inode_unlock(inode);
+
+	return ret;
+}
+
+static int regs_dbg_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
+const struct file_operations queue_dbg_fops = {
+	.owner		= THIS_MODULE,
+	.open		= queue_dbg_open,
+	.llseek		= no_llseek,
+	.read		= queue_dbg_read,
+	.release	= queue_dbg_release,
+};
+
+const struct file_operations regs_dbg_fops = {
+	.owner		= THIS_MODULE,
+	.open		= regs_dbg_open,
+	.llseek		= generic_file_llseek,
+	.read		= regs_dbg_read,
+	.release	= regs_dbg_release,
+};
+
+static void usba_ep_init_debugfs(struct usba_udc *udc,
+		struct usba_ep *ep)
+{
+	struct dentry *ep_root;
+
+	ep_root = debugfs_create_dir(ep->ep.name, udc->debugfs_root);
+	ep->debugfs_dir = ep_root;
+
+	debugfs_create_file("queue", 0400, ep_root, ep, &queue_dbg_fops);
+	if (ep->can_dma)
+		debugfs_create_u32("dma_status", 0400, ep_root,
+				   &ep->last_dma_status);
+	if (ep_is_control(ep))
+		debugfs_create_u32("state", 0400, ep_root, &ep->state);
+}
+
+static void usba_ep_cleanup_debugfs(struct usba_ep *ep)
+{
+	debugfs_remove_recursive(ep->debugfs_dir);
+}
+
+static void usba_init_debugfs(struct usba_udc *udc)
+{
+	struct dentry *root;
+	struct resource *regs_resource;
+
+	root = debugfs_create_dir(udc->gadget.name, NULL);
+	udc->debugfs_root = root;
+
+	regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,
+				CTRL_IOMEM_ID);
+
+	if (regs_resource) {
+		debugfs_create_file_size("regs", 0400, root, udc,
+					 &regs_dbg_fops,
+					 resource_size(regs_resource));
+	}
+
+	usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0));
+}
+
+static void usba_cleanup_debugfs(struct usba_udc *udc)
+{
+	usba_ep_cleanup_debugfs(to_usba_ep(udc->gadget.ep0));
+	debugfs_remove_recursive(udc->debugfs_root);
+}
+#else
+static inline void usba_ep_init_debugfs(struct usba_udc *udc,
+					 struct usba_ep *ep)
+{
+
+}
+
+static inline void usba_ep_cleanup_debugfs(struct usba_ep *ep)
+{
+
+}
+
+static inline void usba_init_debugfs(struct usba_udc *udc)
+{
+
+}
+
+static inline void usba_cleanup_debugfs(struct usba_udc *udc)
+{
+
+}
+#endif
+
+static ushort fifo_mode;
+
+module_param(fifo_mode, ushort, 0x0);
+MODULE_PARM_DESC(fifo_mode, "Endpoint configuration mode");
+
+/* mode 0 - uses autoconfig */
+
+/* mode 1 - fits in 8KB, generic max fifo configuration */
+static struct usba_fifo_cfg mode_1_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,	.nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,	.nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 1024,	.nr_banks = 1, },
+{ .hw_ep_num = 3, .fifo_size = 1024,	.nr_banks = 1, },
+{ .hw_ep_num = 4, .fifo_size = 1024,	.nr_banks = 1, },
+{ .hw_ep_num = 5, .fifo_size = 1024,	.nr_banks = 1, },
+{ .hw_ep_num = 6, .fifo_size = 1024,	.nr_banks = 1, },
+};
+
+/* mode 2 - fits in 8KB, performance max fifo configuration */
+static struct usba_fifo_cfg mode_2_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,	.nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,	.nr_banks = 3, },
+{ .hw_ep_num = 2, .fifo_size = 1024,	.nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 1024,	.nr_banks = 2, },
+};
+
+/* mode 3 - fits in 8KB, mixed fifo configuration */
+static struct usba_fifo_cfg mode_3_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,	.nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,	.nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 512,	.nr_banks = 2, },
+};
+
+/* mode 4 - fits in 8KB, custom fifo configuration */
+static struct usba_fifo_cfg mode_4_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,	.nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 8,	.nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512,	.nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 16,	.nr_banks = 2, },
+{ .hw_ep_num = 7, .fifo_size = 8,	.nr_banks = 2, },
+{ .hw_ep_num = 8, .fifo_size = 8,	.nr_banks = 2, },
+};
+/* Add additional configurations here */
+
+static int usba_config_fifo_table(struct usba_udc *udc)
+{
+	int n;
+
+	switch (fifo_mode) {
+	default:
+		fifo_mode = 0;
+	case 0:
+		udc->fifo_cfg = NULL;
+		n = 0;
+		break;
+	case 1:
+		udc->fifo_cfg = mode_1_cfg;
+		n = ARRAY_SIZE(mode_1_cfg);
+		break;
+	case 2:
+		udc->fifo_cfg = mode_2_cfg;
+		n = ARRAY_SIZE(mode_2_cfg);
+		break;
+	case 3:
+		udc->fifo_cfg = mode_3_cfg;
+		n = ARRAY_SIZE(mode_3_cfg);
+		break;
+	case 4:
+		udc->fifo_cfg = mode_4_cfg;
+		n = ARRAY_SIZE(mode_4_cfg);
+		break;
+	}
+	DBG(DBG_HW, "Setup fifo_mode %d\n", fifo_mode);
+
+	return n;
+}
+
+static inline u32 usba_int_enb_get(struct usba_udc *udc)
+{
+	return udc->int_enb_cache;
+}
+
+static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
+{
+	usba_writel(udc, INT_ENB, val);
+	udc->int_enb_cache = val;
+}
+
+static int vbus_is_present(struct usba_udc *udc)
+{
+	if (udc->vbus_pin)
+		return gpiod_get_value(udc->vbus_pin);
+
+	/* No Vbus detection: Assume always present */
+	return 1;
+}
+
+static void toggle_bias(struct usba_udc *udc, int is_on)
+{
+	if (udc->errata && udc->errata->toggle_bias)
+		udc->errata->toggle_bias(udc, is_on);
+}
+
+static void generate_bias_pulse(struct usba_udc *udc)
+{
+	if (!udc->bias_pulse_needed)
+		return;
+
+	if (udc->errata && udc->errata->pulse_bias)
+		udc->errata->pulse_bias(udc);
+
+	udc->bias_pulse_needed = false;
+}
+
+static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
+{
+	unsigned int transaction_len;
+
+	transaction_len = req->req.length - req->req.actual;
+	req->last_transaction = 1;
+	if (transaction_len > ep->ep.maxpacket) {
+		transaction_len = ep->ep.maxpacket;
+		req->last_transaction = 0;
+	} else if (transaction_len == ep->ep.maxpacket && req->req.zero)
+		req->last_transaction = 0;
+
+	DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n",
+		ep->ep.name, req, transaction_len,
+		req->last_transaction ? ", done" : "");
+
+	memcpy_toio(ep->fifo, req->req.buf + req->req.actual, transaction_len);
+	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+	req->req.actual += transaction_len;
+}
+
+static void submit_request(struct usba_ep *ep, struct usba_request *req)
+{
+	DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d)\n",
+		ep->ep.name, req, req->req.length);
+
+	req->req.actual = 0;
+	req->submitted = 1;
+
+	if (req->using_dma) {
+		if (req->req.length == 0) {
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+			return;
+		}
+
+		if (req->req.zero)
+			usba_ep_writel(ep, CTL_ENB, USBA_SHORT_PACKET);
+		else
+			usba_ep_writel(ep, CTL_DIS, USBA_SHORT_PACKET);
+
+		usba_dma_writel(ep, ADDRESS, req->req.dma);
+		usba_dma_writel(ep, CONTROL, req->ctrl);
+	} else {
+		next_fifo_transaction(ep, req);
+		if (req->last_transaction) {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+		} else {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+		}
+	}
+}
+
+static void submit_next_request(struct usba_ep *ep)
+{
+	struct usba_request *req;
+
+	if (list_empty(&ep->queue)) {
+		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);
+		return;
+	}
+
+	req = list_entry(ep->queue.next, struct usba_request, queue);
+	if (!req->submitted)
+		submit_request(ep, req);
+}
+
+static void send_status(struct usba_udc *udc, struct usba_ep *ep)
+{
+	ep->state = STATUS_STAGE_IN;
+	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+	usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+}
+
+static void receive_data(struct usba_ep *ep)
+{
+	struct usba_udc *udc = ep->udc;
+	struct usba_request *req;
+	unsigned long status;
+	unsigned int bytecount, nr_busy;
+	int is_complete = 0;
+
+	status = usba_ep_readl(ep, STA);
+	nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+	DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy);
+
+	while (nr_busy > 0) {
+		if (list_empty(&ep->queue)) {
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			break;
+		}
+		req = list_entry(ep->queue.next,
+				 struct usba_request, queue);
+
+		bytecount = USBA_BFEXT(BYTE_COUNT, status);
+
+		if (status & (1 << 31))
+			is_complete = 1;
+		if (req->req.actual + bytecount >= req->req.length) {
+			is_complete = 1;
+			bytecount = req->req.length - req->req.actual;
+		}
+
+		memcpy_fromio(req->req.buf + req->req.actual,
+				ep->fifo, bytecount);
+		req->req.actual += bytecount;
+
+		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+
+		if (is_complete) {
+			DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name);
+			req->req.status = 0;
+			list_del_init(&req->queue);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			spin_unlock(&udc->lock);
+			usb_gadget_giveback_request(&ep->ep, &req->req);
+			spin_lock(&udc->lock);
+		}
+
+		status = usba_ep_readl(ep, STA);
+		nr_busy = USBA_BFEXT(BUSY_BANKS, status);
+
+		if (is_complete && ep_is_control(ep)) {
+			send_status(udc, ep);
+			break;
+		}
+	}
+}
+
+static void
+request_complete(struct usba_ep *ep, struct usba_request *req, int status)
+{
+	struct usba_udc *udc = ep->udc;
+
+	WARN_ON(!list_empty(&req->queue));
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+
+	if (req->using_dma)
+		usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in);
+
+	DBG(DBG_GADGET | DBG_REQ,
+		"%s: req %p complete: status %d, actual %u\n",
+		ep->ep.name, req, req->req.status, req->req.actual);
+
+	spin_unlock(&udc->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&udc->lock);
+}
+
+static void
+request_complete_list(struct usba_ep *ep, struct list_head *list, int status)
+{
+	struct usba_request *req, *tmp_req;
+
+	list_for_each_entry_safe(req, tmp_req, list, queue) {
+		list_del_init(&req->queue);
+		request_complete(ep, req, status);
+	}
+}
+
+static int
+usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	unsigned long flags, maxpacket;
+	unsigned int nr_trans;
+
+	DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
+
+	maxpacket = usb_endpoint_maxp(desc);
+
+	if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
+			|| ep->index == 0
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| maxpacket == 0
+			|| maxpacket > ep->fifo_size) {
+		DBG(DBG_ERR, "ep_enable: Invalid argument");
+		return -EINVAL;
+	}
+
+	ep->is_isoc = 0;
+	ep->is_in = 0;
+
+	DBG(DBG_ERR, "%s: EPT_CFG = 0x%lx (maxpacket = %lu)\n",
+			ep->ep.name, ep->ept_cfg, maxpacket);
+
+	if (usb_endpoint_dir_in(desc)) {
+		ep->is_in = 1;
+		ep->ept_cfg |= USBA_EPT_DIR_IN;
+	}
+
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (!ep->can_isoc) {
+			DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n",
+					ep->ep.name);
+			return -EINVAL;
+		}
+
+		/*
+		 * Bits 11:12 specify number of _additional_
+		 * transactions per microframe.
+		 */
+		nr_trans = usb_endpoint_maxp_mult(desc);
+		if (nr_trans > 3)
+			return -EINVAL;
+
+		ep->is_isoc = 1;
+		ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+		ep->ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
+
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
+		break;
+	}
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	ep->ep.desc = desc;
+	ep->ep.maxpacket = maxpacket;
+
+	usba_ep_writel(ep, CFG, ep->ept_cfg);
+	usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+
+	if (ep->can_dma) {
+		u32 ctrl;
+
+		usba_int_enb_set(udc, usba_int_enb_get(udc) |
+				      USBA_BF(EPT_INT, 1 << ep->index) |
+				      USBA_BF(DMA_INT, 1 << ep->index));
+		ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
+		usba_ep_writel(ep, CTL_ENB, ctrl);
+	} else {
+		usba_int_enb_set(udc, usba_int_enb_get(udc) |
+				      USBA_BF(EPT_INT, 1 << ep->index));
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
+			(unsigned long)usba_ep_readl(ep, CFG));
+	DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
+			(unsigned long)usba_int_enb_get(udc));
+
+	return 0;
+}
+
+static int usba_ep_disable(struct usb_ep *_ep)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	LIST_HEAD(req_list);
+	unsigned long flags;
+
+	DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (!ep->ep.desc) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		/* REVISIT because this driver disables endpoints in
+		 * reset_all_endpoints() before calling disconnect(),
+		 * most gadget drivers would trigger this non-error ...
+		 */
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN)
+			DBG(DBG_ERR, "ep_disable: %s not enabled\n",
+					ep->ep.name);
+		return -EINVAL;
+	}
+	ep->ep.desc = NULL;
+
+	list_splice_init(&ep->queue, &req_list);
+	if (ep->can_dma) {
+		usba_dma_writel(ep, CONTROL, 0);
+		usba_dma_writel(ep, ADDRESS, 0);
+		usba_dma_readl(ep, STATUS);
+	}
+	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
+	usba_int_enb_set(udc, usba_int_enb_get(udc) &
+			      ~USBA_BF(EPT_INT, 1 << ep->index));
+
+	request_complete_list(ep, &req_list, -ESHUTDOWN);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static struct usb_request *
+usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct usba_request *req;
+
+	DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags);
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void
+usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct usba_request *req = to_usba_req(_req);
+
+	DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req);
+
+	kfree(req);
+}
+
+static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
+		struct usba_request *req, gfp_t gfp_flags)
+{
+	unsigned long flags;
+	int ret;
+
+	DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n",
+		ep->ep.name, req->req.length, &req->req.dma,
+		req->req.zero ? 'Z' : 'z',
+		req->req.short_not_ok ? 'S' : 's',
+		req->req.no_interrupt ? 'I' : 'i');
+
+	if (req->req.length > 0x10000) {
+		/* Lengths from 0 to 65536 (inclusive) are supported */
+		DBG(DBG_ERR, "invalid request length %u\n", req->req.length);
+		return -EINVAL;
+	}
+
+	ret = usb_gadget_map_request(&udc->gadget, &req->req, ep->is_in);
+	if (ret)
+		return ret;
+
+	req->using_dma = 1;
+	req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length)
+			| USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE
+			| USBA_DMA_END_BUF_EN;
+
+	if (!ep->is_in)
+		req->ctrl |= USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE;
+
+	/*
+	 * Add this request to the queue and submit for DMA if
+	 * possible. Check if we're still alive first -- we may have
+	 * received a reset since last time we checked.
+	 */
+	ret = -ESHUTDOWN;
+	spin_lock_irqsave(&udc->lock, flags);
+	if (ep->ep.desc) {
+		if (list_empty(&ep->queue))
+			submit_request(ep, req);
+
+		list_add_tail(&req->queue, &ep->queue);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+static int
+usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct usba_request *req = to_usba_req(_req);
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	unsigned long flags;
+	int ret;
+
+	DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n",
+			ep->ep.name, req, _req->length);
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN ||
+	    !ep->ep.desc)
+		return -ESHUTDOWN;
+
+	req->submitted = 0;
+	req->using_dma = 0;
+	req->last_transaction = 0;
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (ep->can_dma)
+		return queue_dma(udc, ep, req, gfp_flags);
+
+	/* May have received a reset since last time we checked */
+	ret = -ESHUTDOWN;
+	spin_lock_irqsave(&udc->lock, flags);
+	if (ep->ep.desc) {
+		list_add_tail(&req->queue, &ep->queue);
+
+		if ((!ep_is_control(ep) && ep->is_in) ||
+			(ep_is_control(ep)
+				&& (ep->state == DATA_STAGE_IN
+					|| ep->state == STATUS_STAGE_IN)))
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);
+		else
+			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+static void
+usba_update_req(struct usba_ep *ep, struct usba_request *req, u32 status)
+{
+	req->req.actual = req->req.length - USBA_BFEXT(DMA_BUF_LEN, status);
+}
+
+static int stop_dma(struct usba_ep *ep, u32 *pstatus)
+{
+	unsigned int timeout;
+	u32 status;
+
+	/*
+	 * Stop the DMA controller. When writing both CH_EN
+	 * and LINK to 0, the other bits are not affected.
+	 */
+	usba_dma_writel(ep, CONTROL, 0);
+
+	/* Wait for the FIFO to empty */
+	for (timeout = 40; timeout; --timeout) {
+		status = usba_dma_readl(ep, STATUS);
+		if (!(status & USBA_DMA_CH_EN))
+			break;
+		udelay(1);
+	}
+
+	if (pstatus)
+		*pstatus = status;
+
+	if (timeout == 0) {
+		dev_err(&ep->udc->pdev->dev,
+			"%s: timed out waiting for DMA FIFO to empty\n",
+			ep->ep.name);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	struct usba_request *req;
+	unsigned long flags;
+	u32 status;
+
+	DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n",
+			ep->ep.name, req);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+
+	if (req->using_dma) {
+		/*
+		 * If this request is currently being transferred,
+		 * stop the DMA controller and reset the FIFO.
+		 */
+		if (ep->queue.next == &req->queue) {
+			status = usba_dma_readl(ep, STATUS);
+			if (status & USBA_DMA_CH_EN)
+				stop_dma(ep, &status);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+			ep->last_dma_status = status;
+#endif
+
+			usba_writel(udc, EPT_RST, 1 << ep->index);
+
+			usba_update_req(ep, req, status);
+		}
+	}
+
+	/*
+	 * Errors should stop the queue from advancing until the
+	 * completion function returns.
+	 */
+	list_del_init(&req->queue);
+
+	request_complete(ep, req, -ECONNRESET);
+
+	/* Process the next request if any */
+	submit_next_request(ep);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int usba_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+	unsigned long flags;
+	int ret = 0;
+
+	DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name,
+			value ? "set" : "clear");
+
+	if (!ep->ep.desc) {
+		DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n",
+				ep->ep.name);
+		return -ENODEV;
+	}
+	if (ep->is_isoc) {
+		DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n",
+				ep->ep.name);
+		return -ENOTTY;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/*
+	 * We can't halt IN endpoints while there are still data to be
+	 * transferred
+	 */
+	if (!list_empty(&ep->queue)
+			|| ((value && ep->is_in && (usba_ep_readl(ep, STA)
+					& USBA_BF(BUSY_BANKS, -1L))))) {
+		ret = -EAGAIN;
+	} else {
+		if (value)
+			usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+		else
+			usba_ep_writel(ep, CLR_STA,
+					USBA_FORCE_STALL | USBA_TOGGLE_CLR);
+		usba_ep_readl(ep, STA);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+static int usba_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+
+	return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+}
+
+static void usba_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct usba_ep *ep = to_usba_ep(_ep);
+	struct usba_udc *udc = ep->udc;
+
+	usba_writel(udc, EPT_RST, 1 << ep->index);
+}
+
+static const struct usb_ep_ops usba_ep_ops = {
+	.enable		= usba_ep_enable,
+	.disable	= usba_ep_disable,
+	.alloc_request	= usba_ep_alloc_request,
+	.free_request	= usba_ep_free_request,
+	.queue		= usba_ep_queue,
+	.dequeue	= usba_ep_dequeue,
+	.set_halt	= usba_ep_set_halt,
+	.fifo_status	= usba_ep_fifo_status,
+	.fifo_flush	= usba_ep_fifo_flush,
+};
+
+static int usba_udc_get_frame(struct usb_gadget *gadget)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+
+	return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM));
+}
+
+static int usba_udc_wakeup(struct usb_gadget *gadget)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+	unsigned long flags;
+	u32 ctrl;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+		ctrl = usba_readl(udc, CTRL);
+		usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP);
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+static int
+usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+	struct usba_udc *udc = to_usba_udc(gadget);
+	unsigned long flags;
+
+	gadget->is_selfpowered = (is_selfpowered != 0);
+	spin_lock_irqsave(&udc->lock, flags);
+	if (is_selfpowered)
+		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int atmel_usba_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver);
+static int atmel_usba_stop(struct usb_gadget *gadget);
+
+static struct usb_ep *atmel_usba_match_ep(struct usb_gadget *gadget,
+				struct usb_endpoint_descriptor	*desc,
+				struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+	struct usb_ep	*_ep;
+	struct usba_ep *ep;
+
+	/* Look at endpoints until an unclaimed one looks usable */
+	list_for_each_entry(_ep, &gadget->ep_list, ep_list) {
+		if (usb_gadget_ep_match_desc(gadget, _ep, desc, ep_comp))
+			goto found_ep;
+	}
+	/* Fail */
+	return NULL;
+
+found_ep:
+
+	if (fifo_mode == 0) {
+		/* Optimize hw fifo size based on ep type and other info */
+		ep = to_usba_ep(_ep);
+
+		switch (usb_endpoint_type(desc)) {
+		case USB_ENDPOINT_XFER_CONTROL:
+			break;
+
+		case USB_ENDPOINT_XFER_ISOC:
+			ep->fifo_size = 1024;
+			ep->nr_banks = 2;
+			break;
+
+		case USB_ENDPOINT_XFER_BULK:
+			ep->fifo_size = 512;
+			ep->nr_banks = 1;
+			break;
+
+		case USB_ENDPOINT_XFER_INT:
+			if (desc->wMaxPacketSize == 0)
+				ep->fifo_size =
+				    roundup_pow_of_two(_ep->maxpacket_limit);
+			else
+				ep->fifo_size =
+				    roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
+			ep->nr_banks = 1;
+			break;
+		}
+
+		/* It might be a little bit late to set this */
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
+
+		/* Generate ept_cfg basd on FIFO size and number of banks */
+		if (ep->fifo_size  <= 8)
+			ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+		else
+			/* LSB is bit 1, not 0 */
+			ep->ept_cfg =
+				USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+		ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+
+		ep->udc->configured_ep++;
+	}
+
+	return _ep;
+}
+
+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,
+	.udc_start		= atmel_usba_start,
+	.udc_stop		= atmel_usba_stop,
+	.match_ep		= atmel_usba_match_ep,
+};
+
+static struct usb_endpoint_descriptor usba_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 0,
+	.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize = cpu_to_le16(64),
+	/* FIXME: I have no idea what to put here */
+	.bInterval = 1,
+};
+
+static struct usb_gadget usba_gadget_template = {
+	.ops		= &usba_udc_ops,
+	.max_speed	= USB_SPEED_HIGH,
+	.name		= "atmel_usba_udc",
+};
+
+/*
+ * Called with interrupts disabled and udc->lock held.
+ */
+static void reset_all_endpoints(struct usba_udc *udc)
+{
+	struct usba_ep *ep;
+	struct usba_request *req, *tmp_req;
+
+	usba_writel(udc, EPT_RST, ~0UL);
+
+	ep = to_usba_ep(udc->gadget.ep0);
+	list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) {
+		list_del_init(&req->queue);
+		request_complete(ep, req, -ECONNRESET);
+	}
+}
+
+static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex)
+{
+	struct usba_ep *ep;
+
+	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+		return to_usba_ep(udc->gadget.ep0);
+
+	list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
+		u8 bEndpointAddress;
+
+		if (!ep->ep.desc)
+			continue;
+		bEndpointAddress = ep->ep.desc->bEndpointAddress;
+		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+		if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+				== (wIndex & USB_ENDPOINT_NUMBER_MASK))
+			return ep;
+	}
+
+	return NULL;
+}
+
+/* Called with interrupts disabled and udc->lock held */
+static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep)
+{
+	usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);
+	ep->state = WAIT_FOR_SETUP;
+}
+
+static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep)
+{
+	if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL)
+		return 1;
+	return 0;
+}
+
+static inline void set_address(struct usba_udc *udc, unsigned int addr)
+{
+	u32 regval;
+
+	DBG(DBG_BUS, "setting address %u...\n", addr);
+	regval = usba_readl(udc, CTRL);
+	regval = USBA_BFINS(DEV_ADDR, addr, regval);
+	usba_writel(udc, CTRL, regval);
+}
+
+static int do_test_mode(struct usba_udc *udc)
+{
+	static const char test_packet_buffer[] = {
+		/* JKJKJKJK * 9 */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		/* JJKKJJKK * 8 */
+		0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+		/* JJKKJJKK * 8 */
+		0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+		/* JJJJJJJKKKKKKK * 8 */
+		0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		/* JJJJJJJK * 8 */
+		0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
+		/* {JKKKKKKK * 10}, JK */
+		0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E
+	};
+	struct usba_ep *ep;
+	struct device *dev = &udc->pdev->dev;
+	int test_mode;
+
+	test_mode = udc->test_mode;
+
+	/* Start from a clean slate */
+	reset_all_endpoints(udc);
+
+	switch (test_mode) {
+	case 0x0100:
+		/* Test_J */
+		usba_writel(udc, TST, USBA_TST_J_MODE);
+		dev_info(dev, "Entering Test_J mode...\n");
+		break;
+	case 0x0200:
+		/* Test_K */
+		usba_writel(udc, TST, USBA_TST_K_MODE);
+		dev_info(dev, "Entering Test_K mode...\n");
+		break;
+	case 0x0300:
+		/*
+		 * Test_SE0_NAK: Force high-speed mode and set up ep0
+		 * for Bulk IN transfers
+		 */
+		ep = &udc->usba_ep[0];
+		usba_writel(udc, TST,
+				USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH));
+		usba_ep_writel(ep, CFG,
+				USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)
+				| USBA_EPT_DIR_IN
+				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)
+				| USBA_BF(BK_NUMBER, 1));
+		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {
+			set_protocol_stall(udc, ep);
+			dev_err(dev, "Test_SE0_NAK: ep0 not mapped\n");
+		} else {
+			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+			dev_info(dev, "Entering Test_SE0_NAK mode...\n");
+		}
+		break;
+	case 0x0400:
+		/* Test_Packet */
+		ep = &udc->usba_ep[0];
+		usba_ep_writel(ep, CFG,
+				USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)
+				| USBA_EPT_DIR_IN
+				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)
+				| USBA_BF(BK_NUMBER, 1));
+		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {
+			set_protocol_stall(udc, ep);
+			dev_err(dev, "Test_Packet: ep0 not mapped\n");
+		} else {
+			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
+			usba_writel(udc, TST, USBA_TST_PKT_MODE);
+			memcpy_toio(ep->fifo, test_packet_buffer,
+					sizeof(test_packet_buffer));
+			usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+			dev_info(dev, "Entering Test_Packet mode...\n");
+		}
+		break;
+	default:
+		dev_err(dev, "Invalid test mode: 0x%04x\n", test_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Avoid overly long expressions */
+static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq)
+{
+	if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP))
+		return true;
+	return false;
+}
+
+static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq)
+{
+	if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE))
+		return true;
+	return false;
+}
+
+static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq)
+{
+	if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT))
+		return true;
+	return false;
+}
+
+static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
+		struct usb_ctrlrequest *crq)
+{
+	int retval = 0;
+
+	switch (crq->bRequest) {
+	case USB_REQ_GET_STATUS: {
+		u16 status;
+
+		if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {
+			status = cpu_to_le16(udc->devstatus);
+		} else if (crq->bRequestType
+				== (USB_DIR_IN | USB_RECIP_INTERFACE)) {
+			status = cpu_to_le16(0);
+		} else if (crq->bRequestType
+				== (USB_DIR_IN | USB_RECIP_ENDPOINT)) {
+			struct usba_ep *target;
+
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			status = 0;
+			if (is_stalled(udc, target))
+				status |= cpu_to_le16(1);
+		} else
+			goto delegate;
+
+		/* Write directly to the FIFO. No queueing is done. */
+		if (crq->wLength != cpu_to_le16(sizeof(status)))
+			goto stall;
+		ep->state = DATA_STAGE_IN;
+		writew_relaxed(status, ep->fifo);
+		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
+		break;
+	}
+
+	case USB_REQ_CLEAR_FEATURE: {
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			if (feature_is_dev_remote_wakeup(crq))
+				udc->devstatus
+					&= ~(1 << USB_DEVICE_REMOTE_WAKEUP);
+			else
+				/* Can't CLEAR_FEATURE TEST_MODE */
+				goto stall;
+		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+			struct usba_ep *target;
+
+			if (crq->wLength != cpu_to_le16(0)
+					|| !feature_is_ep_halt(crq))
+				goto stall;
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL);
+			if (target->index != 0)
+				usba_ep_writel(target, CLR_STA,
+						USBA_TOGGLE_CLR);
+		} else {
+			goto delegate;
+		}
+
+		send_status(udc, ep);
+		break;
+	}
+
+	case USB_REQ_SET_FEATURE: {
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			if (feature_is_dev_test_mode(crq)) {
+				send_status(udc, ep);
+				ep->state = STATUS_STAGE_TEST;
+				udc->test_mode = le16_to_cpu(crq->wIndex);
+				return 0;
+			} else if (feature_is_dev_remote_wakeup(crq)) {
+				udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+			} else {
+				goto stall;
+			}
+		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
+			struct usba_ep *target;
+
+			if (crq->wLength != cpu_to_le16(0)
+					|| !feature_is_ep_halt(crq))
+				goto stall;
+
+			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));
+			if (!target)
+				goto stall;
+
+			usba_ep_writel(target, SET_STA, USBA_FORCE_STALL);
+		} else
+			goto delegate;
+
+		send_status(udc, ep);
+		break;
+	}
+
+	case USB_REQ_SET_ADDRESS:
+		if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+			goto delegate;
+
+		set_address(udc, le16_to_cpu(crq->wValue));
+		send_status(udc, ep);
+		ep->state = STATUS_STAGE_ADDR;
+		break;
+
+	default:
+delegate:
+		spin_unlock(&udc->lock);
+		retval = udc->driver->setup(&udc->gadget, crq);
+		spin_lock(&udc->lock);
+	}
+
+	return retval;
+
+stall:
+	pr_err("udc: %s: Invalid setup request: %02x.%02x v%04x i%04x l%d, "
+		"halting endpoint...\n",
+		ep->ep.name, crq->bRequestType, crq->bRequest,
+		le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex),
+		le16_to_cpu(crq->wLength));
+	set_protocol_stall(udc, ep);
+	return -1;
+}
+
+static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+	struct usba_request *req;
+	u32 epstatus;
+	u32 epctrl;
+
+restart:
+	epstatus = usba_ep_readl(ep, STA);
+	epctrl = usba_ep_readl(ep, CTL);
+
+	DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n",
+			ep->ep.name, ep->state, epstatus, epctrl);
+
+	req = NULL;
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next,
+				 struct usba_request, queue);
+
+	if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+		if (req->submitted)
+			next_fifo_transaction(ep, req);
+		else
+			submit_request(ep, req);
+
+		if (req->last_transaction) {
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);
+		}
+		goto restart;
+	}
+	if ((epstatus & epctrl) & USBA_TX_COMPLETE) {
+		usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE);
+
+		switch (ep->state) {
+		case DATA_STAGE_IN:
+			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = STATUS_STAGE_OUT;
+			break;
+		case STATUS_STAGE_ADDR:
+			/* Activate our new address */
+			usba_writel(udc, CTRL, (usba_readl(udc, CTRL)
+						| USBA_FADDR_EN));
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = WAIT_FOR_SETUP;
+			break;
+		case STATUS_STAGE_IN:
+			if (req) {
+				list_del_init(&req->queue);
+				request_complete(ep, req, 0);
+				submit_next_request(ep);
+			}
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = WAIT_FOR_SETUP;
+			break;
+		case STATUS_STAGE_TEST:
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);
+			ep->state = WAIT_FOR_SETUP;
+			if (do_test_mode(udc))
+				set_protocol_stall(udc, ep);
+			break;
+		default:
+			pr_err("udc: %s: TXCOMP: Invalid endpoint state %d, "
+				"halting endpoint...\n",
+				ep->ep.name, ep->state);
+			set_protocol_stall(udc, ep);
+			break;
+		}
+
+		goto restart;
+	}
+	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+		switch (ep->state) {
+		case STATUS_STAGE_OUT:
+			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+
+			if (req) {
+				list_del_init(&req->queue);
+				request_complete(ep, req, 0);
+			}
+			ep->state = WAIT_FOR_SETUP;
+			break;
+
+		case DATA_STAGE_OUT:
+			receive_data(ep);
+			break;
+
+		default:
+			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);
+			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+			pr_err("udc: %s: RXRDY: Invalid endpoint state %d, "
+				"halting endpoint...\n",
+				ep->ep.name, ep->state);
+			set_protocol_stall(udc, ep);
+			break;
+		}
+
+		goto restart;
+	}
+	if (epstatus & USBA_RX_SETUP) {
+		union {
+			struct usb_ctrlrequest crq;
+			unsigned long data[2];
+		} crq;
+		unsigned int pkt_len;
+		int ret;
+
+		if (ep->state != WAIT_FOR_SETUP) {
+			/*
+			 * Didn't expect a SETUP packet at this
+			 * point. Clean up any pending requests (which
+			 * may be successful).
+			 */
+			int status = -EPROTO;
+
+			/*
+			 * RXRDY and TXCOMP are dropped when SETUP
+			 * packets arrive.  Just pretend we received
+			 * the status packet.
+			 */
+			if (ep->state == STATUS_STAGE_OUT
+					|| ep->state == STATUS_STAGE_IN) {
+				usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
+				status = 0;
+			}
+
+			if (req) {
+				list_del_init(&req->queue);
+				request_complete(ep, req, status);
+			}
+		}
+
+		pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
+		DBG(DBG_HW, "Packet length: %u\n", pkt_len);
+		if (pkt_len != sizeof(crq)) {
+			pr_warn("udc: Invalid packet length %u (expected %zu)\n",
+				pkt_len, sizeof(crq));
+			set_protocol_stall(udc, ep);
+			return;
+		}
+
+		DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo);
+		memcpy_fromio(crq.data, ep->fifo, sizeof(crq));
+
+		/* Free up one bank in the FIFO so that we can
+		 * generate or receive a reply right away. */
+		usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP);
+
+		/* printk(KERN_DEBUG "setup: %d: %02x.%02x\n",
+			ep->state, crq.crq.bRequestType,
+			crq.crq.bRequest); */
+
+		if (crq.crq.bRequestType & USB_DIR_IN) {
+			/*
+			 * The USB 2.0 spec states that "if wLength is
+			 * zero, there is no data transfer phase."
+			 * However, testusb #14 seems to actually
+			 * expect a data phase even if wLength = 0...
+			 */
+			ep->state = DATA_STAGE_IN;
+		} else {
+			if (crq.crq.wLength != cpu_to_le16(0))
+				ep->state = DATA_STAGE_OUT;
+			else
+				ep->state = STATUS_STAGE_IN;
+		}
+
+		ret = -1;
+		if (ep->index == 0)
+			ret = handle_ep0_setup(udc, ep, &crq.crq);
+		else {
+			spin_unlock(&udc->lock);
+			ret = udc->driver->setup(&udc->gadget, &crq.crq);
+			spin_lock(&udc->lock);
+		}
+
+		DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n",
+			crq.crq.bRequestType, crq.crq.bRequest,
+			le16_to_cpu(crq.crq.wLength), ep->state, ret);
+
+		if (ret < 0) {
+			/* Let the host know that we failed */
+			set_protocol_stall(udc, ep);
+		}
+	}
+}
+
+static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+	struct usba_request *req;
+	u32 epstatus;
+	u32 epctrl;
+
+	epstatus = usba_ep_readl(ep, STA);
+	epctrl = usba_ep_readl(ep, CTL);
+
+	DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus);
+
+	while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {
+		DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name);
+
+		if (list_empty(&ep->queue)) {
+			dev_warn(&udc->pdev->dev, "ep_irq: queue empty\n");
+			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);
+			return;
+		}
+
+		req = list_entry(ep->queue.next, struct usba_request, queue);
+
+		if (req->using_dma) {
+			/* Send a zero-length packet */
+			usba_ep_writel(ep, SET_STA,
+					USBA_TX_PK_RDY);
+			usba_ep_writel(ep, CTL_DIS,
+					USBA_TX_PK_RDY);
+			list_del_init(&req->queue);
+			submit_next_request(ep);
+			request_complete(ep, req, 0);
+		} else {
+			if (req->submitted)
+				next_fifo_transaction(ep, req);
+			else
+				submit_request(ep, req);
+
+			if (req->last_transaction) {
+				list_del_init(&req->queue);
+				submit_next_request(ep);
+				request_complete(ep, req, 0);
+			}
+		}
+
+		epstatus = usba_ep_readl(ep, STA);
+		epctrl = usba_ep_readl(ep, CTL);
+	}
+	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {
+		DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name);
+		receive_data(ep);
+	}
+}
+
+static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep)
+{
+	struct usba_request *req;
+	u32 status, control, pending;
+
+	status = usba_dma_readl(ep, STATUS);
+	control = usba_dma_readl(ep, CONTROL);
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+	ep->last_dma_status = status;
+#endif
+	pending = status & control;
+	DBG(DBG_INT | DBG_DMA, "dma irq, s/%#08x, c/%#08x\n", status, control);
+
+	if (status & USBA_DMA_CH_EN) {
+		dev_err(&udc->pdev->dev,
+			"DMA_CH_EN is set after transfer is finished!\n");
+		dev_err(&udc->pdev->dev,
+			"status=%#08x, pending=%#08x, control=%#08x\n",
+			status, pending, control);
+
+		/*
+		 * try to pretend nothing happened. We might have to
+		 * do something here...
+		 */
+	}
+
+	if (list_empty(&ep->queue))
+		/* Might happen if a reset comes along at the right moment */
+		return;
+
+	if (pending & (USBA_DMA_END_TR_ST | USBA_DMA_END_BUF_ST)) {
+		req = list_entry(ep->queue.next, struct usba_request, queue);
+		usba_update_req(ep, req, status);
+
+		list_del_init(&req->queue);
+		submit_next_request(ep);
+		request_complete(ep, req, 0);
+	}
+}
+
+static irqreturn_t usba_udc_irq(int irq, void *devid)
+{
+	struct usba_udc *udc = devid;
+	u32 status, int_enb;
+	u32 dma_status;
+	u32 ep_status;
+
+	spin_lock(&udc->lock);
+
+	int_enb = usba_int_enb_get(udc);
+	status = usba_readl(udc, INT_STA) & (int_enb | USBA_HIGH_SPEED);
+	DBG(DBG_INT, "irq, status=%#08x\n", status);
+
+	if (status & USBA_DET_SUSPEND) {
+		toggle_bias(udc, 0);
+		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+		usba_int_enb_set(udc, int_enb | USBA_WAKE_UP);
+		udc->bias_pulse_needed = true;
+		DBG(DBG_BUS, "Suspend detected\n");
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN
+				&& udc->driver && udc->driver->suspend) {
+			spin_unlock(&udc->lock);
+			udc->driver->suspend(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+
+	if (status & USBA_WAKE_UP) {
+		toggle_bias(udc, 1);
+		usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+		usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP);
+		DBG(DBG_BUS, "Wake Up CPU detected\n");
+	}
+
+	if (status & USBA_END_OF_RESUME) {
+		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+		generate_bias_pulse(udc);
+		DBG(DBG_BUS, "Resume detected\n");
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN
+				&& udc->driver && udc->driver->resume) {
+			spin_unlock(&udc->lock);
+			udc->driver->resume(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+
+	dma_status = USBA_BFEXT(DMA_INT, status);
+	if (dma_status) {
+		int i;
+
+		for (i = 1; i <= USBA_NR_DMAS; i++)
+			if (dma_status & (1 << i))
+				usba_dma_irq(udc, &udc->usba_ep[i]);
+	}
+
+	ep_status = USBA_BFEXT(EPT_INT, status);
+	if (ep_status) {
+		int i;
+
+		for (i = 0; i < udc->num_ep; i++)
+			if (ep_status & (1 << i)) {
+				if (ep_is_control(&udc->usba_ep[i]))
+					usba_control_irq(udc, &udc->usba_ep[i]);
+				else
+					usba_ep_irq(udc, &udc->usba_ep[i]);
+			}
+	}
+
+	if (status & USBA_END_OF_RESET) {
+		struct usba_ep *ep0, *ep;
+		int i, n;
+
+		usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+		generate_bias_pulse(udc);
+		reset_all_endpoints(udc);
+
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
+			udc->gadget.speed = USB_SPEED_UNKNOWN;
+			spin_unlock(&udc->lock);
+			usb_gadget_udc_reset(&udc->gadget, udc->driver);
+			spin_lock(&udc->lock);
+		}
+
+		if (status & USBA_HIGH_SPEED)
+			udc->gadget.speed = USB_SPEED_HIGH;
+		else
+			udc->gadget.speed = USB_SPEED_FULL;
+		DBG(DBG_BUS, "%s bus reset detected\n",
+		    usb_speed_string(udc->gadget.speed));
+
+		ep0 = &udc->usba_ep[0];
+		ep0->ep.desc = &usba_ep0_desc;
+		ep0->state = WAIT_FOR_SETUP;
+		usba_ep_writel(ep0, CFG,
+				(USBA_BF(EPT_SIZE, EP0_EPT_SIZE)
+				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL)
+				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
+		usba_ep_writel(ep0, CTL_ENB,
+				USBA_EPT_ENABLE | USBA_RX_SETUP);
+		usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) |
+				      USBA_DET_SUSPEND | USBA_END_OF_RESUME);
+
+		/*
+		 * Unclear why we hit this irregularly, e.g. in usbtest,
+		 * but it's clearly harmless...
+		 */
+		if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))
+			dev_err(&udc->pdev->dev,
+				"ODD: EP0 configuration is invalid!\n");
+
+		/* Preallocate other endpoints */
+		n = fifo_mode ? udc->num_ep : udc->configured_ep;
+		for (i = 1; i < n; i++) {
+			ep = &udc->usba_ep[i];
+			usba_ep_writel(ep, CFG, ep->ept_cfg);
+			if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
+				dev_err(&udc->pdev->dev,
+					"ODD: EP%d configuration is invalid!\n", i);
+		}
+	}
+
+	spin_unlock(&udc->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int start_clock(struct usba_udc *udc)
+{
+	int ret;
+
+	if (udc->clocked)
+		return 0;
+
+	ret = clk_prepare_enable(udc->pclk);
+	if (ret)
+		return ret;
+	ret = clk_prepare_enable(udc->hclk);
+	if (ret) {
+		clk_disable_unprepare(udc->pclk);
+		return ret;
+	}
+
+	udc->clocked = true;
+	return 0;
+}
+
+static void stop_clock(struct usba_udc *udc)
+{
+	if (!udc->clocked)
+		return;
+
+	clk_disable_unprepare(udc->hclk);
+	clk_disable_unprepare(udc->pclk);
+
+	udc->clocked = false;
+}
+
+static int usba_start(struct usba_udc *udc)
+{
+	unsigned long flags;
+	int ret;
+
+	ret = start_clock(udc);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	toggle_bias(udc, 1);
+	usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+	usba_int_enb_set(udc, USBA_END_OF_RESET);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static void usba_stop(struct usba_udc *udc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	reset_all_endpoints(udc);
+
+	/* This will also disable the DP pullup */
+	toggle_bias(udc, 0);
+	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	stop_clock(udc);
+}
+
+static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
+{
+	struct usba_udc *udc = devid;
+	int vbus;
+
+	/* debounce */
+	udelay(10);
+
+	mutex_lock(&udc->vbus_mutex);
+
+	vbus = vbus_is_present(udc);
+	if (vbus != udc->vbus_prev) {
+		if (vbus) {
+			usba_start(udc);
+		} else {
+			usba_stop(udc);
+
+			if (udc->driver->disconnect)
+				udc->driver->disconnect(&udc->gadget);
+		}
+		udc->vbus_prev = vbus;
+	}
+
+	mutex_unlock(&udc->vbus_mutex);
+	return IRQ_HANDLED;
+}
+
+static int atmel_usba_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	int ret;
+	struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
+	udc->driver = driver;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	mutex_lock(&udc->vbus_mutex);
+
+	if (udc->vbus_pin)
+		enable_irq(gpiod_to_irq(udc->vbus_pin));
+
+	/* If Vbus is present, enable the controller and wait for reset */
+	udc->vbus_prev = vbus_is_present(udc);
+	if (udc->vbus_prev) {
+		ret = usba_start(udc);
+		if (ret)
+			goto err;
+	}
+
+	mutex_unlock(&udc->vbus_mutex);
+	return 0;
+
+err:
+	if (udc->vbus_pin)
+		disable_irq(gpiod_to_irq(udc->vbus_pin));
+
+	mutex_unlock(&udc->vbus_mutex);
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+	udc->driver = NULL;
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return ret;
+}
+
+static int atmel_usba_stop(struct usb_gadget *gadget)
+{
+	struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
+
+	if (udc->vbus_pin)
+		disable_irq(gpiod_to_irq(udc->vbus_pin));
+
+	if (fifo_mode == 0)
+		udc->configured_ep = 1;
+
+	usba_stop(udc);
+
+	udc->driver = NULL;
+
+	return 0;
+}
+
+static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
+{
+	regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN,
+			   is_on ? AT91_PMC_BIASEN : 0);
+}
+
+static void at91sam9g45_pulse_bias(struct usba_udc *udc)
+{
+	regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN, 0);
+	regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN,
+			   AT91_PMC_BIASEN);
+}
+
+static const struct usba_udc_errata at91sam9rl_errata = {
+	.toggle_bias = at91sam9rl_toggle_bias,
+};
+
+static const struct usba_udc_errata at91sam9g45_errata = {
+	.pulse_bias = at91sam9g45_pulse_bias,
+};
+
+static const struct of_device_id atmel_udc_dt_ids[] = {
+	{ .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
+	{ .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
+	{ .compatible = "atmel,sama5d3-udc" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+
+static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
+						    struct usba_udc *udc)
+{
+	u32 val;
+	const char *name;
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	struct device_node *pp;
+	int i, ret;
+	struct usba_ep *eps, *ep;
+
+	match = of_match_node(atmel_udc_dt_ids, np);
+	if (!match)
+		return ERR_PTR(-EINVAL);
+
+	udc->errata = match->data;
+	udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
+	if (IS_ERR(udc->pmc))
+		udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
+	if (IS_ERR(udc->pmc))
+		udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
+	if (udc->errata && IS_ERR(udc->pmc))
+		return ERR_CAST(udc->pmc);
+
+	udc->num_ep = 0;
+
+	udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus",
+						GPIOD_IN);
+
+	if (fifo_mode == 0) {
+		pp = NULL;
+		while ((pp = of_get_next_child(np, pp)))
+			udc->num_ep++;
+		udc->configured_ep = 1;
+	} else {
+		udc->num_ep = usba_config_fifo_table(udc);
+	}
+
+	eps = devm_kcalloc(&pdev->dev, udc->num_ep, sizeof(struct usba_ep),
+			   GFP_KERNEL);
+	if (!eps)
+		return ERR_PTR(-ENOMEM);
+
+	udc->gadget.ep0 = &eps[0].ep;
+
+	INIT_LIST_HEAD(&eps[0].ep.ep_list);
+
+	pp = NULL;
+	i = 0;
+	while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
+		ep = &eps[i];
+
+		ret = of_property_read_u32(pp, "reg", &val);
+		if (ret) {
+			dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
+			goto err;
+		}
+		ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
+
+		ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
+		if (ret) {
+			dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
+			goto err;
+		}
+		if (fifo_mode) {
+			if (val < udc->fifo_cfg[i].fifo_size) {
+				dev_warn(&pdev->dev,
+					 "Using max fifo-size value from DT\n");
+				ep->fifo_size = val;
+			} else {
+				ep->fifo_size = udc->fifo_cfg[i].fifo_size;
+			}
+		} else {
+			ep->fifo_size = val;
+		}
+
+		ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
+		if (ret) {
+			dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
+			goto err;
+		}
+		if (fifo_mode) {
+			if (val < udc->fifo_cfg[i].nr_banks) {
+				dev_warn(&pdev->dev,
+					 "Using max nb-banks value from DT\n");
+				ep->nr_banks = val;
+			} else {
+				ep->nr_banks = udc->fifo_cfg[i].nr_banks;
+			}
+		} else {
+			ep->nr_banks = val;
+		}
+
+		ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
+		ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
+
+		ret = of_property_read_string(pp, "name", &name);
+		if (ret) {
+			dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
+			goto err;
+		}
+		sprintf(ep->name, "ep%d", ep->index);
+		ep->ep.name = ep->name;
+
+		ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
+		ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
+		ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
+		ep->ep.ops = &usba_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
+		ep->udc = udc;
+		INIT_LIST_HEAD(&ep->queue);
+
+		if (ep->index == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = ep->can_isoc;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+
+		if (fifo_mode != 0) {
+			/*
+			 * Generate ept_cfg based on FIFO size and
+			 * banks number
+			 */
+			if (ep->fifo_size  <= 8)
+				ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+			else
+				/* LSB is bit 1, not 0 */
+				ep->ept_cfg =
+				  USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+			ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+		}
+
+		if (i)
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+		i++;
+	}
+
+	if (i == 0) {
+		dev_err(&pdev->dev, "of_probe: no endpoint specified\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return eps;
+err:
+	return ERR_PTR(ret);
+}
+
+static int usba_udc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct clk *pclk, *hclk;
+	struct usba_udc *udc;
+	int irq, ret, i;
+
+	udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	udc->gadget = usba_gadget_template;
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
+	udc->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(udc->regs))
+		return PTR_ERR(udc->regs);
+	dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
+		 res, udc->regs);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
+	udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(udc->fifo))
+		return PTR_ERR(udc->fifo);
+	dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(pclk))
+		return PTR_ERR(pclk);
+	hclk = devm_clk_get(&pdev->dev, "hclk");
+	if (IS_ERR(hclk))
+		return PTR_ERR(hclk);
+
+	spin_lock_init(&udc->lock);
+	mutex_init(&udc->vbus_mutex);
+	udc->pdev = pdev;
+	udc->pclk = pclk;
+	udc->hclk = hclk;
+
+	platform_set_drvdata(pdev, udc);
+
+	/* Make sure we start from a clean slate */
+	ret = clk_prepare_enable(pclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
+		return ret;
+	}
+
+	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+	clk_disable_unprepare(pclk);
+
+	udc->usba_ep = atmel_udc_of_init(pdev, udc);
+
+	toggle_bias(udc, 0);
+
+	if (IS_ERR(udc->usba_ep))
+		return PTR_ERR(udc->usba_ep);
+
+	ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0,
+				"atmel_usba_udc", udc);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n",
+			irq, ret);
+		return ret;
+	}
+	udc->irq = irq;
+
+	if (udc->vbus_pin) {
+		irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
+		ret = devm_request_threaded_irq(&pdev->dev,
+				gpiod_to_irq(udc->vbus_pin), NULL,
+				usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
+				"atmel_usba_udc", udc);
+		if (ret) {
+			udc->vbus_pin = NULL;
+			dev_warn(&udc->pdev->dev,
+				 "failed to request vbus irq; "
+				 "assuming always on\n");
+		}
+	}
+
+	ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+	if (ret)
+		return ret;
+	device_init_wakeup(&pdev->dev, 1);
+
+	usba_init_debugfs(udc);
+	for (i = 1; i < udc->num_ep; i++)
+		usba_ep_init_debugfs(udc, &udc->usba_ep[i]);
+
+	return 0;
+}
+
+static int usba_udc_remove(struct platform_device *pdev)
+{
+	struct usba_udc *udc;
+	int i;
+
+	udc = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	usb_del_gadget_udc(&udc->gadget);
+
+	for (i = 1; i < udc->num_ep; i++)
+		usba_ep_cleanup_debugfs(&udc->usba_ep[i]);
+	usba_cleanup_debugfs(udc);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int usba_udc_suspend(struct device *dev)
+{
+	struct usba_udc *udc = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!udc->driver)
+		return 0;
+
+	mutex_lock(&udc->vbus_mutex);
+
+	if (!device_may_wakeup(dev)) {
+		usba_stop(udc);
+		goto out;
+	}
+
+	/*
+	 * Device may wake up. We stay clocked if we failed
+	 * to request vbus irq, assuming always on.
+	 */
+	if (udc->vbus_pin) {
+		usba_stop(udc);
+		enable_irq_wake(gpiod_to_irq(udc->vbus_pin));
+	}
+
+out:
+	mutex_unlock(&udc->vbus_mutex);
+	return 0;
+}
+
+static int usba_udc_resume(struct device *dev)
+{
+	struct usba_udc *udc = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!udc->driver)
+		return 0;
+
+	if (device_may_wakeup(dev) && udc->vbus_pin)
+		disable_irq_wake(gpiod_to_irq(udc->vbus_pin));
+
+	/* If Vbus is present, enable the controller and wait for reset */
+	mutex_lock(&udc->vbus_mutex);
+	udc->vbus_prev = vbus_is_present(udc);
+	if (udc->vbus_prev)
+		usba_start(udc);
+	mutex_unlock(&udc->vbus_mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
+
+static struct platform_driver udc_driver = {
+	.remove		= usba_udc_remove,
+	.driver		= {
+		.name		= "atmel_usba_udc",
+		.pm		= &usba_udc_pm_ops,
+		.of_match_table	= atmel_udc_dt_ids,
+	},
+};
+
+module_platform_driver_probe(udc_driver, usba_udc_probe);
+
+MODULE_DESCRIPTION("Atmel USBA UDC driver");
+MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel_usba_udc");
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
new file mode 100644
index 0000000..030bf79
--- /dev/null
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Atmel USBA high speed USB device controller
+ *
+ * Copyright (C) 2005-2007 Atmel Corporation
+ */
+#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
+#define __LINUX_USB_GADGET_USBA_UDC_H__
+
+#include <linux/gpio/consumer.h>
+
+/* USB register offsets */
+#define USBA_CTRL				0x0000
+#define USBA_FNUM				0x0004
+#define USBA_INT_ENB				0x0010
+#define USBA_INT_STA				0x0014
+#define USBA_INT_CLR				0x0018
+#define USBA_EPT_RST				0x001c
+#define USBA_TST				0x00e0
+
+/* USB endpoint register offsets */
+#define USBA_EPT_CFG				0x0000
+#define USBA_EPT_CTL_ENB			0x0004
+#define USBA_EPT_CTL_DIS			0x0008
+#define USBA_EPT_CTL				0x000c
+#define USBA_EPT_SET_STA			0x0014
+#define USBA_EPT_CLR_STA			0x0018
+#define USBA_EPT_STA				0x001c
+
+/* USB DMA register offsets */
+#define USBA_DMA_NXT_DSC			0x0000
+#define USBA_DMA_ADDRESS			0x0004
+#define USBA_DMA_CONTROL			0x0008
+#define USBA_DMA_STATUS				0x000c
+
+/* Bitfields in CTRL */
+#define USBA_DEV_ADDR_OFFSET			0
+#define USBA_DEV_ADDR_SIZE			7
+#define USBA_FADDR_EN				(1 <<  7)
+#define USBA_EN_USBA				(1 <<  8)
+#define USBA_DETACH				(1 <<  9)
+#define USBA_REMOTE_WAKE_UP			(1 << 10)
+#define USBA_PULLD_DIS				(1 << 11)
+
+#define USBA_ENABLE_MASK			(USBA_EN_USBA | USBA_PULLD_DIS)
+#define USBA_DISABLE_MASK			USBA_DETACH
+
+/* Bitfields in FNUM */
+#define USBA_MICRO_FRAME_NUM_OFFSET		0
+#define USBA_MICRO_FRAME_NUM_SIZE		3
+#define USBA_FRAME_NUMBER_OFFSET		3
+#define USBA_FRAME_NUMBER_SIZE			11
+#define USBA_FRAME_NUM_ERROR			(1 << 31)
+
+/* Bitfields in INT_ENB/INT_STA/INT_CLR */
+#define USBA_HIGH_SPEED				(1 <<  0)
+#define USBA_DET_SUSPEND			(1 <<  1)
+#define USBA_MICRO_SOF				(1 <<  2)
+#define USBA_SOF				(1 <<  3)
+#define USBA_END_OF_RESET			(1 <<  4)
+#define USBA_WAKE_UP				(1 <<  5)
+#define USBA_END_OF_RESUME			(1 <<  6)
+#define USBA_UPSTREAM_RESUME			(1 <<  7)
+#define USBA_EPT_INT_OFFSET			8
+#define USBA_EPT_INT_SIZE			16
+#define USBA_DMA_INT_OFFSET			24
+#define USBA_DMA_INT_SIZE			8
+
+/* Bitfields in EPT_RST */
+#define USBA_RST_OFFSET				0
+#define USBA_RST_SIZE				16
+
+/* Bitfields in USBA_TST */
+#define USBA_SPEED_CFG_OFFSET			0
+#define USBA_SPEED_CFG_SIZE			2
+#define USBA_TST_J_MODE				(1 <<  2)
+#define USBA_TST_K_MODE				(1 <<  3)
+#define USBA_TST_PKT_MODE			(1 <<  4)
+#define USBA_OPMODE2				(1 <<  5)
+
+/* Bitfields in EPT_CFG */
+#define USBA_EPT_SIZE_OFFSET			0
+#define USBA_EPT_SIZE_SIZE			3
+#define USBA_EPT_DIR_IN				(1 <<  3)
+#define USBA_EPT_TYPE_OFFSET			4
+#define USBA_EPT_TYPE_SIZE			2
+#define USBA_BK_NUMBER_OFFSET			6
+#define USBA_BK_NUMBER_SIZE			2
+#define USBA_NB_TRANS_OFFSET			8
+#define USBA_NB_TRANS_SIZE			2
+#define USBA_EPT_MAPPED				(1 << 31)
+
+/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
+#define USBA_EPT_ENABLE				(1 <<  0)
+#define USBA_AUTO_VALID				(1 <<  1)
+#define USBA_INTDIS_DMA				(1 <<  3)
+#define USBA_NYET_DIS				(1 <<  4)
+#define USBA_DATAX_RX				(1 <<  6)
+#define USBA_MDATA_RX				(1 <<  7)
+/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
+#define USBA_BUSY_BANK_IE			(1 << 18)
+
+/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
+#define USBA_FORCE_STALL			(1 <<  5)
+#define USBA_TOGGLE_CLR				(1 <<  6)
+#define USBA_TOGGLE_SEQ_OFFSET			6
+#define USBA_TOGGLE_SEQ_SIZE			2
+#define USBA_ERR_OVFLW				(1 <<  8)
+#define USBA_RX_BK_RDY				(1 <<  9)
+#define USBA_KILL_BANK				(1 <<  9)
+#define USBA_TX_COMPLETE			(1 << 10)
+#define USBA_TX_PK_RDY				(1 << 11)
+#define USBA_ISO_ERR_TRANS			(1 << 11)
+#define USBA_RX_SETUP				(1 << 12)
+#define USBA_ISO_ERR_FLOW			(1 << 12)
+#define USBA_STALL_SENT				(1 << 13)
+#define USBA_ISO_ERR_CRC			(1 << 13)
+#define USBA_ISO_ERR_NBTRANS			(1 << 13)
+#define USBA_NAK_IN				(1 << 14)
+#define USBA_ISO_ERR_FLUSH			(1 << 14)
+#define USBA_NAK_OUT				(1 << 15)
+#define USBA_CURRENT_BANK_OFFSET		16
+#define USBA_CURRENT_BANK_SIZE			2
+#define USBA_BUSY_BANKS_OFFSET			18
+#define USBA_BUSY_BANKS_SIZE			2
+#define USBA_BYTE_COUNT_OFFSET			20
+#define USBA_BYTE_COUNT_SIZE			11
+#define USBA_SHORT_PACKET			(1 << 31)
+
+/* Bitfields in DMA_CONTROL */
+#define USBA_DMA_CH_EN				(1 <<  0)
+#define USBA_DMA_LINK				(1 <<  1)
+#define USBA_DMA_END_TR_EN			(1 <<  2)
+#define USBA_DMA_END_BUF_EN			(1 <<  3)
+#define USBA_DMA_END_TR_IE			(1 <<  4)
+#define USBA_DMA_END_BUF_IE			(1 <<  5)
+#define USBA_DMA_DESC_LOAD_IE			(1 <<  6)
+#define USBA_DMA_BURST_LOCK			(1 <<  7)
+#define USBA_DMA_BUF_LEN_OFFSET			16
+#define USBA_DMA_BUF_LEN_SIZE			16
+
+/* Bitfields in DMA_STATUS */
+#define USBA_DMA_CH_ACTIVE			(1 <<  1)
+#define USBA_DMA_END_TR_ST			(1 <<  4)
+#define USBA_DMA_END_BUF_ST			(1 <<  5)
+#define USBA_DMA_DESC_LOAD_ST			(1 <<  6)
+
+/* Constants for SPEED_CFG */
+#define USBA_SPEED_CFG_NORMAL			0
+#define USBA_SPEED_CFG_FORCE_HIGH		2
+#define USBA_SPEED_CFG_FORCE_FULL		3
+
+/* Constants for EPT_SIZE */
+#define USBA_EPT_SIZE_8				0
+#define USBA_EPT_SIZE_16			1
+#define USBA_EPT_SIZE_32			2
+#define USBA_EPT_SIZE_64			3
+#define USBA_EPT_SIZE_128			4
+#define USBA_EPT_SIZE_256			5
+#define USBA_EPT_SIZE_512			6
+#define USBA_EPT_SIZE_1024			7
+
+/* Constants for EPT_TYPE */
+#define USBA_EPT_TYPE_CONTROL			0
+#define USBA_EPT_TYPE_ISO			1
+#define USBA_EPT_TYPE_BULK			2
+#define USBA_EPT_TYPE_INT			3
+
+/* Constants for BK_NUMBER */
+#define USBA_BK_NUMBER_ZERO			0
+#define USBA_BK_NUMBER_ONE			1
+#define USBA_BK_NUMBER_DOUBLE			2
+#define USBA_BK_NUMBER_TRIPLE			3
+
+/* Bit manipulation macros */
+#define USBA_BF(name, value)					\
+	(((value) & ((1 << USBA_##name##_SIZE) - 1))		\
+	 << USBA_##name##_OFFSET)
+#define USBA_BFEXT(name, value)					\
+	(((value) >> USBA_##name##_OFFSET)			\
+	 & ((1 << USBA_##name##_SIZE) - 1))
+#define USBA_BFINS(name, value, old)				\
+	(((old) & ~(((1 << USBA_##name##_SIZE) - 1)		\
+		    << USBA_##name##_OFFSET))			\
+	 | USBA_BF(name, value))
+
+/* Register access macros */
+#define usba_readl(udc, reg)					\
+	readl_relaxed((udc)->regs + USBA_##reg)
+#define usba_writel(udc, reg, value)				\
+	writel_relaxed((value), (udc)->regs + USBA_##reg)
+#define usba_ep_readl(ep, reg)					\
+	readl_relaxed((ep)->ep_regs + USBA_EPT_##reg)
+#define usba_ep_writel(ep, reg, value)				\
+	writel_relaxed((value), (ep)->ep_regs + USBA_EPT_##reg)
+#define usba_dma_readl(ep, reg)					\
+	readl_relaxed((ep)->dma_regs + USBA_DMA_##reg)
+#define usba_dma_writel(ep, reg, value)				\
+	writel_relaxed((value), (ep)->dma_regs + USBA_DMA_##reg)
+
+/* Calculate base address for a given endpoint or DMA controller */
+#define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20)
+#define USBA_DMA_BASE(x)	(0x300 + (x) * 0x10)
+#define USBA_FIFO_BASE(x)	((x) << 16)
+
+/* Synth parameters */
+#define USBA_NR_DMAS		7
+
+#define EP0_FIFO_SIZE		64
+#define EP0_EPT_SIZE		USBA_EPT_SIZE_64
+#define EP0_NR_BANKS		1
+
+#define FIFO_IOMEM_ID	0
+#define CTRL_IOMEM_ID	1
+
+#define DBG_ERR		0x0001	/* report all error returns */
+#define DBG_HW		0x0002	/* debug hardware initialization */
+#define DBG_GADGET	0x0004	/* calls to/from gadget driver */
+#define DBG_INT		0x0008	/* interrupts */
+#define DBG_BUS		0x0010	/* report changes in bus state */
+#define DBG_QUEUE	0x0020  /* debug request queue processing */
+#define DBG_FIFO	0x0040  /* debug FIFO contents */
+#define DBG_DMA		0x0080  /* debug DMA handling */
+#define DBG_REQ		0x0100	/* print out queued request length */
+#define DBG_ALL		0xffff
+#define DBG_NONE	0x0000
+
+#define DEBUG_LEVEL	(DBG_ERR)
+
+#define DBG(level, fmt, ...)					\
+	do {							\
+		if ((level) & DEBUG_LEVEL)			\
+			pr_debug("udc: " fmt, ## __VA_ARGS__);	\
+	} while (0)
+
+enum usba_ctrl_state {
+	WAIT_FOR_SETUP,
+	DATA_STAGE_IN,
+	DATA_STAGE_OUT,
+	STATUS_STAGE_IN,
+	STATUS_STAGE_OUT,
+	STATUS_STAGE_ADDR,
+	STATUS_STAGE_TEST,
+};
+/*
+  EP_STATE_IDLE,
+  EP_STATE_SETUP,
+  EP_STATE_IN_DATA,
+  EP_STATE_OUT_DATA,
+  EP_STATE_SET_ADDR_STATUS,
+  EP_STATE_RX_STATUS,
+  EP_STATE_TX_STATUS,
+  EP_STATE_HALT,
+*/
+
+struct usba_dma_desc {
+	dma_addr_t next;
+	dma_addr_t addr;
+	u32 ctrl;
+};
+
+struct usba_fifo_cfg {
+	u8			hw_ep_num;
+	u16			fifo_size;
+	u8			nr_banks;
+};
+
+struct usba_ep {
+	int					state;
+	void __iomem				*ep_regs;
+	void __iomem				*dma_regs;
+	void __iomem				*fifo;
+	char					name[8];
+	struct usb_ep				ep;
+	struct usba_udc				*udc;
+
+	struct list_head			queue;
+
+	u16					fifo_size;
+	u8					nr_banks;
+	u8					index;
+	unsigned int				can_dma:1;
+	unsigned int				can_isoc:1;
+	unsigned int				is_isoc:1;
+	unsigned int				is_in:1;
+	unsigned long				ept_cfg;
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+	u32					last_dma_status;
+	struct dentry				*debugfs_dir;
+#endif
+};
+
+struct usba_request {
+	struct usb_request			req;
+	struct list_head			queue;
+
+	u32					ctrl;
+
+	unsigned int				submitted:1;
+	unsigned int				last_transaction:1;
+	unsigned int				using_dma:1;
+	unsigned int				mapped:1;
+};
+
+struct usba_udc_errata {
+	void (*toggle_bias)(struct usba_udc *udc, int is_on);
+	void (*pulse_bias)(struct usba_udc *udc);
+};
+
+struct usba_udc {
+	/* Protect hw registers from concurrent modifications */
+	spinlock_t lock;
+
+	/* Mutex to prevent concurrent start or stop */
+	struct mutex vbus_mutex;
+
+	void __iomem *regs;
+	void __iomem *fifo;
+
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct platform_device *pdev;
+	const struct usba_udc_errata *errata;
+	int irq;
+	struct gpio_desc *vbus_pin;
+	int num_ep;
+	int configured_ep;
+	struct usba_fifo_cfg *fifo_cfg;
+	struct clk *pclk;
+	struct clk *hclk;
+	struct usba_ep *usba_ep;
+	bool bias_pulse_needed;
+	bool clocked;
+
+	u16 devstatus;
+
+	u16 test_mode;
+	int vbus_prev;
+
+	u32 int_enb_cache;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+	struct dentry *debugfs_root;
+#endif
+
+	struct regmap *pmc;
+};
+
+static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct usba_ep, ep);
+}
+
+static inline struct usba_request *to_usba_req(struct usb_request *req)
+{
+	return container_of(req, struct usba_request, req);
+}
+
+static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct usba_udc, gadget);
+}
+
+#define ep_is_control(ep)	((ep)->index == 0)
+#define ep_is_idle(ep)		((ep)->state == EP_STATE_IDLE)
+
+#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
new file mode 100644
index 0000000..c1fcc77
--- /dev/null
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -0,0 +1,2395 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ * Copyright (C) 2012 Broadcom Corporation
+ */
+
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_iudma.h>
+#include <bcm63xx_dev_usb_usbd.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+#define DRV_MODULE_NAME		"bcm63xx_udc"
+
+static const char bcm63xx_ep0name[] = "ep0";
+
+static const struct {
+	const char *name;
+	const struct usb_ep_caps caps;
+} bcm63xx_ep_info[] = {
+#define EP_INFO(_name, _caps) \
+	{ \
+		.name = _name, \
+		.caps = _caps, \
+	}
+
+	EP_INFO(bcm63xx_ep0name,
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep1in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep2out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep3in-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep4out-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_OUT)),
+
+#undef EP_INFO
+};
+
+static bool use_fullspeed;
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
+
+/*
+ * RX IRQ coalescing options:
+ *
+ * false (default) - one IRQ per DATAx packet.  Slow but reliable.  The
+ * driver is able to pass the "testusb" suite and recover from conditions like:
+ *
+ *   1) Device queues up a 2048-byte RX IUDMA transaction on an OUT bulk ep
+ *   2) Host sends 512 bytes of data
+ *   3) Host decides to reconfigure the device and sends SET_INTERFACE
+ *   4) Device shuts down the endpoint and cancels the RX transaction
+ *
+ * true - one IRQ per transfer, for transfers <= 2048B.  Generates
+ * considerably fewer IRQs, but error recovery is less robust.  Does not
+ * reliably pass "testusb".
+ *
+ * TX always uses coalescing, because we can cancel partially complete TX
+ * transfers by repeatedly flushing the FIFO.  The hardware doesn't allow
+ * this on RX.
+ */
+static bool irq_coalesce;
+module_param(irq_coalesce, bool, S_IRUGO);
+MODULE_PARM_DESC(irq_coalesce, "take one IRQ per RX transfer");
+
+#define BCM63XX_NUM_EP			5
+#define BCM63XX_NUM_IUDMA		6
+#define BCM63XX_NUM_FIFO_PAIRS		3
+
+#define IUDMA_RESET_TIMEOUT_US		10000
+
+#define IUDMA_EP0_RXCHAN		0
+#define IUDMA_EP0_TXCHAN		1
+
+#define IUDMA_MAX_FRAGMENT		2048
+#define BCM63XX_MAX_CTRL_PKT		64
+
+#define BCMEP_CTRL			0x00
+#define BCMEP_ISOC			0x01
+#define BCMEP_BULK			0x02
+#define BCMEP_INTR			0x03
+
+#define BCMEP_OUT			0x00
+#define BCMEP_IN			0x01
+
+#define BCM63XX_SPD_FULL		1
+#define BCM63XX_SPD_HIGH		0
+
+#define IUDMA_DMAC_OFFSET		0x200
+#define IUDMA_DMAS_OFFSET		0x400
+
+enum bcm63xx_ep0_state {
+	EP0_REQUEUE,
+	EP0_IDLE,
+	EP0_IN_DATA_PHASE_SETUP,
+	EP0_IN_DATA_PHASE_COMPLETE,
+	EP0_OUT_DATA_PHASE_SETUP,
+	EP0_OUT_DATA_PHASE_COMPLETE,
+	EP0_OUT_STATUS_PHASE,
+	EP0_IN_FAKE_STATUS_PHASE,
+	EP0_SHUTDOWN,
+};
+
+static const char __maybe_unused bcm63xx_ep0_state_names[][32] = {
+	"REQUEUE",
+	"IDLE",
+	"IN_DATA_PHASE_SETUP",
+	"IN_DATA_PHASE_COMPLETE",
+	"OUT_DATA_PHASE_SETUP",
+	"OUT_DATA_PHASE_COMPLETE",
+	"OUT_STATUS_PHASE",
+	"IN_FAKE_STATUS_PHASE",
+	"SHUTDOWN",
+};
+
+/**
+ * struct iudma_ch_cfg - Static configuration for an IUDMA channel.
+ * @ep_num: USB endpoint number.
+ * @n_bds: Number of buffer descriptors in the ring.
+ * @ep_type: Endpoint type (control, bulk, interrupt).
+ * @dir: Direction (in, out).
+ * @n_fifo_slots: Number of FIFO entries to allocate for this channel.
+ * @max_pkt_hs: Maximum packet size in high speed mode.
+ * @max_pkt_fs: Maximum packet size in full speed mode.
+ */
+struct iudma_ch_cfg {
+	int				ep_num;
+	int				n_bds;
+	int				ep_type;
+	int				dir;
+	int				n_fifo_slots;
+	int				max_pkt_hs;
+	int				max_pkt_fs;
+};
+
+static const struct iudma_ch_cfg iudma_defaults[] = {
+
+	/* This controller was designed to support a CDC/RNDIS application.
+	   It may be possible to reconfigure some of the endpoints, but
+	   the hardware limitations (FIFO sizing and number of DMA channels)
+	   may significantly impact flexibility and/or stability.  Change
+	   these values at your own risk.
+
+	      ep_num       ep_type           n_fifo_slots    max_pkt_fs
+	idx      |  n_bds     |         dir       |  max_pkt_hs  |
+	 |       |    |       |          |        |      |       |       */
+	[0] = { -1,   4, BCMEP_CTRL, BCMEP_OUT,  32,    64,     64 },
+	[1] = {  0,   4, BCMEP_CTRL, BCMEP_OUT,  32,    64,     64 },
+	[2] = {  2,  16, BCMEP_BULK, BCMEP_OUT, 128,   512,     64 },
+	[3] = {  1,  16, BCMEP_BULK, BCMEP_IN,  128,   512,     64 },
+	[4] = {  4,   4, BCMEP_INTR, BCMEP_OUT,  32,    64,     64 },
+	[5] = {  3,   4, BCMEP_INTR, BCMEP_IN,   32,    64,     64 },
+};
+
+struct bcm63xx_udc;
+
+/**
+ * struct iudma_ch - Represents the current state of a single IUDMA channel.
+ * @ch_idx: IUDMA channel index (0 to BCM63XX_NUM_IUDMA-1).
+ * @ep_num: USB endpoint number.  -1 for ep0 RX.
+ * @enabled: Whether bcm63xx_ep_enable() has been called.
+ * @max_pkt: "Chunk size" on the USB interface.  Based on interface speed.
+ * @is_tx: true for TX, false for RX.
+ * @bep: Pointer to the associated endpoint.  NULL for ep0 RX.
+ * @udc: Reference to the device controller.
+ * @read_bd: Next buffer descriptor to reap from the hardware.
+ * @write_bd: Next BD available for a new packet.
+ * @end_bd: Points to the final BD in the ring.
+ * @n_bds_used: Number of BD entries currently occupied.
+ * @bd_ring: Base pointer to the BD ring.
+ * @bd_ring_dma: Physical (DMA) address of bd_ring.
+ * @n_bds: Total number of BDs in the ring.
+ *
+ * ep0 has two IUDMA channels (IUDMA_EP0_RXCHAN and IUDMA_EP0_TXCHAN), as it is
+ * bidirectional.  The "struct usb_ep" associated with ep0 is for TX (IN)
+ * only.
+ *
+ * Each bulk/intr endpoint has a single IUDMA channel and a single
+ * struct usb_ep.
+ */
+struct iudma_ch {
+	unsigned int			ch_idx;
+	int				ep_num;
+	bool				enabled;
+	int				max_pkt;
+	bool				is_tx;
+	struct bcm63xx_ep		*bep;
+	struct bcm63xx_udc		*udc;
+
+	struct bcm_enet_desc		*read_bd;
+	struct bcm_enet_desc		*write_bd;
+	struct bcm_enet_desc		*end_bd;
+	int				n_bds_used;
+
+	struct bcm_enet_desc		*bd_ring;
+	dma_addr_t			bd_ring_dma;
+	unsigned int			n_bds;
+};
+
+/**
+ * struct bcm63xx_ep - Internal (driver) state of a single endpoint.
+ * @ep_num: USB endpoint number.
+ * @iudma: Pointer to IUDMA channel state.
+ * @ep: USB gadget layer representation of the EP.
+ * @udc: Reference to the device controller.
+ * @queue: Linked list of outstanding requests for this EP.
+ * @halted: 1 if the EP is stalled; 0 otherwise.
+ */
+struct bcm63xx_ep {
+	unsigned int			ep_num;
+	struct iudma_ch			*iudma;
+	struct usb_ep			ep;
+	struct bcm63xx_udc		*udc;
+	struct list_head		queue;
+	unsigned			halted:1;
+};
+
+/**
+ * struct bcm63xx_req - Internal (driver) state of a single request.
+ * @queue: Links back to the EP's request list.
+ * @req: USB gadget layer representation of the request.
+ * @offset: Current byte offset into the data buffer (next byte to queue).
+ * @bd_bytes: Number of data bytes in outstanding BD entries.
+ * @iudma: IUDMA channel used for the request.
+ */
+struct bcm63xx_req {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+	unsigned int			offset;
+	unsigned int			bd_bytes;
+	struct iudma_ch			*iudma;
+};
+
+/**
+ * struct bcm63xx_udc - Driver/hardware private context.
+ * @lock: Spinlock to mediate access to this struct, and (most) HW regs.
+ * @dev: Generic Linux device structure.
+ * @pd: Platform data (board/port info).
+ * @usbd_clk: Clock descriptor for the USB device block.
+ * @usbh_clk: Clock descriptor for the USB host block.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ * @usbd_regs: Base address of the USBD/USB20D block.
+ * @iudma_regs: Base address of the USBD's associated IUDMA block.
+ * @bep: Array of endpoints, including ep0.
+ * @iudma: Array of all IUDMA channels used by this controller.
+ * @cfg: USB configuration number, from SET_CONFIGURATION wValue.
+ * @iface: USB interface number, from SET_INTERFACE wIndex.
+ * @alt_iface: USB alt interface number, from SET_INTERFACE wValue.
+ * @ep0_ctrl_req: Request object for bcm63xx_udc-initiated ep0 transactions.
+ * @ep0_ctrl_buf: Data buffer for ep0_ctrl_req.
+ * @ep0state: Current state of the ep0 state machine.
+ * @ep0_wq: Workqueue struct used to wake up the ep0 state machine.
+ * @wedgemap: Bitmap of wedged endpoints.
+ * @ep0_req_reset: USB reset is pending.
+ * @ep0_req_set_cfg: Need to spoof a SET_CONFIGURATION packet.
+ * @ep0_req_set_iface: Need to spoof a SET_INTERFACE packet.
+ * @ep0_req_shutdown: Driver is shutting down; requesting ep0 to halt activity.
+ * @ep0_req_completed: ep0 request has completed; worker has not seen it yet.
+ * @ep0_reply: Pending reply from gadget driver.
+ * @ep0_request: Outstanding ep0 request.
+ * @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>.
+ */
+struct bcm63xx_udc {
+	spinlock_t			lock;
+
+	struct device			*dev;
+	struct bcm63xx_usbd_platform_data *pd;
+	struct clk			*usbd_clk;
+	struct clk			*usbh_clk;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	void __iomem			*usbd_regs;
+	void __iomem			*iudma_regs;
+
+	struct bcm63xx_ep		bep[BCM63XX_NUM_EP];
+	struct iudma_ch			iudma[BCM63XX_NUM_IUDMA];
+
+	int				cfg;
+	int				iface;
+	int				alt_iface;
+
+	struct bcm63xx_req		ep0_ctrl_req;
+	u8				*ep0_ctrl_buf;
+
+	int				ep0state;
+	struct work_struct		ep0_wq;
+
+	unsigned long			wedgemap;
+
+	unsigned			ep0_req_reset:1;
+	unsigned			ep0_req_set_cfg:1;
+	unsigned			ep0_req_set_iface:1;
+	unsigned			ep0_req_shutdown:1;
+
+	unsigned			ep0_req_completed:1;
+	struct usb_request		*ep0_reply;
+	struct usb_request		*ep0_request;
+
+	struct dentry			*debugfs_root;
+};
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops;
+
+/***********************************************************************
+ * Convenience functions
+ ***********************************************************************/
+
+static inline struct bcm63xx_udc *gadget_to_udc(struct usb_gadget *g)
+{
+	return container_of(g, struct bcm63xx_udc, gadget);
+}
+
+static inline struct bcm63xx_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct bcm63xx_ep, ep);
+}
+
+static inline struct bcm63xx_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct bcm63xx_req, req);
+}
+
+static inline u32 usbd_readl(struct bcm63xx_udc *udc, u32 off)
+{
+	return bcm_readl(udc->usbd_regs + off);
+}
+
+static inline void usbd_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+	bcm_writel(val, udc->usbd_regs + off);
+}
+
+static inline u32 usb_dma_readl(struct bcm63xx_udc *udc, u32 off)
+{
+	return bcm_readl(udc->iudma_regs + off);
+}
+
+static inline void usb_dma_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+	bcm_writel(val, udc->iudma_regs + off);
+}
+
+static inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off, int chan)
+{
+	return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off +
+			(ENETDMA_CHAN_WIDTH * chan));
+}
+
+static inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off,
+					int chan)
+{
+	bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off +
+			(ENETDMA_CHAN_WIDTH * chan));
+}
+
+static inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off, int chan)
+{
+	return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off +
+			(ENETDMA_CHAN_WIDTH * chan));
+}
+
+static inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off,
+					int chan)
+{
+	bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off +
+			(ENETDMA_CHAN_WIDTH * chan));
+}
+
+static inline void set_clocks(struct bcm63xx_udc *udc, bool is_enabled)
+{
+	if (is_enabled) {
+		clk_enable(udc->usbh_clk);
+		clk_enable(udc->usbd_clk);
+		udelay(10);
+	} else {
+		clk_disable(udc->usbd_clk);
+		clk_disable(udc->usbh_clk);
+	}
+}
+
+/***********************************************************************
+ * Low-level IUDMA / FIFO operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_dma_select - Helper function to set up the init_sel signal.
+ * @udc: Reference to the device controller.
+ * @idx: Desired init_sel value.
+ *
+ * The "init_sel" signal is used as a selection index for both endpoints
+ * and IUDMA channels.  Since these do not map 1:1, the use of this signal
+ * depends on the context.
+ */
+static void bcm63xx_ep_dma_select(struct bcm63xx_udc *udc, int idx)
+{
+	u32 val = usbd_readl(udc, USBD_CONTROL_REG);
+
+	val &= ~USBD_CONTROL_INIT_SEL_MASK;
+	val |= idx << USBD_CONTROL_INIT_SEL_SHIFT;
+	usbd_writel(udc, val, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_set_stall - Enable/disable stall on one endpoint.
+ * @udc: Reference to the device controller.
+ * @bep: Endpoint on which to operate.
+ * @is_stalled: true to enable stall, false to disable.
+ *
+ * See notes in bcm63xx_update_wedge() regarding automatic clearing of
+ * halt/stall conditions.
+ */
+static void bcm63xx_set_stall(struct bcm63xx_udc *udc, struct bcm63xx_ep *bep,
+	bool is_stalled)
+{
+	u32 val;
+
+	val = USBD_STALL_UPDATE_MASK |
+		(is_stalled ? USBD_STALL_ENABLE_MASK : 0) |
+		(bep->ep_num << USBD_STALL_EPNUM_SHIFT);
+	usbd_writel(udc, val, USBD_STALL_REG);
+}
+
+/**
+ * bcm63xx_fifo_setup - (Re)initialize FIFO boundaries and settings.
+ * @udc: Reference to the device controller.
+ *
+ * These parameters depend on the USB link speed.  Settings are
+ * per-IUDMA-channel-pair.
+ */
+static void bcm63xx_fifo_setup(struct bcm63xx_udc *udc)
+{
+	int is_hs = udc->gadget.speed == USB_SPEED_HIGH;
+	u32 i, val, rx_fifo_slot, tx_fifo_slot;
+
+	/* set up FIFO boundaries and packet sizes; this is done in pairs */
+	rx_fifo_slot = tx_fifo_slot = 0;
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i += 2) {
+		const struct iudma_ch_cfg *rx_cfg = &iudma_defaults[i];
+		const struct iudma_ch_cfg *tx_cfg = &iudma_defaults[i + 1];
+
+		bcm63xx_ep_dma_select(udc, i >> 1);
+
+		val = (rx_fifo_slot << USBD_RXFIFO_CONFIG_START_SHIFT) |
+			((rx_fifo_slot + rx_cfg->n_fifo_slots - 1) <<
+			 USBD_RXFIFO_CONFIG_END_SHIFT);
+		rx_fifo_slot += rx_cfg->n_fifo_slots;
+		usbd_writel(udc, val, USBD_RXFIFO_CONFIG_REG);
+		usbd_writel(udc,
+			    is_hs ? rx_cfg->max_pkt_hs : rx_cfg->max_pkt_fs,
+			    USBD_RXFIFO_EPSIZE_REG);
+
+		val = (tx_fifo_slot << USBD_TXFIFO_CONFIG_START_SHIFT) |
+			((tx_fifo_slot + tx_cfg->n_fifo_slots - 1) <<
+			 USBD_TXFIFO_CONFIG_END_SHIFT);
+		tx_fifo_slot += tx_cfg->n_fifo_slots;
+		usbd_writel(udc, val, USBD_TXFIFO_CONFIG_REG);
+		usbd_writel(udc,
+			    is_hs ? tx_cfg->max_pkt_hs : tx_cfg->max_pkt_fs,
+			    USBD_TXFIFO_EPSIZE_REG);
+
+		usbd_readl(udc, USBD_TXFIFO_EPSIZE_REG);
+	}
+}
+
+/**
+ * bcm63xx_fifo_reset_ep - Flush a single endpoint's FIFO.
+ * @udc: Reference to the device controller.
+ * @ep_num: Endpoint number.
+ */
+static void bcm63xx_fifo_reset_ep(struct bcm63xx_udc *udc, int ep_num)
+{
+	u32 val;
+
+	bcm63xx_ep_dma_select(udc, ep_num);
+
+	val = usbd_readl(udc, USBD_CONTROL_REG);
+	val |= USBD_CONTROL_FIFO_RESET_MASK;
+	usbd_writel(udc, val, USBD_CONTROL_REG);
+	usbd_readl(udc, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_fifo_reset - Flush all hardware FIFOs.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_fifo_reset(struct bcm63xx_udc *udc)
+{
+	int i;
+
+	for (i = 0; i < BCM63XX_NUM_FIFO_PAIRS; i++)
+		bcm63xx_fifo_reset_ep(udc, i);
+}
+
+/**
+ * bcm63xx_ep_init - Initial (one-time) endpoint initialization.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_ep_init(struct bcm63xx_udc *udc)
+{
+	u32 i, val;
+
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+		const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+
+		if (cfg->ep_num < 0)
+			continue;
+
+		bcm63xx_ep_dma_select(udc, cfg->ep_num);
+		val = (cfg->ep_type << USBD_EPNUM_TYPEMAP_TYPE_SHIFT) |
+			((i >> 1) << USBD_EPNUM_TYPEMAP_DMA_CH_SHIFT);
+		usbd_writel(udc, val, USBD_EPNUM_TYPEMAP_REG);
+	}
+}
+
+/**
+ * bcm63xx_ep_setup - Configure per-endpoint settings.
+ * @udc: Reference to the device controller.
+ *
+ * This needs to be rerun if the speed/cfg/intf/altintf changes.
+ */
+static void bcm63xx_ep_setup(struct bcm63xx_udc *udc)
+{
+	u32 val, i;
+
+	usbd_writel(udc, USBD_CSR_SETUPADDR_DEF, USBD_CSR_SETUPADDR_REG);
+
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+		const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+		int max_pkt = udc->gadget.speed == USB_SPEED_HIGH ?
+			      cfg->max_pkt_hs : cfg->max_pkt_fs;
+		int idx = cfg->ep_num;
+
+		udc->iudma[i].max_pkt = max_pkt;
+
+		if (idx < 0)
+			continue;
+		usb_ep_set_maxpacket_limit(&udc->bep[idx].ep, max_pkt);
+
+		val = (idx << USBD_CSR_EP_LOG_SHIFT) |
+		      (cfg->dir << USBD_CSR_EP_DIR_SHIFT) |
+		      (cfg->ep_type << USBD_CSR_EP_TYPE_SHIFT) |
+		      (udc->cfg << USBD_CSR_EP_CFG_SHIFT) |
+		      (udc->iface << USBD_CSR_EP_IFACE_SHIFT) |
+		      (udc->alt_iface << USBD_CSR_EP_ALTIFACE_SHIFT) |
+		      (max_pkt << USBD_CSR_EP_MAXPKT_SHIFT);
+		usbd_writel(udc, val, USBD_CSR_EP_REG(idx));
+	}
+}
+
+/**
+ * iudma_write - Queue a single IUDMA transaction.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ * @breq: Request containing the transaction data.
+ *
+ * For RX IUDMA, this will queue a single buffer descriptor, as RX IUDMA
+ * does not honor SOP/EOP so the handling of multiple buffers is ambiguous.
+ * So iudma_write() may be called several times to fulfill a single
+ * usb_request.
+ *
+ * For TX IUDMA, this can queue multiple buffer descriptors if needed.
+ */
+static void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma,
+	struct bcm63xx_req *breq)
+{
+	int first_bd = 1, last_bd = 0, extra_zero_pkt = 0;
+	unsigned int bytes_left = breq->req.length - breq->offset;
+	const int max_bd_bytes = !irq_coalesce && !iudma->is_tx ?
+		iudma->max_pkt : IUDMA_MAX_FRAGMENT;
+
+	iudma->n_bds_used = 0;
+	breq->bd_bytes = 0;
+	breq->iudma = iudma;
+
+	if ((bytes_left % iudma->max_pkt == 0) && bytes_left && breq->req.zero)
+		extra_zero_pkt = 1;
+
+	do {
+		struct bcm_enet_desc *d = iudma->write_bd;
+		u32 dmaflags = 0;
+		unsigned int n_bytes;
+
+		if (d == iudma->end_bd) {
+			dmaflags |= DMADESC_WRAP_MASK;
+			iudma->write_bd = iudma->bd_ring;
+		} else {
+			iudma->write_bd++;
+		}
+		iudma->n_bds_used++;
+
+		n_bytes = min_t(int, bytes_left, max_bd_bytes);
+		if (n_bytes)
+			dmaflags |= n_bytes << DMADESC_LENGTH_SHIFT;
+		else
+			dmaflags |= (1 << DMADESC_LENGTH_SHIFT) |
+				    DMADESC_USB_ZERO_MASK;
+
+		dmaflags |= DMADESC_OWNER_MASK;
+		if (first_bd) {
+			dmaflags |= DMADESC_SOP_MASK;
+			first_bd = 0;
+		}
+
+		/*
+		 * extra_zero_pkt forces one more iteration through the loop
+		 * after all data is queued up, to send the zero packet
+		 */
+		if (extra_zero_pkt && !bytes_left)
+			extra_zero_pkt = 0;
+
+		if (!iudma->is_tx || iudma->n_bds_used == iudma->n_bds ||
+		    (n_bytes == bytes_left && !extra_zero_pkt)) {
+			last_bd = 1;
+			dmaflags |= DMADESC_EOP_MASK;
+		}
+
+		d->address = breq->req.dma + breq->offset;
+		mb();
+		d->len_stat = dmaflags;
+
+		breq->offset += n_bytes;
+		breq->bd_bytes += n_bytes;
+		bytes_left -= n_bytes;
+	} while (!last_bd);
+
+	usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK,
+			ENETDMAC_CHANCFG_REG, iudma->ch_idx);
+}
+
+/**
+ * iudma_read - Check for IUDMA buffer completion.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ *
+ * This checks to see if ALL of the outstanding BDs on the DMA channel
+ * have been filled.  If so, it returns the actual transfer length;
+ * otherwise it returns -EBUSY.
+ */
+static int iudma_read(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+	int i, actual_len = 0;
+	struct bcm_enet_desc *d = iudma->read_bd;
+
+	if (!iudma->n_bds_used)
+		return -EINVAL;
+
+	for (i = 0; i < iudma->n_bds_used; i++) {
+		u32 dmaflags;
+
+		dmaflags = d->len_stat;
+
+		if (dmaflags & DMADESC_OWNER_MASK)
+			return -EBUSY;
+
+		actual_len += (dmaflags & DMADESC_LENGTH_MASK) >>
+			      DMADESC_LENGTH_SHIFT;
+		if (d == iudma->end_bd)
+			d = iudma->bd_ring;
+		else
+			d++;
+	}
+
+	iudma->read_bd = d;
+	iudma->n_bds_used = 0;
+	return actual_len;
+}
+
+/**
+ * iudma_reset_channel - Stop DMA on a single channel.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to reset.
+ */
+static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+	int timeout = IUDMA_RESET_TIMEOUT_US;
+	struct bcm_enet_desc *d;
+	int ch_idx = iudma->ch_idx;
+
+	if (!iudma->is_tx)
+		bcm63xx_fifo_reset_ep(udc, max(0, iudma->ep_num));
+
+	/* stop DMA, then wait for the hardware to wrap up */
+	usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG, ch_idx);
+
+	while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx) &
+				   ENETDMAC_CHANCFG_EN_MASK) {
+		udelay(1);
+
+		/* repeatedly flush the FIFO data until the BD completes */
+		if (iudma->is_tx && iudma->ep_num >= 0)
+			bcm63xx_fifo_reset_ep(udc, iudma->ep_num);
+
+		if (!timeout--) {
+			dev_err(udc->dev, "can't reset IUDMA channel %d\n",
+				ch_idx);
+			break;
+		}
+		if (timeout == IUDMA_RESET_TIMEOUT_US / 2) {
+			dev_warn(udc->dev, "forcibly halting IUDMA channel %d\n",
+				 ch_idx);
+			usb_dmac_writel(udc, ENETDMAC_CHANCFG_BUFHALT_MASK,
+					ENETDMAC_CHANCFG_REG, ch_idx);
+		}
+	}
+	usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG, ch_idx);
+
+	/* don't leave "live" HW-owned entries for the next guy to step on */
+	for (d = iudma->bd_ring; d <= iudma->end_bd; d++)
+		d->len_stat = 0;
+	mb();
+
+	iudma->read_bd = iudma->write_bd = iudma->bd_ring;
+	iudma->n_bds_used = 0;
+
+	/* set up IRQs, UBUS burst size, and BD base for this channel */
+	usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+			ENETDMAC_IRMASK_REG, ch_idx);
+	usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG, ch_idx);
+
+	usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG, ch_idx);
+	usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG, ch_idx);
+}
+
+/**
+ * iudma_init_channel - One-time IUDMA channel initialization.
+ * @udc: Reference to the device controller.
+ * @ch_idx: Channel to initialize.
+ */
+static int iudma_init_channel(struct bcm63xx_udc *udc, unsigned int ch_idx)
+{
+	struct iudma_ch *iudma = &udc->iudma[ch_idx];
+	const struct iudma_ch_cfg *cfg = &iudma_defaults[ch_idx];
+	unsigned int n_bds = cfg->n_bds;
+	struct bcm63xx_ep *bep = NULL;
+
+	iudma->ep_num = cfg->ep_num;
+	iudma->ch_idx = ch_idx;
+	iudma->is_tx = !!(ch_idx & 0x01);
+	if (iudma->ep_num >= 0) {
+		bep = &udc->bep[iudma->ep_num];
+		bep->iudma = iudma;
+		INIT_LIST_HEAD(&bep->queue);
+	}
+
+	iudma->bep = bep;
+	iudma->udc = udc;
+
+	/* ep0 is always active; others are controlled by the gadget driver */
+	if (iudma->ep_num <= 0)
+		iudma->enabled = true;
+
+	iudma->n_bds = n_bds;
+	iudma->bd_ring = dmam_alloc_coherent(udc->dev,
+		n_bds * sizeof(struct bcm_enet_desc),
+		&iudma->bd_ring_dma, GFP_KERNEL);
+	if (!iudma->bd_ring)
+		return -ENOMEM;
+	iudma->end_bd = &iudma->bd_ring[n_bds - 1];
+
+	return 0;
+}
+
+/**
+ * iudma_init - One-time initialization of all IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Enable DMA, flush channels, and enable global IUDMA IRQs.
+ */
+static int iudma_init(struct bcm63xx_udc *udc)
+{
+	int i, rc;
+
+	usb_dma_writel(udc, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
+
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+		rc = iudma_init_channel(udc, i);
+		if (rc)
+			return rc;
+		iudma_reset_channel(udc, &udc->iudma[i]);
+	}
+
+	usb_dma_writel(udc, BIT(BCM63XX_NUM_IUDMA)-1, ENETDMA_GLB_IRQMASK_REG);
+	return 0;
+}
+
+/**
+ * iudma_uninit - Uninitialize IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Kill global IUDMA IRQs, flush channels, and kill DMA.
+ */
+static void iudma_uninit(struct bcm63xx_udc *udc)
+{
+	int i;
+
+	usb_dma_writel(udc, 0, ENETDMA_GLB_IRQMASK_REG);
+
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i++)
+		iudma_reset_channel(udc, &udc->iudma[i]);
+
+	usb_dma_writel(udc, 0, ENETDMA_CFG_REG);
+}
+
+/***********************************************************************
+ * Other low-level USBD operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_set_ctrl_irqs - Mask/unmask control path interrupts.
+ * @udc: Reference to the device controller.
+ * @enable_irqs: true to enable, false to disable.
+ */
+static void bcm63xx_set_ctrl_irqs(struct bcm63xx_udc *udc, bool enable_irqs)
+{
+	u32 val;
+
+	usbd_writel(udc, 0, USBD_STATUS_REG);
+
+	val = BIT(USBD_EVENT_IRQ_USB_RESET) |
+	      BIT(USBD_EVENT_IRQ_SETUP) |
+	      BIT(USBD_EVENT_IRQ_SETCFG) |
+	      BIT(USBD_EVENT_IRQ_SETINTF) |
+	      BIT(USBD_EVENT_IRQ_USB_LINK);
+	usbd_writel(udc, enable_irqs ? val : 0, USBD_EVENT_IRQ_MASK_REG);
+	usbd_writel(udc, val, USBD_EVENT_IRQ_STATUS_REG);
+}
+
+/**
+ * bcm63xx_select_phy_mode - Select between USB device and host mode.
+ * @udc: Reference to the device controller.
+ * @is_device: true for device, false for host.
+ *
+ * This should probably be reworked to use the drivers/usb/otg
+ * infrastructure.
+ *
+ * By default, the AFE/pullups are disabled in device mode, until
+ * bcm63xx_select_pullup() is called.
+ */
+static void bcm63xx_select_phy_mode(struct bcm63xx_udc *udc, bool is_device)
+{
+	u32 val, portmask = BIT(udc->pd->port_no);
+
+	if (BCMCPU_IS_6328()) {
+		/* configure pinmux to sense VBUS signal */
+		val = bcm_gpio_readl(GPIO_PINMUX_OTHR_REG);
+		val &= ~GPIO_PINMUX_OTHR_6328_USB_MASK;
+		val |= is_device ? GPIO_PINMUX_OTHR_6328_USB_DEV :
+			       GPIO_PINMUX_OTHR_6328_USB_HOST;
+		bcm_gpio_writel(val, GPIO_PINMUX_OTHR_REG);
+	}
+
+	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+	if (is_device) {
+		val |= (portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+		val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+	} else {
+		val &= ~(portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+		val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+	}
+	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+
+	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG);
+	if (is_device)
+		val |= USBH_PRIV_SWAP_USBD_MASK;
+	else
+		val &= ~USBH_PRIV_SWAP_USBD_MASK;
+	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG);
+}
+
+/**
+ * bcm63xx_select_pullup - Enable/disable the pullup on D+
+ * @udc: Reference to the device controller.
+ * @is_on: true to enable the pullup, false to disable.
+ *
+ * If the pullup is active, the host will sense a FS/HS device connected to
+ * the port.  If the pullup is inactive, the host will think the USB
+ * device has been disconnected.
+ */
+static void bcm63xx_select_pullup(struct bcm63xx_udc *udc, bool is_on)
+{
+	u32 val, portmask = BIT(udc->pd->port_no);
+
+	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+	if (is_on)
+		val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+	else
+		val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+}
+
+/**
+ * bcm63xx_uninit_udc_hw - Shut down the hardware prior to driver removal.
+ * @udc: Reference to the device controller.
+ *
+ * This just masks the IUDMA IRQs and releases the clocks.  It is assumed
+ * that bcm63xx_udc_stop() has already run, and the clocks are stopped.
+ */
+static void bcm63xx_uninit_udc_hw(struct bcm63xx_udc *udc)
+{
+	set_clocks(udc, true);
+	iudma_uninit(udc);
+	set_clocks(udc, false);
+
+	clk_put(udc->usbd_clk);
+	clk_put(udc->usbh_clk);
+}
+
+/**
+ * bcm63xx_init_udc_hw - Initialize the controller hardware and data structures.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc)
+{
+	int i, rc = 0;
+	u32 val;
+
+	udc->ep0_ctrl_buf = devm_kzalloc(udc->dev, BCM63XX_MAX_CTRL_PKT,
+					 GFP_KERNEL);
+	if (!udc->ep0_ctrl_buf)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	for (i = 0; i < BCM63XX_NUM_EP; i++) {
+		struct bcm63xx_ep *bep = &udc->bep[i];
+
+		bep->ep.name = bcm63xx_ep_info[i].name;
+		bep->ep.caps = bcm63xx_ep_info[i].caps;
+		bep->ep_num = i;
+		bep->ep.ops = &bcm63xx_udc_ep_ops;
+		list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list);
+		bep->halted = 0;
+		usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT);
+		bep->udc = udc;
+		bep->ep.desc = NULL;
+		INIT_LIST_HEAD(&bep->queue);
+	}
+
+	udc->gadget.ep0 = &udc->bep[0].ep;
+	list_del(&udc->bep[0].ep.ep_list);
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->ep0state = EP0_SHUTDOWN;
+
+	udc->usbh_clk = clk_get(udc->dev, "usbh");
+	if (IS_ERR(udc->usbh_clk))
+		return -EIO;
+
+	udc->usbd_clk = clk_get(udc->dev, "usbd");
+	if (IS_ERR(udc->usbd_clk)) {
+		clk_put(udc->usbh_clk);
+		return -EIO;
+	}
+
+	set_clocks(udc, true);
+
+	val = USBD_CONTROL_AUTO_CSRS_MASK |
+	      USBD_CONTROL_DONE_CSRS_MASK |
+	      (irq_coalesce ? USBD_CONTROL_RXZSCFG_MASK : 0);
+	usbd_writel(udc, val, USBD_CONTROL_REG);
+
+	val = USBD_STRAPS_APP_SELF_PWR_MASK |
+	      USBD_STRAPS_APP_RAM_IF_MASK |
+	      USBD_STRAPS_APP_CSRPRGSUP_MASK |
+	      USBD_STRAPS_APP_8BITPHY_MASK |
+	      USBD_STRAPS_APP_RMTWKUP_MASK;
+
+	if (udc->gadget.max_speed == USB_SPEED_HIGH)
+		val |= (BCM63XX_SPD_HIGH << USBD_STRAPS_SPEED_SHIFT);
+	else
+		val |= (BCM63XX_SPD_FULL << USBD_STRAPS_SPEED_SHIFT);
+	usbd_writel(udc, val, USBD_STRAPS_REG);
+
+	bcm63xx_set_ctrl_irqs(udc, false);
+
+	usbd_writel(udc, 0, USBD_EVENT_IRQ_CFG_LO_REG);
+
+	val = USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_ENUM_ON) |
+	      USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_SET_CSRS);
+	usbd_writel(udc, val, USBD_EVENT_IRQ_CFG_HI_REG);
+
+	rc = iudma_init(udc);
+	set_clocks(udc, false);
+	if (rc)
+		bcm63xx_uninit_udc_hw(udc);
+
+	return 0;
+}
+
+/***********************************************************************
+ * Standard EP gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_enable - Enable one endpoint.
+ * @ep: Endpoint to enable.
+ * @desc: Contains max packet, direction, etc.
+ *
+ * Most of the endpoint parameters are fixed in this controller, so there
+ * isn't much for this function to do.
+ */
+static int bcm63xx_ep_enable(struct usb_ep *ep,
+	const struct usb_endpoint_descriptor *desc)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	struct iudma_ch *iudma = bep->iudma;
+	unsigned long flags;
+
+	if (!ep || !desc || ep->name == bcm63xx_ep0name)
+		return -EINVAL;
+
+	if (!udc->driver)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (iudma->enabled) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+
+	iudma->enabled = true;
+	BUG_ON(!list_empty(&bep->queue));
+
+	iudma_reset_channel(udc, iudma);
+
+	bep->halted = 0;
+	bcm63xx_set_stall(udc, bep, false);
+	clear_bit(bep->ep_num, &udc->wedgemap);
+
+	ep->desc = desc;
+	ep->maxpacket = usb_endpoint_maxp(desc);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/**
+ * bcm63xx_ep_disable - Disable one endpoint.
+ * @ep: Endpoint to disable.
+ */
+static int bcm63xx_ep_disable(struct usb_ep *ep)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	struct iudma_ch *iudma = bep->iudma;
+	struct bcm63xx_req *breq, *n;
+	unsigned long flags;
+
+	if (!ep || !ep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (!iudma->enabled) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+	iudma->enabled = false;
+
+	iudma_reset_channel(udc, iudma);
+
+	if (!list_empty(&bep->queue)) {
+		list_for_each_entry_safe(breq, n, &bep->queue, queue) {
+			usb_gadget_unmap_request(&udc->gadget, &breq->req,
+						 iudma->is_tx);
+			list_del(&breq->queue);
+			breq->req.status = -ESHUTDOWN;
+
+			spin_unlock_irqrestore(&udc->lock, flags);
+			usb_gadget_giveback_request(&iudma->bep->ep, &breq->req);
+			spin_lock_irqsave(&udc->lock, flags);
+		}
+	}
+	ep->desc = NULL;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/**
+ * bcm63xx_udc_alloc_request - Allocate a new request.
+ * @ep: Endpoint associated with the request.
+ * @mem_flags: Flags to pass to kzalloc().
+ */
+static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep,
+	gfp_t mem_flags)
+{
+	struct bcm63xx_req *breq;
+
+	breq = kzalloc(sizeof(*breq), mem_flags);
+	if (!breq)
+		return NULL;
+	return &breq->req;
+}
+
+/**
+ * bcm63xx_udc_free_request - Free a request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to free.
+ */
+static void bcm63xx_udc_free_request(struct usb_ep *ep,
+	struct usb_request *req)
+{
+	struct bcm63xx_req *breq = our_req(req);
+	kfree(breq);
+}
+
+/**
+ * bcm63xx_udc_queue - Queue up a new request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to add.
+ * @mem_flags: Unused.
+ *
+ * If the queue is empty, start this request immediately.  Otherwise, add
+ * it to the list.
+ *
+ * ep0 replies are sent through this function from the gadget driver, but
+ * they are treated differently because they need to be handled by the ep0
+ * state machine.  (Sometimes they are replies to control requests that
+ * were spoofed by this driver, and so they shouldn't be transmitted at all.)
+ */
+static int bcm63xx_udc_queue(struct usb_ep *ep, struct usb_request *req,
+	gfp_t mem_flags)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	struct bcm63xx_req *breq = our_req(req);
+	unsigned long flags;
+	int rc = 0;
+
+	if (unlikely(!req || !req->complete || !req->buf || !ep))
+		return -EINVAL;
+
+	req->actual = 0;
+	req->status = 0;
+	breq->offset = 0;
+
+	if (bep == &udc->bep[0]) {
+		/* only one reply per request, please */
+		if (udc->ep0_reply)
+			return -EINVAL;
+
+		udc->ep0_reply = req;
+		schedule_work(&udc->ep0_wq);
+		return 0;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (!bep->iudma->enabled) {
+		rc = -ESHUTDOWN;
+		goto out;
+	}
+
+	rc = usb_gadget_map_request(&udc->gadget, req, bep->iudma->is_tx);
+	if (rc == 0) {
+		list_add_tail(&breq->queue, &bep->queue);
+		if (list_is_singular(&bep->queue))
+			iudma_write(udc, bep->iudma, breq);
+	}
+
+out:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return rc;
+}
+
+/**
+ * bcm63xx_udc_dequeue - Remove a pending request from the queue.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to remove.
+ *
+ * If the request is not at the head of the queue, this is easy - just nuke
+ * it.  If the request is at the head of the queue, we'll need to stop the
+ * DMA transaction and then queue up the successor.
+ */
+static int bcm63xx_udc_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	struct bcm63xx_req *breq = our_req(req), *cur;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (list_empty(&bep->queue)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	cur = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+	usb_gadget_unmap_request(&udc->gadget, &breq->req, bep->iudma->is_tx);
+
+	if (breq == cur) {
+		iudma_reset_channel(udc, bep->iudma);
+		list_del(&breq->queue);
+
+		if (!list_empty(&bep->queue)) {
+			struct bcm63xx_req *next;
+
+			next = list_first_entry(&bep->queue,
+				struct bcm63xx_req, queue);
+			iudma_write(udc, bep->iudma, next);
+		}
+	} else {
+		list_del(&breq->queue);
+	}
+
+out:
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	req->status = -ESHUTDOWN;
+	req->complete(ep, req);
+
+	return rc;
+}
+
+/**
+ * bcm63xx_udc_set_halt - Enable/disable STALL flag in the hardware.
+ * @ep: Endpoint to halt.
+ * @value: Zero to clear halt; nonzero to set halt.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_halt(struct usb_ep *ep, int value)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	bcm63xx_set_stall(udc, bep, !!value);
+	bep->halted = value;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * bcm63xx_udc_set_wedge - Stall the endpoint until the next reset.
+ * @ep: Endpoint to wedge.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_wedge(struct usb_ep *ep)
+{
+	struct bcm63xx_ep *bep = our_ep(ep);
+	struct bcm63xx_udc *udc = bep->udc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	set_bit(bep->ep_num, &udc->wedgemap);
+	bcm63xx_set_stall(udc, bep, true);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops = {
+	.enable		= bcm63xx_ep_enable,
+	.disable	= bcm63xx_ep_disable,
+
+	.alloc_request	= bcm63xx_udc_alloc_request,
+	.free_request	= bcm63xx_udc_free_request,
+
+	.queue		= bcm63xx_udc_queue,
+	.dequeue	= bcm63xx_udc_dequeue,
+
+	.set_halt	= bcm63xx_udc_set_halt,
+	.set_wedge	= bcm63xx_udc_set_wedge,
+};
+
+/***********************************************************************
+ * EP0 handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep0_setup_callback - Drop spinlock to invoke ->setup callback.
+ * @udc: Reference to the device controller.
+ * @ctrl: 8-byte SETUP request.
+ */
+static int bcm63xx_ep0_setup_callback(struct bcm63xx_udc *udc,
+	struct usb_ctrlrequest *ctrl)
+{
+	int rc;
+
+	spin_unlock_irq(&udc->lock);
+	rc = udc->driver->setup(&udc->gadget, ctrl);
+	spin_lock_irq(&udc->lock);
+	return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_cfg - Synthesize a SET_CONFIGURATION request.
+ * @udc: Reference to the device controller.
+ *
+ * Many standard requests are handled automatically in the hardware, but
+ * we still need to pass them to the gadget driver so that it can
+ * reconfigure the interfaces/endpoints if necessary.
+ *
+ * Unfortunately we are not able to send a STALL response if the host
+ * requests an invalid configuration.  If this happens, we'll have to be
+ * content with printing a warning.
+ */
+static int bcm63xx_ep0_spoof_set_cfg(struct bcm63xx_udc *udc)
+{
+	struct usb_ctrlrequest ctrl;
+	int rc;
+
+	ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_DEVICE;
+	ctrl.bRequest = USB_REQ_SET_CONFIGURATION;
+	ctrl.wValue = cpu_to_le16(udc->cfg);
+	ctrl.wIndex = 0;
+	ctrl.wLength = 0;
+
+	rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+	if (rc < 0) {
+		dev_warn_ratelimited(udc->dev,
+			"hardware auto-acked bad SET_CONFIGURATION(%d) request\n",
+			udc->cfg);
+	}
+	return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_iface - Synthesize a SET_INTERFACE request.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_spoof_set_iface(struct bcm63xx_udc *udc)
+{
+	struct usb_ctrlrequest ctrl;
+	int rc;
+
+	ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_INTERFACE;
+	ctrl.bRequest = USB_REQ_SET_INTERFACE;
+	ctrl.wValue = cpu_to_le16(udc->alt_iface);
+	ctrl.wIndex = cpu_to_le16(udc->iface);
+	ctrl.wLength = 0;
+
+	rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+	if (rc < 0) {
+		dev_warn_ratelimited(udc->dev,
+			"hardware auto-acked bad SET_INTERFACE(%d,%d) request\n",
+			udc->iface, udc->alt_iface);
+	}
+	return rc;
+}
+
+/**
+ * bcm63xx_ep0_map_write - dma_map and iudma_write a single request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @req: USB gadget layer representation of the request.
+ */
+static void bcm63xx_ep0_map_write(struct bcm63xx_udc *udc, int ch_idx,
+	struct usb_request *req)
+{
+	struct bcm63xx_req *breq = our_req(req);
+	struct iudma_ch *iudma = &udc->iudma[ch_idx];
+
+	BUG_ON(udc->ep0_request);
+	udc->ep0_request = req;
+
+	req->actual = 0;
+	breq->offset = 0;
+	usb_gadget_map_request(&udc->gadget, req, iudma->is_tx);
+	iudma_write(udc, iudma, breq);
+}
+
+/**
+ * bcm63xx_ep0_complete - Set completion status and "stage" the callback.
+ * @udc: Reference to the device controller.
+ * @req: USB gadget layer representation of the request.
+ * @status: Status to return to the gadget driver.
+ */
+static void bcm63xx_ep0_complete(struct bcm63xx_udc *udc,
+	struct usb_request *req, int status)
+{
+	req->status = status;
+	if (status)
+		req->actual = 0;
+	if (req->complete) {
+		spin_unlock_irq(&udc->lock);
+		req->complete(&udc->bep[0].ep, req);
+		spin_lock_irq(&udc->lock);
+	}
+}
+
+/**
+ * bcm63xx_ep0_nuke_reply - Abort request from the gadget driver due to
+ *   reset/shutdown.
+ * @udc: Reference to the device controller.
+ * @is_tx: Nonzero for TX (IN), zero for RX (OUT).
+ */
+static void bcm63xx_ep0_nuke_reply(struct bcm63xx_udc *udc, int is_tx)
+{
+	struct usb_request *req = udc->ep0_reply;
+
+	udc->ep0_reply = NULL;
+	usb_gadget_unmap_request(&udc->gadget, req, is_tx);
+	if (udc->ep0_request == req) {
+		udc->ep0_req_completed = 0;
+		udc->ep0_request = NULL;
+	}
+	bcm63xx_ep0_complete(udc, req, -ESHUTDOWN);
+}
+
+/**
+ * bcm63xx_ep0_read_complete - Close out the pending ep0 request; return
+ *   transfer len.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_read_complete(struct bcm63xx_udc *udc)
+{
+	struct usb_request *req = udc->ep0_request;
+
+	udc->ep0_req_completed = 0;
+	udc->ep0_request = NULL;
+
+	return req->actual;
+}
+
+/**
+ * bcm63xx_ep0_internal_request - Helper function to submit an ep0 request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @length: Number of bytes to TX/RX.
+ *
+ * Used for simple transfers performed by the ep0 worker.  This will always
+ * use ep0_ctrl_req / ep0_ctrl_buf.
+ */
+static void bcm63xx_ep0_internal_request(struct bcm63xx_udc *udc, int ch_idx,
+	int length)
+{
+	struct usb_request *req = &udc->ep0_ctrl_req.req;
+
+	req->buf = udc->ep0_ctrl_buf;
+	req->length = length;
+	req->complete = NULL;
+
+	bcm63xx_ep0_map_write(udc, ch_idx, req);
+}
+
+/**
+ * bcm63xx_ep0_do_setup - Parse new SETUP packet and decide how to handle it.
+ * @udc: Reference to the device controller.
+ *
+ * EP0_IDLE probably shouldn't ever happen.  EP0_REQUEUE means we're ready
+ * for the next packet.  Anything else means the transaction requires multiple
+ * stages of handling.
+ */
+static enum bcm63xx_ep0_state bcm63xx_ep0_do_setup(struct bcm63xx_udc *udc)
+{
+	int rc;
+	struct usb_ctrlrequest *ctrl = (void *)udc->ep0_ctrl_buf;
+
+	rc = bcm63xx_ep0_read_complete(udc);
+
+	if (rc < 0) {
+		dev_err(udc->dev, "missing SETUP packet\n");
+		return EP0_IDLE;
+	}
+
+	/*
+	 * Handle 0-byte IN STATUS acknowledgement.  The hardware doesn't
+	 * ALWAYS deliver these 100% of the time, so if we happen to see one,
+	 * just throw it away.
+	 */
+	if (rc == 0)
+		return EP0_REQUEUE;
+
+	/* Drop malformed SETUP packets */
+	if (rc != sizeof(*ctrl)) {
+		dev_warn_ratelimited(udc->dev,
+			"malformed SETUP packet (%d bytes)\n", rc);
+		return EP0_REQUEUE;
+	}
+
+	/* Process new SETUP packet arriving on ep0 */
+	rc = bcm63xx_ep0_setup_callback(udc, ctrl);
+	if (rc < 0) {
+		bcm63xx_set_stall(udc, &udc->bep[0], true);
+		return EP0_REQUEUE;
+	}
+
+	if (!ctrl->wLength)
+		return EP0_REQUEUE;
+	else if (ctrl->bRequestType & USB_DIR_IN)
+		return EP0_IN_DATA_PHASE_SETUP;
+	else
+		return EP0_OUT_DATA_PHASE_SETUP;
+}
+
+/**
+ * bcm63xx_ep0_do_idle - Check for outstanding requests if ep0 is idle.
+ * @udc: Reference to the device controller.
+ *
+ * In state EP0_IDLE, the RX descriptor is either pending, or has been
+ * filled with a SETUP packet from the host.  This function handles new
+ * SETUP packets, control IRQ events (which can generate fake SETUP packets),
+ * and reset/shutdown events.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_do_idle(struct bcm63xx_udc *udc)
+{
+	if (udc->ep0_req_reset) {
+		udc->ep0_req_reset = 0;
+	} else if (udc->ep0_req_set_cfg) {
+		udc->ep0_req_set_cfg = 0;
+		if (bcm63xx_ep0_spoof_set_cfg(udc) >= 0)
+			udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+	} else if (udc->ep0_req_set_iface) {
+		udc->ep0_req_set_iface = 0;
+		if (bcm63xx_ep0_spoof_set_iface(udc) >= 0)
+			udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+	} else if (udc->ep0_req_completed) {
+		udc->ep0state = bcm63xx_ep0_do_setup(udc);
+		return udc->ep0state == EP0_IDLE ? -EAGAIN : 0;
+	} else if (udc->ep0_req_shutdown) {
+		udc->ep0_req_shutdown = 0;
+		udc->ep0_req_completed = 0;
+		udc->ep0_request = NULL;
+		iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+		usb_gadget_unmap_request(&udc->gadget,
+			&udc->ep0_ctrl_req.req, 0);
+
+		/* bcm63xx_udc_pullup() is waiting for this */
+		mb();
+		udc->ep0state = EP0_SHUTDOWN;
+	} else if (udc->ep0_reply) {
+		/*
+		 * This could happen if a USB RESET shows up during an ep0
+		 * transaction (especially if a laggy driver like gadgetfs
+		 * is in use).
+		 */
+		dev_warn(udc->dev, "nuking unexpected reply\n");
+		bcm63xx_ep0_nuke_reply(udc, 0);
+	} else {
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+/**
+ * bcm63xx_ep0_one_round - Handle the current ep0 state.
+ * @udc: Reference to the device controller.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_one_round(struct bcm63xx_udc *udc)
+{
+	enum bcm63xx_ep0_state ep0state = udc->ep0state;
+	bool shutdown = udc->ep0_req_reset || udc->ep0_req_shutdown;
+
+	switch (udc->ep0state) {
+	case EP0_REQUEUE:
+		/* set up descriptor to receive SETUP packet */
+		bcm63xx_ep0_internal_request(udc, IUDMA_EP0_RXCHAN,
+					     BCM63XX_MAX_CTRL_PKT);
+		ep0state = EP0_IDLE;
+		break;
+	case EP0_IDLE:
+		return bcm63xx_ep0_do_idle(udc);
+	case EP0_IN_DATA_PHASE_SETUP:
+		/*
+		 * Normal case: TX request is in ep0_reply (queued by the
+		 * callback), or will be queued shortly.  When it's here,
+		 * send it to the HW and go to EP0_IN_DATA_PHASE_COMPLETE.
+		 *
+		 * Shutdown case: Stop waiting for the reply.  Just
+		 * REQUEUE->IDLE.  The gadget driver is NOT expected to
+		 * queue anything else now.
+		 */
+		if (udc->ep0_reply) {
+			bcm63xx_ep0_map_write(udc, IUDMA_EP0_TXCHAN,
+					      udc->ep0_reply);
+			ep0state = EP0_IN_DATA_PHASE_COMPLETE;
+		} else if (shutdown) {
+			ep0state = EP0_REQUEUE;
+		}
+		break;
+	case EP0_IN_DATA_PHASE_COMPLETE: {
+		/*
+		 * Normal case: TX packet (ep0_reply) is in flight; wait for
+		 * it to finish, then go back to REQUEUE->IDLE.
+		 *
+		 * Shutdown case: Reset the TX channel, send -ESHUTDOWN
+		 * completion to the gadget driver, then REQUEUE->IDLE.
+		 */
+		if (udc->ep0_req_completed) {
+			udc->ep0_reply = NULL;
+			bcm63xx_ep0_read_complete(udc);
+			/*
+			 * the "ack" sometimes gets eaten (see
+			 * bcm63xx_ep0_do_idle)
+			 */
+			ep0state = EP0_REQUEUE;
+		} else if (shutdown) {
+			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+			bcm63xx_ep0_nuke_reply(udc, 1);
+			ep0state = EP0_REQUEUE;
+		}
+		break;
+	}
+	case EP0_OUT_DATA_PHASE_SETUP:
+		/* Similar behavior to EP0_IN_DATA_PHASE_SETUP */
+		if (udc->ep0_reply) {
+			bcm63xx_ep0_map_write(udc, IUDMA_EP0_RXCHAN,
+					      udc->ep0_reply);
+			ep0state = EP0_OUT_DATA_PHASE_COMPLETE;
+		} else if (shutdown) {
+			ep0state = EP0_REQUEUE;
+		}
+		break;
+	case EP0_OUT_DATA_PHASE_COMPLETE: {
+		/* Similar behavior to EP0_IN_DATA_PHASE_COMPLETE */
+		if (udc->ep0_req_completed) {
+			udc->ep0_reply = NULL;
+			bcm63xx_ep0_read_complete(udc);
+
+			/* send 0-byte ack to host */
+			bcm63xx_ep0_internal_request(udc, IUDMA_EP0_TXCHAN, 0);
+			ep0state = EP0_OUT_STATUS_PHASE;
+		} else if (shutdown) {
+			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+			bcm63xx_ep0_nuke_reply(udc, 0);
+			ep0state = EP0_REQUEUE;
+		}
+		break;
+	}
+	case EP0_OUT_STATUS_PHASE:
+		/*
+		 * Normal case: 0-byte OUT ack packet is in flight; wait
+		 * for it to finish, then go back to REQUEUE->IDLE.
+		 *
+		 * Shutdown case: just cancel the transmission.  Don't bother
+		 * calling the completion, because it originated from this
+		 * function anyway.  Then go back to REQUEUE->IDLE.
+		 */
+		if (udc->ep0_req_completed) {
+			bcm63xx_ep0_read_complete(udc);
+			ep0state = EP0_REQUEUE;
+		} else if (shutdown) {
+			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+			udc->ep0_request = NULL;
+			ep0state = EP0_REQUEUE;
+		}
+		break;
+	case EP0_IN_FAKE_STATUS_PHASE: {
+		/*
+		 * Normal case: we spoofed a SETUP packet and are now
+		 * waiting for the gadget driver to send a 0-byte reply.
+		 * This doesn't actually get sent to the HW because the
+		 * HW has already sent its own reply.  Once we get the
+		 * response, return to IDLE.
+		 *
+		 * Shutdown case: return to IDLE immediately.
+		 *
+		 * Note that the ep0 RX descriptor has remained queued
+		 * (and possibly unfilled) during this entire transaction.
+		 * The HW datapath (IUDMA) never even sees SET_CONFIGURATION
+		 * or SET_INTERFACE transactions.
+		 */
+		struct usb_request *r = udc->ep0_reply;
+
+		if (!r) {
+			if (shutdown)
+				ep0state = EP0_IDLE;
+			break;
+		}
+
+		bcm63xx_ep0_complete(udc, r, 0);
+		udc->ep0_reply = NULL;
+		ep0state = EP0_IDLE;
+		break;
+	}
+	case EP0_SHUTDOWN:
+		break;
+	}
+
+	if (udc->ep0state == ep0state)
+		return -EAGAIN;
+
+	udc->ep0state = ep0state;
+	return 0;
+}
+
+/**
+ * bcm63xx_ep0_process - ep0 worker thread / state machine.
+ * @w: Workqueue struct.
+ *
+ * bcm63xx_ep0_process is triggered any time an event occurs on ep0.  It
+ * is used to synchronize ep0 events and ensure that both HW and SW events
+ * occur in a well-defined order.  When the ep0 IUDMA queues are idle, it may
+ * synthesize SET_CONFIGURATION / SET_INTERFACE requests that were consumed
+ * by the USBD hardware.
+ *
+ * The worker function will continue iterating around the state machine
+ * until there is nothing left to do.  Usually "nothing left to do" means
+ * that we're waiting for a new event from the hardware.
+ */
+static void bcm63xx_ep0_process(struct work_struct *w)
+{
+	struct bcm63xx_udc *udc = container_of(w, struct bcm63xx_udc, ep0_wq);
+	spin_lock_irq(&udc->lock);
+	while (bcm63xx_ep0_one_round(udc) == 0)
+		;
+	spin_unlock_irq(&udc->lock);
+}
+
+/***********************************************************************
+ * Standard UDC gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_get_frame - Read current SOF frame number from the HW.
+ * @gadget: USB slave device.
+ */
+static int bcm63xx_udc_get_frame(struct usb_gadget *gadget)
+{
+	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+
+	return (usbd_readl(udc, USBD_STATUS_REG) &
+		USBD_STATUS_SOF_MASK) >> USBD_STATUS_SOF_SHIFT;
+}
+
+/**
+ * bcm63xx_udc_pullup - Enable/disable pullup on D+ line.
+ * @gadget: USB slave device.
+ * @is_on: 0 to disable pullup, 1 to enable.
+ *
+ * See notes in bcm63xx_select_pullup().
+ */
+static int bcm63xx_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+	unsigned long flags;
+	int i, rc = -EINVAL;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (is_on && udc->ep0state == EP0_SHUTDOWN) {
+		udc->gadget.speed = USB_SPEED_UNKNOWN;
+		udc->ep0state = EP0_REQUEUE;
+		bcm63xx_fifo_setup(udc);
+		bcm63xx_fifo_reset(udc);
+		bcm63xx_ep_setup(udc);
+
+		bitmap_zero(&udc->wedgemap, BCM63XX_NUM_EP);
+		for (i = 0; i < BCM63XX_NUM_EP; i++)
+			bcm63xx_set_stall(udc, &udc->bep[i], false);
+
+		bcm63xx_set_ctrl_irqs(udc, true);
+		bcm63xx_select_pullup(gadget_to_udc(gadget), true);
+		rc = 0;
+	} else if (!is_on && udc->ep0state != EP0_SHUTDOWN) {
+		bcm63xx_select_pullup(gadget_to_udc(gadget), false);
+
+		udc->ep0_req_shutdown = 1;
+		spin_unlock_irqrestore(&udc->lock, flags);
+
+		while (1) {
+			schedule_work(&udc->ep0_wq);
+			if (udc->ep0state == EP0_SHUTDOWN)
+				break;
+			msleep(50);
+		}
+		bcm63xx_set_ctrl_irqs(udc, false);
+		cancel_work_sync(&udc->ep0_wq);
+		return 0;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return rc;
+}
+
+/**
+ * bcm63xx_udc_start - Start the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+	unsigned long flags;
+
+	if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+	    !driver->setup)
+		return -EINVAL;
+	if (!udc)
+		return -ENODEV;
+	if (udc->driver)
+		return -EBUSY;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	set_clocks(udc, true);
+	bcm63xx_fifo_setup(udc);
+	bcm63xx_ep_init(udc);
+	bcm63xx_ep_setup(udc);
+	bcm63xx_fifo_reset(udc);
+	bcm63xx_select_phy_mode(udc, true);
+
+	udc->driver = driver;
+	driver->driver.bus = NULL;
+	udc->gadget.dev.of_node = udc->dev->of_node;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * bcm63xx_udc_stop - Shut down the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_stop(struct usb_gadget *gadget)
+{
+	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	udc->driver = NULL;
+
+	/*
+	 * If we switch the PHY too abruptly after dropping D+, the host
+	 * will often complain:
+	 *
+	 *     hub 1-0:1.0: port 1 disabled by hub (EMI?), re-enabling...
+	 */
+	msleep(100);
+
+	bcm63xx_select_phy_mode(udc, false);
+	set_clocks(udc, false);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops bcm63xx_udc_ops = {
+	.get_frame	= bcm63xx_udc_get_frame,
+	.pullup		= bcm63xx_udc_pullup,
+	.udc_start	= bcm63xx_udc_start,
+	.udc_stop	= bcm63xx_udc_stop,
+};
+
+/***********************************************************************
+ * IRQ handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_update_cfg_iface - Read current configuration/interface settings.
+ * @udc: Reference to the device controller.
+ *
+ * This controller intercepts SET_CONFIGURATION and SET_INTERFACE messages.
+ * The driver never sees the raw control packets coming in on the ep0
+ * IUDMA channel, but at least we get an interrupt event to tell us that
+ * new values are waiting in the USBD_STATUS register.
+ */
+static void bcm63xx_update_cfg_iface(struct bcm63xx_udc *udc)
+{
+	u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+
+	udc->cfg = (reg & USBD_STATUS_CFG_MASK) >> USBD_STATUS_CFG_SHIFT;
+	udc->iface = (reg & USBD_STATUS_INTF_MASK) >> USBD_STATUS_INTF_SHIFT;
+	udc->alt_iface = (reg & USBD_STATUS_ALTINTF_MASK) >>
+			 USBD_STATUS_ALTINTF_SHIFT;
+	bcm63xx_ep_setup(udc);
+}
+
+/**
+ * bcm63xx_update_link_speed - Check to see if the link speed has changed.
+ * @udc: Reference to the device controller.
+ *
+ * The link speed update coincides with a SETUP IRQ.  Returns 1 if the
+ * speed has changed, so that the caller can update the endpoint settings.
+ */
+static int bcm63xx_update_link_speed(struct bcm63xx_udc *udc)
+{
+	u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+	enum usb_device_speed oldspeed = udc->gadget.speed;
+
+	switch ((reg & USBD_STATUS_SPD_MASK) >> USBD_STATUS_SPD_SHIFT) {
+	case BCM63XX_SPD_HIGH:
+		udc->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case BCM63XX_SPD_FULL:
+		udc->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		/* this should never happen */
+		udc->gadget.speed = USB_SPEED_UNKNOWN;
+		dev_err(udc->dev,
+			"received SETUP packet with invalid link speed\n");
+		return 0;
+	}
+
+	if (udc->gadget.speed != oldspeed) {
+		dev_info(udc->dev, "link up, %s-speed mode\n",
+			 udc->gadget.speed == USB_SPEED_HIGH ? "high" : "full");
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/**
+ * bcm63xx_update_wedge - Iterate through wedged endpoints.
+ * @udc: Reference to the device controller.
+ * @new_status: true to "refresh" wedge status; false to clear it.
+ *
+ * On a SETUP interrupt, we need to manually "refresh" the wedge status
+ * because the controller hardware is designed to automatically clear
+ * stalls in response to a CLEAR_FEATURE request from the host.
+ *
+ * On a RESET interrupt, we do want to restore all wedged endpoints.
+ */
+static void bcm63xx_update_wedge(struct bcm63xx_udc *udc, bool new_status)
+{
+	int i;
+
+	for_each_set_bit(i, &udc->wedgemap, BCM63XX_NUM_EP) {
+		bcm63xx_set_stall(udc, &udc->bep[i], new_status);
+		if (!new_status)
+			clear_bit(i, &udc->wedgemap);
+	}
+}
+
+/**
+ * bcm63xx_udc_ctrl_isr - ISR for control path events (USBD).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the device controller.
+ *
+ * This is where we handle link (VBUS) down, USB reset, speed changes,
+ * SET_CONFIGURATION, and SET_INTERFACE events.
+ */
+static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
+{
+	struct bcm63xx_udc *udc = dev_id;
+	u32 stat;
+	bool disconnected = false, bus_reset = false;
+
+	stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
+	       usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
+
+	usbd_writel(udc, stat, USBD_EVENT_IRQ_STATUS_REG);
+
+	spin_lock(&udc->lock);
+	if (stat & BIT(USBD_EVENT_IRQ_USB_LINK)) {
+		/* VBUS toggled */
+
+		if (!(usbd_readl(udc, USBD_EVENTS_REG) &
+		      USBD_EVENTS_USB_LINK_MASK) &&
+		      udc->gadget.speed != USB_SPEED_UNKNOWN)
+			dev_info(udc->dev, "link down\n");
+
+		udc->gadget.speed = USB_SPEED_UNKNOWN;
+		disconnected = true;
+	}
+	if (stat & BIT(USBD_EVENT_IRQ_USB_RESET)) {
+		bcm63xx_fifo_setup(udc);
+		bcm63xx_fifo_reset(udc);
+		bcm63xx_ep_setup(udc);
+
+		bcm63xx_update_wedge(udc, false);
+
+		udc->ep0_req_reset = 1;
+		schedule_work(&udc->ep0_wq);
+		bus_reset = true;
+	}
+	if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
+		if (bcm63xx_update_link_speed(udc)) {
+			bcm63xx_fifo_setup(udc);
+			bcm63xx_ep_setup(udc);
+		}
+		bcm63xx_update_wedge(udc, true);
+	}
+	if (stat & BIT(USBD_EVENT_IRQ_SETCFG)) {
+		bcm63xx_update_cfg_iface(udc);
+		udc->ep0_req_set_cfg = 1;
+		schedule_work(&udc->ep0_wq);
+	}
+	if (stat & BIT(USBD_EVENT_IRQ_SETINTF)) {
+		bcm63xx_update_cfg_iface(udc);
+		udc->ep0_req_set_iface = 1;
+		schedule_work(&udc->ep0_wq);
+	}
+	spin_unlock(&udc->lock);
+
+	if (disconnected && udc->driver)
+		udc->driver->disconnect(&udc->gadget);
+	else if (bus_reset && udc->driver)
+		usb_gadget_udc_reset(&udc->gadget, udc->driver);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * bcm63xx_udc_data_isr - ISR for data path events (IUDMA).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the IUDMA channel that generated the interrupt.
+ *
+ * For the two ep0 channels, we have special handling that triggers the
+ * ep0 worker thread.  For normal bulk/intr channels, either queue up
+ * the next buffer descriptor for the transaction (incomplete transaction),
+ * or invoke the completion callback (complete transactions).
+ */
+static irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id)
+{
+	struct iudma_ch *iudma = dev_id;
+	struct bcm63xx_udc *udc = iudma->udc;
+	struct bcm63xx_ep *bep;
+	struct usb_request *req = NULL;
+	struct bcm63xx_req *breq = NULL;
+	int rc;
+	bool is_done = false;
+
+	spin_lock(&udc->lock);
+
+	usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+			ENETDMAC_IR_REG, iudma->ch_idx);
+	bep = iudma->bep;
+	rc = iudma_read(udc, iudma);
+
+	/* special handling for EP0 RX (0) and TX (1) */
+	if (iudma->ch_idx == IUDMA_EP0_RXCHAN ||
+	    iudma->ch_idx == IUDMA_EP0_TXCHAN) {
+		req = udc->ep0_request;
+		breq = our_req(req);
+
+		/* a single request could require multiple submissions */
+		if (rc >= 0) {
+			req->actual += rc;
+
+			if (req->actual >= req->length || breq->bd_bytes > rc) {
+				udc->ep0_req_completed = 1;
+				is_done = true;
+				schedule_work(&udc->ep0_wq);
+
+				/* "actual" on a ZLP is 1 byte */
+				req->actual = min(req->actual, req->length);
+			} else {
+				/* queue up the next BD (same request) */
+				iudma_write(udc, iudma, breq);
+			}
+		}
+	} else if (!list_empty(&bep->queue)) {
+		breq = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+		req = &breq->req;
+
+		if (rc >= 0) {
+			req->actual += rc;
+
+			if (req->actual >= req->length || breq->bd_bytes > rc) {
+				is_done = true;
+				list_del(&breq->queue);
+
+				req->actual = min(req->actual, req->length);
+
+				if (!list_empty(&bep->queue)) {
+					struct bcm63xx_req *next;
+
+					next = list_first_entry(&bep->queue,
+						struct bcm63xx_req, queue);
+					iudma_write(udc, iudma, next);
+				}
+			} else {
+				iudma_write(udc, iudma, breq);
+			}
+		}
+	}
+	spin_unlock(&udc->lock);
+
+	if (is_done) {
+		usb_gadget_unmap_request(&udc->gadget, req, iudma->is_tx);
+		if (req->complete)
+			req->complete(&bep->ep, req);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/***********************************************************************
+ * Debug filesystem
+ ***********************************************************************/
+
+/*
+ * bcm63xx_usbd_dbg_show - Show USBD controller state.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/usbd
+ */
+static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p)
+{
+	struct bcm63xx_udc *udc = s->private;
+
+	if (!udc->driver)
+		return -ENODEV;
+
+	seq_printf(s, "ep0 state: %s\n",
+		   bcm63xx_ep0_state_names[udc->ep0state]);
+	seq_printf(s, "  pending requests: %s%s%s%s%s%s%s\n",
+		   udc->ep0_req_reset ? "reset " : "",
+		   udc->ep0_req_set_cfg ? "set_cfg " : "",
+		   udc->ep0_req_set_iface ? "set_iface " : "",
+		   udc->ep0_req_shutdown ? "shutdown " : "",
+		   udc->ep0_request ? "pending " : "",
+		   udc->ep0_req_completed ? "completed " : "",
+		   udc->ep0_reply ? "reply " : "");
+	seq_printf(s, "cfg: %d; iface: %d; alt_iface: %d\n",
+		   udc->cfg, udc->iface, udc->alt_iface);
+	seq_printf(s, "regs:\n");
+	seq_printf(s, "  control: %08x; straps: %08x; status: %08x\n",
+		   usbd_readl(udc, USBD_CONTROL_REG),
+		   usbd_readl(udc, USBD_STRAPS_REG),
+		   usbd_readl(udc, USBD_STATUS_REG));
+	seq_printf(s, "  events:  %08x; stall:  %08x\n",
+		   usbd_readl(udc, USBD_EVENTS_REG),
+		   usbd_readl(udc, USBD_STALL_REG));
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bcm63xx_usbd_dbg);
+
+/*
+ * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/iudma
+ */
+static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p)
+{
+	struct bcm63xx_udc *udc = s->private;
+	int ch_idx, i;
+	u32 sram2, sram3;
+
+	if (!udc->driver)
+		return -ENODEV;
+
+	for (ch_idx = 0; ch_idx < BCM63XX_NUM_IUDMA; ch_idx++) {
+		struct iudma_ch *iudma = &udc->iudma[ch_idx];
+		struct list_head *pos;
+
+		seq_printf(s, "IUDMA channel %d -- ", ch_idx);
+		switch (iudma_defaults[ch_idx].ep_type) {
+		case BCMEP_CTRL:
+			seq_printf(s, "control");
+			break;
+		case BCMEP_BULK:
+			seq_printf(s, "bulk");
+			break;
+		case BCMEP_INTR:
+			seq_printf(s, "interrupt");
+			break;
+		}
+		seq_printf(s, ch_idx & 0x01 ? " tx" : " rx");
+		seq_printf(s, " [ep%d]:\n",
+			   max_t(int, iudma_defaults[ch_idx].ep_num, 0));
+		seq_printf(s, "  cfg: %08x; irqstat: %08x; irqmask: %08x; maxburst: %08x\n",
+			   usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx),
+			   usb_dmac_readl(udc, ENETDMAC_IR_REG, ch_idx),
+			   usb_dmac_readl(udc, ENETDMAC_IRMASK_REG, ch_idx),
+			   usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG, ch_idx));
+
+		sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG, ch_idx);
+		sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG, ch_idx);
+		seq_printf(s, "  base: %08x; index: %04x_%04x; desc: %04x_%04x %08x\n",
+			   usb_dmas_readl(udc, ENETDMAS_RSTART_REG, ch_idx),
+			   sram2 >> 16, sram2 & 0xffff,
+			   sram3 >> 16, sram3 & 0xffff,
+			   usb_dmas_readl(udc, ENETDMAS_SRAM4_REG, ch_idx));
+		seq_printf(s, "  desc: %d/%d used", iudma->n_bds_used,
+			   iudma->n_bds);
+
+		if (iudma->bep) {
+			i = 0;
+			list_for_each(pos, &iudma->bep->queue)
+				i++;
+			seq_printf(s, "; %d queued\n", i);
+		} else {
+			seq_printf(s, "\n");
+		}
+
+		for (i = 0; i < iudma->n_bds; i++) {
+			struct bcm_enet_desc *d = &iudma->bd_ring[i];
+
+			seq_printf(s, "  %03x (%02x): len_stat: %04x_%04x; pa %08x",
+				   i * sizeof(*d), i,
+				   d->len_stat >> 16, d->len_stat & 0xffff,
+				   d->address);
+			if (d == iudma->read_bd)
+				seq_printf(s, "   <<RD");
+			if (d == iudma->write_bd)
+				seq_printf(s, "   <<WR");
+			seq_printf(s, "\n");
+		}
+
+		seq_printf(s, "\n");
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bcm63xx_iudma_dbg);
+
+/**
+ * bcm63xx_udc_init_debugfs - Create debugfs entries.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
+{
+	struct dentry *root;
+
+	if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
+		return;
+
+	root = debugfs_create_dir(udc->gadget.name, NULL);
+	udc->debugfs_root = root;
+
+	debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops);
+	debugfs_create_file("iudma", 0400, root, udc, &bcm63xx_iudma_dbg_fops);
+}
+
+/**
+ * bcm63xx_udc_cleanup_debugfs - Remove debugfs entries.
+ * @udc: Reference to the device controller.
+ *
+ * debugfs_remove() is safe to call with a NULL argument.
+ */
+static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
+{
+	debugfs_remove_recursive(udc->debugfs_root);
+}
+
+/***********************************************************************
+ * Driver init/exit
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_probe - Initialize a new instance of the UDC.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ *
+ * Note that platform data is required, because pd.port_no varies from chip
+ * to chip and is used to switch the correct USB port to device mode.
+ */
+static int bcm63xx_udc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev);
+	struct bcm63xx_udc *udc;
+	struct resource *res;
+	int rc = -ENOMEM, i, irq;
+
+	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, udc);
+	udc->dev = dev;
+	udc->pd = pd;
+
+	if (!pd) {
+		dev_err(dev, "missing platform data\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->usbd_regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(udc->usbd_regs))
+		return PTR_ERR(udc->usbd_regs);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	udc->iudma_regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(udc->iudma_regs))
+		return PTR_ERR(udc->iudma_regs);
+
+	spin_lock_init(&udc->lock);
+	INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process);
+
+	udc->gadget.ops = &bcm63xx_udc_ops;
+	udc->gadget.name = dev_name(dev);
+
+	if (!pd->use_fullspeed && !use_fullspeed)
+		udc->gadget.max_speed = USB_SPEED_HIGH;
+	else
+		udc->gadget.max_speed = USB_SPEED_FULL;
+
+	/* request clocks, allocate buffers, and clear any pending IRQs */
+	rc = bcm63xx_init_udc_hw(udc);
+	if (rc)
+		return rc;
+
+	rc = -ENXIO;
+
+	/* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "missing IRQ resource #0\n");
+		goto out_uninit;
+	}
+	if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
+			     dev_name(dev), udc) < 0)
+		goto report_request_failure;
+
+	/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
+	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+		irq = platform_get_irq(pdev, i + 1);
+		if (irq < 0) {
+			dev_err(dev, "missing IRQ resource #%d\n", i + 1);
+			goto out_uninit;
+		}
+		if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
+				     dev_name(dev), &udc->iudma[i]) < 0)
+			goto report_request_failure;
+	}
+
+	bcm63xx_udc_init_debugfs(udc);
+	rc = usb_add_gadget_udc(dev, &udc->gadget);
+	if (!rc)
+		return 0;
+
+	bcm63xx_udc_cleanup_debugfs(udc);
+out_uninit:
+	bcm63xx_uninit_udc_hw(udc);
+	return rc;
+
+report_request_failure:
+	dev_err(dev, "error requesting IRQ #%d\n", irq);
+	goto out_uninit;
+}
+
+/**
+ * bcm63xx_udc_remove - Remove the device from the system.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ */
+static int bcm63xx_udc_remove(struct platform_device *pdev)
+{
+	struct bcm63xx_udc *udc = platform_get_drvdata(pdev);
+
+	bcm63xx_udc_cleanup_debugfs(udc);
+	usb_del_gadget_udc(&udc->gadget);
+	BUG_ON(udc->driver);
+
+	bcm63xx_uninit_udc_hw(udc);
+
+	return 0;
+}
+
+static struct platform_driver bcm63xx_udc_driver = {
+	.probe		= bcm63xx_udc_probe,
+	.remove		= bcm63xx_udc_remove,
+	.driver		= {
+		.name	= DRV_MODULE_NAME,
+	},
+};
+module_platform_driver(bcm63xx_udc_driver);
+
+MODULE_DESCRIPTION("BCM63xx USB Peripheral Controller");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
diff --git a/drivers/usb/gadget/udc/bdc/Kconfig b/drivers/usb/gadget/udc/bdc/Kconfig
new file mode 100644
index 0000000..c74ac25
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/Kconfig
@@ -0,0 +1,22 @@
+config USB_BDC_UDC
+	tristate "Broadcom USB3.0 device controller IP driver(BDC)"
+	depends on USB_GADGET && HAS_DMA
+	default ARCH_BRCMSTB
+
+	help
+	BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP
+	then select this driver.
+
+	Say "y" here to link the driver statically, or "m" to build a dynamically
+	linked module called "bdc".
+
+if USB_BDC_UDC
+
+comment "Platform Support"
+config	USB_BDC_PCI
+	tristate "BDC support for PCIe based platforms"
+	depends on USB_PCI
+	default USB_BDC_UDC
+	help
+		Enable support for platforms which have BDC connected through PCIe, such as Lego3 FPGA platform.
+endif
diff --git a/drivers/usb/gadget/udc/bdc/Makefile b/drivers/usb/gadget/udc/bdc/Makefile
new file mode 100644
index 0000000..52cb5ea
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_USB_BDC_UDC)	+= bdc.o
+bdc-y	:= bdc_core.o bdc_cmd.o bdc_ep.o bdc_udc.o
+
+ifneq ($(CONFIG_USB_GADGET_VERBOSE),)
+	bdc-y			+= bdc_dbg.o
+endif
+
+obj-$(CONFIG_USB_BDC_PCI)	+= bdc_pci.o
diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h
new file mode 100644
index 0000000..6e1e881
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc.h
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc.h - header for the BRCM BDC USB3.0 device controller
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+
+#ifndef	__LINUX_BDC_H__
+#define	__LINUX_BDC_H__
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <asm/unaligned.h>
+
+#define BRCM_BDC_NAME "bdc"
+#define BRCM_BDC_DESC "Broadcom USB Device Controller driver"
+
+#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+/* BDC command operation timeout in usec*/
+#define BDC_CMD_TIMEOUT	1000
+/* BDC controller operation timeout in usec*/
+#define BDC_COP_TIMEOUT	500
+
+/*
+ * Maximum size of ep0 response buffer for ch9 requests,
+ * the set_sel request uses 6 so far, the max.
+*/
+#define EP0_RESPONSE_BUFF  6
+/* Start with SS as default */
+#define EP0_MAX_PKT_SIZE 512
+
+/* 64 entries in a SRR */
+#define NUM_SR_ENTRIES	64
+
+/* Num of bds per table */
+#define NUM_BDS_PER_TABLE	32
+
+/* Num of tables in bd list for control,bulk and Int ep */
+#define NUM_TABLES	2
+
+/* Num of tables in bd list for Isoch ep */
+#define NUM_TABLES_ISOCH	6
+
+/* U1 Timeout default: 248usec */
+#define U1_TIMEOUT	0xf8
+
+/* Interrupt coalescence in usec */
+#define	INT_CLS	500
+
+/* Register offsets */
+/* Configuration and Capability registers */
+#define BDC_BDCCFG0	0x00
+#define BDC_BDCCFG1	0x04
+#define BDC_BDCCAP0	0x08
+#define BDC_BDCCAP1	0x0c
+#define BDC_CMDPAR0	0x10
+#define BDC_CMDPAR1	0x14
+#define BDC_CMDPAR2	0x18
+#define BDC_CMDSC	0x1c
+#define BDC_USPC	0x20
+#define BDC_USPPMS	0x28
+#define BDC_USPPM2	0x2c
+#define BDC_SPBBAL	0x38
+#define BDC_SPBBAH	0x3c
+#define BDC_BDCSC	0x40
+#define BDC_XSFNTF	0x4c
+
+#define BDC_DVCSA	0x50
+#define BDC_DVCSB	0x54
+#define BDC_EPSTS0	0x60
+#define BDC_EPSTS1	0x64
+#define BDC_EPSTS2	0x68
+#define BDC_EPSTS3	0x6c
+#define BDC_EPSTS4	0x70
+#define BDC_EPSTS5	0x74
+#define BDC_EPSTS6	0x78
+#define BDC_EPSTS7	0x7c
+#define BDC_SRRBAL(n)	(0x200 + (n * 0x10))
+#define BDC_SRRBAH(n)	(0x204 + (n * 0x10))
+#define BDC_SRRINT(n)	(0x208 + (n * 0x10))
+#define BDC_INTCTLS(n)	(0x20c + (n * 0x10))
+
+/* Extended capability regs */
+#define BDC_FSCNOC	0xcd4
+#define BDC_FSCNIC	0xce4
+#define NUM_NCS(p)	(p >> 28)
+
+/* Register bit fields and Masks */
+/* BDC Configuration 0 */
+#define BDC_PGS(p)	(((p) & (0x7 << 8)) >> 8)
+#define BDC_SPB(p)	(p & 0x7)
+
+/* BDC Capability1 */
+#define BDC_P64		(1 << 0)
+
+/* BDC Command register */
+#define BDC_CMD_FH	0xe
+#define BDC_CMD_DNC	0x6
+#define BDC_CMD_EPO	0x4
+#define BDC_CMD_BLA	0x3
+#define BDC_CMD_EPC	0x2
+#define BDC_CMD_DVC	0x1
+#define BDC_CMD_CWS		(0x1 << 5)
+#define BDC_CMD_CST(p)		(((p) & (0xf << 6))>>6)
+#define BDC_CMD_EPN(p)		((p & 0x1f) << 10)
+#define BDC_SUB_CMD_ADD		(0x1 << 17)
+#define BDC_SUB_CMD_FWK		(0x4 << 17)
+/* Reset sequence number */
+#define BDC_CMD_EPO_RST_SN	(0x1 << 16)
+#define BDC_CMD_EP0_XSD		(0x1 << 16)
+#define BDC_SUB_CMD_ADD_EP	(0x1 << 17)
+#define BDC_SUB_CMD_DRP_EP	(0x2 << 17)
+#define BDC_SUB_CMD_EP_STP	(0x2 << 17)
+#define BDC_SUB_CMD_EP_STL	(0x4 << 17)
+#define BDC_SUB_CMD_EP_RST	(0x1 << 17)
+#define BDC_CMD_SRD		(1 << 27)
+
+/* CMD completion status */
+#define BDC_CMDS_SUCC	0x1
+#define BDC_CMDS_PARA	0x3
+#define BDC_CMDS_STAT	0x4
+#define BDC_CMDS_FAIL	0x5
+#define BDC_CMDS_INTL	0x6
+#define BDC_CMDS_BUSY	0xf
+
+/* CMDSC Param 2 shifts */
+#define EPT_SHIFT	22
+#define MP_SHIFT	10
+#define MB_SHIFT	6
+#define EPM_SHIFT	4
+
+/* BDC USPSC */
+#define BDC_VBC		(1 << 31)
+#define BDC_PRC		(1 << 30)
+#define BDC_PCE		(1 << 29)
+#define BDC_CFC		(1 << 28)
+#define BDC_PCC		(1 << 27)
+#define BDC_PSC		(1 << 26)
+#define BDC_VBS		(1 << 25)
+#define BDC_PRS		(1 << 24)
+#define BDC_PCS		(1 << 23)
+#define BDC_PSP(p)	(((p) & (0x7 << 20))>>20)
+#define BDC_SCN		(1 << 8)
+#define BDC_SDC		(1 << 7)
+#define BDC_SWS		(1 << 4)
+
+#define BDC_USPSC_RW	(BDC_SCN|BDC_SDC|BDC_SWS|0xf)
+#define BDC_PSP(p)	(((p) & (0x7 << 20))>>20)
+
+#define BDC_SPEED_FS	0x1
+#define BDC_SPEED_LS	0x2
+#define BDC_SPEED_HS	0x3
+#define BDC_SPEED_SS	0x4
+
+#define BDC_PST(p)	(p & 0xf)
+#define BDC_PST_MASK	0xf
+
+/* USPPMS */
+#define BDC_U2E		(0x1 << 31)
+#define BDC_U1E		(0x1 << 30)
+#define BDC_U2A		(0x1 << 29)
+#define BDC_PORT_W1S	(0x1 << 17)
+#define BDC_U1T(p)	((p) & 0xff)
+#define BDC_U2T(p)	(((p) & 0xff) << 8)
+#define BDC_U1T_MASK	0xff
+
+/* USBPM2 */
+/* Hardware LPM Enable */
+#define BDC_HLE		(1 << 16)
+
+/* BDC Status and Control */
+#define BDC_COP_RST	(1 << 29)
+#define BDC_COP_RUN	(2 << 29)
+#define BDC_COP_STP	(4 << 29)
+
+#define BDC_COP_MASK (BDC_COP_RST|BDC_COP_RUN|BDC_COP_STP)
+
+#define BDC_COS		(1 << 28)
+#define BDC_CSTS(p)	(((p) & (0x7 << 20)) >> 20)
+#define BDC_MASK_MCW	(1 << 7)
+#define BDC_GIE		(1 << 1)
+#define BDC_GIP		(1 << 0)
+
+#define BDC_HLT	1
+#define BDC_NOR	2
+#define BDC_OIP	7
+
+/* Buffer descriptor and Status report bit fields and masks */
+#define BD_TYPE_BITMASK	(0xf)
+#define BD_CHAIN	0xf
+
+#define BD_TFS_SHIFT	4
+#define BD_SOT		(1 << 26)
+#define BD_EOT		(1 << 27)
+#define BD_ISP		(1 << 29)
+#define BD_IOC		(1 << 30)
+#define BD_SBF		(1 << 31)
+
+#define BD_INTR_TARGET(p)	(((p) & 0x1f) << 27)
+
+#define BDC_SRR_RWS		(1 << 4)
+#define BDC_SRR_RST		(1 << 3)
+#define BDC_SRR_ISR		(1 << 2)
+#define BDC_SRR_IE		(1 << 1)
+#define BDC_SRR_IP		(1 << 0)
+#define BDC_SRR_EPI(p)	(((p) & (0xff << 24)) >> 24)
+#define BDC_SRR_DPI(p) (((p) & (0xff << 16)) >> 16)
+#define BDC_SRR_DPI_MASK	0x00ff0000
+
+#define MARK_CHAIN_BD	(BD_CHAIN|BD_EOT|BD_SOT)
+
+/* Control transfer BD specific fields */
+#define BD_DIR_IN		(1 << 25)
+
+#define BDC_PTC_MASK	0xf0000000
+
+/* status report defines */
+#define SR_XSF		0
+#define SR_USPC		4
+#define SR_BD_LEN(p)    (p & 0xffffff)
+
+#define XSF_SUCC	0x1
+#define XSF_SHORT	0x3
+#define XSF_BABB	0x4
+#define XSF_SETUP_RECV	0x6
+#define XSF_DATA_START	0x7
+#define XSF_STATUS_START 0x8
+
+#define XSF_STS(p) (((p) >> 28) & 0xf)
+
+/* Transfer BD fields */
+#define BD_LEN(p) ((p) & 0x1ffff)
+#define BD_LTF		(1 << 25)
+#define BD_TYPE_DS	0x1
+#define BD_TYPE_SS	0x2
+
+#define BDC_EP_ENABLED     (1 << 0)
+#define BDC_EP_STALL       (1 << 1)
+#define BDC_EP_STOP        (1 << 2)
+
+/* One BD can transfer max 65536 bytes */
+#define BD_MAX_BUFF_SIZE	(1 << 16)
+/* Maximum bytes in one XFR, Refer to BDC spec */
+#define MAX_XFR_LEN		16777215
+
+/* defines for Force Header command */
+#define DEV_NOTF_TYPE 6
+#define FWK_SUBTYPE  1
+#define TRA_PACKET   4
+
+#define to_bdc_ep(e)		container_of(e, struct bdc_ep, usb_ep)
+#define to_bdc_req(r)		container_of(r, struct bdc_req, usb_req)
+#define gadget_to_bdc(g)	container_of(g, struct bdc, gadget)
+
+/* FUNCTION WAKE DEV NOTIFICATION interval, USB3 spec table 8.13 */
+#define BDC_TNOTIFY 2500 /*in ms*/
+/* Devstatus bitfields */
+#define REMOTE_WAKEUP_ISSUED	(1 << 16)
+#define DEVICE_SUSPENDED	(1 << 17)
+#define FUNC_WAKE_ISSUED	(1 << 18)
+#define REMOTE_WAKE_ENABLE	(1 << USB_DEVICE_REMOTE_WAKEUP)
+
+/* On disconnect, preserve these bits and clear rest */
+#define DEVSTATUS_CLEAR		(1 << USB_DEVICE_SELF_POWERED)
+/* Hardware and software Data structures */
+
+/* Endpoint bd: buffer descriptor */
+struct bdc_bd {
+	__le32 offset[4];
+};
+
+/* Status report in Status report ring(srr) */
+struct bdc_sr {
+	__le32 offset[4];
+};
+
+/* bd_table: contiguous bd's in a table */
+struct bd_table {
+	struct bdc_bd *start_bd;
+	/* dma address of start bd of table*/
+	dma_addr_t dma;
+};
+
+/*
+ * Each endpoint has a bdl(buffer descriptor list), bdl consists of 1 or more bd
+ * table's chained to each other through a chain bd, every table has equal
+ * number of bds. the software uses bdi(bd index) to refer to particular bd in
+ * the list.
+ */
+struct bd_list {
+	/* Array of bd table pointers*/
+	struct bd_table **bd_table_array;
+	/* How many tables chained to each other */
+	int num_tabs;
+	/* Max_bdi = num_tabs * num_bds_table - 1 */
+	int max_bdi;
+	/* current enq bdi from sw point of view */
+	int eqp_bdi;
+	/* current deq bdi from sw point of view */
+	int hwd_bdi;
+	/* numbers of bds per table */
+	int num_bds_table;
+};
+
+struct bdc_req;
+
+/* Representation of a transfer, one transfer can have multiple bd's */
+struct bd_transfer {
+	struct bdc_req *req;
+	/* start bd index */
+	int start_bdi;
+	/* this will be the next hw dqp when this transfer completes */
+	int next_hwd_bdi;
+	/* number of bds in this transfer */
+	int num_bds;
+};
+
+/*
+ * Representation of a gadget request, every gadget request is contained
+ * by 1 bd_transfer.
+ */
+struct bdc_req {
+	struct usb_request	usb_req;
+	struct list_head	queue;
+	struct bdc_ep		*ep;
+	/* only one Transfer per request */
+	struct bd_transfer bd_xfr;
+	int	epnum;
+};
+
+/* scratchpad buffer needed by bdc hardware */
+struct bdc_scratchpad {
+	dma_addr_t sp_dma;
+	void *buff;
+	u32 size;
+};
+
+/* endpoint representation */
+struct bdc_ep {
+	struct usb_ep	usb_ep;
+	struct list_head queue;
+	struct bdc *bdc;
+	u8  ep_type;
+	u8  dir;
+	u8  ep_num;
+	const struct usb_ss_ep_comp_descriptor	*comp_desc;
+	const struct usb_endpoint_descriptor	*desc;
+	unsigned int flags;
+	char name[20];
+	/* endpoint bd list*/
+	struct bd_list bd_list;
+	/*
+	 * HW generates extra event for multi bd tranfers, this flag helps in
+	 * ignoring the extra event
+	 */
+	bool ignore_next_sr;
+};
+
+/* bdc cmmand parameter structure */
+struct bdc_cmd_params {
+	u32	param2;
+	u32	param1;
+	u32	param0;
+};
+
+/* status report ring(srr), currently one srr is supported for entire system */
+struct srr {
+	struct bdc_sr *sr_bds;
+	u16	eqp_index;
+	u16	dqp_index;
+	dma_addr_t	dma_addr;
+};
+
+/* EP0 states */
+enum bdc_ep0_state {
+	WAIT_FOR_SETUP = 0,
+	WAIT_FOR_DATA_START,
+	WAIT_FOR_DATA_XMIT,
+	WAIT_FOR_STATUS_START,
+	WAIT_FOR_STATUS_XMIT,
+	STATUS_PENDING
+};
+
+/* Link states */
+enum bdc_link_state {
+	BDC_LINK_STATE_U0	= 0x00,
+	BDC_LINK_STATE_U3	= 0x03,
+	BDC_LINK_STATE_RX_DET	= 0x05,
+	BDC_LINK_STATE_RESUME	= 0x0f
+};
+
+/* representation of bdc */
+struct bdc {
+	struct usb_gadget	gadget;
+	struct usb_gadget_driver	*gadget_driver;
+	struct device	*dev;
+	/* device lock */
+	spinlock_t	lock;
+
+	/* generic phy */
+	struct phy      **phys;
+	int num_phys;
+	/* num of endpoints for a particular instantiation of IP */
+	unsigned int num_eps;
+	/*
+	 * Array of ep's, it uses the same index covention as bdc hw i.e.
+	 * 1 for ep0, 2 for 1out,3 for 1in ....
+	 */
+	struct bdc_ep		**bdc_ep_array;
+	void __iomem		*regs;
+	struct bdc_scratchpad	scratchpad;
+	u32	sp_buff_size;
+	/* current driver supports 1 status ring */
+	struct srr	srr;
+	/* Last received setup packet */
+	struct	usb_ctrlrequest setup_pkt;
+	struct	bdc_req ep0_req;
+	struct	bdc_req status_req;
+	enum	bdc_ep0_state ep0_state;
+	bool	delayed_status;
+	bool	zlp_needed;
+	bool	reinit;
+	bool	pullup;
+	/* Bits 0-15 are standard and 16-31 for proprietary information */
+	u32	devstatus;
+	int	irq;
+	void	*mem;
+	u32	dev_addr;
+	/* DMA pools */
+	struct dma_pool	*bd_table_pool;
+	u8		test_mode;
+	/* array of callbacks for various status report handlers */
+	void (*sr_handler[2])(struct bdc *, struct bdc_sr *);
+	/* ep0 callback handlers */
+	void (*sr_xsf_ep0[3])(struct bdc *, struct bdc_sr *);
+	/* ep0 response buffer for ch9 requests like GET_STATUS and SET_SEL */
+	unsigned char		ep0_response_buff[EP0_RESPONSE_BUFF];
+	/*
+	 * Timer to check if host resumed transfer after bdc sent Func wake
+	 * notification  packet after a remote wakeup. if not, then resend the
+	 * Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4
+	 */
+	struct delayed_work	func_wake_notify;
+	struct clk		*clk;
+};
+
+static inline u32 bdc_readl(void __iomem *base, u32 offset)
+{
+	return readl(base + offset);
+}
+
+static inline void bdc_writel(void __iomem *base, u32 offset, u32 value)
+{
+	writel(value, base + offset);
+}
+
+/* Buffer descriptor list operations */
+void bdc_notify_xfr(struct bdc *, u32);
+void bdc_softconn(struct bdc *);
+void bdc_softdisconn(struct bdc *);
+int bdc_run(struct bdc *);
+int bdc_stop(struct bdc *);
+int bdc_reset(struct bdc *);
+int bdc_udc_init(struct bdc *);
+void bdc_udc_exit(struct bdc *);
+int bdc_reinit(struct bdc *);
+
+/* Status report handlers */
+/* Upstream port status change sr */
+void bdc_sr_uspc(struct bdc *, struct bdc_sr *);
+/* transfer sr */
+void bdc_sr_xsf(struct bdc *, struct bdc_sr *);
+/* EP0 XSF handlers */
+void bdc_xsf_ep0_setup_recv(struct bdc *, struct bdc_sr *);
+void bdc_xsf_ep0_data_start(struct bdc *, struct bdc_sr *);
+void bdc_xsf_ep0_status_start(struct bdc *, struct bdc_sr *);
+
+#endif /* __LINUX_BDC_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
new file mode 100644
index 0000000..6305bf2
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_cmd.c - BRCM BDC USB3.0 device controller
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+
+#include "bdc.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+/* Issues a cmd to cmd processor and waits for cmd completion */
+static int bdc_issue_cmd(struct bdc *bdc, u32 cmd_sc, u32 param0,
+							u32 param1, u32 param2)
+{
+	u32 timeout = BDC_CMD_TIMEOUT;
+	u32 cmd_status;
+	u32 temp;
+
+	bdc_writel(bdc->regs, BDC_CMDPAR0, param0);
+	bdc_writel(bdc->regs, BDC_CMDPAR1, param1);
+	bdc_writel(bdc->regs, BDC_CMDPAR2, param2);
+
+	/* Issue the cmd */
+	/* Make sure the cmd params are written before asking HW to exec cmd */
+	wmb();
+	bdc_writel(bdc->regs, BDC_CMDSC, cmd_sc | BDC_CMD_CWS | BDC_CMD_SRD);
+	do {
+		temp = bdc_readl(bdc->regs, BDC_CMDSC);
+		dev_dbg_ratelimited(bdc->dev, "cmdsc=%x", temp);
+		cmd_status =  BDC_CMD_CST(temp);
+		if (cmd_status != BDC_CMDS_BUSY)  {
+			dev_dbg(bdc->dev,
+				"command completed cmd_sts:%x\n", cmd_status);
+			return cmd_status;
+		}
+		udelay(1);
+	} while (timeout--);
+
+	dev_err(bdc->dev,
+		"command operation timedout cmd_status=%d\n", cmd_status);
+
+	return cmd_status;
+}
+
+/* Submits cmd and analyze the return value of bdc_issue_cmd */
+static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
+					u32 param0, u32 param1,	u32 param2)
+{
+	u32 temp, cmd_status;
+	int ret;
+
+	temp = bdc_readl(bdc->regs, BDC_CMDSC);
+	dev_dbg(bdc->dev,
+		"%s:CMDSC:%08x cmdsc:%08x param0=%08x param1=%08x param2=%08x\n",
+		 __func__, temp, cmd_sc, param0, param1, param2);
+
+	cmd_status = BDC_CMD_CST(temp);
+	if (cmd_status  ==  BDC_CMDS_BUSY) {
+		dev_err(bdc->dev, "command processor busy: %x\n", cmd_status);
+		return -EBUSY;
+	}
+	ret = bdc_issue_cmd(bdc, cmd_sc, param0, param1, param2);
+	switch (ret) {
+	case BDC_CMDS_SUCC:
+		dev_dbg(bdc->dev, "command completed successfully\n");
+		ret = 0;
+		break;
+
+	case BDC_CMDS_PARA:
+		dev_err(bdc->dev, "command parameter error\n");
+		ret = -EINVAL;
+		break;
+
+	case BDC_CMDS_STAT:
+		dev_err(bdc->dev, "Invalid device/ep state\n");
+		ret = -EINVAL;
+		break;
+
+	case BDC_CMDS_FAIL:
+		dev_err(bdc->dev, "Command failed?\n");
+		ret = -EAGAIN;
+		break;
+
+	case BDC_CMDS_INTL:
+		dev_err(bdc->dev, "BDC Internal error\n");
+		ret = -ECONNRESET;
+		break;
+
+	case BDC_CMDS_BUSY:
+		dev_err(bdc->dev,
+			"command timedout waited for %dusec\n",
+			BDC_CMD_TIMEOUT);
+		ret = -ECONNRESET;
+		break;
+	default:
+		dev_dbg(bdc->dev, "Unknown command completion code:%x\n", ret);
+	}
+
+	return ret;
+}
+
+/* Deconfigure the endpoint from HW */
+int bdc_dconfig_ep(struct bdc *bdc, struct bdc_ep *ep)
+{
+	u32 cmd_sc;
+
+	cmd_sc = BDC_SUB_CMD_DRP_EP|BDC_CMD_EPN(ep->ep_num)|BDC_CMD_EPC;
+	dev_dbg(bdc->dev, "%s ep->ep_num =%d cmd_sc=%x\n", __func__,
+							ep->ep_num, cmd_sc);
+
+	return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+}
+
+/* Reinitalize the bdlist after config ep command */
+static void ep_bd_list_reinit(struct bdc_ep *ep)
+{
+	struct bdc *bdc = ep->bdc;
+	struct bdc_bd *bd;
+
+	ep->bd_list.eqp_bdi = 0;
+	ep->bd_list.hwd_bdi = 0;
+	bd = ep->bd_list.bd_table_array[0]->start_bd;
+	dev_dbg(bdc->dev, "%s ep:%p bd:%p\n", __func__, ep, bd);
+	memset(bd, 0, sizeof(struct bdc_bd));
+	bd->offset[3] |= cpu_to_le32(BD_SBF);
+}
+
+/* Configure an endpoint */
+int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
+{
+	const struct usb_ss_ep_comp_descriptor *comp_desc;
+	const struct usb_endpoint_descriptor	*desc;
+	u32 param0, param1, param2, cmd_sc;
+	u32 mps, mbs, mul, si;
+	int ret;
+
+	desc = ep->desc;
+	comp_desc = ep->comp_desc;
+	cmd_sc = mul = mbs = param2 = 0;
+	param0 = lower_32_bits(ep->bd_list.bd_table_array[0]->dma);
+	param1 = upper_32_bits(ep->bd_list.bd_table_array[0]->dma);
+	cpu_to_le32s(&param0);
+	cpu_to_le32s(&param1);
+
+	dev_dbg(bdc->dev, "%s: param0=%08x param1=%08x",
+						__func__, param0, param1);
+	si = desc->bInterval;
+	si = clamp_val(si, 1, 16) - 1;
+
+	mps = usb_endpoint_maxp(desc);
+	mps &= 0x7ff;
+	param2 |= mps << MP_SHIFT;
+	param2 |= usb_endpoint_type(desc) << EPT_SHIFT;
+
+	switch (bdc->gadget.speed) {
+	case USB_SPEED_SUPER:
+		if (usb_endpoint_xfer_int(desc) ||
+					usb_endpoint_xfer_isoc(desc)) {
+			param2 |= si;
+			if (usb_endpoint_xfer_isoc(desc) && comp_desc)
+					mul = comp_desc->bmAttributes;
+
+		}
+		param2 |= mul << EPM_SHIFT;
+		if (comp_desc)
+			mbs = comp_desc->bMaxBurst;
+		param2 |= mbs << MB_SHIFT;
+		break;
+
+	case USB_SPEED_HIGH:
+		if (usb_endpoint_xfer_isoc(desc) ||
+					usb_endpoint_xfer_int(desc)) {
+			param2 |= si;
+
+			mbs = usb_endpoint_maxp_mult(desc);
+			param2 |= mbs << MB_SHIFT;
+		}
+		break;
+
+	case USB_SPEED_FULL:
+	case USB_SPEED_LOW:
+		/* the hardware accepts SI in 125usec range */
+		if (usb_endpoint_xfer_isoc(desc))
+			si += 3;
+
+		/*
+		 * FS Int endpoints can have si of 1-255ms but the controller
+		 * accepts 2^bInterval*125usec, so convert ms to nearest power
+		 * of 2
+		 */
+		if (usb_endpoint_xfer_int(desc))
+			si = fls(desc->bInterval * 8) - 1;
+
+		param2 |= si;
+		break;
+	default:
+		dev_err(bdc->dev, "UNKNOWN speed ERR\n");
+		return -EINVAL;
+	}
+
+	cmd_sc |= BDC_CMD_EPC|BDC_CMD_EPN(ep->ep_num)|BDC_SUB_CMD_ADD_EP;
+
+	dev_dbg(bdc->dev, "cmd_sc=%x param2=%08x\n", cmd_sc, param2);
+	ret = bdc_submit_cmd(bdc, cmd_sc, param0, param1, param2);
+	if (ret) {
+		dev_err(bdc->dev, "command failed :%x\n", ret);
+		return ret;
+	}
+	ep_bd_list_reinit(ep);
+
+	return ret;
+}
+
+/*
+ * Change the HW deq pointer, if this command is successful, HW will start
+ * fetching the next bd from address dma_addr.
+ */
+int bdc_ep_bla(struct bdc *bdc, struct bdc_ep *ep, dma_addr_t dma_addr)
+{
+	u32 param0, param1;
+	u32 cmd_sc = 0;
+
+	dev_dbg(bdc->dev, "%s: add=%08llx\n", __func__,
+				(unsigned long long)(dma_addr));
+	param0 = lower_32_bits(dma_addr);
+	param1 = upper_32_bits(dma_addr);
+	cpu_to_le32s(&param0);
+	cpu_to_le32s(&param1);
+
+	cmd_sc |= BDC_CMD_EPN(ep->ep_num)|BDC_CMD_BLA;
+	dev_dbg(bdc->dev, "cmd_sc=%x\n", cmd_sc);
+
+	return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
+}
+
+/* Set the address sent bu Host in SET_ADD request */
+int bdc_address_device(struct bdc *bdc, u32 add)
+{
+	u32 cmd_sc = 0;
+	u32 param2;
+
+	dev_dbg(bdc->dev, "%s: add=%d\n", __func__, add);
+	cmd_sc |=  BDC_SUB_CMD_ADD|BDC_CMD_DVC;
+	param2 = add & 0x7f;
+
+	return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
+}
+
+/* Send a Function Wake notification packet using FH command */
+int bdc_function_wake_fh(struct bdc *bdc, u8 intf)
+{
+	u32 param0, param1;
+	u32 cmd_sc = 0;
+
+	param0 = param1 = 0;
+	dev_dbg(bdc->dev, "%s intf=%d\n", __func__, intf);
+	cmd_sc  |=  BDC_CMD_FH;
+	param0 |= TRA_PACKET;
+	param0 |= (bdc->dev_addr << 25);
+	param1 |= DEV_NOTF_TYPE;
+	param1 |= (FWK_SUBTYPE<<4);
+	dev_dbg(bdc->dev, "param0=%08x param1=%08x\n", param0, param1);
+
+	return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
+}
+
+/* Send a Function Wake notification packet using DNC command */
+int bdc_function_wake(struct bdc *bdc, u8 intf)
+{
+	u32 cmd_sc = 0;
+	u32 param2 = 0;
+
+	dev_dbg(bdc->dev, "%s intf=%d", __func__, intf);
+	param2 |= intf;
+	cmd_sc |= BDC_SUB_CMD_FWK|BDC_CMD_DNC;
+
+	return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
+}
+
+/* Stall the endpoint */
+int bdc_ep_set_stall(struct bdc *bdc, int epnum)
+{
+	u32 cmd_sc = 0;
+
+	dev_dbg(bdc->dev, "%s epnum=%d\n", __func__, epnum);
+	/* issue a stall endpoint command */
+	cmd_sc |=  BDC_SUB_CMD_EP_STL | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+	return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+}
+
+/* resets the endpoint, called when host sends CLEAR_FEATURE(HALT) */
+int bdc_ep_clear_stall(struct bdc *bdc, int epnum)
+{
+	struct bdc_ep *ep;
+	u32 cmd_sc = 0;
+	int ret;
+
+	dev_dbg(bdc->dev, "%s: epnum=%d\n", __func__, epnum);
+	ep = bdc->bdc_ep_array[epnum];
+	/*
+	 * If we are not in stalled then stall Endpoint and issue clear stall,
+	 * his will reset the seq number for non EP0.
+	 */
+	if (epnum != 1) {
+		/* if the endpoint it not stallled */
+		if (!(ep->flags & BDC_EP_STALL)) {
+			ret = bdc_ep_set_stall(bdc, epnum);
+				if (ret)
+					return ret;
+		}
+	}
+	/* Preserve the seq number for ep0 only */
+	if (epnum != 1)
+		cmd_sc |= BDC_CMD_EPO_RST_SN;
+
+	/* issue a reset endpoint command */
+	cmd_sc |=  BDC_SUB_CMD_EP_RST | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+	ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+	if (ret) {
+		dev_err(bdc->dev, "command failed:%x\n", ret);
+		return ret;
+	}
+	bdc_notify_xfr(bdc, epnum);
+
+	return ret;
+}
+
+/* Stop the endpoint, called when software wants to dequeue some request */
+int bdc_stop_ep(struct bdc *bdc, int epnum)
+{
+	struct bdc_ep *ep;
+	u32 cmd_sc = 0;
+	int ret;
+
+	ep = bdc->bdc_ep_array[epnum];
+	dev_dbg(bdc->dev, "%s: ep:%s ep->flags:%08x\n", __func__,
+						ep->name, ep->flags);
+	/* Endpoint has to be in running state to execute stop ep command */
+	if (!(ep->flags & BDC_EP_ENABLED)) {
+		dev_err(bdc->dev, "stop endpoint called for disabled ep\n");
+		return   -EINVAL;
+	}
+	if ((ep->flags & BDC_EP_STALL) || (ep->flags & BDC_EP_STOP))
+		return 0;
+
+	/* issue a stop endpoint command */
+	cmd_sc |= BDC_CMD_EP0_XSD | BDC_SUB_CMD_EP_STP
+				| BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+	ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+	if (ret) {
+		dev_err(bdc->dev,
+			"stop endpoint command didn't complete:%d ep:%s\n",
+			ret, ep->name);
+		return ret;
+	}
+	ep->flags |= BDC_EP_STOP;
+	bdc_dump_epsts(bdc);
+
+	return ret;
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.h b/drivers/usb/gadget/udc/bdc/bdc_cmd.h
new file mode 100644
index 0000000..29cc988
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_cmd.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+#ifndef __LINUX_BDC_CMD_H__
+#define __LINUX_BDC_CMD_H__
+
+/* Command operations */
+int bdc_address_device(struct bdc *, u32);
+int bdc_config_ep(struct bdc *, struct bdc_ep *);
+int bdc_dconfig_ep(struct bdc *, struct bdc_ep *);
+int bdc_stop_ep(struct bdc *, int);
+int bdc_ep_set_stall(struct bdc *, int);
+int bdc_ep_clear_stall(struct bdc *, int);
+int bdc_ep_set_halt(struct bdc_ep *, u32 , int);
+int bdc_ep_bla(struct bdc *, struct bdc_ep *, dma_addr_t);
+int bdc_function_wake(struct bdc*, u8);
+int bdc_function_wake_fh(struct bdc*, u8);
+
+#endif /* __LINUX_BDC_CMD_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
new file mode 100644
index 0000000..01b44e1
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -0,0 +1,655 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_core.c - BRCM BDC USB3.0 device controller core operations
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/clk.h>
+
+#include "bdc.h"
+#include "bdc_dbg.h"
+
+/* Poll till controller status is not OIP */
+static int poll_oip(struct bdc *bdc, int usec)
+{
+	u32 status;
+	/* Poll till STS!= OIP */
+	while (usec) {
+		status = bdc_readl(bdc->regs, BDC_BDCSC);
+		if (BDC_CSTS(status) != BDC_OIP) {
+			dev_dbg(bdc->dev,
+				"poll_oip complete status=%d",
+				BDC_CSTS(status));
+			return 0;
+		}
+		udelay(10);
+		usec -= 10;
+	}
+	dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status);
+
+	return -ETIMEDOUT;
+}
+
+/* Stop the BDC controller */
+int bdc_stop(struct bdc *bdc)
+{
+	int ret;
+	u32 temp;
+
+	dev_dbg(bdc->dev, "%s ()\n\n", __func__);
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	/* Check if BDC is already halted */
+	if (BDC_CSTS(temp) == BDC_HLT) {
+		dev_vdbg(bdc->dev, "BDC already halted\n");
+		return 0;
+	}
+	temp &= ~BDC_COP_MASK;
+	temp |= BDC_COS|BDC_COP_STP;
+	bdc_writel(bdc->regs, BDC_BDCSC, temp);
+
+	ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+	if (ret)
+		dev_err(bdc->dev, "bdc stop operation failed");
+
+	return ret;
+}
+
+/* Issue a reset to BDC controller */
+int bdc_reset(struct bdc *bdc)
+{
+	u32 temp;
+	int ret;
+
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	/* First halt the controller */
+	ret = bdc_stop(bdc);
+	if (ret)
+		return ret;
+
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	temp &= ~BDC_COP_MASK;
+	temp |= BDC_COS|BDC_COP_RST;
+	bdc_writel(bdc->regs, BDC_BDCSC, temp);
+	ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+	if (ret)
+		dev_err(bdc->dev, "bdc reset operation failed");
+
+	return ret;
+}
+
+/* Run the BDC controller */
+int bdc_run(struct bdc *bdc)
+{
+	u32 temp;
+	int ret;
+
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	/* if BDC is already in running state then do not do anything */
+	if (BDC_CSTS(temp) == BDC_NOR) {
+		dev_warn(bdc->dev, "bdc is already in running state\n");
+		return 0;
+	}
+	temp &= ~BDC_COP_MASK;
+	temp |= BDC_COP_RUN;
+	temp |= BDC_COS;
+	bdc_writel(bdc->regs, BDC_BDCSC, temp);
+	ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+	if (ret) {
+		dev_err(bdc->dev, "bdc run operation failed:%d", ret);
+		return ret;
+	}
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	if (BDC_CSTS(temp) != BDC_NOR) {
+		dev_err(bdc->dev, "bdc not in normal mode after RUN op :%d\n",
+								BDC_CSTS(temp));
+		return -ESHUTDOWN;
+	}
+
+	return 0;
+}
+
+/*
+ * Present the termination to the host, typically called from upstream port
+ * event with Vbus present =1
+ */
+void bdc_softconn(struct bdc *bdc)
+{
+	u32 uspc;
+
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	uspc &= ~BDC_PST_MASK;
+	uspc |= BDC_LINK_STATE_RX_DET;
+	uspc |= BDC_SWS;
+	dev_dbg(bdc->dev, "%s () uspc=%08x\n", __func__, uspc);
+	bdc_writel(bdc->regs, BDC_USPC, uspc);
+}
+
+/* Remove the termination */
+void bdc_softdisconn(struct bdc *bdc)
+{
+	u32 uspc;
+
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	uspc |= BDC_SDC;
+	uspc &= ~BDC_SCN;
+	dev_dbg(bdc->dev, "%s () uspc=%x\n", __func__, uspc);
+	bdc_writel(bdc->regs, BDC_USPC, uspc);
+}
+
+/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
+static int scratchpad_setup(struct bdc *bdc)
+{
+	int sp_buff_size;
+	u32 low32;
+	u32 upp32;
+
+	sp_buff_size = BDC_SPB(bdc_readl(bdc->regs, BDC_BDCCFG0));
+	dev_dbg(bdc->dev, "%s() sp_buff_size=%d\n", __func__, sp_buff_size);
+	if (!sp_buff_size) {
+		dev_dbg(bdc->dev, "Scratchpad buffer not needed\n");
+		return 0;
+	}
+	/* Refer to BDC spec, Table 4 for description of SPB */
+	sp_buff_size = 1 << (sp_buff_size + 5);
+	dev_dbg(bdc->dev, "Allocating %d bytes for scratchpad\n", sp_buff_size);
+	bdc->scratchpad.buff  =  dma_zalloc_coherent(bdc->dev, sp_buff_size,
+					&bdc->scratchpad.sp_dma, GFP_KERNEL);
+
+	if (!bdc->scratchpad.buff)
+		goto fail;
+
+	bdc->sp_buff_size = sp_buff_size;
+	bdc->scratchpad.size = sp_buff_size;
+	low32 = lower_32_bits(bdc->scratchpad.sp_dma);
+	upp32 = upper_32_bits(bdc->scratchpad.sp_dma);
+	cpu_to_le32s(&low32);
+	cpu_to_le32s(&upp32);
+	bdc_writel(bdc->regs, BDC_SPBBAL, low32);
+	bdc_writel(bdc->regs, BDC_SPBBAH, upp32);
+	return 0;
+
+fail:
+	bdc->scratchpad.buff = NULL;
+
+	return -ENOMEM;
+}
+
+/* Allocate the status report ring */
+static int setup_srr(struct bdc *bdc, int interrupter)
+{
+	dev_dbg(bdc->dev, "%s() NUM_SR_ENTRIES:%d\n", __func__, NUM_SR_ENTRIES);
+	/* Reset the SRR */
+	bdc_writel(bdc->regs, BDC_SRRINT(0), BDC_SRR_RWS | BDC_SRR_RST);
+	bdc->srr.dqp_index = 0;
+	/* allocate the status report descriptors */
+	bdc->srr.sr_bds = dma_zalloc_coherent(
+					bdc->dev,
+					NUM_SR_ENTRIES * sizeof(struct bdc_bd),
+					&bdc->srr.dma_addr,
+					GFP_KERNEL);
+	if (!bdc->srr.sr_bds)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/* Initialize the HW regs and internal data structures */
+static void bdc_mem_init(struct bdc *bdc, bool reinit)
+{
+	u8 size = 0;
+	u32 usb2_pm;
+	u32 low32;
+	u32 upp32;
+	u32 temp;
+
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	bdc->ep0_state = WAIT_FOR_SETUP;
+	bdc->dev_addr = 0;
+	bdc->srr.eqp_index = 0;
+	bdc->srr.dqp_index = 0;
+	bdc->zlp_needed = false;
+	bdc->delayed_status = false;
+
+	bdc_writel(bdc->regs, BDC_SPBBAL, bdc->scratchpad.sp_dma);
+	/* Init the SRR */
+	temp = BDC_SRR_RWS | BDC_SRR_RST;
+	/* Reset the SRR */
+	bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
+	dev_dbg(bdc->dev, "bdc->srr.sr_bds =%p\n", bdc->srr.sr_bds);
+	temp = lower_32_bits(bdc->srr.dma_addr);
+	size = fls(NUM_SR_ENTRIES) - 2;
+	temp |= size;
+	dev_dbg(bdc->dev, "SRRBAL[0]=%08x NUM_SR_ENTRIES:%d size:%d\n",
+						temp, NUM_SR_ENTRIES, size);
+
+	low32 = lower_32_bits(temp);
+	upp32 = upper_32_bits(bdc->srr.dma_addr);
+	cpu_to_le32s(&low32);
+	cpu_to_le32s(&upp32);
+
+	/* Write the dma addresses into regs*/
+	bdc_writel(bdc->regs, BDC_SRRBAL(0), low32);
+	bdc_writel(bdc->regs, BDC_SRRBAH(0), upp32);
+
+	temp = bdc_readl(bdc->regs, BDC_SRRINT(0));
+	temp |= BDC_SRR_IE;
+	temp &= ~(BDC_SRR_RST | BDC_SRR_RWS);
+	bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
+
+	/* Set the Interrupt Coalescence ~500 usec */
+	temp = bdc_readl(bdc->regs, BDC_INTCTLS(0));
+	temp &= ~0xffff;
+	temp |= INT_CLS;
+	bdc_writel(bdc->regs, BDC_INTCTLS(0), temp);
+
+	usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+	dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
+	/* Enable hardware LPM Enable */
+	usb2_pm |= BDC_HLE;
+	bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
+
+	/* readback for debug */
+	usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+	dev_dbg(bdc->dev, "usb2_pm=%08x\n", usb2_pm);
+
+	/* Disable any unwanted SR's on SRR */
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	/* We don't want Microframe counter wrap SR */
+	temp |= BDC_MASK_MCW;
+	bdc_writel(bdc->regs, BDC_BDCSC, temp);
+
+	/*
+	 * In some error cases, driver has to reset the entire BDC controller
+	 * in that case reinit is passed as 1
+	 */
+	if (reinit) {
+		/* Enable interrupts */
+		temp = bdc_readl(bdc->regs, BDC_BDCSC);
+		temp |= BDC_GIE;
+		bdc_writel(bdc->regs, BDC_BDCSC, temp);
+		/* Init scratchpad to 0 */
+		memset(bdc->scratchpad.buff, 0, bdc->sp_buff_size);
+		/* Initialize SRR to 0 */
+		memset(bdc->srr.sr_bds, 0,
+					NUM_SR_ENTRIES * sizeof(struct bdc_bd));
+	} else {
+		/* One time initiaization only */
+		/* Enable status report function pointers */
+		bdc->sr_handler[0] = bdc_sr_xsf;
+		bdc->sr_handler[1] = bdc_sr_uspc;
+
+		/* EP0 status report function pointers */
+		bdc->sr_xsf_ep0[0] = bdc_xsf_ep0_setup_recv;
+		bdc->sr_xsf_ep0[1] = bdc_xsf_ep0_data_start;
+		bdc->sr_xsf_ep0[2] = bdc_xsf_ep0_status_start;
+	}
+}
+
+/* Free the dynamic memory */
+static void bdc_mem_free(struct bdc *bdc)
+{
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	/* Free SRR */
+	if (bdc->srr.sr_bds)
+		dma_free_coherent(bdc->dev,
+					NUM_SR_ENTRIES * sizeof(struct bdc_bd),
+					bdc->srr.sr_bds, bdc->srr.dma_addr);
+
+	/* Free scratchpad */
+	if (bdc->scratchpad.buff)
+		dma_free_coherent(bdc->dev, bdc->sp_buff_size,
+				bdc->scratchpad.buff, bdc->scratchpad.sp_dma);
+
+	/* Destroy the dma pools */
+	dma_pool_destroy(bdc->bd_table_pool);
+
+	/* Free the bdc_ep array */
+	kfree(bdc->bdc_ep_array);
+
+	bdc->srr.sr_bds = NULL;
+	bdc->scratchpad.buff = NULL;
+	bdc->bd_table_pool = NULL;
+	bdc->bdc_ep_array = NULL;
+}
+
+/*
+ * bdc reinit gives a controller reset and reinitialize the registers,
+ * called from disconnect/bus reset scenario's, to ensure proper HW cleanup
+ */
+int bdc_reinit(struct bdc *bdc)
+{
+	int ret;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	ret = bdc_stop(bdc);
+	if (ret)
+		goto out;
+
+	ret = bdc_reset(bdc);
+	if (ret)
+		goto out;
+
+	/* the reinit flag is 1 */
+	bdc_mem_init(bdc, true);
+	ret = bdc_run(bdc);
+out:
+	bdc->reinit = false;
+
+	return ret;
+}
+
+/* Allocate all the dyanmic memory */
+static int bdc_mem_alloc(struct bdc *bdc)
+{
+	u32 page_size;
+	unsigned int num_ieps, num_oeps;
+
+	dev_dbg(bdc->dev,
+		"%s() NUM_BDS_PER_TABLE:%d\n", __func__,
+		NUM_BDS_PER_TABLE);
+	page_size = BDC_PGS(bdc_readl(bdc->regs, BDC_BDCCFG0));
+	/* page size is 2^pgs KB */
+	page_size = 1 << page_size;
+	/* KB */
+	page_size <<= 10;
+	dev_dbg(bdc->dev, "page_size=%d\n", page_size);
+
+	/* Create a pool of bd tables */
+	bdc->bd_table_pool =
+	    dma_pool_create("BDC BD tables", bdc->dev, NUM_BDS_PER_TABLE * 16,
+								16, page_size);
+
+	if (!bdc->bd_table_pool)
+		goto fail;
+
+	if (scratchpad_setup(bdc))
+		goto fail;
+
+	/* read from regs */
+	num_ieps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNIC));
+	num_oeps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNOC));
+	/* +2: 1 for ep0 and the other is rsvd i.e. bdc_ep[0] is rsvd */
+	bdc->num_eps = num_ieps + num_oeps + 2;
+	dev_dbg(bdc->dev,
+		"ieps:%d eops:%d num_eps:%d\n",
+		num_ieps, num_oeps, bdc->num_eps);
+	/* allocate array of ep pointers */
+	bdc->bdc_ep_array = kcalloc(bdc->num_eps, sizeof(struct bdc_ep *),
+								GFP_KERNEL);
+	if (!bdc->bdc_ep_array)
+		goto fail;
+
+	dev_dbg(bdc->dev, "Allocating sr report0\n");
+	if (setup_srr(bdc, 0))
+		goto fail;
+
+	return 0;
+fail:
+	dev_warn(bdc->dev, "Couldn't initialize memory\n");
+	bdc_mem_free(bdc);
+
+	return -ENOMEM;
+}
+
+/* opposite to bdc_hw_init */
+static void bdc_hw_exit(struct bdc *bdc)
+{
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	bdc_mem_free(bdc);
+}
+
+/* Initialize the bdc HW and memory */
+static int bdc_hw_init(struct bdc *bdc)
+{
+	int ret;
+
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	ret = bdc_reset(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "err resetting bdc abort bdc init%d\n", ret);
+		return ret;
+	}
+	ret = bdc_mem_alloc(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "Mem alloc failed, aborting\n");
+		return -ENOMEM;
+	}
+	bdc_mem_init(bdc, 0);
+	bdc_dbg_regs(bdc);
+	dev_dbg(bdc->dev, "HW Init done\n");
+
+	return 0;
+}
+
+static int bdc_phy_init(struct bdc *bdc)
+{
+	int phy_num;
+	int ret;
+
+	for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+		ret = phy_init(bdc->phys[phy_num]);
+		if (ret)
+			goto err_exit_phy;
+		ret = phy_power_on(bdc->phys[phy_num]);
+		if (ret) {
+			phy_exit(bdc->phys[phy_num]);
+			goto err_exit_phy;
+		}
+	}
+
+	return 0;
+
+err_exit_phy:
+	while (--phy_num >= 0) {
+		phy_power_off(bdc->phys[phy_num]);
+		phy_exit(bdc->phys[phy_num]);
+	}
+
+	return ret;
+}
+
+static void bdc_phy_exit(struct bdc *bdc)
+{
+	int phy_num;
+
+	for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+		phy_power_off(bdc->phys[phy_num]);
+		phy_exit(bdc->phys[phy_num]);
+	}
+}
+
+static int bdc_probe(struct platform_device *pdev)
+{
+	struct bdc *bdc;
+	struct resource *res;
+	int ret = -ENOMEM;
+	int irq;
+	u32 temp;
+	struct device *dev = &pdev->dev;
+	struct clk *clk;
+	int phy_num;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	clk = devm_clk_get(dev, "sw_usbd");
+	if (IS_ERR(clk)) {
+		dev_info(dev, "Clock not found in Device Tree\n");
+		clk = NULL;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		dev_err(dev, "could not enable clock\n");
+		return ret;
+	}
+
+	bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
+	if (!bdc)
+		return -ENOMEM;
+
+	bdc->clk = clk;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bdc->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(bdc->regs)) {
+		dev_err(dev, "ioremap error\n");
+		return -ENOMEM;
+	}
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "platform_get_irq failed:%d\n", irq);
+		return irq;
+	}
+	spin_lock_init(&bdc->lock);
+	platform_set_drvdata(pdev, bdc);
+	bdc->irq = irq;
+	bdc->dev = dev;
+	dev_dbg(dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
+
+	bdc->num_phys = of_count_phandle_with_args(dev->of_node,
+						"phys", "#phy-cells");
+	if (bdc->num_phys > 0) {
+		bdc->phys = devm_kcalloc(dev, bdc->num_phys,
+					sizeof(struct phy *), GFP_KERNEL);
+		if (!bdc->phys)
+			return -ENOMEM;
+	} else {
+		bdc->num_phys = 0;
+	}
+	dev_info(dev, "Using %d phy(s)\n", bdc->num_phys);
+
+	for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
+		bdc->phys[phy_num] = devm_of_phy_get_by_index(
+			dev, dev->of_node, phy_num);
+		if (IS_ERR(bdc->phys[phy_num])) {
+			ret = PTR_ERR(bdc->phys[phy_num]);
+			dev_err(bdc->dev,
+				"BDC phy specified but not found:%d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = bdc_phy_init(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "BDC phy init failure:%d\n", ret);
+		return ret;
+	}
+
+	temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
+	if ((temp & BDC_P64) &&
+			!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
+		dev_dbg(dev, "Using 64-bit address\n");
+	} else {
+		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+		if (ret) {
+			dev_err(dev,
+				"No suitable DMA config available, abort\n");
+			return -ENOTSUPP;
+		}
+		dev_dbg(dev, "Using 32-bit address\n");
+	}
+	ret = bdc_hw_init(bdc);
+	if (ret) {
+		dev_err(dev, "BDC init failure:%d\n", ret);
+		goto phycleanup;
+	}
+	ret = bdc_udc_init(bdc);
+	if (ret) {
+		dev_err(dev, "BDC Gadget init failure:%d\n", ret);
+		goto cleanup;
+	}
+	return 0;
+
+cleanup:
+	bdc_hw_exit(bdc);
+phycleanup:
+	bdc_phy_exit(bdc);
+	return ret;
+}
+
+static int bdc_remove(struct platform_device *pdev)
+{
+	struct bdc *bdc;
+
+	bdc  = platform_get_drvdata(pdev);
+	dev_dbg(bdc->dev, "%s ()\n", __func__);
+	bdc_udc_exit(bdc);
+	bdc_hw_exit(bdc);
+	bdc_phy_exit(bdc);
+	clk_disable_unprepare(bdc->clk);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bdc_suspend(struct device *dev)
+{
+	struct bdc *bdc = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(bdc->clk);
+	return 0;
+}
+
+static int bdc_resume(struct device *dev)
+{
+	struct bdc *bdc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(bdc->clk);
+	if (ret) {
+		dev_err(bdc->dev, "err enabling the clock\n");
+		return ret;
+	}
+	ret = bdc_reinit(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "err in bdc reinit\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(bdc_pm_ops, bdc_suspend,
+		bdc_resume);
+
+static const struct of_device_id bdc_of_match[] = {
+	{ .compatible = "brcm,bdc-v0.16" },
+	{ .compatible = "brcm,bdc" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver bdc_driver = {
+	.driver		= {
+		.name	= BRCM_BDC_NAME,
+		.pm = &bdc_pm_ops,
+		.of_match_table	= bdc_of_match,
+	},
+	.probe		= bdc_probe,
+	.remove		= bdc_remove,
+};
+
+module_platform_driver(bdc_driver);
+MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(BRCM_BDC_DESC);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.c b/drivers/usb/gadget/udc/bdc/bdc_dbg.c
new file mode 100644
index 0000000..7ba7448
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_dbg.c - BRCM BDC USB3.0 device controller debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+
+#include "bdc.h"
+#include "bdc_dbg.h"
+
+void bdc_dbg_regs(struct bdc *bdc)
+{
+	u32 temp;
+
+	dev_vdbg(bdc->dev, "bdc->regs:%p\n", bdc->regs);
+	temp = bdc_readl(bdc->regs, BDC_BDCCFG0);
+	dev_vdbg(bdc->dev, "bdccfg0:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_BDCCFG1);
+	dev_vdbg(bdc->dev, "bdccfg1:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_BDCCAP0);
+	dev_vdbg(bdc->dev, "bdccap0:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
+	dev_vdbg(bdc->dev, "bdccap1:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_USPC);
+	dev_vdbg(bdc->dev, "uspc:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_DVCSA);
+	dev_vdbg(bdc->dev, "dvcsa:0x%08x\n", temp);
+	temp = bdc_readl(bdc->regs, BDC_DVCSB);
+	dev_vdbg(bdc->dev, "dvcsb:0x%x08\n", temp);
+}
+
+void bdc_dump_epsts(struct bdc *bdc)
+{
+	u32 temp;
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS0);
+	dev_vdbg(bdc->dev, "BDC_EPSTS0:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS1);
+	dev_vdbg(bdc->dev, "BDC_EPSTS1:0x%x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS2);
+	dev_vdbg(bdc->dev, "BDC_EPSTS2:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS3);
+	dev_vdbg(bdc->dev, "BDC_EPSTS3:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS4);
+	dev_vdbg(bdc->dev, "BDC_EPSTS4:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS5);
+	dev_vdbg(bdc->dev, "BDC_EPSTS5:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS6);
+	dev_vdbg(bdc->dev, "BDC_EPSTS6:0x%08x\n", temp);
+
+	temp = bdc_readl(bdc->regs, BDC_EPSTS7);
+	dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp);
+}
+
+void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
+{
+	struct bdc_sr *sr;
+	dma_addr_t addr;
+	int i;
+
+	sr = bdc->srr.sr_bds;
+	addr = bdc->srr.dma_addr;
+	dev_vdbg(bdc->dev, "bdc_dbg_srr sr:%p dqp_index:%d\n",
+						sr, bdc->srr.dqp_index);
+	for (i = 0; i < NUM_SR_ENTRIES; i++) {
+		sr = &bdc->srr.sr_bds[i];
+		dev_vdbg(bdc->dev, "%llx %08x %08x %08x %08x\n",
+					(unsigned long long)addr,
+					le32_to_cpu(sr->offset[0]),
+					le32_to_cpu(sr->offset[1]),
+					le32_to_cpu(sr->offset[2]),
+					le32_to_cpu(sr->offset[3]));
+		addr += sizeof(*sr);
+	}
+}
+
+void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
+{
+	struct bd_list *bd_list = &ep->bd_list;
+	struct bd_table *bd_table;
+	struct bdc_bd *bd;
+	int tbi, bdi, gbdi;
+	dma_addr_t dma;
+
+	gbdi = 0;
+	dev_vdbg(bdc->dev,
+		"Dump bd list for %s epnum:%d\n",
+		ep->name, ep->ep_num);
+
+	dev_vdbg(bdc->dev,
+		"tabs:%d max_bdi:%d eqp_bdi:%d hwd_bdi:%d num_bds_table:%d\n",
+		bd_list->num_tabs, bd_list->max_bdi, bd_list->eqp_bdi,
+		bd_list->hwd_bdi, bd_list->num_bds_table);
+
+	for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
+		bd_table = bd_list->bd_table_array[tbi];
+		for (bdi = 0; bdi < bd_list->num_bds_table; bdi++) {
+			bd =  bd_table->start_bd + bdi;
+			dma = bd_table->dma + (sizeof(struct bdc_bd) * bdi);
+			dev_vdbg(bdc->dev,
+				"tbi:%2d bdi:%2d gbdi:%2d virt:%p phys:%llx %08x %08x %08x %08x\n",
+				tbi, bdi, gbdi++, bd, (unsigned long long)dma,
+				le32_to_cpu(bd->offset[0]),
+				le32_to_cpu(bd->offset[1]),
+				le32_to_cpu(bd->offset[2]),
+				le32_to_cpu(bd->offset[3]));
+		}
+		dev_vdbg(bdc->dev, "\n\n");
+	}
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.h b/drivers/usb/gadget/udc/bdc/bdc_dbg.h
new file mode 100644
index 0000000..373d5ab
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_dbg.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+#ifndef __LINUX_BDC_DBG_H__
+#define __LINUX_BDC_DBG_H__
+
+#include "bdc.h"
+
+#ifdef CONFIG_USB_GADGET_VERBOSE
+void bdc_dbg_bd_list(struct bdc *, struct bdc_ep*);
+void bdc_dbg_srr(struct bdc *, u32);
+void bdc_dbg_regs(struct bdc *);
+void bdc_dump_epsts(struct bdc *);
+#else
+static inline void bdc_dbg_regs(struct bdc *bdc)
+{ }
+
+static inline void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
+{ }
+
+static inline void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
+{ }
+
+static inline void bdc_dump_epsts(struct bdc *bdc)
+{ }
+#endif /* CONFIG_USB_GADGET_VERBOSE */
+#endif /* __LINUX_BDC_DBG_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
new file mode 100644
index 0000000..a4d9b5e
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -0,0 +1,2026 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_ep.c - BRCM BDC USB3.0 device controller endpoint related functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/dmapool.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/unaligned.h>
+#include <linux/platform_device.h>
+#include <linux/usb/composite.h>
+
+#include "bdc.h"
+#include "bdc_ep.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+static const char * const ep0_state_string[] =  {
+	"WAIT_FOR_SETUP",
+	"WAIT_FOR_DATA_START",
+	"WAIT_FOR_DATA_XMIT",
+	"WAIT_FOR_STATUS_START",
+	"WAIT_FOR_STATUS_XMIT",
+	"STATUS_PENDING"
+};
+
+/* Free the bdl during ep disable */
+static void ep_bd_list_free(struct bdc_ep *ep, u32 num_tabs)
+{
+	struct bd_list *bd_list = &ep->bd_list;
+	struct bdc *bdc = ep->bdc;
+	struct bd_table *bd_table;
+	int index;
+
+	dev_dbg(bdc->dev, "%s ep:%s num_tabs:%d\n",
+				 __func__, ep->name, num_tabs);
+
+	if (!bd_list->bd_table_array) {
+		dev_dbg(bdc->dev, "%s already freed\n", ep->name);
+		return;
+	}
+	for (index = 0; index < num_tabs; index++) {
+		/*
+		 * check if the bd_table struct is allocated ?
+		 * if yes, then check if bd memory has been allocated, then
+		 * free the dma_pool and also the bd_table struct memory
+		*/
+		bd_table = bd_list->bd_table_array[index];
+		dev_dbg(bdc->dev, "bd_table:%p index:%d\n", bd_table, index);
+		if (!bd_table) {
+			dev_dbg(bdc->dev, "bd_table not allocated\n");
+			continue;
+		}
+		if (!bd_table->start_bd) {
+			dev_dbg(bdc->dev, "bd dma pool not allocated\n");
+			continue;
+		}
+
+		dev_dbg(bdc->dev,
+				"Free dma pool start_bd:%p dma:%llx\n",
+				bd_table->start_bd,
+				(unsigned long long)bd_table->dma);
+
+		dma_pool_free(bdc->bd_table_pool,
+				bd_table->start_bd,
+				bd_table->dma);
+		/* Free the bd_table structure */
+		kfree(bd_table);
+	}
+	/* Free the bd table array */
+	kfree(ep->bd_list.bd_table_array);
+}
+
+/*
+ * chain the tables, by insteting a chain bd at the end of prev_table, pointing
+ * to next_table
+ */
+static inline void chain_table(struct bd_table *prev_table,
+					struct bd_table *next_table,
+					u32 bd_p_tab)
+{
+	/* Chain the prev table to next table */
+	prev_table->start_bd[bd_p_tab-1].offset[0] =
+				cpu_to_le32(lower_32_bits(next_table->dma));
+
+	prev_table->start_bd[bd_p_tab-1].offset[1] =
+				cpu_to_le32(upper_32_bits(next_table->dma));
+
+	prev_table->start_bd[bd_p_tab-1].offset[2] =
+				0x0;
+
+	prev_table->start_bd[bd_p_tab-1].offset[3] =
+				cpu_to_le32(MARK_CHAIN_BD);
+}
+
+/* Allocate the bdl for ep, during config ep */
+static int ep_bd_list_alloc(struct bdc_ep *ep)
+{
+	struct bd_table *prev_table = NULL;
+	int index, num_tabs, bd_p_tab;
+	struct bdc *bdc = ep->bdc;
+	struct bd_table *bd_table;
+	dma_addr_t dma;
+
+	if (usb_endpoint_xfer_isoc(ep->desc))
+		num_tabs = NUM_TABLES_ISOCH;
+	else
+		num_tabs = NUM_TABLES;
+
+	bd_p_tab = NUM_BDS_PER_TABLE;
+	/* if there is only 1 table in bd list then loop chain to self */
+	dev_dbg(bdc->dev,
+		"%s ep:%p num_tabs:%d\n",
+		__func__, ep, num_tabs);
+
+	/* Allocate memory for table array */
+	ep->bd_list.bd_table_array = kcalloc(num_tabs,
+					     sizeof(struct bd_table *),
+					     GFP_ATOMIC);
+	if (!ep->bd_list.bd_table_array)
+		return -ENOMEM;
+
+	/* Allocate memory for each table */
+	for (index = 0; index < num_tabs; index++) {
+		/* Allocate memory for bd_table structure */
+		bd_table = kzalloc(sizeof(struct bd_table), GFP_ATOMIC);
+		if (!bd_table)
+			goto fail;
+
+		bd_table->start_bd = dma_pool_zalloc(bdc->bd_table_pool,
+							GFP_ATOMIC,
+							&dma);
+		if (!bd_table->start_bd) {
+			kfree(bd_table);
+			goto fail;
+		}
+
+		bd_table->dma = dma;
+
+		dev_dbg(bdc->dev,
+			"index:%d start_bd:%p dma=%08llx prev_table:%p\n",
+			index, bd_table->start_bd,
+			(unsigned long long)bd_table->dma, prev_table);
+
+		ep->bd_list.bd_table_array[index] = bd_table;
+		if (prev_table)
+			chain_table(prev_table, bd_table, bd_p_tab);
+
+		prev_table = bd_table;
+	}
+	chain_table(prev_table, ep->bd_list.bd_table_array[0], bd_p_tab);
+	/* Memory allocation is successful, now init the internal fields */
+	ep->bd_list.num_tabs = num_tabs;
+	ep->bd_list.max_bdi  = (num_tabs * bd_p_tab) - 1;
+	ep->bd_list.num_tabs = num_tabs;
+	ep->bd_list.num_bds_table = bd_p_tab;
+	ep->bd_list.eqp_bdi = 0;
+	ep->bd_list.hwd_bdi = 0;
+
+	return 0;
+fail:
+	/* Free the bd_table_array, bd_table struct, bd's */
+	ep_bd_list_free(ep, num_tabs);
+
+	return -ENOMEM;
+}
+
+/* returns how many bd's are need for this transfer */
+static inline int bd_needed_req(struct bdc_req *req)
+{
+	int bd_needed = 0;
+	int remaining;
+
+	/* 1 bd needed for 0 byte transfer */
+	if (req->usb_req.length == 0)
+		return 1;
+
+	/* remaining bytes after tranfering all max BD size BD's */
+	remaining = req->usb_req.length % BD_MAX_BUFF_SIZE;
+	if (remaining)
+		bd_needed++;
+
+	/* How many maximum BUFF size BD's ? */
+	remaining = req->usb_req.length / BD_MAX_BUFF_SIZE;
+	bd_needed += remaining;
+
+	return bd_needed;
+}
+
+/* returns the bd index(bdi) corresponding to bd dma address */
+static int bd_add_to_bdi(struct bdc_ep *ep, dma_addr_t bd_dma_addr)
+{
+	struct bd_list *bd_list = &ep->bd_list;
+	dma_addr_t dma_first_bd, dma_last_bd;
+	struct bdc *bdc = ep->bdc;
+	struct bd_table *bd_table;
+	bool found = false;
+	int tbi, bdi;
+
+	dma_first_bd = dma_last_bd = 0;
+	dev_dbg(bdc->dev, "%s  %llx\n",
+			__func__, (unsigned long long)bd_dma_addr);
+	/*
+	 * Find in which table this bd_dma_addr belongs?, go through the table
+	 * array and compare addresses of first and last address of bd of each
+	 * table
+	 */
+	for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
+		bd_table = bd_list->bd_table_array[tbi];
+		dma_first_bd = bd_table->dma;
+		dma_last_bd = bd_table->dma +
+					(sizeof(struct bdc_bd) *
+					(bd_list->num_bds_table - 1));
+		dev_dbg(bdc->dev, "dma_first_bd:%llx dma_last_bd:%llx\n",
+					(unsigned long long)dma_first_bd,
+					(unsigned long long)dma_last_bd);
+		if (bd_dma_addr >= dma_first_bd && bd_dma_addr <= dma_last_bd) {
+			found = true;
+			break;
+		}
+	}
+	if (unlikely(!found)) {
+		dev_err(bdc->dev, "%s FATAL err, bd not found\n", __func__);
+		return -EINVAL;
+	}
+	/* Now we know the table, find the bdi */
+	bdi = (bd_dma_addr - dma_first_bd) / sizeof(struct bdc_bd);
+
+	/* return the global bdi, to compare with ep eqp_bdi */
+	return (bdi + (tbi * bd_list->num_bds_table));
+}
+
+/* returns the table index(tbi) of the given bdi */
+static int bdi_to_tbi(struct bdc_ep *ep, int bdi)
+{
+	int tbi;
+
+	tbi = bdi / ep->bd_list.num_bds_table;
+	dev_vdbg(ep->bdc->dev,
+		"bdi:%d num_bds_table:%d tbi:%d\n",
+		bdi, ep->bd_list.num_bds_table, tbi);
+
+	return tbi;
+}
+
+/* Find the bdi last bd in the transfer */
+static inline int find_end_bdi(struct bdc_ep *ep, int next_hwd_bdi)
+{
+	int end_bdi;
+
+	end_bdi = next_hwd_bdi - 1;
+	if (end_bdi < 0)
+		end_bdi = ep->bd_list.max_bdi - 1;
+	 else if ((end_bdi % (ep->bd_list.num_bds_table-1)) == 0)
+		end_bdi--;
+
+	return end_bdi;
+}
+
+/*
+ * How many transfer bd's are available on this ep bdl, chain bds are not
+ * counted in available bds
+ */
+static int bd_available_ep(struct bdc_ep *ep)
+{
+	struct bd_list *bd_list = &ep->bd_list;
+	int available1, available2;
+	struct bdc *bdc = ep->bdc;
+	int chain_bd1, chain_bd2;
+	int available_bd = 0;
+
+	available1 = available2 = chain_bd1 = chain_bd2 = 0;
+	/* if empty then we have all bd's available - number of chain bd's */
+	if (bd_list->eqp_bdi == bd_list->hwd_bdi)
+		return bd_list->max_bdi - bd_list->num_tabs;
+
+	/*
+	 * Depending upon where eqp and dqp pointers are, caculate number
+	 * of avaialble bd's
+	 */
+	if (bd_list->hwd_bdi < bd_list->eqp_bdi) {
+		/* available bd's are from eqp..max_bds + 0..dqp - chain_bds */
+		available1 = bd_list->max_bdi - bd_list->eqp_bdi;
+		available2 = bd_list->hwd_bdi;
+		chain_bd1 = available1 / bd_list->num_bds_table;
+		chain_bd2 = available2 / bd_list->num_bds_table;
+		dev_vdbg(bdc->dev, "chain_bd1:%d chain_bd2:%d\n",
+						chain_bd1, chain_bd2);
+		available_bd = available1 + available2 - chain_bd1 - chain_bd2;
+	} else {
+		/* available bd's are from eqp..dqp - number of chain bd's */
+		available1 = bd_list->hwd_bdi -  bd_list->eqp_bdi;
+		/* if gap between eqp and dqp is less than NUM_BDS_PER_TABLE */
+		if ((bd_list->hwd_bdi - bd_list->eqp_bdi)
+					<= bd_list->num_bds_table) {
+			/* If there any chain bd in between */
+			if (!(bdi_to_tbi(ep, bd_list->hwd_bdi)
+					== bdi_to_tbi(ep, bd_list->eqp_bdi))) {
+				available_bd = available1 - 1;
+			}
+		} else {
+			chain_bd1 = available1 / bd_list->num_bds_table;
+			available_bd = available1 - chain_bd1;
+		}
+	}
+	/*
+	 * we need to keep one extra bd to check if ring is full or empty so
+	 * reduce by 1
+	 */
+	available_bd--;
+	dev_vdbg(bdc->dev, "available_bd:%d\n", available_bd);
+
+	return available_bd;
+}
+
+/* Notify the hardware after queueing the bd to bdl */
+void bdc_notify_xfr(struct bdc *bdc, u32 epnum)
+{
+	struct bdc_ep *ep = bdc->bdc_ep_array[epnum];
+
+	dev_vdbg(bdc->dev, "%s epnum:%d\n", __func__, epnum);
+	/*
+	 * We don't have anyway to check if ep state is running,
+	 * except the software flags.
+	 */
+	if (unlikely(ep->flags & BDC_EP_STOP))
+		ep->flags &= ~BDC_EP_STOP;
+
+	bdc_writel(bdc->regs, BDC_XSFNTF, epnum);
+}
+
+/* returns the bd corresponding to bdi */
+static struct bdc_bd *bdi_to_bd(struct bdc_ep *ep, int bdi)
+{
+	int tbi = bdi_to_tbi(ep, bdi);
+	int local_bdi = 0;
+
+	local_bdi = bdi - (tbi * ep->bd_list.num_bds_table);
+	dev_vdbg(ep->bdc->dev,
+		"%s bdi:%d local_bdi:%d\n",
+		 __func__, bdi, local_bdi);
+
+	return (ep->bd_list.bd_table_array[tbi]->start_bd + local_bdi);
+}
+
+/* Advance the enqueue pointer */
+static void ep_bdlist_eqp_adv(struct bdc_ep *ep)
+{
+	ep->bd_list.eqp_bdi++;
+	/* if it's chain bd, then move to next */
+	if (((ep->bd_list.eqp_bdi + 1) % ep->bd_list.num_bds_table) == 0)
+		ep->bd_list.eqp_bdi++;
+
+	/* if the eqp is pointing to last + 1 then move back to 0 */
+	if (ep->bd_list.eqp_bdi == (ep->bd_list.max_bdi + 1))
+		ep->bd_list.eqp_bdi = 0;
+}
+
+/* Setup the first bd for ep0 transfer */
+static int setup_first_bd_ep0(struct bdc *bdc, struct bdc_req *req, u32 *dword3)
+{
+	u16 wValue;
+	u32 req_len;
+
+	req->ep->dir = 0;
+	req_len = req->usb_req.length;
+	switch (bdc->ep0_state) {
+	case WAIT_FOR_DATA_START:
+		*dword3 |= BD_TYPE_DS;
+		if (bdc->setup_pkt.bRequestType & USB_DIR_IN)
+			*dword3 |= BD_DIR_IN;
+
+		/* check if zlp will be needed */
+		wValue = le16_to_cpu(bdc->setup_pkt.wValue);
+		if ((wValue > req_len) &&
+				(req_len % bdc->gadget.ep0->maxpacket == 0)) {
+			dev_dbg(bdc->dev, "ZLP needed wVal:%d len:%d MaxP:%d\n",
+					wValue, req_len,
+					bdc->gadget.ep0->maxpacket);
+			bdc->zlp_needed = true;
+		}
+		break;
+
+	case WAIT_FOR_STATUS_START:
+		*dword3 |= BD_TYPE_SS;
+		if (!le16_to_cpu(bdc->setup_pkt.wLength) ||
+				!(bdc->setup_pkt.bRequestType & USB_DIR_IN))
+			*dword3 |= BD_DIR_IN;
+		break;
+	default:
+		dev_err(bdc->dev,
+			"Unknown ep0 state for queueing bd ep0_state:%s\n",
+			ep0_state_string[bdc->ep0_state]);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Setup the bd dma descriptor for a given request */
+static int setup_bd_list_xfr(struct bdc *bdc, struct bdc_req *req, int num_bds)
+{
+	dma_addr_t buf_add = req->usb_req.dma;
+	u32 maxp, tfs, dword2, dword3;
+	struct bd_transfer *bd_xfr;
+	struct bd_list *bd_list;
+	struct bdc_ep *ep;
+	struct bdc_bd *bd;
+	int ret, bdnum;
+	u32 req_len;
+
+	ep = req->ep;
+	bd_list = &ep->bd_list;
+	bd_xfr = &req->bd_xfr;
+	bd_xfr->req = req;
+	bd_xfr->start_bdi = bd_list->eqp_bdi;
+	bd = bdi_to_bd(ep, bd_list->eqp_bdi);
+	req_len = req->usb_req.length;
+	maxp = usb_endpoint_maxp(ep->desc);
+	tfs = roundup(req->usb_req.length, maxp);
+	tfs = tfs/maxp;
+	dev_vdbg(bdc->dev, "%s ep:%s num_bds:%d tfs:%d r_len:%d bd:%p\n",
+				__func__, ep->name, num_bds, tfs, req_len, bd);
+
+	for (bdnum = 0; bdnum < num_bds; bdnum++) {
+		dword2 = dword3 = 0;
+		/* First bd */
+		if (!bdnum) {
+			dword3 |= BD_SOT|BD_SBF|(tfs<<BD_TFS_SHIFT);
+			dword2 |= BD_LTF;
+			/* format of first bd for ep0 is different than other */
+			if (ep->ep_num == 1) {
+				ret = setup_first_bd_ep0(bdc, req, &dword3);
+				if (ret)
+					return ret;
+			}
+		}
+		if (!req->ep->dir)
+			dword3 |= BD_ISP;
+
+		if (req_len > BD_MAX_BUFF_SIZE) {
+			dword2 |= BD_MAX_BUFF_SIZE;
+			req_len -= BD_MAX_BUFF_SIZE;
+		} else {
+			/* this should be the last bd */
+			dword2 |= req_len;
+			dword3 |= BD_IOC;
+			dword3 |= BD_EOT;
+		}
+		/* Currently only 1 INT target is supported */
+		dword2 |= BD_INTR_TARGET(0);
+		bd = bdi_to_bd(ep, ep->bd_list.eqp_bdi);
+		if (unlikely(!bd)) {
+			dev_err(bdc->dev, "Err bd pointing to wrong addr\n");
+			return -EINVAL;
+		}
+		/* write bd */
+		bd->offset[0] = cpu_to_le32(lower_32_bits(buf_add));
+		bd->offset[1] = cpu_to_le32(upper_32_bits(buf_add));
+		bd->offset[2] = cpu_to_le32(dword2);
+		bd->offset[3] = cpu_to_le32(dword3);
+		/* advance eqp pointer */
+		ep_bdlist_eqp_adv(ep);
+		/* advance the buff pointer */
+		buf_add += BD_MAX_BUFF_SIZE;
+		dev_vdbg(bdc->dev, "buf_add:%08llx req_len:%d bd:%p eqp:%d\n",
+				(unsigned long long)buf_add, req_len, bd,
+							ep->bd_list.eqp_bdi);
+		bd = bdi_to_bd(ep, ep->bd_list.eqp_bdi);
+		bd->offset[3] = cpu_to_le32(BD_SBF);
+	}
+	/* clear the STOP BD fetch bit from the first bd of this xfr */
+	bd = bdi_to_bd(ep, bd_xfr->start_bdi);
+	bd->offset[3] &= cpu_to_le32(~BD_SBF);
+	/* the new eqp will be next hw dqp */
+	bd_xfr->num_bds  = num_bds;
+	bd_xfr->next_hwd_bdi = ep->bd_list.eqp_bdi;
+	/* everything is written correctly before notifying the HW */
+	wmb();
+
+	return 0;
+}
+
+/* Queue the xfr */
+static int bdc_queue_xfr(struct bdc *bdc, struct bdc_req *req)
+{
+	int num_bds, bd_available;
+	struct bdc_ep *ep;
+	int ret;
+
+	ep = req->ep;
+	dev_dbg(bdc->dev, "%s req:%p\n", __func__, req);
+	dev_dbg(bdc->dev, "eqp_bdi:%d hwd_bdi:%d\n",
+			ep->bd_list.eqp_bdi, ep->bd_list.hwd_bdi);
+
+	num_bds =  bd_needed_req(req);
+	bd_available = bd_available_ep(ep);
+
+	/* how many bd's are avaialble on ep */
+	if (num_bds > bd_available)
+		return -ENOMEM;
+
+	ret = setup_bd_list_xfr(bdc, req, num_bds);
+	if (ret)
+		return ret;
+	list_add_tail(&req->queue, &ep->queue);
+	bdc_dbg_bd_list(bdc, ep);
+	bdc_notify_xfr(bdc, ep->ep_num);
+
+	return 0;
+}
+
+/* callback to gadget layer when xfr completes */
+static void bdc_req_complete(struct bdc_ep *ep, struct bdc_req *req,
+						int status)
+{
+	struct bdc *bdc = ep->bdc;
+
+	if (req == NULL  || &req->queue == NULL || &req->usb_req == NULL)
+		return;
+
+	dev_dbg(bdc->dev, "%s ep:%s status:%d\n", __func__, ep->name, status);
+	list_del(&req->queue);
+	req->usb_req.status = status;
+	usb_gadget_unmap_request(&bdc->gadget, &req->usb_req, ep->dir);
+	if (req->usb_req.complete) {
+		spin_unlock(&bdc->lock);
+		usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req);
+		spin_lock(&bdc->lock);
+	}
+}
+
+/* Disable the endpoint */
+int bdc_ep_disable(struct bdc_ep *ep)
+{
+	struct bdc_req *req;
+	struct bdc *bdc;
+	int ret;
+
+	ret = 0;
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s() ep->ep_num=%d\n", __func__, ep->ep_num);
+	/* Stop the endpoint */
+	ret = bdc_stop_ep(bdc, ep->ep_num);
+
+	/*
+	 * Intentionally don't check the ret value of stop, it can fail in
+	 * disconnect scenarios, continue with dconfig
+	 */
+	/* de-queue any pending requests */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct bdc_req,
+				queue);
+		bdc_req_complete(ep, req, -ESHUTDOWN);
+	}
+	/* deconfigure the endpoint */
+	ret = bdc_dconfig_ep(bdc, ep);
+	if (ret)
+		dev_warn(bdc->dev,
+			"dconfig fail but continue with memory free");
+
+	ep->flags = 0;
+	/* ep0 memory is not freed, but reused on next connect sr */
+	if (ep->ep_num == 1)
+		return 0;
+
+	/* Free the bdl memory */
+	ep_bd_list_free(ep, ep->bd_list.num_tabs);
+	ep->desc = NULL;
+	ep->comp_desc = NULL;
+	ep->usb_ep.desc = NULL;
+	ep->ep_type = 0;
+
+	return ret;
+}
+
+/* Enable the ep */
+int bdc_ep_enable(struct bdc_ep *ep)
+{
+	struct bdc *bdc;
+	int ret = 0;
+
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s NUM_TABLES:%d %d\n",
+					__func__, NUM_TABLES, NUM_TABLES_ISOCH);
+
+	ret = ep_bd_list_alloc(ep);
+	if (ret) {
+		dev_err(bdc->dev, "ep bd list allocation failed:%d\n", ret);
+		return -ENOMEM;
+	}
+	bdc_dbg_bd_list(bdc, ep);
+	/* only for ep0: config ep is called for ep0 from connect event */
+	ep->flags |= BDC_EP_ENABLED;
+	if (ep->ep_num == 1)
+		return ret;
+
+	/* Issue a configure endpoint command */
+	ret = bdc_config_ep(bdc, ep);
+	if (ret)
+		return ret;
+
+	ep->usb_ep.maxpacket = usb_endpoint_maxp(ep->desc);
+	ep->usb_ep.desc = ep->desc;
+	ep->usb_ep.comp_desc = ep->comp_desc;
+	ep->ep_type = usb_endpoint_type(ep->desc);
+	ep->flags |= BDC_EP_ENABLED;
+
+	return 0;
+}
+
+/* EP0 related code */
+
+/* Queue a status stage BD */
+static int ep0_queue_status_stage(struct bdc *bdc)
+{
+	struct bdc_req *status_req;
+	struct bdc_ep *ep;
+
+	status_req = &bdc->status_req;
+	ep = bdc->bdc_ep_array[1];
+	status_req->ep = ep;
+	status_req->usb_req.length = 0;
+	status_req->usb_req.status = -EINPROGRESS;
+	status_req->usb_req.actual = 0;
+	status_req->usb_req.complete = NULL;
+	bdc_queue_xfr(bdc, status_req);
+
+	return 0;
+}
+
+/* Queue xfr on ep0 */
+static int ep0_queue(struct bdc_ep *ep, struct bdc_req *req)
+{
+	struct bdc *bdc;
+	int ret;
+
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	req->usb_req.actual = 0;
+	req->usb_req.status = -EINPROGRESS;
+	req->epnum = ep->ep_num;
+
+	if (bdc->delayed_status) {
+		bdc->delayed_status = false;
+		/* if status stage was delayed? */
+		if (bdc->ep0_state == WAIT_FOR_STATUS_START) {
+			/* Queue a status stage BD */
+			ep0_queue_status_stage(bdc);
+			bdc->ep0_state = WAIT_FOR_STATUS_XMIT;
+			return 0;
+		}
+	} else {
+		/*
+		 * if delayed status is false and 0 length transfer is requested
+		 * i.e. for status stage of some setup request, then just
+		 * return from here the status stage is queued independently
+		 */
+		if (req->usb_req.length == 0)
+			return 0;
+
+	}
+	ret = usb_gadget_map_request(&bdc->gadget, &req->usb_req, ep->dir);
+	if (ret) {
+		dev_err(bdc->dev, "dma mapping failed %s\n", ep->name);
+		return ret;
+	}
+
+	return bdc_queue_xfr(bdc, req);
+}
+
+/* Queue data stage */
+static int ep0_queue_data_stage(struct bdc *bdc)
+{
+	struct bdc_ep *ep;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	ep = bdc->bdc_ep_array[1];
+	bdc->ep0_req.ep = ep;
+	bdc->ep0_req.usb_req.complete = NULL;
+
+	return ep0_queue(ep, &bdc->ep0_req);
+}
+
+/* Queue req on ep */
+static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
+{
+	struct bdc *bdc;
+	int ret = 0;
+
+	if (!req || !ep->usb_ep.desc)
+		return -EINVAL;
+
+	bdc = ep->bdc;
+
+	req->usb_req.actual = 0;
+	req->usb_req.status = -EINPROGRESS;
+	req->epnum = ep->ep_num;
+
+	ret = usb_gadget_map_request(&bdc->gadget, &req->usb_req, ep->dir);
+	if (ret) {
+		dev_err(bdc->dev, "dma mapping failed\n");
+		return ret;
+	}
+
+	return bdc_queue_xfr(bdc, req);
+}
+
+/* Dequeue a request from ep */
+static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req)
+{
+	int start_bdi, end_bdi, tbi, eqp_bdi, curr_hw_dqpi;
+	bool start_pending, end_pending;
+	bool first_remove = false;
+	struct bdc_req *first_req;
+	struct bdc_bd *bd_start;
+	struct bd_table *table;
+	dma_addr_t next_bd_dma;
+	u64   deq_ptr_64 = 0;
+	struct bdc  *bdc;
+	u32    tmp_32;
+	int ret;
+
+	bdc = ep->bdc;
+	start_pending = end_pending = false;
+	eqp_bdi = ep->bd_list.eqp_bdi - 1;
+
+	if (eqp_bdi < 0)
+		eqp_bdi = ep->bd_list.max_bdi;
+
+	start_bdi = req->bd_xfr.start_bdi;
+	end_bdi = find_end_bdi(ep, req->bd_xfr.next_hwd_bdi);
+
+	dev_dbg(bdc->dev, "%s ep:%s start:%d end:%d\n",
+					__func__, ep->name, start_bdi, end_bdi);
+	dev_dbg(bdc->dev, "ep_dequeue ep=%p ep->desc=%p\n",
+						ep, (void *)ep->usb_ep.desc);
+	/* Stop the ep to see where the HW is ? */
+	ret = bdc_stop_ep(bdc, ep->ep_num);
+	/* if there is an issue with stopping ep, then no need to go further */
+	if (ret)
+		return 0;
+
+	/*
+	 * After endpoint is stopped, there can be 3 cases, the request
+	 * is processed, pending or in the middle of processing
+	 */
+
+	/* The current hw dequeue pointer */
+	tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS0);
+	deq_ptr_64 = tmp_32;
+	tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS1);
+	deq_ptr_64 |= ((u64)tmp_32 << 32);
+
+	/* we have the dma addr of next bd that will be fetched by hardware */
+	curr_hw_dqpi = bd_add_to_bdi(ep, deq_ptr_64);
+	if (curr_hw_dqpi < 0)
+		return curr_hw_dqpi;
+
+	/*
+	 * curr_hw_dqpi points to actual dqp of HW and HW owns bd's from
+	 * curr_hw_dqbdi..eqp_bdi.
+	 */
+
+	/* Check if start_bdi and end_bdi are in range of HW owned BD's */
+	if (curr_hw_dqpi > eqp_bdi) {
+		/* there is a wrap from last to 0 */
+		if (start_bdi >= curr_hw_dqpi || start_bdi <= eqp_bdi) {
+			start_pending = true;
+			end_pending = true;
+		} else if (end_bdi >= curr_hw_dqpi || end_bdi <= eqp_bdi) {
+				end_pending = true;
+		}
+	} else {
+		if (start_bdi >= curr_hw_dqpi) {
+			start_pending = true;
+			end_pending = true;
+		} else if (end_bdi >= curr_hw_dqpi) {
+			end_pending = true;
+		}
+	}
+	dev_dbg(bdc->dev,
+		"start_pending:%d end_pending:%d speed:%d\n",
+		start_pending, end_pending, bdc->gadget.speed);
+
+	/* If both start till end are processes, we cannot deq req */
+	if (!start_pending && !end_pending)
+		return -EINVAL;
+
+	/*
+	 * if ep_dequeue is called after disconnect then just return
+	 * success from here
+	 */
+	if (bdc->gadget.speed == USB_SPEED_UNKNOWN)
+		return 0;
+	tbi = bdi_to_tbi(ep, req->bd_xfr.next_hwd_bdi);
+	table = ep->bd_list.bd_table_array[tbi];
+	next_bd_dma =  table->dma +
+			sizeof(struct bdc_bd)*(req->bd_xfr.next_hwd_bdi -
+					tbi * ep->bd_list.num_bds_table);
+
+	first_req = list_first_entry(&ep->queue, struct bdc_req,
+			queue);
+
+	if (req == first_req)
+		first_remove = true;
+
+	/*
+	 * Due to HW limitation we need to bypadd chain bd's and issue ep_bla,
+	 * incase if start is pending this is the first request in the list
+	 * then issue ep_bla instead of marking as chain bd
+	 */
+	if (start_pending && !first_remove) {
+		/*
+		 * Mark the start bd as Chain bd, and point the chain
+		 * bd to next_bd_dma
+		 */
+		bd_start = bdi_to_bd(ep, start_bdi);
+		bd_start->offset[0] = cpu_to_le32(lower_32_bits(next_bd_dma));
+		bd_start->offset[1] = cpu_to_le32(upper_32_bits(next_bd_dma));
+		bd_start->offset[2] = 0x0;
+		bd_start->offset[3] = cpu_to_le32(MARK_CHAIN_BD);
+		bdc_dbg_bd_list(bdc, ep);
+	} else if (end_pending) {
+		/*
+		 * The transfer is stopped in the middle, move the
+		 * HW deq pointer to next_bd_dma
+		 */
+		ret = bdc_ep_bla(bdc, ep, next_bd_dma);
+		if (ret) {
+			dev_err(bdc->dev, "error in ep_bla:%d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/* Halt/Clear the ep based on value */
+static int ep_set_halt(struct bdc_ep *ep, u32 value)
+{
+	struct bdc *bdc;
+	int ret;
+
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s ep:%s value=%d\n", __func__, ep->name, value);
+
+	if (value) {
+		dev_dbg(bdc->dev, "Halt\n");
+		if (ep->ep_num == 1)
+			bdc->ep0_state = WAIT_FOR_SETUP;
+
+		ret = bdc_ep_set_stall(bdc, ep->ep_num);
+		if (ret)
+			dev_err(bdc->dev, "failed to set STALL on %s\n",
+				ep->name);
+		else
+			ep->flags |= BDC_EP_STALL;
+	} else {
+		/* Clear */
+		dev_dbg(bdc->dev, "Before Clear\n");
+		ret = bdc_ep_clear_stall(bdc, ep->ep_num);
+		if (ret)
+			dev_err(bdc->dev, "failed to clear STALL on %s\n",
+				ep->name);
+		else
+			ep->flags &= ~BDC_EP_STALL;
+		dev_dbg(bdc->dev, "After  Clear\n");
+	}
+
+	return ret;
+}
+
+/* Free all the ep */
+void bdc_free_ep(struct bdc *bdc)
+{
+	struct bdc_ep *ep;
+	u8	epnum;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	for (epnum = 1; epnum < bdc->num_eps; epnum++) {
+		ep = bdc->bdc_ep_array[epnum];
+		if (!ep)
+			continue;
+
+		if (ep->flags & BDC_EP_ENABLED)
+			ep_bd_list_free(ep, ep->bd_list.num_tabs);
+
+		/* ep0 is not in this gadget list */
+		if (epnum != 1)
+			list_del(&ep->usb_ep.ep_list);
+
+		kfree(ep);
+	}
+}
+
+/* USB2 spec, section 7.1.20 */
+static int bdc_set_test_mode(struct bdc *bdc)
+{
+	u32 usb2_pm;
+
+	usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+	usb2_pm &= ~BDC_PTC_MASK;
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	switch (bdc->test_mode) {
+	case TEST_J:
+	case TEST_K:
+	case TEST_SE0_NAK:
+	case TEST_PACKET:
+	case TEST_FORCE_EN:
+		usb2_pm |= bdc->test_mode << 28;
+		break;
+	default:
+		return -EINVAL;
+	}
+	dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
+	bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
+
+	return 0;
+}
+
+/*
+ * Helper function to handle Transfer status report with status as either
+ * success or short
+ */
+static void handle_xsr_succ_status(struct bdc *bdc, struct bdc_ep *ep,
+							struct bdc_sr *sreport)
+{
+	int short_bdi, start_bdi, end_bdi, max_len_bds, chain_bds;
+	struct bd_list *bd_list = &ep->bd_list;
+	int actual_length, length_short;
+	struct bd_transfer *bd_xfr;
+	struct bdc_bd *short_bd;
+	struct bdc_req *req;
+	u64   deq_ptr_64 = 0;
+	int status = 0;
+	int sr_status;
+	u32    tmp_32;
+
+	dev_dbg(bdc->dev, "%s  ep:%p\n", __func__, ep);
+	bdc_dbg_srr(bdc, 0);
+	/* do not process thie sr if ignore flag is set */
+	if (ep->ignore_next_sr) {
+		ep->ignore_next_sr = false;
+		return;
+	}
+
+	if (unlikely(list_empty(&ep->queue))) {
+		dev_warn(bdc->dev, "xfr srr with no BD's queued\n");
+		return;
+	}
+	req = list_entry(ep->queue.next, struct bdc_req,
+			queue);
+
+	bd_xfr = &req->bd_xfr;
+	sr_status = XSF_STS(le32_to_cpu(sreport->offset[3]));
+
+	/*
+	 * sr_status is short and this transfer has more than 1 bd then it needs
+	 * special handling,  this is only applicable for bulk and ctrl
+	 */
+	if (sr_status == XSF_SHORT &&  bd_xfr->num_bds > 1) {
+		/*
+		 * This is multi bd xfr, lets see which bd
+		 * caused short transfer and how many bytes have been
+		 * transferred so far.
+		 */
+		tmp_32 = le32_to_cpu(sreport->offset[0]);
+		deq_ptr_64 = tmp_32;
+		tmp_32 = le32_to_cpu(sreport->offset[1]);
+		deq_ptr_64 |= ((u64)tmp_32 << 32);
+		short_bdi = bd_add_to_bdi(ep, deq_ptr_64);
+		if (unlikely(short_bdi < 0))
+			dev_warn(bdc->dev, "bd doesn't exist?\n");
+
+		start_bdi =  bd_xfr->start_bdi;
+		/*
+		 * We know the start_bdi and short_bdi, how many xfr
+		 * bds in between
+		 */
+		if (start_bdi <= short_bdi) {
+			max_len_bds = short_bdi - start_bdi;
+			if (max_len_bds <= bd_list->num_bds_table) {
+				if (!(bdi_to_tbi(ep, start_bdi) ==
+						bdi_to_tbi(ep, short_bdi)))
+					max_len_bds--;
+			} else {
+				chain_bds = max_len_bds/bd_list->num_bds_table;
+				max_len_bds -= chain_bds;
+			}
+		} else {
+			/* there is a wrap in the ring within a xfr */
+			chain_bds = (bd_list->max_bdi - start_bdi)/
+							bd_list->num_bds_table;
+			chain_bds += short_bdi/bd_list->num_bds_table;
+			max_len_bds = bd_list->max_bdi - start_bdi;
+			max_len_bds += short_bdi;
+			max_len_bds -= chain_bds;
+		}
+		/* max_len_bds is the number of full length bds */
+		end_bdi = find_end_bdi(ep, bd_xfr->next_hwd_bdi);
+		if (!(end_bdi == short_bdi))
+			ep->ignore_next_sr = true;
+
+		actual_length = max_len_bds * BD_MAX_BUFF_SIZE;
+		short_bd = bdi_to_bd(ep, short_bdi);
+		/* length queued */
+		length_short = le32_to_cpu(short_bd->offset[2]) & 0x1FFFFF;
+		/* actual length trensfered */
+		length_short -= SR_BD_LEN(le32_to_cpu(sreport->offset[2]));
+		actual_length += length_short;
+		req->usb_req.actual = actual_length;
+	} else {
+		req->usb_req.actual = req->usb_req.length -
+			SR_BD_LEN(le32_to_cpu(sreport->offset[2]));
+		dev_dbg(bdc->dev,
+			"len=%d actual=%d bd_xfr->next_hwd_bdi:%d\n",
+			req->usb_req.length, req->usb_req.actual,
+			bd_xfr->next_hwd_bdi);
+	}
+
+	/* Update the dequeue pointer */
+	ep->bd_list.hwd_bdi = bd_xfr->next_hwd_bdi;
+	if (req->usb_req.actual < req->usb_req.length) {
+		dev_dbg(bdc->dev, "short xfr on %d\n", ep->ep_num);
+		if (req->usb_req.short_not_ok)
+			status = -EREMOTEIO;
+	}
+	bdc_req_complete(ep, bd_xfr->req, status);
+}
+
+/* EP0 setup related packet handlers */
+
+/*
+ * Setup packet received, just store the packet and process on next DS or SS
+ * started SR
+ */
+void bdc_xsf_ep0_setup_recv(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	struct usb_ctrlrequest *setup_pkt;
+	u32 len;
+
+	dev_dbg(bdc->dev,
+		"%s ep0_state:%s\n",
+		__func__, ep0_state_string[bdc->ep0_state]);
+	/* Store received setup packet */
+	setup_pkt = &bdc->setup_pkt;
+	memcpy(setup_pkt, &sreport->offset[0], sizeof(*setup_pkt));
+	len = le16_to_cpu(setup_pkt->wLength);
+	if (!len)
+		bdc->ep0_state = WAIT_FOR_STATUS_START;
+	else
+		bdc->ep0_state = WAIT_FOR_DATA_START;
+
+
+	dev_dbg(bdc->dev,
+		"%s exit ep0_state:%s\n",
+		__func__, ep0_state_string[bdc->ep0_state]);
+}
+
+/* Stall ep0 */
+static void ep0_stall(struct bdc *bdc)
+{
+	struct bdc_ep	*ep = bdc->bdc_ep_array[1];
+	struct bdc_req *req;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	bdc->delayed_status = false;
+	ep_set_halt(ep, 1);
+
+	/* de-queue any pendig requests */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct bdc_req,
+				queue);
+		bdc_req_complete(ep, req, -ESHUTDOWN);
+	}
+}
+
+/* SET_ADD handlers */
+static int ep0_set_address(struct bdc *bdc, struct usb_ctrlrequest *ctrl)
+{
+	enum usb_device_state state = bdc->gadget.state;
+	int ret = 0;
+	u32 addr;
+
+	addr = le16_to_cpu(ctrl->wValue);
+	dev_dbg(bdc->dev,
+		"%s addr:%d dev state:%d\n",
+		__func__, addr, state);
+
+	if (addr > 127)
+		return -EINVAL;
+
+	switch (state) {
+	case USB_STATE_DEFAULT:
+	case USB_STATE_ADDRESS:
+		/* Issue Address device command */
+		ret = bdc_address_device(bdc, addr);
+		if (ret)
+			return ret;
+
+		if (addr)
+			usb_gadget_set_state(&bdc->gadget, USB_STATE_ADDRESS);
+		else
+			usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
+
+		bdc->dev_addr = addr;
+		break;
+	default:
+		dev_warn(bdc->dev,
+			"SET Address in wrong device state %d\n",
+			state);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/* Handler for SET/CLEAR FEATURE requests for device */
+static int ep0_handle_feature_dev(struct bdc *bdc, u16 wValue,
+							u16 wIndex, bool set)
+{
+	enum usb_device_state state = bdc->gadget.state;
+	u32	usppms = 0;
+
+	dev_dbg(bdc->dev, "%s set:%d dev state:%d\n",
+					__func__, set, state);
+	switch (wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		dev_dbg(bdc->dev, "USB_DEVICE_REMOTE_WAKEUP\n");
+		if (set)
+			bdc->devstatus |= REMOTE_WAKE_ENABLE;
+		else
+			bdc->devstatus &= ~REMOTE_WAKE_ENABLE;
+		break;
+
+	case USB_DEVICE_TEST_MODE:
+		dev_dbg(bdc->dev, "USB_DEVICE_TEST_MODE\n");
+		if ((wIndex & 0xFF) ||
+				(bdc->gadget.speed != USB_SPEED_HIGH) || !set)
+			return -EINVAL;
+
+		bdc->test_mode = wIndex >> 8;
+		break;
+
+	case USB_DEVICE_U1_ENABLE:
+		dev_dbg(bdc->dev, "USB_DEVICE_U1_ENABLE\n");
+
+		if (bdc->gadget.speed != USB_SPEED_SUPER ||
+						state != USB_STATE_CONFIGURED)
+			return -EINVAL;
+
+		usppms =  bdc_readl(bdc->regs, BDC_USPPMS);
+		if (set) {
+			/* clear previous u1t */
+			usppms &= ~BDC_U1T(BDC_U1T_MASK);
+			usppms |= BDC_U1T(U1_TIMEOUT);
+			usppms |= BDC_U1E | BDC_PORT_W1S;
+			bdc->devstatus |= (1 << USB_DEV_STAT_U1_ENABLED);
+		} else {
+			usppms &= ~BDC_U1E;
+			usppms |= BDC_PORT_W1S;
+			bdc->devstatus &= ~(1 << USB_DEV_STAT_U1_ENABLED);
+		}
+		bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+		break;
+
+	case USB_DEVICE_U2_ENABLE:
+		dev_dbg(bdc->dev, "USB_DEVICE_U2_ENABLE\n");
+
+		if (bdc->gadget.speed != USB_SPEED_SUPER ||
+						state != USB_STATE_CONFIGURED)
+			return -EINVAL;
+
+		usppms = bdc_readl(bdc->regs, BDC_USPPMS);
+		if (set) {
+			usppms |= BDC_U2E;
+			usppms |= BDC_U2A;
+			bdc->devstatus |= (1 << USB_DEV_STAT_U2_ENABLED);
+		} else {
+			usppms &= ~BDC_U2E;
+			usppms &= ~BDC_U2A;
+			bdc->devstatus &= ~(1 << USB_DEV_STAT_U2_ENABLED);
+		}
+		bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+		break;
+
+	case USB_DEVICE_LTM_ENABLE:
+		dev_dbg(bdc->dev, "USB_DEVICE_LTM_ENABLE?\n");
+		if (bdc->gadget.speed != USB_SPEED_SUPER ||
+						state != USB_STATE_CONFIGURED)
+			return -EINVAL;
+		break;
+	default:
+		dev_err(bdc->dev, "Unknown wValue:%d\n", wValue);
+		return -EOPNOTSUPP;
+	} /* USB_RECIP_DEVICE end */
+
+	return 0;
+}
+
+/* SET/CLEAR FEATURE handler */
+static int ep0_handle_feature(struct bdc *bdc,
+			      struct usb_ctrlrequest *setup_pkt, bool set)
+{
+	enum usb_device_state state = bdc->gadget.state;
+	struct bdc_ep *ep;
+	u16 wValue;
+	u16 wIndex;
+	int epnum;
+
+	wValue = le16_to_cpu(setup_pkt->wValue);
+	wIndex = le16_to_cpu(setup_pkt->wIndex);
+
+	dev_dbg(bdc->dev,
+		"%s wValue=%d wIndex=%d	devstate=%08x speed=%d set=%d",
+		__func__, wValue, wIndex, state,
+		bdc->gadget.speed, set);
+
+	switch (setup_pkt->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		return ep0_handle_feature_dev(bdc, wValue, wIndex, set);
+	case USB_RECIP_INTERFACE:
+		dev_dbg(bdc->dev, "USB_RECIP_INTERFACE\n");
+		/* USB3 spec, sec 9.4.9 */
+		if (wValue != USB_INTRF_FUNC_SUSPEND)
+			return -EINVAL;
+		/* USB3 spec, Table 9-8 */
+		if (set) {
+			if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) {
+				dev_dbg(bdc->dev, "SET REMOTE_WAKEUP\n");
+				bdc->devstatus |= REMOTE_WAKE_ENABLE;
+			} else {
+				dev_dbg(bdc->dev, "CLEAR REMOTE_WAKEUP\n");
+				bdc->devstatus &= ~REMOTE_WAKE_ENABLE;
+			}
+		}
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		dev_dbg(bdc->dev, "USB_RECIP_ENDPOINT\n");
+		if (wValue != USB_ENDPOINT_HALT)
+			return -EINVAL;
+
+		epnum = wIndex & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum) {
+			if ((wIndex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+				epnum = epnum * 2 + 1;
+			else
+				epnum *= 2;
+		} else {
+			epnum = 1; /*EP0*/
+		}
+		/*
+		 * If CLEAR_FEATURE on ep0 then don't do anything as the stall
+		 * condition on ep0 has already been cleared when SETUP packet
+		 * was received.
+		 */
+		if (epnum == 1 && !set) {
+			dev_dbg(bdc->dev, "ep0 stall already cleared\n");
+			return 0;
+		}
+		dev_dbg(bdc->dev, "epnum=%d\n", epnum);
+		ep = bdc->bdc_ep_array[epnum];
+		if (!ep)
+			return -EINVAL;
+
+		return ep_set_halt(ep, set);
+	default:
+		dev_err(bdc->dev, "Unknown recipient\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* GET_STATUS request handler */
+static int ep0_handle_status(struct bdc *bdc,
+			     struct usb_ctrlrequest *setup_pkt)
+{
+	enum usb_device_state state = bdc->gadget.state;
+	struct bdc_ep *ep;
+	u16 usb_status = 0;
+	u32 epnum;
+	u16 wIndex;
+
+	/* USB2.0 spec sec 9.4.5 */
+	if (state == USB_STATE_DEFAULT)
+		return -EINVAL;
+	wIndex = le16_to_cpu(setup_pkt->wIndex);
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	usb_status = bdc->devstatus;
+	switch (setup_pkt->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		dev_dbg(bdc->dev,
+			"USB_RECIP_DEVICE devstatus:%08x\n",
+			bdc->devstatus);
+		/* USB3 spec, sec 9.4.5 */
+		if (bdc->gadget.speed == USB_SPEED_SUPER)
+			usb_status &= ~REMOTE_WAKE_ENABLE;
+		break;
+
+	case USB_RECIP_INTERFACE:
+		dev_dbg(bdc->dev, "USB_RECIP_INTERFACE\n");
+		if (bdc->gadget.speed == USB_SPEED_SUPER) {
+			/*
+			 * This should come from func for Func remote wkup
+			 * usb_status |=1;
+			 */
+			if (bdc->devstatus & REMOTE_WAKE_ENABLE)
+				usb_status |= REMOTE_WAKE_ENABLE;
+		} else {
+			usb_status = 0;
+		}
+
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		dev_dbg(bdc->dev, "USB_RECIP_ENDPOINT\n");
+		epnum = wIndex & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum) {
+			if ((wIndex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+				epnum = epnum*2 + 1;
+			else
+				epnum *= 2;
+		} else {
+			epnum = 1; /* EP0 */
+		}
+
+		ep = bdc->bdc_ep_array[epnum];
+		if (!ep) {
+			dev_err(bdc->dev, "ISSUE, GET_STATUS for invalid EP ?");
+			return -EINVAL;
+		}
+		if (ep->flags & BDC_EP_STALL)
+			usb_status |= 1 << USB_ENDPOINT_HALT;
+
+		break;
+	default:
+		dev_err(bdc->dev, "Unknown recipient for get_status\n");
+		return -EINVAL;
+	}
+	/* prepare a data stage for GET_STATUS */
+	dev_dbg(bdc->dev, "usb_status=%08x\n", usb_status);
+	*(__le16 *)bdc->ep0_response_buff = cpu_to_le16(usb_status);
+	bdc->ep0_req.usb_req.length = 2;
+	bdc->ep0_req.usb_req.buf = &bdc->ep0_response_buff;
+	ep0_queue_data_stage(bdc);
+
+	return 0;
+}
+
+static void ep0_set_sel_cmpl(struct usb_ep *_ep, struct usb_request *_req)
+{
+	/* ep0_set_sel_cmpl */
+}
+
+/* Queue data stage to handle 6 byte SET_SEL request */
+static int ep0_set_sel(struct bdc *bdc,
+			     struct usb_ctrlrequest *setup_pkt)
+{
+	struct bdc_ep	*ep;
+	u16	wLength;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	wLength = le16_to_cpu(setup_pkt->wLength);
+	if (unlikely(wLength != 6)) {
+		dev_err(bdc->dev, "%s Wrong wLength:%d\n", __func__, wLength);
+		return -EINVAL;
+	}
+	ep = bdc->bdc_ep_array[1];
+	bdc->ep0_req.ep = ep;
+	bdc->ep0_req.usb_req.length = 6;
+	bdc->ep0_req.usb_req.buf = bdc->ep0_response_buff;
+	bdc->ep0_req.usb_req.complete = ep0_set_sel_cmpl;
+	ep0_queue_data_stage(bdc);
+
+	return 0;
+}
+
+/*
+ * Queue a 0 byte bd only if wLength is more than the length and and length is
+ * a multiple of MaxPacket then queue 0 byte BD
+ */
+static int ep0_queue_zlp(struct bdc *bdc)
+{
+	int ret;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	bdc->ep0_req.ep = bdc->bdc_ep_array[1];
+	bdc->ep0_req.usb_req.length = 0;
+	bdc->ep0_req.usb_req.complete = NULL;
+	bdc->ep0_state = WAIT_FOR_DATA_START;
+	ret = bdc_queue_xfr(bdc, &bdc->ep0_req);
+	if (ret) {
+		dev_err(bdc->dev, "err queueing zlp :%d\n", ret);
+		return ret;
+	}
+	bdc->ep0_state = WAIT_FOR_DATA_XMIT;
+
+	return 0;
+}
+
+/* Control request handler */
+static int handle_control_request(struct bdc *bdc)
+{
+	enum usb_device_state state = bdc->gadget.state;
+	struct usb_ctrlrequest *setup_pkt;
+	int delegate_setup = 0;
+	int ret = 0;
+	int config = 0;
+
+	setup_pkt = &bdc->setup_pkt;
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	if ((setup_pkt->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (setup_pkt->bRequest) {
+		case USB_REQ_SET_ADDRESS:
+			dev_dbg(bdc->dev, "USB_REQ_SET_ADDRESS\n");
+			ret = ep0_set_address(bdc, setup_pkt);
+			bdc->devstatus &= DEVSTATUS_CLEAR;
+			break;
+
+		case USB_REQ_SET_CONFIGURATION:
+			dev_dbg(bdc->dev, "USB_REQ_SET_CONFIGURATION\n");
+			if (state == USB_STATE_ADDRESS) {
+				usb_gadget_set_state(&bdc->gadget,
+							USB_STATE_CONFIGURED);
+			} else if (state == USB_STATE_CONFIGURED) {
+				/*
+				 * USB2 spec sec 9.4.7, if wValue is 0 then dev
+				 * is moved to addressed state
+				 */
+				config = le16_to_cpu(setup_pkt->wValue);
+				if (!config)
+					usb_gadget_set_state(
+							&bdc->gadget,
+							USB_STATE_ADDRESS);
+			}
+			delegate_setup = 1;
+			break;
+
+		case USB_REQ_SET_FEATURE:
+			dev_dbg(bdc->dev, "USB_REQ_SET_FEATURE\n");
+			ret = ep0_handle_feature(bdc, setup_pkt, 1);
+			break;
+
+		case USB_REQ_CLEAR_FEATURE:
+			dev_dbg(bdc->dev, "USB_REQ_CLEAR_FEATURE\n");
+			ret = ep0_handle_feature(bdc, setup_pkt, 0);
+			break;
+
+		case USB_REQ_GET_STATUS:
+			dev_dbg(bdc->dev, "USB_REQ_GET_STATUS\n");
+			ret = ep0_handle_status(bdc, setup_pkt);
+			break;
+
+		case USB_REQ_SET_SEL:
+			dev_dbg(bdc->dev, "USB_REQ_SET_SEL\n");
+			ret = ep0_set_sel(bdc, setup_pkt);
+			break;
+
+		case USB_REQ_SET_ISOCH_DELAY:
+			dev_warn(bdc->dev,
+			"USB_REQ_SET_ISOCH_DELAY not handled\n");
+			ret = 0;
+			break;
+		default:
+			delegate_setup = 1;
+		}
+	} else {
+		delegate_setup = 1;
+	}
+
+	if (delegate_setup) {
+		spin_unlock(&bdc->lock);
+		ret = bdc->gadget_driver->setup(&bdc->gadget, setup_pkt);
+		spin_lock(&bdc->lock);
+	}
+
+	return ret;
+}
+
+/* EP0: Data stage started */
+void bdc_xsf_ep0_data_start(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	struct bdc_ep *ep;
+	int ret = 0;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	ep = bdc->bdc_ep_array[1];
+	/* If ep0 was stalled, the clear it first */
+	if (ep->flags & BDC_EP_STALL) {
+		ret = ep_set_halt(ep, 0);
+		if (ret)
+			goto err;
+	}
+	if (bdc->ep0_state != WAIT_FOR_DATA_START)
+		dev_warn(bdc->dev,
+			"Data stage not expected ep0_state:%s\n",
+			ep0_state_string[bdc->ep0_state]);
+
+	ret = handle_control_request(bdc);
+	if (ret == USB_GADGET_DELAYED_STATUS) {
+		/*
+		 * The ep0 state will remain WAIT_FOR_DATA_START till
+		 * we received ep_queue on ep0
+		 */
+		bdc->delayed_status = true;
+		return;
+	}
+	if (!ret) {
+		bdc->ep0_state = WAIT_FOR_DATA_XMIT;
+		dev_dbg(bdc->dev,
+			"ep0_state:%s", ep0_state_string[bdc->ep0_state]);
+		return;
+	}
+err:
+	ep0_stall(bdc);
+}
+
+/* EP0: status stage started */
+void bdc_xsf_ep0_status_start(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	struct usb_ctrlrequest *setup_pkt;
+	struct bdc_ep *ep;
+	int ret = 0;
+
+	dev_dbg(bdc->dev,
+		"%s ep0_state:%s",
+		__func__, ep0_state_string[bdc->ep0_state]);
+	ep = bdc->bdc_ep_array[1];
+
+	/* check if ZLP was queued? */
+	if (bdc->zlp_needed)
+		bdc->zlp_needed = false;
+
+	if (ep->flags & BDC_EP_STALL) {
+		ret = ep_set_halt(ep, 0);
+		if (ret)
+			goto err;
+	}
+
+	if ((bdc->ep0_state != WAIT_FOR_STATUS_START) &&
+				(bdc->ep0_state != WAIT_FOR_DATA_XMIT))
+		dev_err(bdc->dev,
+			"Status stage recv but ep0_state:%s\n",
+			ep0_state_string[bdc->ep0_state]);
+
+	/* check if data stage is in progress ? */
+	if (bdc->ep0_state == WAIT_FOR_DATA_XMIT) {
+		bdc->ep0_state = STATUS_PENDING;
+		/* Status stage will be queued upon Data stage transmit event */
+		dev_dbg(bdc->dev,
+			"status started but data  not transmitted yet\n");
+		return;
+	}
+	setup_pkt = &bdc->setup_pkt;
+
+	/*
+	 * 2 stage setup then only process the setup, for 3 stage setup the date
+	 * stage is already handled
+	 */
+	if (!le16_to_cpu(setup_pkt->wLength)) {
+		ret = handle_control_request(bdc);
+		if (ret == USB_GADGET_DELAYED_STATUS) {
+			bdc->delayed_status = true;
+			/* ep0_state will remain WAIT_FOR_STATUS_START */
+			return;
+		}
+	}
+	if (!ret) {
+		/* Queue a status stage BD */
+		ep0_queue_status_stage(bdc);
+		bdc->ep0_state = WAIT_FOR_STATUS_XMIT;
+		dev_dbg(bdc->dev,
+			"ep0_state:%s", ep0_state_string[bdc->ep0_state]);
+		return;
+	}
+err:
+	ep0_stall(bdc);
+}
+
+/* Helper function to update ep0 upon SR with xsf_succ or xsf_short */
+static void ep0_xsf_complete(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	switch (bdc->ep0_state) {
+	case WAIT_FOR_DATA_XMIT:
+		bdc->ep0_state = WAIT_FOR_STATUS_START;
+		break;
+	case WAIT_FOR_STATUS_XMIT:
+		bdc->ep0_state = WAIT_FOR_SETUP;
+		if (bdc->test_mode) {
+			int ret;
+
+			dev_dbg(bdc->dev, "test_mode:%d\n", bdc->test_mode);
+			ret = bdc_set_test_mode(bdc);
+			if (ret < 0) {
+				dev_err(bdc->dev, "Err in setting Test mode\n");
+				return;
+			}
+			bdc->test_mode = 0;
+		}
+		break;
+	case STATUS_PENDING:
+		bdc_xsf_ep0_status_start(bdc, sreport);
+		break;
+
+	default:
+		dev_err(bdc->dev,
+			"Unknown ep0_state:%s\n",
+			ep0_state_string[bdc->ep0_state]);
+
+	}
+}
+
+/* xfr completion status report handler */
+void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	struct bdc_ep *ep;
+	u32 sr_status;
+	u8 ep_num;
+
+	ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f;
+	ep = bdc->bdc_ep_array[ep_num];
+	if (!ep || !(ep->flags & BDC_EP_ENABLED)) {
+		dev_err(bdc->dev, "xsf for ep not enabled\n");
+		return;
+	}
+	/*
+	 * check if this transfer is after link went from U3->U0 due
+	 * to remote wakeup
+	 */
+	if (bdc->devstatus & FUNC_WAKE_ISSUED) {
+		bdc->devstatus &= ~(FUNC_WAKE_ISSUED);
+		dev_dbg(bdc->dev, "%s clearing FUNC_WAKE_ISSUED flag\n",
+								__func__);
+	}
+	sr_status = XSF_STS(le32_to_cpu(sreport->offset[3]));
+	dev_dbg_ratelimited(bdc->dev, "%s sr_status=%d ep:%s\n",
+					__func__, sr_status, ep->name);
+
+	switch (sr_status) {
+	case XSF_SUCC:
+	case XSF_SHORT:
+		handle_xsr_succ_status(bdc, ep, sreport);
+		if (ep_num == 1)
+			ep0_xsf_complete(bdc, sreport);
+		break;
+
+	case XSF_SETUP_RECV:
+	case XSF_DATA_START:
+	case XSF_STATUS_START:
+		if (ep_num != 1) {
+			dev_err(bdc->dev,
+				"ep0 related packets on non ep0 endpoint");
+			return;
+		}
+		bdc->sr_xsf_ep0[sr_status - XSF_SETUP_RECV](bdc, sreport);
+		break;
+
+	case XSF_BABB:
+		if (ep_num == 1) {
+			dev_dbg(bdc->dev, "Babble on ep0 zlp_need:%d\n",
+							bdc->zlp_needed);
+			/*
+			 * If the last completed transfer had wLength >Data Len,
+			 * and Len is multiple of MaxPacket,then queue ZLP
+			 */
+			if (bdc->zlp_needed) {
+				/* queue 0 length bd */
+				ep0_queue_zlp(bdc);
+				return;
+			}
+		}
+		dev_warn(bdc->dev, "Babble on ep not handled\n");
+		break;
+	default:
+		dev_warn(bdc->dev, "sr status not handled:%x\n", sr_status);
+		break;
+	}
+}
+
+static int bdc_gadget_ep_queue(struct usb_ep *_ep,
+				struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct bdc_req *req;
+	unsigned long flags;
+	struct bdc_ep *ep;
+	struct bdc *bdc;
+	int ret;
+
+	if (!_ep || !_ep->desc)
+		return -ESHUTDOWN;
+
+	if (!_req || !_req->complete || !_req->buf)
+		return -EINVAL;
+
+	ep = to_bdc_ep(_ep);
+	req = to_bdc_req(_req);
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s ep:%p req:%p\n", __func__, ep, req);
+	dev_dbg(bdc->dev, "queuing request %p to %s length %d zero:%d\n",
+				_req, ep->name, _req->length, _req->zero);
+
+	if (!ep->usb_ep.desc) {
+		dev_warn(bdc->dev,
+			"trying to queue req %p to disabled %s\n",
+			_req, ep->name);
+		return -ESHUTDOWN;
+	}
+
+	if (_req->length > MAX_XFR_LEN) {
+		dev_warn(bdc->dev,
+			"req length > supported MAX:%d requested:%d\n",
+			MAX_XFR_LEN, _req->length);
+		return -EOPNOTSUPP;
+	}
+	spin_lock_irqsave(&bdc->lock, flags);
+	if (ep == bdc->bdc_ep_array[1])
+		ret = ep0_queue(ep, req);
+	else
+		ret = ep_queue(ep, req);
+
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
+				  struct usb_request *_req)
+{
+	struct bdc_req *req;
+	unsigned long flags;
+	struct bdc_ep *ep;
+	struct bdc *bdc;
+	int ret;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	ep = to_bdc_ep(_ep);
+	req = to_bdc_req(_req);
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
+	bdc_dbg_bd_list(bdc, ep);
+	spin_lock_irqsave(&bdc->lock, flags);
+	/* make sure it's still queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->usb_req == _req)
+			break;
+	}
+	if (&req->usb_req != _req) {
+		spin_unlock_irqrestore(&bdc->lock, flags);
+		dev_err(bdc->dev, "usb_req !=req n");
+		return -EINVAL;
+	}
+	ret = ep_dequeue(ep, req);
+	if (ret) {
+		ret = -EOPNOTSUPP;
+		goto err;
+	}
+	bdc_req_complete(ep, req, -ECONNRESET);
+
+err:
+	bdc_dbg_bd_list(bdc, ep);
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static int bdc_gadget_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	unsigned long flags;
+	struct bdc_ep *ep;
+	struct bdc *bdc;
+	int ret;
+
+	ep = to_bdc_ep(_ep);
+	bdc = ep->bdc;
+	dev_dbg(bdc->dev, "%s ep:%s value=%d\n", __func__, ep->name, value);
+	spin_lock_irqsave(&bdc->lock, flags);
+	if (usb_endpoint_xfer_isoc(ep->usb_ep.desc))
+		ret = -EINVAL;
+	else if (!list_empty(&ep->queue))
+		ret = -EAGAIN;
+	else
+		ret = ep_set_halt(ep, value);
+
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static struct usb_request *bdc_gadget_alloc_request(struct usb_ep *_ep,
+						     gfp_t gfp_flags)
+{
+	struct bdc_req *req;
+	struct bdc_ep *ep;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	ep = to_bdc_ep(_ep);
+	req->ep = ep;
+	req->epnum = ep->ep_num;
+	req->usb_req.dma = DMA_ADDR_INVALID;
+	dev_dbg(ep->bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
+
+	return &req->usb_req;
+}
+
+static void bdc_gadget_free_request(struct usb_ep *_ep,
+				     struct usb_request *_req)
+{
+	struct bdc_req *req;
+
+	req = to_bdc_req(_req);
+	kfree(req);
+}
+
+/* endpoint operations */
+
+/* configure endpoint and also allocate resources */
+static int bdc_gadget_ep_enable(struct usb_ep *_ep,
+				 const struct usb_endpoint_descriptor *desc)
+{
+	unsigned long flags;
+	struct bdc_ep *ep;
+	struct bdc *bdc;
+	int ret;
+
+	if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_debug("bdc_gadget_ep_enable invalid parameters\n");
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		pr_debug("bdc_gadget_ep_enable missing wMaxPacketSize\n");
+		return -EINVAL;
+	}
+
+	ep = to_bdc_ep(_ep);
+	bdc = ep->bdc;
+
+	/* Sanity check, upper layer will not send enable for ep0 */
+	if (ep == bdc->bdc_ep_array[1])
+		return -EINVAL;
+
+	if (!bdc->gadget_driver
+	    || bdc->gadget.speed == USB_SPEED_UNKNOWN) {
+		return -ESHUTDOWN;
+	}
+
+	dev_dbg(bdc->dev, "%s Enabling %s\n", __func__, ep->name);
+	spin_lock_irqsave(&bdc->lock, flags);
+	ep->desc = desc;
+	ep->comp_desc = _ep->comp_desc;
+	ret = bdc_ep_enable(ep);
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static int bdc_gadget_ep_disable(struct usb_ep *_ep)
+{
+	unsigned long flags;
+	struct bdc_ep *ep;
+	struct bdc *bdc;
+	int ret;
+
+	if (!_ep) {
+		pr_debug("bdc: invalid parameters\n");
+		return -EINVAL;
+	}
+	ep = to_bdc_ep(_ep);
+	bdc = ep->bdc;
+
+	/* Upper layer will not call this for ep0, but do a sanity check */
+	if (ep == bdc->bdc_ep_array[1]) {
+		dev_warn(bdc->dev, "%s called for ep0\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(bdc->dev,
+		"%s() ep:%s ep->flags:%08x\n",
+		__func__, ep->name, ep->flags);
+
+	if (!(ep->flags & BDC_EP_ENABLED)) {
+		dev_warn(bdc->dev, "%s is already disabled\n", ep->name);
+		return 0;
+	}
+	spin_lock_irqsave(&bdc->lock, flags);
+	ret = bdc_ep_disable(ep);
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static const struct usb_ep_ops bdc_gadget_ep_ops = {
+	.enable = bdc_gadget_ep_enable,
+	.disable = bdc_gadget_ep_disable,
+	.alloc_request = bdc_gadget_alloc_request,
+	.free_request = bdc_gadget_free_request,
+	.queue = bdc_gadget_ep_queue,
+	.dequeue = bdc_gadget_ep_dequeue,
+	.set_halt = bdc_gadget_ep_set_halt
+};
+
+/* dir = 1 is IN */
+static int init_ep(struct bdc *bdc, u32 epnum, u32 dir)
+{
+	struct bdc_ep *ep;
+
+	dev_dbg(bdc->dev, "%s epnum=%d dir=%d\n", __func__, epnum, dir);
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->bdc = bdc;
+	ep->dir = dir;
+
+	if (dir)
+		ep->usb_ep.caps.dir_in = true;
+	else
+		ep->usb_ep.caps.dir_out = true;
+
+	/* ep->ep_num is the index inside bdc_ep */
+	if (epnum == 1) {
+		ep->ep_num = 1;
+		bdc->bdc_ep_array[ep->ep_num] = ep;
+		snprintf(ep->name, sizeof(ep->name), "ep%d", epnum - 1);
+		usb_ep_set_maxpacket_limit(&ep->usb_ep, EP0_MAX_PKT_SIZE);
+		ep->usb_ep.caps.type_control = true;
+		ep->comp_desc = NULL;
+		bdc->gadget.ep0 = &ep->usb_ep;
+	} else {
+		if (dir)
+			ep->ep_num = epnum * 2 - 1;
+		else
+			ep->ep_num = epnum * 2 - 2;
+
+		bdc->bdc_ep_array[ep->ep_num] = ep;
+		snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum - 1,
+			 dir & 1 ? "in" : "out");
+
+		usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024);
+		ep->usb_ep.caps.type_iso = true;
+		ep->usb_ep.caps.type_bulk = true;
+		ep->usb_ep.caps.type_int = true;
+		ep->usb_ep.max_streams = 0;
+		list_add_tail(&ep->usb_ep.ep_list, &bdc->gadget.ep_list);
+	}
+	ep->usb_ep.ops = &bdc_gadget_ep_ops;
+	ep->usb_ep.name = ep->name;
+	ep->flags = 0;
+	ep->ignore_next_sr = false;
+	dev_dbg(bdc->dev, "ep=%p ep->usb_ep.name=%s epnum=%d ep->epnum=%d\n",
+				ep, ep->usb_ep.name, epnum, ep->ep_num);
+
+	INIT_LIST_HEAD(&ep->queue);
+
+	return 0;
+}
+
+/* Init all ep */
+int bdc_init_ep(struct bdc *bdc)
+{
+	u8 epnum;
+	int ret;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	INIT_LIST_HEAD(&bdc->gadget.ep_list);
+	/* init ep0 */
+	ret = init_ep(bdc, 1, 0);
+	if (ret) {
+		dev_err(bdc->dev, "init ep ep0 fail %d\n", ret);
+		return ret;
+	}
+
+	for (epnum = 2; epnum <= bdc->num_eps / 2; epnum++) {
+		/* OUT */
+		ret = init_ep(bdc, epnum, 0);
+		if (ret) {
+			dev_err(bdc->dev,
+				"init ep failed for:%d error: %d\n",
+				epnum, ret);
+			return ret;
+		}
+
+		/* IN */
+		ret = init_ep(bdc, epnum, 1);
+		if (ret) {
+			dev_err(bdc->dev,
+				"init ep failed for:%d error: %d\n",
+				epnum, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.h b/drivers/usb/gadget/udc/bdc/bdc_ep.h
new file mode 100644
index 0000000..a37ff80
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_ep.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ */
+#ifndef __LINUX_BDC_EP_H__
+#define __LINUX_BDC_EP_H__
+
+int bdc_init_ep(struct bdc *);
+int bdc_ep_disable(struct bdc_ep *);
+int bdc_ep_enable(struct bdc_ep *);
+void bdc_free_ep(struct bdc *);
+
+#endif /* __LINUX_BDC_EP_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_pci.c b/drivers/usb/gadget/udc/bdc/bdc_pci.c
new file mode 100644
index 0000000..6dbc489
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file.
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/platform_device.h>
+
+#include "bdc.h"
+
+#define BDC_PCI_PID 0x1570
+
+struct bdc_pci {
+	struct device *dev;
+	struct platform_device *bdc;
+};
+
+static int bdc_setup_msi(struct pci_dev *pci)
+{
+	int ret;
+
+	ret = pci_enable_msi(pci);
+	if (ret) {
+		pr_err("failed to allocate MSI entry\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	struct resource res[2];
+	struct platform_device *bdc;
+	struct bdc_pci *glue;
+	int ret = -ENOMEM;
+
+	glue = devm_kzalloc(&pci->dev, sizeof(*glue), GFP_KERNEL);
+	if (!glue)
+		return -ENOMEM;
+
+	glue->dev = &pci->dev;
+	ret = pci_enable_device(pci);
+	if (ret) {
+		dev_err(&pci->dev, "failed to enable pci device\n");
+		return -ENODEV;
+	}
+	pci_set_master(pci);
+
+	bdc = platform_device_alloc(BRCM_BDC_NAME, PLATFORM_DEVID_AUTO);
+	if (!bdc)
+		return -ENOMEM;
+
+	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
+	bdc_setup_msi(pci);
+
+	res[0].start	= pci_resource_start(pci, 0);
+	res[0].end	= pci_resource_end(pci, 0);
+	res[0].name	= BRCM_BDC_NAME;
+	res[0].flags	= IORESOURCE_MEM;
+
+	res[1].start	= pci->irq;
+	res[1].name	= BRCM_BDC_NAME;
+	res[1].flags	= IORESOURCE_IRQ;
+
+	ret = platform_device_add_resources(bdc, res, ARRAY_SIZE(res));
+	if (ret) {
+		dev_err(&pci->dev,
+			"couldn't add resources to bdc device\n");
+		platform_device_put(bdc);
+		return ret;
+	}
+
+	pci_set_drvdata(pci, glue);
+
+	dma_set_coherent_mask(&bdc->dev, pci->dev.coherent_dma_mask);
+
+	bdc->dev.dma_mask = pci->dev.dma_mask;
+	bdc->dev.dma_parms = pci->dev.dma_parms;
+	bdc->dev.parent = &pci->dev;
+	glue->bdc = bdc;
+
+	ret = platform_device_add(bdc);
+	if (ret) {
+		dev_err(&pci->dev, "failed to register bdc device\n");
+		platform_device_put(bdc);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void bdc_pci_remove(struct pci_dev *pci)
+{
+	struct bdc_pci *glue = pci_get_drvdata(pci);
+
+	platform_device_unregister(glue->bdc);
+	pci_disable_msi(pci);
+}
+
+static struct pci_device_id bdc_pci_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, BDC_PCI_PID), },
+	{} /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE(pci, bdc_pci_id_table);
+
+static struct pci_driver bdc_pci_driver = {
+	.name = "bdc-pci",
+	.id_table = bdc_pci_id_table,
+	.probe = bdc_pci_probe,
+	.remove = bdc_pci_remove,
+};
+
+MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("BRCM BDC USB3 PCI Glue layer");
+module_pci_driver(bdc_pci_driver);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
new file mode 100644
index 0000000..7bfd58c
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/gadget/udc/
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/unaligned.h>
+#include <linux/platform_device.h>
+
+#include "bdc.h"
+#include "bdc_ep.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+static const struct usb_gadget_ops bdc_gadget_ops;
+
+static const char * const conn_speed_str[] =  {
+	"Not connected",
+	"Full Speed",
+	"Low Speed",
+	"High Speed",
+	"Super Speed",
+};
+
+/* EP0 initial descripror */
+static struct usb_endpoint_descriptor bdc_gadget_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+	.bEndpointAddress = 0,
+	.wMaxPacketSize	= cpu_to_le16(EP0_MAX_PKT_SIZE),
+};
+
+/* Advance the srr dqp maintained by SW */
+static void srr_dqp_index_advc(struct bdc *bdc, u32 srr_num)
+{
+	struct srr *srr;
+
+	srr = &bdc->srr;
+	dev_dbg_ratelimited(bdc->dev, "srr->dqp_index:%d\n", srr->dqp_index);
+	srr->dqp_index++;
+	/* rollback to 0 if we are past the last */
+	if (srr->dqp_index == NUM_SR_ENTRIES)
+		srr->dqp_index = 0;
+}
+
+/* connect sr */
+static void bdc_uspc_connected(struct bdc *bdc)
+{
+	u32 speed, temp;
+	u32 usppms;
+	int ret;
+
+	temp = bdc_readl(bdc->regs, BDC_USPC);
+	speed = BDC_PSP(temp);
+	dev_dbg(bdc->dev, "%s speed=%x\n", __func__, speed);
+	switch (speed) {
+	case BDC_SPEED_SS:
+		bdc_gadget_ep0_desc.wMaxPacketSize =
+						cpu_to_le16(EP0_MAX_PKT_SIZE);
+		bdc->gadget.ep0->maxpacket = EP0_MAX_PKT_SIZE;
+		bdc->gadget.speed = USB_SPEED_SUPER;
+		/* Enable U1T in SS mode */
+		usppms =  bdc_readl(bdc->regs, BDC_USPPMS);
+		usppms &= ~BDC_U1T(0xff);
+		usppms |= BDC_U1T(U1_TIMEOUT);
+		usppms |= BDC_PORT_W1S;
+		bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+		break;
+
+	case BDC_SPEED_HS:
+		bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+		bdc->gadget.ep0->maxpacket = 64;
+		bdc->gadget.speed = USB_SPEED_HIGH;
+		break;
+
+	case BDC_SPEED_FS:
+		bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+		bdc->gadget.ep0->maxpacket = 64;
+		bdc->gadget.speed = USB_SPEED_FULL;
+		break;
+
+	case BDC_SPEED_LS:
+		bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
+		bdc->gadget.ep0->maxpacket = 8;
+		bdc->gadget.speed = USB_SPEED_LOW;
+		break;
+	default:
+		dev_err(bdc->dev, "UNDEFINED SPEED\n");
+		return;
+	}
+	dev_dbg(bdc->dev, "connected at %s\n", conn_speed_str[speed]);
+	/* Now we know the speed, configure ep0 */
+	bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
+	ret = bdc_config_ep(bdc, bdc->bdc_ep_array[1]);
+	if (ret)
+		dev_err(bdc->dev, "EP0 config failed\n");
+	bdc->bdc_ep_array[1]->usb_ep.desc = &bdc_gadget_ep0_desc;
+	bdc->bdc_ep_array[1]->flags |= BDC_EP_ENABLED;
+	usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
+}
+
+/* device got disconnected */
+static void bdc_uspc_disconnected(struct bdc *bdc, bool reinit)
+{
+	struct bdc_ep *ep;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	/*
+	 * Only stop ep0 from here, rest of the endpoints will be disabled
+	 * from gadget_disconnect
+	 */
+	ep = bdc->bdc_ep_array[1];
+	if (ep && (ep->flags & BDC_EP_ENABLED))
+		/* if enabled then stop and remove requests */
+		bdc_ep_disable(ep);
+
+	if (bdc->gadget_driver && bdc->gadget_driver->disconnect) {
+		spin_unlock(&bdc->lock);
+		bdc->gadget_driver->disconnect(&bdc->gadget);
+		spin_lock(&bdc->lock);
+	}
+	/* Set Unknown speed */
+	bdc->gadget.speed = USB_SPEED_UNKNOWN;
+	bdc->devstatus &= DEVSTATUS_CLEAR;
+	bdc->delayed_status = false;
+	bdc->reinit = reinit;
+	bdc->test_mode = false;
+}
+
+/* TNotify wkaeup timer */
+static void bdc_func_wake_timer(struct work_struct *work)
+{
+	struct bdc *bdc = container_of(work, struct bdc, func_wake_notify.work);
+	unsigned long flags;
+
+	dev_dbg(bdc->dev, "%s\n", __func__);
+	spin_lock_irqsave(&bdc->lock, flags);
+	/*
+	 * Check if host has started transferring on endpoints
+	 * FUNC_WAKE_ISSUED is cleared when transfer has started after resume
+	*/
+	if (bdc->devstatus & FUNC_WAKE_ISSUED) {
+		dev_dbg(bdc->dev, "FUNC_WAKE_ISSUED FLAG IS STILL SET\n");
+		/* flag is still set, so again send func wake */
+		bdc_function_wake_fh(bdc, 0);
+		schedule_delayed_work(&bdc->func_wake_notify,
+						msecs_to_jiffies(BDC_TNOTIFY));
+	}
+	spin_unlock_irqrestore(&bdc->lock, flags);
+}
+
+/* handler for Link state change condition */
+static void handle_link_state_change(struct bdc *bdc, u32 uspc)
+{
+	u32 link_state;
+
+	dev_dbg(bdc->dev, "Link state change");
+	link_state = BDC_PST(uspc);
+	switch (link_state) {
+	case BDC_LINK_STATE_U3:
+		if ((bdc->gadget.speed != USB_SPEED_UNKNOWN) &&
+						bdc->gadget_driver->suspend) {
+			dev_dbg(bdc->dev, "Entered Suspend mode\n");
+			spin_unlock(&bdc->lock);
+			bdc->devstatus |= DEVICE_SUSPENDED;
+			bdc->gadget_driver->suspend(&bdc->gadget);
+			spin_lock(&bdc->lock);
+		}
+		break;
+	case BDC_LINK_STATE_U0:
+		if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
+					bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
+			if (bdc->gadget.speed == USB_SPEED_SUPER) {
+				bdc_function_wake_fh(bdc, 0);
+				bdc->devstatus |= FUNC_WAKE_ISSUED;
+				/*
+				 * Start a Notification timer and check if the
+				 * Host transferred anything on any of the EPs,
+				 * if not then send function wake again every
+				 * TNotification secs until host initiates
+				 * transfer to BDC, USB3 spec Table 8.13
+				*/
+				schedule_delayed_work(
+						&bdc->func_wake_notify,
+						msecs_to_jiffies(BDC_TNOTIFY));
+				dev_dbg(bdc->dev, "sched func_wake_notify\n");
+			}
+		}
+		break;
+
+	case BDC_LINK_STATE_RESUME:
+		dev_dbg(bdc->dev, "Resumed from Suspend\n");
+		if (bdc->devstatus & DEVICE_SUSPENDED) {
+			bdc->gadget_driver->resume(&bdc->gadget);
+			bdc->devstatus &= ~DEVICE_SUSPENDED;
+		}
+		break;
+	default:
+		dev_dbg(bdc->dev, "link state:%d\n", link_state);
+	}
+}
+
+/* something changes on upstream port, handle it here */
+void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
+{
+	u32 clear_flags = 0;
+	u32 uspc;
+	bool connected = false;
+	bool disconn = false;
+
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc);
+
+	/* Port connect changed */
+	if (uspc & BDC_PCC) {
+		/* Vbus not present, and not connected to Downstream port */
+		if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS))
+			disconn = true;
+		else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
+			connected = true;
+		clear_flags |= BDC_PCC;
+	}
+
+	/* Change in VBus and VBus is present */
+	if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) {
+		if (bdc->pullup) {
+			dev_dbg(bdc->dev, "Do a softconnect\n");
+			/* Attached state, do a softconnect */
+			bdc_softconn(bdc);
+			usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED);
+		}
+		clear_flags |= BDC_VBC;
+	} else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
+		/* Hot reset, warm reset, 2.0 bus reset or disconn */
+		dev_dbg(bdc->dev, "Port reset or disconn\n");
+		bdc_uspc_disconnected(bdc, disconn);
+		clear_flags |= BDC_PRC;
+	} else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
+		/* Change in Link state */
+		handle_link_state_change(bdc, uspc);
+		clear_flags |= BDC_PSC;
+	}
+
+	/*
+	 * In SS we might not have PRC bit set before connection, but in 2.0
+	 * the PRC bit is set before connection, so moving this condition out
+	 * of bus reset to handle both SS/2.0 speeds.
+	 */
+	if (connected) {
+		/* This is the connect event for U0/L0 */
+		dev_dbg(bdc->dev, "Connected\n");
+		bdc_uspc_connected(bdc);
+		bdc->devstatus &= ~(DEVICE_SUSPENDED);
+	}
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	uspc &= (~BDC_USPSC_RW);
+	dev_dbg(bdc->dev, "uspc=%x\n", uspc);
+	bdc_writel(bdc->regs, BDC_USPC, clear_flags);
+}
+
+/* Main interrupt handler for bdc */
+static irqreturn_t bdc_udc_interrupt(int irq, void *_bdc)
+{
+	u32 eqp_index, dqp_index, sr_type, srr_int;
+	struct bdc_sr *sreport;
+	struct bdc *bdc = _bdc;
+	u32 status;
+	int ret;
+
+	spin_lock(&bdc->lock);
+	status = bdc_readl(bdc->regs, BDC_BDCSC);
+	if (!(status & BDC_GIP)) {
+		spin_unlock(&bdc->lock);
+		return IRQ_NONE;
+	}
+	srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+	/* Check if the SRR IP bit it set? */
+	if (!(srr_int & BDC_SRR_IP)) {
+		dev_warn(bdc->dev, "Global irq pending but SRR IP is 0\n");
+		spin_unlock(&bdc->lock);
+		return IRQ_NONE;
+	}
+	eqp_index = BDC_SRR_EPI(srr_int);
+	dqp_index = BDC_SRR_DPI(srr_int);
+	dev_dbg(bdc->dev,
+			"%s eqp_index=%d dqp_index=%d  srr.dqp_index=%d\n\n",
+			 __func__, eqp_index, dqp_index, bdc->srr.dqp_index);
+
+	/* check for ring empty condition */
+	if (eqp_index == dqp_index) {
+		dev_dbg(bdc->dev, "SRR empty?\n");
+		spin_unlock(&bdc->lock);
+		return IRQ_HANDLED;
+	}
+
+	while (bdc->srr.dqp_index != eqp_index) {
+		sreport = &bdc->srr.sr_bds[bdc->srr.dqp_index];
+		/* sreport is read before using it */
+		rmb();
+		sr_type = le32_to_cpu(sreport->offset[3]) & BD_TYPE_BITMASK;
+		dev_dbg_ratelimited(bdc->dev, "sr_type=%d\n", sr_type);
+		switch (sr_type) {
+		case SR_XSF:
+			bdc->sr_handler[0](bdc, sreport);
+			break;
+
+		case SR_USPC:
+			bdc->sr_handler[1](bdc, sreport);
+			break;
+		default:
+			dev_warn(bdc->dev, "SR:%d not handled\n", sr_type);
+		}
+		/* Advance the srr dqp index */
+		srr_dqp_index_advc(bdc, 0);
+	}
+	/* update the hw dequeue pointer */
+	srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+	srr_int &= ~BDC_SRR_DPI_MASK;
+	srr_int &= ~(BDC_SRR_RWS|BDC_SRR_RST|BDC_SRR_ISR);
+	srr_int |= ((bdc->srr.dqp_index) << 16);
+	srr_int |= BDC_SRR_IP;
+	bdc_writel(bdc->regs, BDC_SRRINT(0), srr_int);
+	srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+	if (bdc->reinit) {
+		ret = bdc_reinit(bdc);
+		if (ret)
+			dev_err(bdc->dev, "err in bdc reinit\n");
+	}
+
+	spin_unlock(&bdc->lock);
+
+	return IRQ_HANDLED;
+}
+
+/* Gadget ops */
+static int bdc_udc_start(struct usb_gadget *gadget,
+				struct usb_gadget_driver *driver)
+{
+	struct bdc *bdc = gadget_to_bdc(gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	spin_lock_irqsave(&bdc->lock, flags);
+	if (bdc->gadget_driver) {
+		dev_err(bdc->dev, "%s is already bound to %s\n",
+			bdc->gadget.name,
+			bdc->gadget_driver->driver.name);
+		ret = -EBUSY;
+		goto err;
+	}
+	/*
+	 * Run the controller from here and when BDC is connected to
+	 * Host then driver will receive a USPC SR with VBUS present
+	 * and then driver will do a softconnect.
+	*/
+	ret = bdc_run(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "%s bdc run fail\n", __func__);
+		goto err;
+	}
+	bdc->gadget_driver = driver;
+	bdc->gadget.dev.driver = &driver->driver;
+err:
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static int bdc_udc_stop(struct usb_gadget *gadget)
+{
+	struct bdc *bdc = gadget_to_bdc(gadget);
+	unsigned long flags;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	spin_lock_irqsave(&bdc->lock, flags);
+	bdc_stop(bdc);
+	bdc->gadget_driver = NULL;
+	bdc->gadget.dev.driver = NULL;
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return 0;
+}
+
+static int bdc_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct bdc *bdc = gadget_to_bdc(gadget);
+	unsigned long flags;
+	u32 uspc;
+
+	dev_dbg(bdc->dev, "%s() is_on:%d\n", __func__, is_on);
+	if (!gadget)
+		return -EINVAL;
+
+	spin_lock_irqsave(&bdc->lock, flags);
+	if (!is_on) {
+		bdc_softdisconn(bdc);
+		bdc->pullup = false;
+	} else {
+		/*
+		 * For a self powered device, we need to wait till we receive
+		 * a VBUS change and Vbus present event, then if pullup flag
+		 * is set, then only we present the Termintation.
+		 */
+		bdc->pullup = true;
+		/*
+		 * Check if BDC is already connected to Host i.e Vbus=1,
+		 * if yes, then present TERM now, this is typical for bus
+		 * powered devices.
+		 */
+		uspc = bdc_readl(bdc->regs, BDC_USPC);
+		if (uspc & BDC_VBS)
+			bdc_softconn(bdc);
+	}
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return 0;
+}
+
+static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
+		int is_self)
+{
+	struct bdc		*bdc = gadget_to_bdc(gadget);
+	unsigned long           flags;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	gadget->is_selfpowered = (is_self != 0);
+	spin_lock_irqsave(&bdc->lock, flags);
+	if (!is_self)
+		bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+	else
+		bdc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return 0;
+}
+
+static int bdc_udc_wakeup(struct usb_gadget *gadget)
+{
+	struct bdc *bdc = gadget_to_bdc(gadget);
+	unsigned long		flags;
+	u8	link_state;
+	u32	uspc;
+	int ret = 0;
+
+	dev_dbg(bdc->dev,
+		"%s() bdc->devstatus=%08x\n",
+		__func__, bdc->devstatus);
+
+	if (!(bdc->devstatus & REMOTE_WAKE_ENABLE))
+		return  -EOPNOTSUPP;
+
+	spin_lock_irqsave(&bdc->lock, flags);
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	link_state = BDC_PST(uspc);
+	dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
+	if (link_state != BDC_LINK_STATE_U3) {
+		dev_warn(bdc->dev,
+			"can't wakeup from link state %d\n",
+			link_state);
+		ret = -EINVAL;
+		goto out;
+	}
+	if (bdc->gadget.speed == USB_SPEED_SUPER)
+		bdc->devstatus |= REMOTE_WAKEUP_ISSUED;
+
+	uspc &= ~BDC_PST_MASK;
+	uspc &= (~BDC_USPSC_RW);
+	uspc |=  BDC_PST(BDC_LINK_STATE_U0);
+	uspc |=  BDC_SWS;
+	bdc_writel(bdc->regs, BDC_USPC, uspc);
+	uspc = bdc_readl(bdc->regs, BDC_USPC);
+	link_state = BDC_PST(uspc);
+	dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
+out:
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	return ret;
+}
+
+static const struct usb_gadget_ops bdc_gadget_ops = {
+	.wakeup = bdc_udc_wakeup,
+	.set_selfpowered = bdc_udc_set_selfpowered,
+	.pullup = bdc_udc_pullup,
+	.udc_start = bdc_udc_start,
+	.udc_stop = bdc_udc_stop,
+};
+
+/* Init the gadget interface and register the udc */
+int bdc_udc_init(struct bdc *bdc)
+{
+	u32 temp;
+	int ret;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	bdc->gadget.ops = &bdc_gadget_ops;
+	bdc->gadget.max_speed = USB_SPEED_SUPER;
+	bdc->gadget.speed = USB_SPEED_UNKNOWN;
+	bdc->gadget.dev.parent = bdc->dev;
+
+	bdc->gadget.sg_supported = false;
+
+
+	bdc->gadget.name = BRCM_BDC_NAME;
+	ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
+				IRQF_SHARED , BRCM_BDC_NAME, bdc);
+	if (ret) {
+		dev_err(bdc->dev,
+			"failed to request irq #%d %d\n",
+			bdc->irq, ret);
+		return ret;
+	}
+
+	ret = bdc_init_ep(bdc);
+	if (ret) {
+		dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
+		return ret;
+	}
+
+	ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
+	if (ret) {
+		dev_err(bdc->dev, "failed to register udc\n");
+		goto err0;
+	}
+	usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED);
+	bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
+	/*
+	 * Allocate bd list for ep0 only, ep0 will be enabled on connect
+	 * status report when the speed is known
+	 */
+	ret = bdc_ep_enable(bdc->bdc_ep_array[1]);
+	if (ret) {
+		dev_err(bdc->dev, "fail to enable %s\n",
+						bdc->bdc_ep_array[1]->name);
+		goto err1;
+	}
+	INIT_DELAYED_WORK(&bdc->func_wake_notify, bdc_func_wake_timer);
+	/* Enable Interrupts */
+	temp = bdc_readl(bdc->regs, BDC_BDCSC);
+	temp |= BDC_GIE;
+	bdc_writel(bdc->regs, BDC_BDCSC, temp);
+	return 0;
+err1:
+	usb_del_gadget_udc(&bdc->gadget);
+err0:
+	bdc_free_ep(bdc);
+
+	return ret;
+}
+
+void bdc_udc_exit(struct bdc *bdc)
+{
+	unsigned long flags;
+
+	dev_dbg(bdc->dev, "%s()\n", __func__);
+	spin_lock_irqsave(&bdc->lock, flags);
+	bdc_ep_disable(bdc->bdc_ep_array[1]);
+	spin_unlock_irqrestore(&bdc->lock, flags);
+
+	usb_del_gadget_udc(&bdc->gadget);
+	bdc_free_ep(bdc);
+}
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
new file mode 100644
index 0000000..af88b48
--- /dev/null
+++ b/drivers/usb/gadget/udc/core.c
@@ -0,0 +1,1609 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * udc.c - Core UDC Framework
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Felipe Balbi <balbi@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched/task_stack.h>
+#include <linux/workqueue.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+
+#include "trace.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
+ * @vbus - for udcs who care about vbus status, this value is real vbus status;
+ * for udcs who do not care about vbus status, this value is always true
+ *
+ * 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;
+	bool				vbus;
+};
+
+static struct class *udc_class;
+static LIST_HEAD(udc_list);
+static LIST_HEAD(gadget_driver_pending_list);
+static DEFINE_MUTEX(udc_lock);
+
+static int udc_bind_to_driver(struct usb_udc *udc,
+		struct usb_gadget_driver *driver);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
+ * @ep:the endpoint being configured
+ * @maxpacket_limit:value of maximum packet size limit
+ *
+ * This function should be used only in UDC drivers to initialize endpoint
+ * (usually in probe function).
+ */
+void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
+					      unsigned maxpacket_limit)
+{
+	ep->maxpacket_limit = maxpacket_limit;
+	ep->maxpacket = maxpacket_limit;
+
+	trace_usb_ep_set_maxpacket_limit(ep, 0);
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep:the endpoint being configured.  may not be the endpoint named "ep0".
+ *	drivers discover endpoints through the ep_list of a usb_gadget.
+ *
+ * When configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints.  while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation (which calls this routine) must ensure that the
+ * hardware capabilities of each endpoint match the descriptor provided
+ * for it.  for example, an endpoint named "ep2in-bulk" would be usable
+ * for interrupt transfers as well as bulk, but it likely couldn't be used
+ * for iso transfers or for endpoint 14.  some endpoints are fully
+ * configurable, with more generic names like "ep-a".  (remember that for
+ * USB, "in" means "towards the USB master".)
+ *
+ * This routine must be called in process context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_enable(struct usb_ep *ep)
+{
+	int ret = 0;
+
+	if (ep->enabled)
+		goto out;
+
+	ret = ep->ops->enable(ep, ep->desc);
+	if (ret)
+		goto out;
+
+	ep->enabled = true;
+
+out:
+	trace_usb_ep_enable(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_enable);
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep:the endpoint being unconfigured.  may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called.
+ * any pending and uncompleted requests will complete with status
+ * indicating disconnect (-ESHUTDOWN) before this call returns.
+ * gadget drivers must call usb_ep_enable() again before queueing
+ * requests to the endpoint.
+ *
+ * This routine must be called in process context.
+ *
+ * returns zero, or a negative error code.
+ */
+int usb_ep_disable(struct usb_ep *ep)
+{
+	int ret = 0;
+
+	if (!ep->enabled)
+		goto out;
+
+	ret = ep->ops->disable(ep);
+	if (ret)
+		goto out;
+
+	ep->enabled = false;
+
+out:
+	trace_usb_ep_disable(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_disable);
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep:the endpoint to be used with with the request
+ * @gfp_flags:GFP_* flags to use
+ *
+ * Request objects must be allocated with this call, since they normally
+ * need controller-specific setup and may even need endpoint-specific
+ * resources such as allocation of DMA descriptors.
+ * Requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback.  Free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * Returns the request, or null if one could not be allocated.
+ */
+struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
+						       gfp_t gfp_flags)
+{
+	struct usb_request *req = NULL;
+
+	req = ep->ops->alloc_request(ep, gfp_flags);
+
+	trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM);
+
+	return req;
+}
+EXPORT_SYMBOL_GPL(usb_ep_alloc_request);
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep:the endpoint associated with the request
+ * @req:the request being freed
+ *
+ * Reverses the effect of usb_ep_alloc_request().
+ * Caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+void usb_ep_free_request(struct usb_ep *ep,
+				       struct usb_request *req)
+{
+	trace_usb_ep_free_request(ep, req, 0);
+	ep->ops->free_request(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_ep_free_request);
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ *	pre-allocate all necessary memory with the request.
+ *
+ * This tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer).  When the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver.  Any endpoint
+ * (except control endpoints like ep0) may have more than one transfer
+ * request queued; they complete in FIFO order.  Once a gadget driver
+ * submits a request, that request may not be examined or modified until it
+ * is given back to that driver through the completion callback.
+ *
+ * Each request is turned into one or more packets.  The controller driver
+ * never merges adjacent requests into the same packet.  OUT transfers
+ * will sometimes use data that's already buffered in the hardware.
+ * Drivers can rely on the fact that the first byte of the request's buffer
+ * always corresponds to the first byte of some USB packet, for both
+ * IN and OUT transfers.
+ *
+ * Bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically.  The last packet will be short if the request doesn't fill it
+ * out completely.  Zero length packets (ZLPs) should be avoided in portable
+ * protocols since not all usb hardware can successfully handle zero length
+ * packets.  (ZLPs may be explicitly written, and may be implicitly written if
+ * the request 'zero' flag is set.)  Bulk endpoints may also be used
+ * for interrupt transfers; but the reverse is not true, and some endpoints
+ * won't support every interrupt transfer.  (Such as 768 byte packets.)
+ *
+ * Interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size.  They may also treat data
+ * toggle differently.
+ *
+ * Control endpoints ... after getting a setup() callback, the driver queues
+ * one response (even if it would be zero length).  That enables the
+ * status ack, after transferring data as specified in the response.  Setup
+ * functions may return negative error codes to generate protocol stalls.
+ * (Note that some USB device controllers disallow protocol stall responses
+ * in some cases.)  When control responses are deferred (the response is
+ * written after the setup callback returns), then usb_ep_set_halt() may be
+ * used on ep0 to trigger protocol stalls.  Depending on the controller,
+ * it may not be possible to trigger a status-stage protocol stall when the
+ * data stage is over, that is, from within the response's completion
+ * routine.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued some data to transfer at that time.
+ *
+ * Note that @req's ->complete() callback must never be called from
+ * within usb_ep_queue() as that can create deadlock situations.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code.  Endpoints that are not enabled
+ * report errors; errors will also be
+ * reported when the usb peripheral is disconnected.
+ *
+ * If and only if @req is successfully queued (the return value is zero),
+ * @req->complete() will be called exactly once, when the Gadget core and
+ * UDC are finished with the request.  When the completion function is called,
+ * control of the request is returned to the device driver which submitted it.
+ * The completion handler may then immediately free or reuse @req.
+ */
+int usb_ep_queue(struct usb_ep *ep,
+			       struct usb_request *req, gfp_t gfp_flags)
+{
+	int ret = 0;
+
+	if (WARN_ON_ONCE(!ep->enabled && ep->address)) {
+		ret = -ESHUTDOWN;
+		goto out;
+	}
+
+	ret = ep->ops->queue(ep, req, gfp_flags);
+
+out:
+	trace_usb_ep_queue(ep, req, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_queue);
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep:the endpoint associated with the request
+ * @req:the request being canceled
+ *
+ * If the request is still active on the endpoint, it is dequeued and its
+ * completion routine is called (with status -ECONNRESET); else a negative
+ * error code is returned. This is guaranteed to happen before the call to
+ * usb_ep_dequeue() returns.
+ *
+ * Note that some hardware can't clear out write fifos (to unlink the request
+ * at the head of the queue) except as part of disconnecting from usb. Such
+ * restrictions prevent drivers from supporting configuration changes,
+ * even to configuration zero (a "chapter 9" requirement).
+ *
+ * This routine may be called in interrupt context.
+ */
+int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	int ret;
+
+	ret = ep->ops->dequeue(ep, req);
+	trace_usb_ep_dequeue(ep, req, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_dequeue);
+
+/**
+ * usb_ep_set_halt - sets the endpoint halt feature.
+ * @ep: the non-isochronous endpoint being stalled
+ *
+ * Use this to stall an endpoint, perhaps as an error report.
+ * Except for control endpoints,
+ * the endpoint stays halted (will not stream any data) until the host
+ * clears this feature; drivers may need to empty the endpoint's request
+ * queue first, to make sure no inappropriate transfers happen.
+ *
+ * Note that while an endpoint CLEAR_FEATURE will be invisible to the
+ * gadget driver, a SET_INTERFACE will not be.  To reset endpoints for the
+ * current altsetting, see usb_ep_clear_halt().  When switching altsettings,
+ * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code.  On success, this call sets
+ * underlying hardware state that blocks data transfers.
+ * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
+ * transfer requests are still queued, or if the controller hardware
+ * (usually a FIFO) still holds bytes that the host hasn't collected.
+ */
+int usb_ep_set_halt(struct usb_ep *ep)
+{
+	int ret;
+
+	ret = ep->ops->set_halt(ep, 1);
+	trace_usb_ep_set_halt(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_halt);
+
+/**
+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle
+ * @ep:the bulk or interrupt endpoint being reset
+ *
+ * Use this when responding to the standard usb "set interface" request,
+ * for endpoints that aren't reconfigured, after clearing any other state
+ * in the endpoint's i/o queue.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero, or a negative error code.  On success, this call clears
+ * the underlying hardware state reflecting endpoint halt and data toggle.
+ * Note that some hardware can't support this request (like pxa2xx_udc),
+ * and accordingly can't correctly implement interface altsettings.
+ */
+int usb_ep_clear_halt(struct usb_ep *ep)
+{
+	int ret;
+
+	ret = ep->ops->set_halt(ep, 0);
+	trace_usb_ep_clear_halt(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
+
+/**
+ * usb_ep_set_wedge - sets the halt feature and ignores clear requests
+ * @ep: the endpoint being wedged
+ *
+ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
+ * requests. If the gadget driver clears the halt status, it will
+ * automatically unwedge the endpoint.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_ep_set_wedge(struct usb_ep *ep)
+{
+	int ret;
+
+	if (ep->ops->set_wedge)
+		ret = ep->ops->set_wedge(ep);
+	else
+		ret = ep->ops->set_halt(ep, 1);
+
+	trace_usb_ep_set_wedge(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
+
+/**
+ * usb_ep_fifo_status - returns number of bytes in fifo, or error
+ * @ep: the endpoint whose fifo status is being checked.
+ *
+ * FIFO endpoints may have "unclaimed data" in them in certain cases,
+ * such as after aborted transfers.  Hosts may not have collected all
+ * the IN data written by the gadget driver (and reported by a request
+ * completion).  The gadget driver may not have collected all the data
+ * written OUT to it by the host.  Drivers that need precise handling for
+ * fault reporting or recovery may need to use this call.
+ *
+ * This routine may be called in interrupt context.
+ *
+ * This returns the number of such bytes in the fifo, or a negative
+ * errno if the endpoint doesn't use a FIFO or doesn't support such
+ * precise handling.
+ */
+int usb_ep_fifo_status(struct usb_ep *ep)
+{
+	int ret;
+
+	if (ep->ops->fifo_status)
+		ret = ep->ops->fifo_status(ep);
+	else
+		ret = -EOPNOTSUPP;
+
+	trace_usb_ep_fifo_status(ep, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
+
+/**
+ * usb_ep_fifo_flush - flushes contents of a fifo
+ * @ep: the endpoint whose fifo is being flushed.
+ *
+ * This call may be used to flush the "unclaimed data" that may exist in
+ * an endpoint fifo after abnormal transaction terminations.  The call
+ * must never be used except when endpoint is not being used for any
+ * protocol translation.
+ *
+ * This routine may be called in interrupt context.
+ */
+void usb_ep_fifo_flush(struct usb_ep *ep)
+{
+	if (ep->ops->fifo_flush)
+		ep->ops->fifo_flush(ep);
+
+	trace_usb_ep_fifo_flush(ep, 0);
+}
+EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_frame_number - returns the current frame number
+ * @gadget: controller that reports the frame number
+ *
+ * Returns the usb frame number, normally eleven bits from a SOF packet,
+ * or negative errno if this device doesn't support this capability.
+ */
+int usb_gadget_frame_number(struct usb_gadget *gadget)
+{
+	int ret;
+
+	ret = gadget->ops->get_frame(gadget);
+
+	trace_usb_gadget_frame_number(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_frame_number);
+
+/**
+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget
+ * @gadget: controller used to wake up the host
+ *
+ * Returns zero on success, else negative error code if the hardware
+ * doesn't support such attempts, or its support has not been enabled
+ * by the usb host.  Drivers must return device descriptors that report
+ * their ability to support this, or hosts won't enable it.
+ *
+ * This may also try to use SRP to wake the host and start enumeration,
+ * even if OTG isn't otherwise in use.  OTG devices may also start
+ * remote wakeup even when hosts don't explicitly enable it.
+ */
+int usb_gadget_wakeup(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->wakeup) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->wakeup(gadget);
+
+out:
+	trace_usb_gadget_wakeup(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
+
+/**
+ * usb_gadget_set_selfpowered - sets the device selfpowered feature.
+ * @gadget:the device being declared as self-powered
+ *
+ * this affects the device status reported by the hardware driver
+ * to reflect that it now has a local power supply.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->set_selfpowered) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->set_selfpowered(gadget, 1);
+
+out:
+	trace_usb_gadget_set_selfpowered(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered);
+
+/**
+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature.
+ * @gadget:the device being declared as bus-powered
+ *
+ * this affects the device status reported by the hardware driver.
+ * some hardware may not support bus-powered operation, in which
+ * case this feature's value can never change.
+ *
+ * returns zero on success, else negative errno.
+ */
+int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->set_selfpowered) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->set_selfpowered(gadget, 0);
+
+out:
+	trace_usb_gadget_clear_selfpowered(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered);
+
+/**
+ * usb_gadget_vbus_connect - Notify controller that VBUS is powered
+ * @gadget:The device which now has VBUS power.
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session starting.  Common responses include
+ * resuming the controller, activating the D+ (or D-) pullup to let the
+ * host detect that a USB device is attached, and starting to draw power
+ * (8mA or possibly more, especially after SET_CONFIGURATION).
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_connect(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->vbus_session) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->vbus_session(gadget, 1);
+
+out:
+	trace_usb_gadget_vbus_connect(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect);
+
+/**
+ * usb_gadget_vbus_draw - constrain controller's VBUS power usage
+ * @gadget:The device whose VBUS usage is being described
+ * @mA:How much current to draw, in milliAmperes.  This should be twice
+ *	the value listed in the configuration descriptor bMaxPower field.
+ *
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	int ret = 0;
+
+	if (!gadget->ops->vbus_draw) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->vbus_draw(gadget, mA);
+	if (!ret)
+		gadget->mA = mA;
+
+out:
+	trace_usb_gadget_vbus_draw(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw);
+
+/**
+ * usb_gadget_vbus_disconnect - notify controller about VBUS session end
+ * @gadget:the device whose VBUS supply is being described
+ * Context: can sleep
+ *
+ * This call is used by a driver for an external transceiver (or GPIO)
+ * that detects a VBUS power session ending.  Common responses include
+ * reversing everything done in usb_gadget_vbus_connect().
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->vbus_session) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ret = gadget->ops->vbus_session(gadget, 0);
+
+out:
+	trace_usb_gadget_vbus_disconnect(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
+
+/**
+ * usb_gadget_connect - software-controlled connect to USB host
+ * @gadget:the peripheral being connected
+ *
+ * Enables the D+ (or potentially D-) pullup.  The host will start
+ * enumerating this gadget when the pullup is active and a VBUS session
+ * is active (the link is powered).  This pullup is always enabled unless
+ * usb_gadget_disconnect() has been used to disable it.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_connect(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->pullup) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (gadget->deactivated) {
+		/*
+		 * If gadget is deactivated we only save new state.
+		 * Gadget will be connected automatically after activation.
+		 */
+		gadget->connected = true;
+		goto out;
+	}
+
+	ret = gadget->ops->pullup(gadget, 1);
+	if (!ret)
+		gadget->connected = 1;
+
+out:
+	trace_usb_gadget_connect(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_connect);
+
+/**
+ * usb_gadget_disconnect - software-controlled disconnect from USB host
+ * @gadget:the peripheral being disconnected
+ *
+ * Disables the D+ (or potentially D-) pullup, which the host may see
+ * as a disconnect (when a VBUS session is active).  Not all systems
+ * support software pullup controls.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_disconnect(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->ops->pullup) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (gadget->deactivated) {
+		/*
+		 * If gadget is deactivated we only save new state.
+		 * Gadget will stay disconnected after activation.
+		 */
+		gadget->connected = false;
+		goto out;
+	}
+
+	ret = gadget->ops->pullup(gadget, 0);
+	if (!ret)
+		gadget->connected = 0;
+
+out:
+	trace_usb_gadget_disconnect(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
+
+/**
+ * usb_gadget_deactivate - deactivate function which is not ready to work
+ * @gadget: the peripheral being deactivated
+ *
+ * This routine may be used during the gadget driver bind() call to prevent
+ * the peripheral from ever being visible to the USB host, unless later
+ * usb_gadget_activate() is called.  For example, user mode components may
+ * need to be activated before the system can talk to hosts.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_deactivate(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (gadget->deactivated)
+		goto out;
+
+	if (gadget->connected) {
+		ret = usb_gadget_disconnect(gadget);
+		if (ret)
+			goto out;
+
+		/*
+		 * If gadget was being connected before deactivation, we want
+		 * to reconnect it in usb_gadget_activate().
+		 */
+		gadget->connected = true;
+	}
+	gadget->deactivated = true;
+
+out:
+	trace_usb_gadget_deactivate(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_deactivate);
+
+/**
+ * usb_gadget_activate - activate function which is not ready to work
+ * @gadget: the peripheral being activated
+ *
+ * This routine activates gadget which was previously deactivated with
+ * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_activate(struct usb_gadget *gadget)
+{
+	int ret = 0;
+
+	if (!gadget->deactivated)
+		goto out;
+
+	gadget->deactivated = false;
+
+	/*
+	 * If gadget has been connected before deactivation, or became connected
+	 * while it was being deactivated, we call usb_gadget_connect().
+	 */
+	if (gadget->connected)
+		ret = usb_gadget_connect(gadget);
+
+out:
+	trace_usb_gadget_activate(gadget, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_activate);
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef	CONFIG_HAS_DMA
+
+int usb_gadget_map_request_by_dev(struct device *dev,
+		struct usb_request *req, int is_in)
+{
+	if (req->length == 0)
+		return 0;
+
+	if (req->num_sgs) {
+		int     mapped;
+
+		mapped = dma_map_sg(dev, req->sg, req->num_sgs,
+				is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+		if (mapped == 0) {
+			dev_err(dev, "failed to map SGs\n");
+			return -EFAULT;
+		}
+
+		req->num_mapped_sgs = mapped;
+	} else {
+		if (is_vmalloc_addr(req->buf)) {
+			dev_err(dev, "buffer is not dma capable\n");
+			return -EFAULT;
+		} else if (object_is_on_stack(req->buf)) {
+			dev_err(dev, "buffer is on stack\n");
+			return -EFAULT;
+		}
+
+		req->dma = dma_map_single(dev, req->buf, req->length,
+				is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+		if (dma_mapping_error(dev, req->dma)) {
+			dev_err(dev, "failed to map buffer\n");
+			return -EFAULT;
+		}
+
+		req->dma_mapped = 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
+
+int usb_gadget_map_request(struct usb_gadget *gadget,
+		struct usb_request *req, int is_in)
+{
+	return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_map_request);
+
+void usb_gadget_unmap_request_by_dev(struct device *dev,
+		struct usb_request *req, int is_in)
+{
+	if (req->length == 0)
+		return;
+
+	if (req->num_mapped_sgs) {
+		dma_unmap_sg(dev, req->sg, req->num_sgs,
+				is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+		req->num_mapped_sgs = 0;
+	} else if (req->dma_mapped) {
+		dma_unmap_single(dev, req->dma, req->length,
+				is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+		req->dma_mapped = 0;
+	}
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
+
+void usb_gadget_unmap_request(struct usb_gadget *gadget,
+		struct usb_request *req, int is_in)
+{
+	usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
+
+#endif	/* CONFIG_HAS_DMA */
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * usb_gadget_giveback_request - give the request back to the gadget layer
+ * Context: in_interrupt()
+ *
+ * This is called by device controller drivers in order to return the
+ * completed request back to the gadget layer.
+ */
+void usb_gadget_giveback_request(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	if (likely(req->status == 0))
+		usb_led_activity(USB_LED_EVENT_GADGET);
+
+	trace_usb_gadget_giveback_request(ep, req, 0);
+
+	req->complete(ep, req);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * gadget_find_ep_by_name - returns ep whose name is the same as sting passed
+ *	in second parameter or NULL if searched endpoint not found
+ * @g: controller to check for quirk
+ * @name: name of searched endpoint
+ */
+struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name)
+{
+	struct usb_ep *ep;
+
+	gadget_for_each_ep(ep, g) {
+		if (!strcmp(ep->name, name))
+			return ep;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gadget_find_ep_by_name);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
+		struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
+		struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+	u8		type;
+	u16		max;
+	int		num_req_streams = 0;
+
+	/* endpoint already claimed? */
+	if (ep->claimed)
+		return 0;
+
+	type = usb_endpoint_type(desc);
+	max = usb_endpoint_maxp(desc);
+
+	if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
+		return 0;
+	if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
+		return 0;
+
+	if (max > ep->maxpacket_limit)
+		return 0;
+
+	/* "high bandwidth" works only at high speed */
+	if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1)
+		return 0;
+
+	switch (type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		/* only support ep0 for portable CONTROL traffic */
+		return 0;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (!ep->caps.type_iso)
+			return 0;
+		/* ISO:  limit 1023 bytes full speed, 1024 high/super speed */
+		if (!gadget_is_dualspeed(gadget) && max > 1023)
+			return 0;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		if (!ep->caps.type_bulk)
+			return 0;
+		if (ep_comp && gadget_is_superspeed(gadget)) {
+			/* Get the number of required streams from the
+			 * EP companion descriptor and see if the EP
+			 * matches it
+			 */
+			num_req_streams = ep_comp->bmAttributes & 0x1f;
+			if (num_req_streams > ep->max_streams)
+				return 0;
+		}
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		/* Bulk endpoints handle interrupt transfers,
+		 * except the toggle-quirky iso-synch kind
+		 */
+		if (!ep->caps.type_int && !ep->caps.type_bulk)
+			return 0;
+		/* INT:  limit 64 bytes full speed, 1024 high/super speed */
+		if (!gadget_is_dualspeed(gadget) && max > 64)
+			return 0;
+		break;
+	}
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
+
+/* ------------------------------------------------------------------------- */
+
+static void usb_gadget_state_work(struct work_struct *work)
+{
+	struct usb_gadget *gadget = work_to_gadget(work);
+	struct usb_udc *udc = gadget->udc;
+
+	if (udc)
+		sysfs_notify(&udc->dev.kobj, NULL, "state");
+}
+
+void usb_gadget_set_state(struct usb_gadget *gadget,
+		enum usb_device_state state)
+{
+	gadget->state = state;
+	schedule_work(&gadget->work);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_set_state);
+
+/* ------------------------------------------------------------------------- */
+
+static void usb_udc_connect_control(struct usb_udc *udc)
+{
+	if (udc->vbus)
+		usb_gadget_connect(udc->gadget);
+	else
+		usb_gadget_disconnect(udc->gadget);
+}
+
+/**
+ * usb_udc_vbus_handler - updates the udc core vbus status, and try to
+ * connect or disconnect gadget
+ * @gadget: The gadget which vbus change occurs
+ * @status: The vbus status
+ *
+ * The udc driver calls it when it wants to connect or disconnect gadget
+ * according to vbus status.
+ */
+void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{
+	struct usb_udc *udc = gadget->udc;
+
+	if (udc) {
+		udc->vbus = status;
+		usb_udc_connect_control(udc);
+	}
+}
+EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
+
+/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	driver->reset(gadget);
+	usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
+ * usb_gadget_udc_start - tells usb device controller to start up
+ * @udc: The UDC to be started
+ *
+ * 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_udc *udc)
+{
+	return udc->gadget->ops->udc_start(udc->gadget, udc->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_udc *udc)
+{
+	udc->gadget->ops->udc_stop(udc->gadget);
+}
+
+/**
+ * usb_gadget_udc_set_speed - tells usb device controller speed supported by
+ *    current driver
+ * @udc: The device we want to set maximum speed
+ * @speed: The maximum speed to allowed to run
+ *
+ * This call is issued by the UDC Class driver before calling
+ * usb_gadget_udc_start() in order to make sure that we don't try to
+ * connect on speeds the gadget driver doesn't support.
+ */
+static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
+					    enum usb_device_speed speed)
+{
+	if (udc->gadget->ops->udc_set_speed) {
+		enum usb_device_speed s;
+
+		s = min(speed, udc->gadget->max_speed);
+		udc->gadget->ops->udc_set_speed(udc->gadget, s);
+	}
+}
+
+/**
+ * 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[];
+
+static void usb_udc_nop_release(struct device *dev)
+{
+	dev_vdbg(dev, "%s\n", __func__);
+}
+
+/* should be called with udc_lock held */
+static int check_pending_gadget_drivers(struct usb_udc *udc)
+{
+	struct usb_gadget_driver *driver;
+	int ret = 0;
+
+	list_for_each_entry(driver, &gadget_driver_pending_list, pending)
+		if (!driver->udc_name || strcmp(driver->udc_name,
+						dev_name(&udc->dev)) == 0) {
+			ret = udc_bind_to_driver(udc, driver);
+			if (ret != -EPROBE_DEFER)
+				list_del(&driver->pending);
+			break;
+		}
+
+	return ret;
+}
+
+/**
+ * usb_add_gadget_udc_release - 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.
+ * @release: a gadget release function.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Calls the gadget release function in the latter case.
+ */
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+		void (*release)(struct device *dev))
+{
+	struct usb_udc		*udc;
+	int			ret = -ENOMEM;
+
+	dev_set_name(&gadget->dev, "gadget");
+	INIT_WORK(&gadget->work, usb_gadget_state_work);
+	gadget->dev.parent = parent;
+
+	if (release)
+		gadget->dev.release = release;
+	else
+		gadget->dev.release = usb_udc_nop_release;
+
+	device_initialize(&gadget->dev);
+
+	udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		goto err_put_gadget;
+
+	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 err_put_udc;
+
+	ret = device_add(&gadget->dev);
+	if (ret)
+		goto err_put_udc;
+
+	udc->gadget = gadget;
+	gadget->udc = udc;
+
+	mutex_lock(&udc_lock);
+	list_add_tail(&udc->list, &udc_list);
+
+	ret = device_add(&udc->dev);
+	if (ret)
+		goto err_unlist_udc;
+
+	usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+	udc->vbus = true;
+
+	/* pick up one of pending gadget drivers */
+	ret = check_pending_gadget_drivers(udc);
+	if (ret)
+		goto err_del_udc;
+
+	mutex_unlock(&udc_lock);
+
+	return 0;
+
+ err_del_udc:
+	device_del(&udc->dev);
+
+ err_unlist_udc:
+	list_del(&udc->list);
+	mutex_unlock(&udc_lock);
+
+	device_del(&gadget->dev);
+
+ err_put_udc:
+	put_device(&udc->dev);
+
+ err_put_gadget:
+	put_device(&gadget->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
+
+/**
+ * usb_get_gadget_udc_name - get the name of the first UDC controller
+ * This functions returns the name of the first UDC controller in the system.
+ * Please note that this interface is usefull only for legacy drivers which
+ * assume that there is only one UDC controller in the system and they need to
+ * get its name before initialization. There is no guarantee that the UDC
+ * of the returned name will be still available, when gadget driver registers
+ * itself.
+ *
+ * Returns pointer to string with UDC controller name on success, NULL
+ * otherwise. Caller should kfree() returned string.
+ */
+char *usb_get_gadget_udc_name(void)
+{
+	struct usb_udc *udc;
+	char *name = NULL;
+
+	/* For now we take the first available UDC */
+	mutex_lock(&udc_lock);
+	list_for_each_entry(udc, &udc_list, list) {
+		if (!udc->driver) {
+			name = kstrdup(udc->gadget->name, GFP_KERNEL);
+			break;
+		}
+	}
+	mutex_unlock(&udc_lock);
+	return name;
+}
+EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
+
+/**
+ * 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)
+{
+	return usb_add_gadget_udc_release(parent, gadget, NULL);
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+
+static void usb_gadget_remove_driver(struct usb_udc *udc)
+{
+	dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
+			udc->driver->function);
+
+	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+
+	usb_gadget_disconnect(udc->gadget);
+	udc->driver->disconnect(udc->gadget);
+	udc->driver->unbind(udc->gadget);
+	usb_gadget_udc_stop(udc);
+
+	udc->driver = NULL;
+	udc->dev.driver = NULL;
+	udc->gadget->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 = gadget->udc;
+
+	if (!udc)
+		return;
+
+	dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
+
+	mutex_lock(&udc_lock);
+	list_del(&udc->list);
+
+	if (udc->driver) {
+		struct usb_gadget_driver *driver = udc->driver;
+
+		usb_gadget_remove_driver(udc);
+		list_add(&driver->pending, &gadget_driver_pending_list);
+	}
+	mutex_unlock(&udc_lock);
+
+	kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+	flush_work(&gadget->work);
+	device_unregister(&udc->dev);
+	device_unregister(&gadget->dev);
+	memset(&gadget->dev, 0x00, sizeof(gadget->dev));
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
+
+/* ------------------------------------------------------------------------- */
+
+static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
+{
+	int ret;
+
+	dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
+			driver->function);
+
+	udc->driver = driver;
+	udc->dev.driver = &driver->driver;
+	udc->gadget->dev.driver = &driver->driver;
+
+	usb_gadget_udc_set_speed(udc, driver->max_speed);
+
+	ret = driver->bind(udc->gadget, driver);
+	if (ret)
+		goto err1;
+	ret = usb_gadget_udc_start(udc);
+	if (ret) {
+		driver->unbind(udc->gadget);
+		goto err1;
+	}
+	usb_udc_connect_control(udc);
+
+	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+	return 0;
+err1:
+	if (ret != -EISNAM)
+		dev_err(&udc->dev, "failed to start %s: %d\n",
+			udc->driver->function, ret);
+	udc->driver = NULL;
+	udc->dev.driver = NULL;
+	udc->gadget->dev.driver = NULL;
+	return ret;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
+{
+	struct usb_udc		*udc = NULL;
+	int			ret = -ENODEV;
+
+	if (!driver || !driver->bind || !driver->setup)
+		return -EINVAL;
+
+	mutex_lock(&udc_lock);
+	if (driver->udc_name) {
+		list_for_each_entry(udc, &udc_list, list) {
+			ret = strcmp(driver->udc_name, dev_name(&udc->dev));
+			if (!ret)
+				break;
+		}
+		if (ret)
+			ret = -ENODEV;
+		else if (udc->driver)
+			ret = -EBUSY;
+		else
+			goto found;
+	} else {
+		list_for_each_entry(udc, &udc_list, list) {
+			/* For now we take the first one */
+			if (!udc->driver)
+				goto found;
+		}
+	}
+
+	if (!driver->match_existing_only) {
+		list_add_tail(&driver->pending, &gadget_driver_pending_list);
+		pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",
+			driver->function);
+		ret = 0;
+	}
+
+	mutex_unlock(&udc_lock);
+	return ret;
+found:
+	ret = udc_bind_to_driver(udc, driver);
+	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);
+			usb_gadget_set_state(udc->gadget,
+					     USB_STATE_NOTATTACHED);
+
+			/* Maybe there is someone waiting for this UDC? */
+			check_pending_gadget_drivers(udc);
+			/*
+			 * For now we ignore bind errors as probably it's
+			 * not a valid reason to fail other's gadget unbind
+			 */
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret) {
+		list_del(&driver->pending);
+		ret = 0;
+	}
+	mutex_unlock(&udc_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t srp_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t n)
+{
+	struct usb_udc		*udc = container_of(dev, struct usb_udc, dev);
+
+	if (sysfs_streq(buf, "1"))
+		usb_gadget_wakeup(udc->gadget);
+
+	return n;
+}
+static DEVICE_ATTR_WO(srp);
+
+static ssize_t soft_connect_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t n)
+{
+	struct usb_udc		*udc = container_of(dev, struct usb_udc, dev);
+
+	if (!udc->driver) {
+		dev_err(dev, "soft-connect without a gadget driver\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (sysfs_streq(buf, "connect")) {
+		usb_gadget_udc_start(udc);
+		usb_gadget_connect(udc->gadget);
+	} else if (sysfs_streq(buf, "disconnect")) {
+		usb_gadget_disconnect(udc->gadget);
+		udc->driver->disconnect(udc->gadget);
+		usb_gadget_udc_stop(udc);
+	} else {
+		dev_err(dev, "unsupported command '%s'\n", buf);
+		return -EINVAL;
+	}
+
+	return n;
+}
+static DEVICE_ATTR_WO(soft_connect);
+
+static ssize_t state_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 sprintf(buf, "%s\n", usb_state_string(gadget->state));
+}
+static DEVICE_ATTR_RO(state);
+
+static ssize_t function_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct usb_udc		*udc = container_of(dev, struct usb_udc, dev);
+	struct usb_gadget_driver *drv = udc->driver;
+
+	if (!drv || !drv->function)
+		return 0;
+	return scnprintf(buf, PAGE_SIZE, "%s\n", drv->function);
+}
+static DEVICE_ATTR_RO(function);
+
+#define USB_UDC_SPEED_ATTR(name, param)					\
+ssize_t name##_show(struct device *dev,					\
+		struct device_attribute *attr, char *buf)		\
+{									\
+	struct usb_udc *udc = container_of(dev, struct usb_udc, dev);	\
+	return scnprintf(buf, PAGE_SIZE, "%s\n",			\
+			usb_speed_string(udc->gadget->param));		\
+}									\
+static DEVICE_ATTR_RO(name)
+
+static USB_UDC_SPEED_ATTR(current_speed, speed);
+static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
+
+#define USB_UDC_ATTR(name)					\
+ssize_t 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 scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name);	\
+}								\
+static DEVICE_ATTR_RO(name)
+
+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 USB_UDC_ATTR(is_selfpowered);
+
+static struct attribute *usb_udc_attrs[] = {
+	&dev_attr_srp.attr,
+	&dev_attr_soft_connect.attr,
+	&dev_attr_state.attr,
+	&dev_attr_function.attr,
+	&dev_attr_current_speed.attr,
+	&dev_attr_maximum_speed.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,
+	&dev_attr_is_selfpowered.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/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
new file mode 100644
index 0000000..baf72f9
--- /dev/null
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -0,0 +1,2874 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * dummy_hcd.c -- Dummy/Loopback USB host and device emulator driver.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2005 Alan Stern
+ */
+
+
+/*
+ * This exposes a device side "USB gadget" API, driven by requests to a
+ * Linux-USB host controller driver.  USB traffic is simulated; there's
+ * no need for USB hardware.  Use this with two other drivers:
+ *
+ *  - Gadget driver, responding to requests (slave);
+ *  - Host-side device driver, as already familiar in Linux.
+ *
+ * Having this all in one kernel can help some stages of development,
+ * bypassing some hardware (and driver) issues.  UML could help too.
+ *
+ * Note: The emulation does not include isochronous transfers!
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/scatterlist.h>
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#define DRIVER_DESC	"USB Host+Gadget Emulator"
+#define DRIVER_VERSION	"02 May 2005"
+
+#define POWER_BUDGET	500	/* in mA; use 8 for low-power port testing */
+
+static const char	driver_name[] = "dummy_hcd";
+static const char	driver_desc[] = "USB Host+Gadget Emulator";
+
+static const char	gadget_name[] = "dummy_udc";
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
+
+struct dummy_hcd_module_parameters {
+	bool is_super_speed;
+	bool is_high_speed;
+	unsigned int num;
+};
+
+static struct dummy_hcd_module_parameters mod_data = {
+	.is_super_speed = false,
+	.is_high_speed = true,
+	.num = 1,
+};
+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");
+module_param_named(num, mod_data.num, uint, S_IRUGO);
+MODULE_PARM_DESC(num, "number of emulated controllers");
+/*-------------------------------------------------------------------------*/
+
+/* gadget side driver data structres */
+struct dummy_ep {
+	struct list_head		queue;
+	unsigned long			last_io;	/* jiffies timestamp */
+	struct usb_gadget		*gadget;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_ep			ep;
+	unsigned			halted:1;
+	unsigned			wedged:1;
+	unsigned			already_seen:1;
+	unsigned			setup_stage:1;
+	unsigned			stream_en:1;
+};
+
+struct dummy_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+static inline struct dummy_ep *usb_ep_to_dummy_ep(struct usb_ep *_ep)
+{
+	return container_of(_ep, struct dummy_ep, ep);
+}
+
+static inline struct dummy_request *usb_request_to_dummy_request
+		(struct usb_request *_req)
+{
+	return container_of(_req, struct dummy_request, req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Every device has ep0 for control requests, plus up to 30 more endpoints,
+ * in one of two types:
+ *
+ *   - Configurable:  direction (in/out), type (bulk, iso, etc), and endpoint
+ *     number can be changed.  Names like "ep-a" are used for this type.
+ *
+ *   - Fixed Function:  in other cases.  some characteristics may be mutable;
+ *     that'd be hardware-specific.  Names like "ep12out-bulk" are used.
+ *
+ * Gadget drivers are responsible for not setting up conflicting endpoint
+ * configurations, illegal or unsupported packet lengths, and so on.
+ */
+
+static const char ep0name[] = "ep0";
+
+static const struct {
+	const char *name;
+	const struct usb_ep_caps caps;
+} ep_info[] = {
+#define EP_INFO(_name, _caps) \
+	{ \
+		.name = _name, \
+		.caps = _caps, \
+	}
+
+/* we don't provide isochronous endpoints since we don't support them */
+#define TYPE_BULK_OR_INT	(USB_EP_CAPS_TYPE_BULK | USB_EP_CAPS_TYPE_INT)
+
+	/* everyone has ep0 */
+	EP_INFO(ep0name,
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
+	/* act like a pxa250: fifteen fixed function endpoints */
+	EP_INFO("ep1in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep2out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+/*
+	EP_INFO("ep3in-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep4out-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)),
+*/
+	EP_INFO("ep5in-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep6in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep7out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+/*
+	EP_INFO("ep8in-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep9out-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)),
+*/
+	EP_INFO("ep10in-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep11in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep12out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+/*
+	EP_INFO("ep13in-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep14out-iso",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)),
+*/
+	EP_INFO("ep15in-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)),
+
+	/* or like sa1100: two fixed function endpoints */
+	EP_INFO("ep1out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep2in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+
+	/* and now some generic EPs so we have enough in multi config */
+	EP_INFO("ep3out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep4in",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep5out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep6out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep7in",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep8out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep9in",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep10out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep11out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep12in",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep13out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep14in",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep15out",
+		USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
+
+#undef EP_INFO
+};
+
+#define DUMMY_ENDPOINTS	ARRAY_SIZE(ep_info)
+
+/*-------------------------------------------------------------------------*/
+
+#define FIFO_SIZE		64
+
+struct urbp {
+	struct urb		*urb;
+	struct list_head	urbp_list;
+	struct sg_mapping_iter	miter;
+	u32			miter_started;
+};
+
+
+enum dummy_rh_state {
+	DUMMY_RH_RESET,
+	DUMMY_RH_SUSPENDED,
+	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;
+	struct urbp			*next_frame_urbp;
+
+	u32				stream_en_ep;
+	u8				num_stream[30 / 2];
+
+	unsigned			active:1;
+	unsigned			old_active:1;
+	unsigned			resuming:1;
+};
+
+struct dummy {
+	spinlock_t			lock;
+
+	/*
+	 * SLAVE/GADGET side support
+	 */
+	struct dummy_ep			ep[DUMMY_ENDPOINTS];
+	int				address;
+	int				callback_usage;
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct dummy_request		fifo_req;
+	u8				fifo_buf[FIFO_SIZE];
+	u16				devstatus;
+	unsigned			ints_enabled:1;
+	unsigned			udc_suspended:1;
+	unsigned			pullup:1;
+
+	/*
+	 * MASTER/HOST side support
+	 */
+	struct dummy_hcd		*hs_hcd;
+	struct dummy_hcd		*ss_hcd;
+};
+
+static inline struct dummy_hcd *hcd_to_dummy_hcd(struct usb_hcd *hcd)
+{
+	return (struct dummy_hcd *) (hcd->hcd_priv);
+}
+
+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_hcd *dum)
+{
+	return dummy_hcd_to_hcd(dum)->self.controller;
+}
+
+static inline struct device *udc_dev(struct dummy *dum)
+{
+	return dum->gadget.dev.parent;
+}
+
+static inline struct dummy *ep_to_dummy(struct dummy_ep *ep)
+{
+	return container_of(ep->gadget, struct dummy, gadget);
+}
+
+static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *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)
+{
+	return container_of(dev, struct dummy, gadget.dev);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* SLAVE/GADGET SIDE UTILITY ROUTINES */
+
+/* called with spinlock held */
+static void nuke(struct dummy *dum, struct dummy_ep *ep)
+{
+	while (!list_empty(&ep->queue)) {
+		struct dummy_request	*req;
+
+		req = list_entry(ep->queue.next, struct dummy_request, queue);
+		list_del_init(&req->queue);
+		req->req.status = -ESHUTDOWN;
+
+		spin_unlock(&dum->lock);
+		usb_gadget_giveback_request(&ep->ep, &req->req);
+		spin_lock(&dum->lock);
+	}
+}
+
+/* caller must hold lock */
+static void stop_activity(struct dummy *dum)
+{
+	int i;
+
+	/* prevent any more requests */
+	dum->address = 0;
+
+	/* The timer is left running so that outstanding URBs can fail */
+
+	/* nuke any pending requests first, so driver i/o is quiesced */
+	for (i = 0; i < DUMMY_ENDPOINTS; ++i)
+		nuke(dum, &dum->ep[i]);
+
+	/* driver now does any non-usb quiescing necessary */
+}
+
+/**
+ * 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) &&
+			    (dum_hcd->port_status &
+			     USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_U0 &&
+			    dum_hcd->rh_state != DUMMY_RH_SUSPENDED)
+				dum_hcd->active = 1;
+		}
+	} else {
+		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;
+	unsigned int power_bit;
+
+	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;
+
+	set_link_state_by_speed(dum_hcd);
+	power_bit = (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 ?
+			USB_SS_PORT_STAT_POWER : USB_PORT_STAT_POWER);
+
+	if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
+	     dum_hcd->active)
+		dum_hcd->resuming = 0;
+
+	/* Currently !connected or in reset */
+	if ((dum_hcd->port_status & power_bit) == 0 ||
+			(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
+		unsigned int disconnect = power_bit &
+				dum_hcd->old_status & (~dum_hcd->port_status);
+		unsigned int reset = USB_PORT_STAT_RESET &
+				(~dum_hcd->old_status) & dum_hcd->port_status;
+
+		/* Report reset and disconnect events to the driver */
+		if (dum->ints_enabled && (disconnect || reset)) {
+			stop_activity(dum);
+			++dum->callback_usage;
+			spin_unlock(&dum->lock);
+			if (reset)
+				usb_gadget_udc_reset(&dum->gadget, dum->driver);
+			else
+				dum->driver->disconnect(&dum->gadget);
+			spin_lock(&dum->lock);
+			--dum->callback_usage;
+		}
+	} else if (dum_hcd->active != dum_hcd->old_active &&
+			dum->ints_enabled) {
+		++dum->callback_usage;
+		spin_unlock(&dum->lock);
+		if (dum_hcd->old_active && dum->driver->suspend)
+			dum->driver->suspend(&dum->gadget);
+		else if (!dum_hcd->old_active &&  dum->driver->resume)
+			dum->driver->resume(&dum->gadget);
+		spin_lock(&dum->lock);
+		--dum->callback_usage;
+	}
+
+	dum_hcd->old_status = dum_hcd->port_status;
+	dum_hcd->old_active = dum_hcd->active;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* SLAVE/GADGET SIDE DRIVER
+ *
+ * This only tracks gadget state.  All the work is done when the host
+ * side tries some (emulated) i/o operation.  Real device controller
+ * drivers would do real i/o using dma, fifos, irqs, timers, etc.
+ */
+
+#define is_enabled(dum) \
+	(dum->port_status & USB_PORT_STAT_ENABLE)
+
+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;
+
+	ep = usb_ep_to_dummy_ep(_ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dum = ep_to_dummy(ep);
+	if (!dum->driver)
+		return -ESHUTDOWN;
+
+	dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
+	if (!is_enabled(dum_hcd))
+		return -ESHUTDOWN;
+
+	/*
+	 * 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 = usb_endpoint_maxp(desc);
+
+	/* drivers must not request bad settings, since lower levels
+	 * (hardware or its drivers) may not check.  some endpoints
+	 * can't do iso, many have maxpacket limitations, etc.
+	 *
+	 * since this "hardware" driver is here to help debugging, we
+	 * have some extra sanity checks.  (there could be more though,
+	 * especially for "ep9out" style fixed function ones.)
+	 */
+	retval = -EINVAL;
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int")) {
+			goto done;
+		}
+		switch (dum->gadget.speed) {
+		case USB_SPEED_SUPER:
+			if (max == 1024)
+				break;
+			goto done;
+		case USB_SPEED_HIGH:
+			if (max == 512)
+				break;
+			goto done;
+		case USB_SPEED_FULL:
+			if (max == 8 || max == 16 || max == 32 || max == 64)
+				/* we'll fake any legal size */
+				break;
+			/* save a return statement */
+		default:
+			goto done;
+		}
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+			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;
+			/* save a return statement */
+			/* fall through */
+		case USB_SPEED_FULL:
+			if (max <= 64)
+				break;
+			/* save a return statement */
+			/* fall through */
+		default:
+			if (max <= 8)
+				break;
+			goto done;
+		}
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (strstr(ep->ep.name, "-bulk")
+				|| strstr(ep->ep.name, "-int"))
+			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;
+			/* save a return statement */
+			/* fall through */
+		case USB_SPEED_FULL:
+			if (max <= 1023)
+				break;
+			/* save a return statement */
+		default:
+			goto done;
+		}
+		break;
+	default:
+		/* few chips support control except on ep0 */
+		goto done;
+	}
+
+	_ep->maxpacket = max;
+	if (usb_ss_max_streams(_ep->comp_desc)) {
+		if (!usb_endpoint_xfer_bulk(desc)) {
+			dev_err(udc_dev(dum), "Can't enable stream support on "
+					"non-bulk ep %s\n", _ep->name);
+			return -EINVAL;
+		}
+		ep->stream_en = 1;
+	}
+	ep->desc = desc;
+
+	dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n",
+		_ep->name,
+		desc->bEndpointAddress & 0x0f,
+		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+		({ char *val;
+		 switch (usb_endpoint_type(desc)) {
+		 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, ep->stream_en ? "enabled" : "disabled");
+
+	/* at this point real hardware should be NAKing transfers
+	 * to that endpoint, until a buffer is queued to it.
+	 */
+	ep->halted = ep->wedged = 0;
+	retval = 0;
+done:
+	return retval;
+}
+
+static int dummy_disable(struct usb_ep *_ep)
+{
+	struct dummy_ep		*ep;
+	struct dummy		*dum;
+	unsigned long		flags;
+
+	ep = usb_ep_to_dummy_ep(_ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name)
+		return -EINVAL;
+	dum = ep_to_dummy(ep);
+
+	spin_lock_irqsave(&dum->lock, flags);
+	ep->desc = NULL;
+	ep->stream_en = 0;
+	nuke(dum, ep);
+	spin_unlock_irqrestore(&dum->lock, flags);
+
+	dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name);
+	return 0;
+}
+
+static struct usb_request *dummy_alloc_request(struct usb_ep *_ep,
+		gfp_t mem_flags)
+{
+	struct dummy_request	*req;
+
+	if (!_ep)
+		return NULL;
+
+	req = kzalloc(sizeof(*req), mem_flags);
+	if (!req)
+		return NULL;
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct dummy_request	*req;
+
+	if (!_ep || !_req) {
+		WARN_ON(1);
+		return;
+	}
+
+	req = usb_request_to_dummy_request(_req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+static void fifo_complete(struct usb_ep *ep, struct usb_request *req)
+{
+}
+
+static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
+		gfp_t mem_flags)
+{
+	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);
+	if (!_req || !list_empty(&req->queue) || !_req->complete)
+		return -EINVAL;
+
+	ep = usb_ep_to_dummy_ep(_ep);
+	if (!_ep || (!ep->desc && _ep->name != ep0name))
+		return -EINVAL;
+
+	dum = ep_to_dummy(ep);
+	dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
+	if (!dum->driver || !is_enabled(dum_hcd))
+		return -ESHUTDOWN;
+
+#if 0
+	dev_dbg(udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
+			ep, _req, _ep->name, _req->length, _req->buf);
+#endif
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	spin_lock_irqsave(&dum->lock, flags);
+
+	/* implement an emulated single-request FIFO */
+	if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+			list_empty(&dum->fifo_req.queue) &&
+			list_empty(&ep->queue) &&
+			_req->length <= FIFO_SIZE) {
+		req = &dum->fifo_req;
+		req->req = *_req;
+		req->req.buf = dum->fifo_buf;
+		memcpy(dum->fifo_buf, _req->buf, _req->length);
+		req->req.context = dum;
+		req->req.complete = fifo_complete;
+
+		list_add_tail(&req->queue, &ep->queue);
+		spin_unlock(&dum->lock);
+		_req->actual = _req->length;
+		_req->status = 0;
+		usb_gadget_giveback_request(_ep, _req);
+		spin_lock(&dum->lock);
+	}  else
+		list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&dum->lock, flags);
+
+	/* real hardware would likely enable transfers here, in case
+	 * it'd been left NAKing.
+	 */
+	return 0;
+}
+
+static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct dummy_ep		*ep;
+	struct dummy		*dum;
+	int			retval = -EINVAL;
+	unsigned long		flags;
+	struct dummy_request	*req = NULL;
+
+	if (!_ep || !_req)
+		return retval;
+	ep = usb_ep_to_dummy_ep(_ep);
+	dum = ep_to_dummy(ep);
+
+	if (!dum->driver)
+		return -ESHUTDOWN;
+
+	local_irq_save(flags);
+	spin_lock(&dum->lock);
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init(&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+	spin_unlock(&dum->lock);
+
+	if (retval == 0) {
+		dev_dbg(udc_dev(dum),
+				"dequeued req %p from %s, len %d buf %p\n",
+				req, _ep->name, _req->length, _req->buf);
+		usb_gadget_giveback_request(_ep, _req);
+	}
+	local_irq_restore(flags);
+	return retval;
+}
+
+static int
+dummy_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+	struct dummy_ep		*ep;
+	struct dummy		*dum;
+
+	if (!_ep)
+		return -EINVAL;
+	ep = usb_ep_to_dummy_ep(_ep);
+	dum = ep_to_dummy(ep);
+	if (!dum->driver)
+		return -ESHUTDOWN;
+	if (!value)
+		ep->halted = ep->wedged = 0;
+	else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+			!list_empty(&ep->queue))
+		return -EAGAIN;
+	else {
+		ep->halted = 1;
+		if (wedged)
+			ep->wedged = 1;
+	}
+	/* FIXME clear emulated data toggle too */
+	return 0;
+}
+
+static int
+dummy_set_halt(struct usb_ep *_ep, int value)
+{
+	return dummy_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int dummy_set_wedge(struct usb_ep *_ep)
+{
+	if (!_ep || _ep->name == ep0name)
+		return -EINVAL;
+	return dummy_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static const struct usb_ep_ops dummy_ep_ops = {
+	.enable		= dummy_enable,
+	.disable	= dummy_disable,
+
+	.alloc_request	= dummy_alloc_request,
+	.free_request	= dummy_free_request,
+
+	.queue		= dummy_queue,
+	.dequeue	= dummy_dequeue,
+
+	.set_halt	= dummy_set_halt,
+	.set_wedge	= dummy_set_wedge,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* there are both host and device side versions of this call ... */
+static int dummy_g_get_frame(struct usb_gadget *_gadget)
+{
+	struct timespec64 ts64;
+
+	ktime_get_ts64(&ts64);
+	return ts64.tv_nsec / NSEC_PER_MSEC;
+}
+
+static int dummy_wakeup(struct usb_gadget *_gadget)
+{
+	struct dummy_hcd *dum_hcd;
+
+	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_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0)
+		return -ENOLINK;
+	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_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;
+}
+
+static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+	struct dummy	*dum;
+
+	_gadget->is_selfpowered = (value != 0);
+	dum = gadget_to_dummy_hcd(_gadget)->dum;
+	if (value)
+		dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
+	else
+		dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+	return 0;
+}
+
+static void dummy_udc_update_ep0(struct dummy *dum)
+{
+	if (dum->gadget.speed == USB_SPEED_SUPER)
+		dum->ep[0].ep.maxpacket = 9;
+	else
+		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_dev_to_dummy(&_gadget->dev);
+	dum_hcd = gadget_to_dummy_hcd(_gadget);
+
+	spin_lock_irqsave(&dum->lock, flags);
+	dum->pullup = (value != 0);
+	set_link_state(dum_hcd);
+	spin_unlock_irqrestore(&dum->lock, flags);
+
+	usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
+	return 0;
+}
+
+static void dummy_udc_set_speed(struct usb_gadget *_gadget,
+		enum usb_device_speed speed)
+{
+	struct dummy	*dum;
+
+	dum = gadget_dev_to_dummy(&_gadget->dev);
+	dum->gadget.speed = speed;
+	dummy_udc_update_ep0(dum);
+}
+
+static int dummy_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int dummy_udc_stop(struct usb_gadget *g);
+
+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,
+	.udc_set_speed	= dummy_udc_set_speed,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* "function" sysfs attribute */
+static ssize_t function_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct dummy	*dum = gadget_dev_to_dummy(dev);
+
+	if (!dum->driver || !dum->driver->function)
+		return 0;
+	return scnprintf(buf, PAGE_SIZE, "%s\n", dum->driver->function);
+}
+static DEVICE_ATTR_RO(function);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Driver registration/unregistration.
+ *
+ * This is basically hardware-specific; there's usually only one real USB
+ * device (not host) controller since that's how USB devices are intended
+ * to work.  So most implementations of these api calls will rely on the
+ * fact that only one driver will ever bind to the hardware.  But curious
+ * hardware can be built with discrete components, so the gadget API doesn't
+ * require that assumption.
+ *
+ * For this emulator, it might be convenient to create a usb slave device
+ * for each driver that registers:  just add to a big root hub.
+ */
+
+static int dummy_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct dummy_hcd	*dum_hcd = gadget_to_dummy_hcd(g);
+	struct dummy		*dum = dum_hcd->dum;
+
+	if (driver->max_speed == USB_SPEED_UNKNOWN)
+		return -EINVAL;
+
+	/*
+	 * SLAVE side init ... the layer above hardware, which
+	 * can't enumerate without help from the driver we're binding.
+	 */
+
+	spin_lock_irq(&dum->lock);
+	dum->devstatus = 0;
+	dum->driver = driver;
+	dum->ints_enabled = 1;
+	spin_unlock_irq(&dum->lock);
+
+	return 0;
+}
+
+static int dummy_udc_stop(struct usb_gadget *g)
+{
+	struct dummy_hcd	*dum_hcd = gadget_to_dummy_hcd(g);
+	struct dummy		*dum = dum_hcd->dum;
+
+	spin_lock_irq(&dum->lock);
+	dum->ints_enabled = 0;
+	stop_activity(dum);
+
+	/* emulate synchronize_irq(): wait for callbacks to finish */
+	while (dum->callback_usage > 0) {
+		spin_unlock_irq(&dum->lock);
+		usleep_range(1000, 2000);
+		spin_lock_irq(&dum->lock);
+	}
+
+	dum->driver = NULL;
+	spin_unlock_irq(&dum->lock);
+
+	return 0;
+}
+
+#undef is_enabled
+
+/* The gadget structure is stored inside the hcd structure and will be
+ * released along with it. */
+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_info[i].name)
+			break;
+		ep->ep.name = ep_info[i].name;
+		ep->ep.caps = ep_info[i].caps;
+		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;
+		usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+		ep->ep.max_streams = 16;
+		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);
+
+#ifdef CONFIG_USB_OTG
+	dum->gadget.is_otg = 1;
+#endif
+}
+
+static int dummy_udc_probe(struct platform_device *pdev)
+{
+	struct dummy	*dum;
+	int		rc;
+
+	dum = *((void **)dev_get_platdata(&pdev->dev));
+	/* Clear usb_gadget region for new registration to udc-core */
+	memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
+	dum->gadget.name = gadget_name;
+	dum->gadget.ops = &dummy_ops;
+	if (mod_data.is_super_speed)
+		dum->gadget.max_speed = USB_SPEED_SUPER;
+	else if (mod_data.is_high_speed)
+		dum->gadget.max_speed = USB_SPEED_HIGH;
+	else
+		dum->gadget.max_speed = USB_SPEED_FULL;
+
+	dum->gadget.dev.parent = &pdev->dev;
+	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)
+		goto err_dev;
+	platform_set_drvdata(pdev, dum);
+	return rc;
+
+err_dev:
+	usb_del_gadget_udc(&dum->gadget);
+err_udc:
+	return rc;
+}
+
+static int dummy_udc_remove(struct platform_device *pdev)
+{
+	struct dummy	*dum = platform_get_drvdata(pdev);
+
+	device_remove_file(&dum->gadget.dev, &dev_attr_function);
+	usb_del_gadget_udc(&dum->gadget);
+	return 0;
+}
+
+static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd,
+		int suspend)
+{
+	spin_lock_irq(&dum->lock);
+	dum->udc_suspended = suspend;
+	set_link_state(dum_hcd);
+	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);
+
+	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)
+{
+	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__);
+	dummy_udc_pm(dum, dum_hcd, 0);
+	usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
+	return 0;
+}
+
+static struct platform_driver dummy_udc_driver = {
+	.probe		= dummy_udc_probe,
+	.remove		= dummy_udc_remove,
+	.suspend	= dummy_udc_suspend,
+	.resume		= dummy_udc_resume,
+	.driver		= {
+		.name	= (char *) gadget_name,
+	},
+};
+
+/*-------------------------------------------------------------------------*/
+
+static unsigned int dummy_get_ep_idx(const struct usb_endpoint_descriptor *desc)
+{
+	unsigned int index;
+
+	index = usb_endpoint_num(desc) << 1;
+	if (usb_endpoint_dir_in(desc))
+		index |= 1;
+	return index;
+}
+
+/* MASTER/HOST SIDE DRIVER
+ *
+ * this uses the hcd framework to hook up to host side drivers.
+ * its root hub will only have one device, otherwise it acts like
+ * a normal host controller.
+ *
+ * when urbs are queued, they're just stuck on a list that we
+ * scan in a timer callback.  that callback connects writes from
+ * the host with reads from the device, and so on, based on the
+ * usb 2.0 rules.
+ */
+
+static int dummy_ep_stream_en(struct dummy_hcd *dum_hcd, struct urb *urb)
+{
+	const struct usb_endpoint_descriptor *desc = &urb->ep->desc;
+	u32 index;
+
+	if (!usb_endpoint_xfer_bulk(desc))
+		return 0;
+
+	index = dummy_get_ep_idx(desc);
+	return (1 << index) & dum_hcd->stream_en_ep;
+}
+
+/*
+ * The max stream number is saved as a nibble so for the 30 possible endpoints
+ * we only 15 bytes of memory. Therefore we are limited to max 16 streams (0
+ * means we use only 1 stream). The maximum according to the spec is 16bit so
+ * if the 16 stream limit is about to go, the array size should be incremented
+ * to 30 elements of type u16.
+ */
+static int get_max_streams_for_pipe(struct dummy_hcd *dum_hcd,
+		unsigned int pipe)
+{
+	int max_streams;
+
+	max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)];
+	if (usb_pipeout(pipe))
+		max_streams >>= 4;
+	else
+		max_streams &= 0xf;
+	max_streams++;
+	return max_streams;
+}
+
+static void set_max_streams_for_pipe(struct dummy_hcd *dum_hcd,
+		unsigned int pipe, unsigned int streams)
+{
+	int max_streams;
+
+	streams--;
+	max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)];
+	if (usb_pipeout(pipe)) {
+		streams <<= 4;
+		max_streams &= 0xf;
+	} else {
+		max_streams &= 0xf0;
+	}
+	max_streams |= streams;
+	dum_hcd->num_stream[usb_pipeendpoint(pipe)] = max_streams;
+}
+
+static int dummy_validate_stream(struct dummy_hcd *dum_hcd, struct urb *urb)
+{
+	unsigned int max_streams;
+	int enabled;
+
+	enabled = dummy_ep_stream_en(dum_hcd, urb);
+	if (!urb->stream_id) {
+		if (enabled)
+			return -EINVAL;
+		return 0;
+	}
+	if (!enabled)
+		return -EINVAL;
+
+	max_streams = get_max_streams_for_pipe(dum_hcd,
+			usb_pipeendpoint(urb->pipe));
+	if (urb->stream_id > max_streams) {
+		dev_err(dummy_dev(dum_hcd), "Stream id %d is out of range.\n",
+				urb->stream_id);
+		BUG();
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dummy_urb_enqueue(
+	struct usb_hcd			*hcd,
+	struct urb			*urb,
+	gfp_t				mem_flags
+) {
+	struct dummy_hcd *dum_hcd;
+	struct urbp	*urbp;
+	unsigned long	flags;
+	int		rc;
+
+	urbp = kmalloc(sizeof *urbp, mem_flags);
+	if (!urbp)
+		return -ENOMEM;
+	urbp->urb = urb;
+	urbp->miter_started = 0;
+
+	dum_hcd = hcd_to_dummy_hcd(hcd);
+	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+
+	rc = dummy_validate_stream(dum_hcd, urb);
+	if (rc) {
+		kfree(urbp);
+		goto done;
+	}
+
+	rc = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (rc) {
+		kfree(urbp);
+		goto done;
+	}
+
+	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_hcd->urbp_list);
+	urb->hcpriv = urbp;
+	if (!dum_hcd->next_frame_urbp)
+		dum_hcd->next_frame_urbp = 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_hcd->timer))
+		mod_timer(&dum_hcd->timer, jiffies + 1);
+
+ done:
+	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_hcd *dum_hcd;
+	unsigned long	flags;
+	int		rc;
+
+	/* giveback happens automatically in timer callback,
+	 * so make sure the callback happens */
+	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_hcd->rh_state != DUMMY_RH_RUNNING &&
+			!list_empty(&dum_hcd->urbp_list))
+		mod_timer(&dum_hcd->timer, jiffies);
+
+	spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
+	return rc;
+}
+
+static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req,
+		u32 len)
+{
+	void *ubuf, *rbuf;
+	struct urbp *urbp = urb->hcpriv;
+	int to_host;
+	struct sg_mapping_iter *miter = &urbp->miter;
+	u32 trans = 0;
+	u32 this_sg;
+	bool next_sg;
+
+	to_host = usb_pipein(urb->pipe);
+	rbuf = req->req.buf + req->req.actual;
+
+	if (!urb->num_sgs) {
+		ubuf = urb->transfer_buffer + urb->actual_length;
+		if (to_host)
+			memcpy(ubuf, rbuf, len);
+		else
+			memcpy(rbuf, ubuf, len);
+		return len;
+	}
+
+	if (!urbp->miter_started) {
+		u32 flags = SG_MITER_ATOMIC;
+
+		if (to_host)
+			flags |= SG_MITER_TO_SG;
+		else
+			flags |= SG_MITER_FROM_SG;
+
+		sg_miter_start(miter, urb->sg, urb->num_sgs, flags);
+		urbp->miter_started = 1;
+	}
+	next_sg = sg_miter_next(miter);
+	if (next_sg == false) {
+		WARN_ON_ONCE(1);
+		return -EINVAL;
+	}
+	do {
+		ubuf = miter->addr;
+		this_sg = min_t(u32, len, miter->length);
+		miter->consumed = this_sg;
+		trans += this_sg;
+
+		if (to_host)
+			memcpy(ubuf, rbuf, this_sg);
+		else
+			memcpy(rbuf, ubuf, this_sg);
+		len -= this_sg;
+
+		if (!len)
+			break;
+		next_sg = sg_miter_next(miter);
+		if (next_sg == false) {
+			WARN_ON_ONCE(1);
+			return -EINVAL;
+		}
+
+		rbuf += this_sg;
+	} while (1);
+
+	sg_miter_stop(miter);
+	return trans;
+}
+
+/* transfer up to a frame's worth; caller must own lock */
+static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
+		struct dummy_ep *ep, int limit, int *status)
+{
+	struct dummy		*dum = dum_hcd->dum;
+	struct dummy_request	*req;
+	int			sent = 0;
+
+top:
+	/* if there's no request queued, the device is NAKing; return */
+	list_for_each_entry(req, &ep->queue, queue) {
+		unsigned	host_len, dev_len, len;
+		int		is_short, to_host;
+		int		rescan = 0;
+
+		if (dummy_ep_stream_en(dum_hcd, urb)) {
+			if ((urb->stream_id != req->req.stream_id))
+				continue;
+		}
+
+		/* 1..N packets of ep->ep.maxpacket each ... the last one
+		 * may be short (including zero length).
+		 *
+		 * writer can send a zlp explicitly (length 0) or implicitly
+		 * (length mod maxpacket zero, and 'zero' flag); they always
+		 * terminate reads.
+		 */
+		host_len = urb->transfer_buffer_length - urb->actual_length;
+		dev_len = req->req.length - req->req.actual;
+		len = min(host_len, dev_len);
+
+		/* FIXME update emulated data toggle too */
+
+		to_host = usb_pipein(urb->pipe);
+		if (unlikely(len == 0))
+			is_short = 1;
+		else {
+			/* not enough bandwidth left? */
+			if (limit < ep->ep.maxpacket && limit < len)
+				break;
+			len = min_t(unsigned, len, limit);
+			if (len == 0)
+				break;
+
+			/* send multiple of maxpacket first, then remainder */
+			if (len >= ep->ep.maxpacket) {
+				is_short = 0;
+				if (len % ep->ep.maxpacket)
+					rescan = 1;
+				len -= len % ep->ep.maxpacket;
+			} else {
+				is_short = 1;
+			}
+
+			len = dummy_perform_transfer(urb, req, len);
+
+			ep->last_io = jiffies;
+			if ((int)len < 0) {
+				req->req.status = len;
+			} else {
+				limit -= len;
+				sent += len;
+				urb->actual_length += len;
+				req->req.actual += len;
+			}
+		}
+
+		/* short packets terminate, maybe with overflow/underflow.
+		 * it's only really an error to write too much.
+		 *
+		 * partially filling a buffer optionally blocks queue advances
+		 * (so completion handlers can clean up the queue) but we don't
+		 * need to emulate such data-in-flight.
+		 */
+		if (is_short) {
+			if (host_len == dev_len) {
+				req->req.status = 0;
+				*status = 0;
+			} else if (to_host) {
+				req->req.status = 0;
+				if (dev_len > host_len)
+					*status = -EOVERFLOW;
+				else
+					*status = 0;
+			} else {
+				*status = 0;
+				if (host_len > dev_len)
+					req->req.status = -EOVERFLOW;
+				else
+					req->req.status = 0;
+			}
+
+		/*
+		 * many requests terminate without a short packet.
+		 * send a zlp if demanded by flags.
+		 */
+		} else {
+			if (req->req.length == req->req.actual) {
+				if (req->req.zero && to_host)
+					rescan = 1;
+				else
+					req->req.status = 0;
+			}
+			if (urb->transfer_buffer_length == urb->actual_length) {
+				if (urb->transfer_flags & URB_ZERO_PACKET &&
+				    !to_host)
+					rescan = 1;
+				else
+					*status = 0;
+			}
+		}
+
+		/* device side completion --> continuable */
+		if (req->req.status != -EINPROGRESS) {
+			list_del_init(&req->queue);
+
+			spin_unlock(&dum->lock);
+			usb_gadget_giveback_request(&ep->ep, &req->req);
+			spin_lock(&dum->lock);
+
+			/* requests might have been unlinked... */
+			rescan = 1;
+		}
+
+		/* host side completion --> terminate */
+		if (*status != -EINPROGRESS)
+			break;
+
+		/* rescan to continue with any other queued i/o */
+		if (rescan)
+			goto top;
+	}
+	return sent;
+}
+
+static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep)
+{
+	int	limit = ep->ep.maxpacket;
+
+	if (dum->gadget.speed == USB_SPEED_HIGH) {
+		int	tmp;
+
+		/* high bandwidth mode */
+		tmp = usb_endpoint_maxp_mult(ep->desc);
+		tmp *= 8 /* applies to entire frame */;
+		limit += limit * tmp;
+	}
+	if (dum->gadget.speed == USB_SPEED_SUPER) {
+		switch (usb_endpoint_type(ep->desc)) {
+		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_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))
+
+static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address)
+{
+	int		i;
+
+	if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
+			dum->ss_hcd : dum->hs_hcd)))
+		return NULL;
+	if (!dum->ints_enabled)
+		return NULL;
+	if ((address & ~USB_DIR_IN) == 0)
+		return &dum->ep[0];
+	for (i = 1; i < DUMMY_ENDPOINTS; i++) {
+		struct dummy_ep	*ep = &dum->ep[i];
+
+		if (!ep->desc)
+			continue;
+		if (ep->desc->bEndpointAddress == address)
+			return ep;
+	}
+	return NULL;
+}
+
+#undef is_active
+
+#define Dev_Request	(USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+#define Dev_InRequest	(Dev_Request | USB_DIR_IN)
+#define Intf_Request	(USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
+#define Intf_InRequest	(Intf_Request | USB_DIR_IN)
+#define Ep_Request	(USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
+#define Ep_InRequest	(Ep_Request | USB_DIR_IN)
+
+
+/**
+ * handle_control_request() - handles all control transfers
+ * @dum: pointer to dummy (the_controller)
+ * @urb: the urb request to handle
+ * @setup: pointer to the setup data for a USB device control
+ *	 request
+ * @status: pointer to request handling status
+ *
+ * Return 0 - if the request was handled
+ *	  1 - if the request wasn't handles
+ *	  error code on error
+ */
+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;
+
+	w_index = le16_to_cpu(setup->wIndex);
+	w_value = le16_to_cpu(setup->wValue);
+	switch (setup->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (setup->bRequestType != Dev_Request)
+			break;
+		dum->address = w_value;
+		*status = 0;
+		dev_dbg(udc_dev(dum), "set_address = %d\n",
+				w_value);
+		ret_val = 0;
+		break;
+	case USB_REQ_SET_FEATURE:
+		if (setup->bRequestType == Dev_Request) {
+			ret_val = 0;
+			switch (w_value) {
+			case USB_DEVICE_REMOTE_WAKEUP:
+				break;
+			case USB_DEVICE_B_HNP_ENABLE:
+				dum->gadget.b_hnp_enable = 1;
+				break;
+			case USB_DEVICE_A_HNP_SUPPORT:
+				dum->gadget.a_hnp_support = 1;
+				break;
+			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;
+			}
+			if (ret_val == 0) {
+				dum->devstatus |= (1 << w_value);
+				*status = 0;
+			}
+		} else if (setup->bRequestType == Ep_Request) {
+			/* endpoint halt */
+			ep2 = find_endpoint(dum, w_index);
+			if (!ep2 || ep2->ep.name == ep0name) {
+				ret_val = -EOPNOTSUPP;
+				break;
+			}
+			ep2->halted = 1;
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		if (setup->bRequestType == Dev_Request) {
+			ret_val = 0;
+			switch (w_value) {
+			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;
+			}
+			if (ret_val == 0) {
+				dum->devstatus &= ~(1 << w_value);
+				*status = 0;
+			}
+		} else if (setup->bRequestType == Ep_Request) {
+			/* endpoint halt */
+			ep2 = find_endpoint(dum, w_index);
+			if (!ep2) {
+				ret_val = -EOPNOTSUPP;
+				break;
+			}
+			if (!ep2->wedged)
+				ep2->halted = 0;
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	case USB_REQ_GET_STATUS:
+		if (setup->bRequestType == Dev_InRequest
+				|| setup->bRequestType == Intf_InRequest
+				|| setup->bRequestType == Ep_InRequest) {
+			char *buf;
+			/*
+			 * device: remote wakeup, selfpowered
+			 * interface: nothing
+			 * endpoint: halt
+			 */
+			buf = (char *)urb->transfer_buffer;
+			if (urb->transfer_buffer_length > 0) {
+				if (setup->bRequestType == Ep_InRequest) {
+					ep2 = find_endpoint(dum, w_index);
+					if (!ep2) {
+						ret_val = -EOPNOTSUPP;
+						break;
+					}
+					buf[0] = ep2->halted;
+				} else if (setup->bRequestType ==
+					   Dev_InRequest) {
+					buf[0] = (u8)dum->devstatus;
+				} else
+					buf[0] = 0;
+			}
+			if (urb->transfer_buffer_length > 1)
+				buf[1] = 0;
+			urb->actual_length = min_t(u32, 2,
+				urb->transfer_buffer_length);
+			ret_val = 0;
+			*status = 0;
+		}
+		break;
+	}
+	return ret_val;
+}
+
+/* drive both sides of the transfers; looks like irq handlers to
+ * both drivers except the callbacks aren't in_irq().
+ */
+static void dummy_timer(struct timer_list *t)
+{
+	struct dummy_hcd	*dum_hcd = from_timer(dum_hcd, t, timer);
+	struct dummy		*dum = dum_hcd->dum;
+	struct urbp		*urbp, *tmp;
+	unsigned long		flags;
+	int			limit, total;
+	int			i;
+
+	/* simplistic model for one frame's bandwidth */
+	/* FIXME: account for transaction and packet overhead */
+	switch (dum->gadget.speed) {
+	case USB_SPEED_LOW:
+		total = 8/*bytes*/ * 12/*packets*/;
+		break;
+	case USB_SPEED_FULL:
+		total = 64/*bytes*/ * 19/*packets*/;
+		break;
+	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_hcd), "bogus device speed\n");
+		return;
+	}
+
+	/* FIXME if HZ != 1000 this will probably misbehave ... */
+
+	/* look at each urb queued by the host side driver */
+	spin_lock_irqsave(&dum->lock, flags);
+
+	if (!dum_hcd->udev) {
+		dev_err(dummy_dev(dum_hcd),
+				"timer fired with no URBs pending?\n");
+		spin_unlock_irqrestore(&dum->lock, flags);
+		return;
+	}
+	dum_hcd->next_frame_urbp = NULL;
+
+	for (i = 0; i < DUMMY_ENDPOINTS; i++) {
+		if (!ep_info[i].name)
+			break;
+		dum->ep[i].already_seen = 0;
+	}
+
+restart:
+	list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) {
+		struct urb		*urb;
+		struct dummy_request	*req;
+		u8			address;
+		struct dummy_ep		*ep = NULL;
+		int			status = -EINPROGRESS;
+
+		/* stop when we reach URBs queued after the timer interrupt */
+		if (urbp == dum_hcd->next_frame_urbp)
+			break;
+
+		urb = urbp->urb;
+		if (urb->unlinked)
+			goto return_urb;
+		else if (dum_hcd->rh_state != DUMMY_RH_RUNNING)
+			continue;
+
+		/* Used up this frame's bandwidth? */
+		if (total <= 0)
+			break;
+
+		/* find the gadget's ep for this request (if configured) */
+		address = usb_pipeendpoint (urb->pipe);
+		if (usb_pipein(urb->pipe))
+			address |= USB_DIR_IN;
+		ep = find_endpoint(dum, address);
+		if (!ep) {
+			/* set_configuration() disagreement */
+			dev_dbg(dummy_dev(dum_hcd),
+				"no ep configured for urb %p\n",
+				urb);
+			status = -EPROTO;
+			goto return_urb;
+		}
+
+		if (ep->already_seen)
+			continue;
+		ep->already_seen = 1;
+		if (ep == &dum->ep[0] && urb->error_count) {
+			ep->setup_stage = 1;	/* a new urb */
+			urb->error_count = 0;
+		}
+		if (ep->halted && !ep->setup_stage) {
+			/* NOTE: must not be iso! */
+			dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n",
+					ep->ep.name, urb);
+			status = -EPIPE;
+			goto return_urb;
+		}
+		/* FIXME make sure both ends agree on maxpacket */
+
+		/* handle control requests */
+		if (ep == &dum->ep[0] && ep->setup_stage) {
+			struct usb_ctrlrequest		setup;
+			int				value = 1;
+
+			setup = *(struct usb_ctrlrequest *) urb->setup_packet;
+			/* paranoia, in case of stale queued data */
+			list_for_each_entry(req, &ep->queue, queue) {
+				list_del_init(&req->queue);
+				req->req.status = -EOVERFLOW;
+				dev_dbg(udc_dev(dum), "stale req = %p\n",
+						req);
+
+				spin_unlock(&dum->lock);
+				usb_gadget_giveback_request(&ep->ep, &req->req);
+				spin_lock(&dum->lock);
+				ep->already_seen = 0;
+				goto restart;
+			}
+
+			/* gadget driver never sees set_address or operations
+			 * on standard feature flags.  some hardware doesn't
+			 * even expose them.
+			 */
+			ep->last_io = jiffies;
+			ep->setup_stage = 0;
+			ep->halted = 0;
+
+			value = handle_control_request(dum_hcd, urb, &setup,
+						       &status);
+
+			/* gadget driver handles all other requests.  block
+			 * until setup() returns; no reentrancy issues etc.
+			 */
+			if (value > 0) {
+				++dum->callback_usage;
+				spin_unlock(&dum->lock);
+				value = dum->driver->setup(&dum->gadget,
+						&setup);
+				spin_lock(&dum->lock);
+				--dum->callback_usage;
+
+				if (value >= 0) {
+					/* no delays (max 64KB data stage) */
+					limit = 64*1024;
+					goto treat_control_like_bulk;
+				}
+				/* error, see below */
+			}
+
+			if (value < 0) {
+				if (value != -EOPNOTSUPP)
+					dev_dbg(udc_dev(dum),
+						"setup --> %d\n",
+						value);
+				status = -EPIPE;
+				urb->actual_length = 0;
+			}
+
+			goto return_urb;
+		}
+
+		/* non-control requests */
+		limit = total;
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_ISOCHRONOUS:
+			/*
+			 * We don't support isochronous.  But if we did,
+			 * here are some of the issues we'd have to face:
+			 *
+			 * Is it urb->interval since the last xfer?
+			 * Use urb->iso_frame_desc[i].
+			 * Complete whether or not ep has requests queued.
+			 * Report random errors, to debug drivers.
+			 */
+			limit = max(limit, periodic_bytes(dum, ep));
+			status = -EINVAL;	/* fail all xfers */
+			break;
+
+		case PIPE_INTERRUPT:
+			/* FIXME is it urb->interval since the last xfer?
+			 * this almost certainly polls too fast.
+			 */
+			limit = max(limit, periodic_bytes(dum, ep));
+			/* FALLTHROUGH */
+
+		default:
+treat_control_like_bulk:
+			ep->last_io = jiffies;
+			total -= transfer(dum_hcd, urb, ep, limit, &status);
+			break;
+		}
+
+		/* incomplete transfer? */
+		if (status == -EINPROGRESS)
+			continue;
+
+return_urb:
+		list_del(&urbp->urbp_list);
+		kfree(urbp);
+		if (ep)
+			ep->already_seen = ep->setup_stage = 0;
+
+		usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb);
+		spin_unlock(&dum->lock);
+		usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status);
+		spin_lock(&dum->lock);
+
+		goto restart;
+	}
+
+	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_hcd->timer, jiffies + msecs_to_jiffies(1));
+	}
+
+	spin_unlock_irqrestore(&dum->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define PORT_C_MASK \
+	((USB_PORT_STAT_C_CONNECTION \
+	| USB_PORT_STAT_C_ENABLE \
+	| USB_PORT_STAT_C_SUSPEND \
+	| USB_PORT_STAT_C_OVERCURRENT \
+	| USB_PORT_STAT_C_RESET) << 16)
+
+static int dummy_hub_status(struct usb_hcd *hcd, char *buf)
+{
+	struct dummy_hcd	*dum_hcd;
+	unsigned long		flags;
+	int			retval = 0;
+
+	dum_hcd = hcd_to_dummy_hcd(hcd);
+
+	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+	if (!HCD_HW_ACCESSIBLE(hcd))
+		goto done;
+
+	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_hcd->port_status & PORT_C_MASK) != 0) {
+		*buf = (1 << 1);
+		dev_dbg(dummy_dev(dum_hcd), "port status 0x%08x has changes\n",
+				dum_hcd->port_status);
+		retval = 1;
+		if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED)
+			usb_hcd_resume_root_hub(hcd);
+	}
+done:
+	spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
+	return retval;
+}
+
+/* usb 3.0 root hub device descriptor */
+static struct {
+	struct usb_bos_descriptor bos;
+	struct usb_ss_cap_descriptor ss_cap;
+} __packed usb3_bos_desc = {
+
+	.bos = {
+		.bLength		= USB_DT_BOS_SIZE,
+		.bDescriptorType	= USB_DT_BOS,
+		.wTotalLength		= cpu_to_le16(sizeof(usb3_bos_desc)),
+		.bNumDeviceCaps		= 1,
+	},
+	.ss_cap = {
+		.bLength		= USB_DT_USB_SS_CAP_SIZE,
+		.bDescriptorType	= USB_DT_DEVICE_CAPABILITY,
+		.bDevCapabilityType	= USB_SS_CAP_TYPE,
+		.wSpeedSupported	= cpu_to_le16(USB_5GBPS_OPERATION),
+		.bFunctionalitySupport	= ilog2(USB_5GBPS_OPERATION),
+	},
+};
+
+static inline void
+ss_hub_descriptor(struct usb_hub_descriptor *desc)
+{
+	memset(desc, 0, sizeof *desc);
+	desc->bDescriptorType = USB_DT_SS_HUB;
+	desc->bDescLength = 12;
+	desc->wHubCharacteristics = cpu_to_le16(
+			HUB_CHAR_INDV_PORT_LPSM |
+			HUB_CHAR_COMMON_OCPM);
+	desc->bNbrPorts = 1;
+	desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
+	desc->u.ss.DeviceRemovable = 0;
+}
+
+static inline void hub_descriptor(struct usb_hub_descriptor *desc)
+{
+	memset(desc, 0, sizeof *desc);
+	desc->bDescriptorType = USB_DT_HUB;
+	desc->bDescLength = 9;
+	desc->wHubCharacteristics = cpu_to_le16(
+			HUB_CHAR_INDV_PORT_LPSM |
+			HUB_CHAR_COMMON_OCPM);
+	desc->bNbrPorts = 1;
+	desc->u.hs.DeviceRemovable[0] = 0;
+	desc->u.hs.DeviceRemovable[1] = 0xff;	/* PortPwrCtrlMask */
+}
+
+static int dummy_hub_control(
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+) {
+	struct dummy_hcd *dum_hcd;
+	int		retval = 0;
+	unsigned long	flags;
+
+	if (!HCD_HW_ACCESSIBLE(hcd))
+		return -ETIMEDOUT;
+
+	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 (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_hcd->resuming = 1;
+				dum_hcd->re_timeout = jiffies +
+						msecs_to_jiffies(20);
+			}
+			break;
+		case USB_PORT_FEAT_POWER:
+			dev_dbg(dummy_dev(dum_hcd), "power-off\n");
+			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;
+		default:
+			dum_hcd->port_status &= ~(1 << wValue);
+			set_link_state(dum_hcd);
+		}
+		break;
+	case GetHubDescriptor:
+		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 DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		if (hcd->speed != HCD_USB3)
+			goto error;
+
+		if ((wValue >> 8) != USB_DT_BOS)
+			goto error;
+
+		memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc));
+		retval = sizeof(usb3_bos_desc);
+		break;
+
+	case GetHubStatus:
+		*(__le32 *) buf = cpu_to_le32(0);
+		break;
+	case GetPortStatus:
+		if (wIndex != 1)
+			retval = -EPIPE;
+
+		/* whoever resets or resumes must GetPortStatus to
+		 * complete it!!
+		 */
+		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_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:
+						break;
+					}
+				}
+			}
+		}
+		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:
+			/* 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_hcd);
+				if (((1 << USB_DEVICE_B_HNP_ENABLE)
+						& dum_hcd->dum->devstatus) != 0)
+					dev_dbg(dummy_dev(dum_hcd),
+							"no HNP yet!\n");
+			}
+			break;
+		case USB_PORT_FEAT_POWER:
+			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 */
+			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);
+			/*
+			 * 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 (hcd->speed == HCD_USB3) {
+				if ((dum_hcd->port_status &
+				     USB_SS_PORT_STAT_POWER) != 0) {
+					dum_hcd->port_status |= (1 << wValue);
+				}
+			} 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_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_hcd->dum->lock, flags);
+
+	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_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+
+	dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
+
+	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_hcd->dum->lock);
+	return 0;
+}
+
+static int dummy_bus_resume(struct usb_hcd *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_hcd->dum->lock);
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		rc = -ESHUTDOWN;
+	} else {
+		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_hcd->dum->lock);
+	return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
+{
+	int ep = usb_pipeendpoint(urb->pipe);
+
+	return scnprintf(buf, size,
+		"urb/%p %s ep%d%s%s len %d/%d\n",
+		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;
+		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; \
+		} s; }),
+		urb->actual_length, urb->transfer_buffer_length);
+}
+
+static ssize_t urbs_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usb_hcd		*hcd = dev_get_drvdata(dev);
+	struct dummy_hcd	*dum_hcd = hcd_to_dummy_hcd(hcd);
+	struct urbp		*urbp;
+	size_t			size = 0;
+	unsigned long		flags;
+
+	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_hcd->dum->lock, flags);
+
+	return size;
+}
+static DEVICE_ATTR_RO(urbs);
+
+static int dummy_start_ss(struct dummy_hcd *dum_hcd)
+{
+	timer_setup(&dum_hcd->timer, dummy_timer, 0);
+	dum_hcd->rh_state = DUMMY_RH_RUNNING;
+	dum_hcd->stream_en_ep = 0;
+	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);
+}
+
+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.
+	 */
+	if (!usb_hcd_is_primary_hcd(hcd))
+		return dummy_start_ss(dum_hcd);
+
+	spin_lock_init(&dum_hcd->dum->lock);
+	timer_setup(&dum_hcd->timer, dummy_timer, 0);
+	dum_hcd->rh_state = DUMMY_RH_RUNNING;
+
+	INIT_LIST_HEAD(&dum_hcd->urbp_list);
+
+	hcd->power_budget = POWER_BUDGET;
+	hcd->state = HC_STATE_RUNNING;
+	hcd->uses_new_polling = 1;
+
+#ifdef CONFIG_USB_OTG
+	hcd->self.otg_port = 1;
+#endif
+
+	/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
+	return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs);
+}
+
+static void dummy_stop(struct usb_hcd *hcd)
+{
+	device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
+	dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int dummy_h_get_frame(struct usb_hcd *hcd)
+{
+	return dummy_g_get_frame(NULL);
+}
+
+static int dummy_setup(struct usb_hcd *hcd)
+{
+	struct dummy *dum;
+
+	dum = *((void **)dev_get_platdata(hcd->self.controller));
+	hcd->self.sg_tablesize = ~0;
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		dum->hs_hcd = hcd_to_dummy_hcd(hcd);
+		dum->hs_hcd->dum = dum;
+		/*
+		 * 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 {
+		dum->ss_hcd = hcd_to_dummy_hcd(hcd);
+		dum->ss_hcd->dum = dum;
+		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 */
+static 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)
+{
+	struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+	unsigned long flags;
+	int max_stream;
+	int ret_streams = num_streams;
+	unsigned int index;
+	unsigned int i;
+
+	if (!num_eps)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		if ((1 << index) & dum_hcd->stream_en_ep) {
+			ret_streams = -EINVAL;
+			goto out;
+		}
+		max_stream = usb_ss_max_streams(&eps[i]->ss_ep_comp);
+		if (!max_stream) {
+			ret_streams = -EINVAL;
+			goto out;
+		}
+		if (max_stream < ret_streams) {
+			dev_dbg(dummy_dev(dum_hcd), "Ep 0x%x only supports %u "
+					"stream IDs.\n",
+					eps[i]->desc.bEndpointAddress,
+					max_stream);
+			ret_streams = max_stream;
+		}
+	}
+
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		dum_hcd->stream_en_ep |= 1 << index;
+		set_max_streams_for_pipe(dum_hcd,
+				usb_endpoint_num(&eps[i]->desc), ret_streams);
+	}
+out:
+	spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
+	return ret_streams;
+}
+
+/* Reverts a group of bulk endpoints back to not using stream IDs. */
+static 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)
+{
+	struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+	unsigned long flags;
+	int ret;
+	unsigned int index;
+	unsigned int i;
+
+	spin_lock_irqsave(&dum_hcd->dum->lock, flags);
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		if (!((1 << index) & dum_hcd->stream_en_ep)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < num_eps; i++) {
+		index = dummy_get_ep_idx(&eps[i]->desc);
+		dum_hcd->stream_en_ep &= ~(1 << index);
+		set_max_streams_for_pipe(dum_hcd,
+				usb_endpoint_num(&eps[i]->desc), 0);
+	}
+	ret = 0;
+out:
+	spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
+	return ret;
+}
+
+static struct hc_driver dummy_hcd = {
+	.description =		(char *) driver_name,
+	.product_desc =		"Dummy host controller",
+	.hcd_priv_size =	sizeof(struct dummy_hcd),
+
+	.reset =		dummy_setup,
+	.start =		dummy_start,
+	.stop =			dummy_stop,
+
+	.urb_enqueue =		dummy_urb_enqueue,
+	.urb_dequeue =		dummy_urb_dequeue,
+
+	.get_frame_number =	dummy_h_get_frame,
+
+	.hub_status_data =	dummy_hub_status,
+	.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 dummy		*dum;
+	struct usb_hcd		*hs_hcd;
+	struct usb_hcd		*ss_hcd;
+	int			retval;
+
+	dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
+	dum = *((void **)dev_get_platdata(&pdev->dev));
+
+	if (mod_data.is_super_speed)
+		dummy_hcd.flags = HCD_USB3 | HCD_SHARED;
+	else if (mod_data.is_high_speed)
+		dummy_hcd.flags = HCD_USB2;
+	else
+		dummy_hcd.flags = HCD_USB11;
+	hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
+	if (!hs_hcd)
+		return -ENOMEM;
+	hs_hcd->has_tt = 1;
+
+	retval = usb_add_hcd(hs_hcd, 0, 0);
+	if (retval)
+		goto put_usb2_hcd;
+
+	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_remove_hcd(hs_hcd);
+put_usb2_hcd:
+	usb_put_hcd(hs_hcd);
+	dum->hs_hcd = dum->ss_hcd = NULL;
+	return retval;
+}
+
+static int dummy_hcd_remove(struct platform_device *pdev)
+{
+	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));
+
+	dum->hs_hcd = NULL;
+	dum->ss_hcd = NULL;
+
+	return 0;
+}
+
+static int dummy_hcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct usb_hcd		*hcd;
+	struct dummy_hcd	*dum_hcd;
+	int			rc = 0;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	hcd = platform_get_drvdata(pdev);
+	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
+		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	return rc;
+}
+
+static int dummy_hcd_resume(struct platform_device *pdev)
+{
+	struct usb_hcd		*hcd;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	hcd = platform_get_drvdata(pdev);
+	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+	usb_hcd_poll_rh_status(hcd);
+	return 0;
+}
+
+static struct platform_driver dummy_hcd_driver = {
+	.probe		= dummy_hcd_probe,
+	.remove		= dummy_hcd_remove,
+	.suspend	= dummy_hcd_suspend,
+	.resume		= dummy_hcd_resume,
+	.driver		= {
+		.name	= (char *) driver_name,
+	},
+};
+
+/*-------------------------------------------------------------------------*/
+#define MAX_NUM_UDC	2
+static struct platform_device *the_udc_pdev[MAX_NUM_UDC];
+static struct platform_device *the_hcd_pdev[MAX_NUM_UDC];
+
+static int __init init(void)
+{
+	int	retval = -ENOMEM;
+	int	i;
+	struct	dummy *dum[MAX_NUM_UDC];
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (!mod_data.is_high_speed && mod_data.is_super_speed)
+		return -EINVAL;
+
+	if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
+		pr_err("Number of emulated UDC must be in range of 1...%d\n",
+				MAX_NUM_UDC);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < mod_data.num; i++) {
+		the_hcd_pdev[i] = platform_device_alloc(driver_name, i);
+		if (!the_hcd_pdev[i]) {
+			i--;
+			while (i >= 0)
+				platform_device_put(the_hcd_pdev[i--]);
+			return retval;
+		}
+	}
+	for (i = 0; i < mod_data.num; i++) {
+		the_udc_pdev[i] = platform_device_alloc(gadget_name, i);
+		if (!the_udc_pdev[i]) {
+			i--;
+			while (i >= 0)
+				platform_device_put(the_udc_pdev[i--]);
+			goto err_alloc_udc;
+		}
+	}
+	for (i = 0; i < mod_data.num; i++) {
+		dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL);
+		if (!dum[i]) {
+			retval = -ENOMEM;
+			goto err_add_pdata;
+		}
+		retval = platform_device_add_data(the_hcd_pdev[i], &dum[i],
+				sizeof(void *));
+		if (retval)
+			goto err_add_pdata;
+		retval = platform_device_add_data(the_udc_pdev[i], &dum[i],
+				sizeof(void *));
+		if (retval)
+			goto err_add_pdata;
+	}
+
+	retval = platform_driver_register(&dummy_hcd_driver);
+	if (retval < 0)
+		goto err_add_pdata;
+	retval = platform_driver_register(&dummy_udc_driver);
+	if (retval < 0)
+		goto err_register_udc_driver;
+
+	for (i = 0; i < mod_data.num; i++) {
+		retval = platform_device_add(the_hcd_pdev[i]);
+		if (retval < 0) {
+			i--;
+			while (i >= 0)
+				platform_device_del(the_hcd_pdev[i--]);
+			goto err_add_hcd;
+		}
+	}
+	for (i = 0; i < mod_data.num; i++) {
+		if (!dum[i]->hs_hcd ||
+				(!dum[i]->ss_hcd && mod_data.is_super_speed)) {
+			/*
+			 * The hcd was added successfully but its probe
+			 * function failed for some reason.
+			 */
+			retval = -EINVAL;
+			goto err_add_udc;
+		}
+	}
+
+	for (i = 0; i < mod_data.num; i++) {
+		retval = platform_device_add(the_udc_pdev[i]);
+		if (retval < 0) {
+			i--;
+			while (i >= 0)
+				platform_device_del(the_udc_pdev[i--]);
+			goto err_add_udc;
+		}
+	}
+
+	for (i = 0; i < mod_data.num; i++) {
+		if (!platform_get_drvdata(the_udc_pdev[i])) {
+			/*
+			 * The udc was added successfully but its probe
+			 * function failed for some reason.
+			 */
+			retval = -EINVAL;
+			goto err_probe_udc;
+		}
+	}
+	return retval;
+
+err_probe_udc:
+	for (i = 0; i < mod_data.num; i++)
+		platform_device_del(the_udc_pdev[i]);
+err_add_udc:
+	for (i = 0; i < mod_data.num; i++)
+		platform_device_del(the_hcd_pdev[i]);
+err_add_hcd:
+	platform_driver_unregister(&dummy_udc_driver);
+err_register_udc_driver:
+	platform_driver_unregister(&dummy_hcd_driver);
+err_add_pdata:
+	for (i = 0; i < mod_data.num; i++)
+		kfree(dum[i]);
+	for (i = 0; i < mod_data.num; i++)
+		platform_device_put(the_udc_pdev[i]);
+err_alloc_udc:
+	for (i = 0; i < mod_data.num; i++)
+		platform_device_put(the_hcd_pdev[i]);
+	return retval;
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < mod_data.num; i++) {
+		struct dummy *dum;
+
+		dum = *((void **)dev_get_platdata(&the_udc_pdev[i]->dev));
+
+		platform_device_unregister(the_udc_pdev[i]);
+		platform_device_unregister(the_hcd_pdev[i]);
+		kfree(dum);
+	}
+	platform_driver_unregister(&dummy_udc_driver);
+	platform_driver_unregister(&dummy_hcd_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
new file mode 100644
index 0000000..587c503
--- /dev/null
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -0,0 +1,1216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FOTG210 UDC Driver supports Bulk transfer so far
+ *
+ * Copyright (C) 2013 Faraday Technology Corporation
+ *
+ * Author : Yuan-Hsin Chen <yhchen@faraday-tech.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "fotg210.h"
+
+#define	DRIVER_DESC	"FOTG210 USB Device Controller Driver"
+#define	DRIVER_VERSION	"30-April-2013"
+
+static const char udc_name[] = "fotg210_udc";
+static const char * const fotg210_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4"};
+
+static void fotg210_disable_fifo_int(struct fotg210_ep *ep)
+{
+	u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1);
+
+	if (ep->dir_in)
+		value |= DMISGR1_MF_IN_INT(ep->epnum - 1);
+	else
+		value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1);
+	iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1);
+}
+
+static void fotg210_enable_fifo_int(struct fotg210_ep *ep)
+{
+	u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1);
+
+	if (ep->dir_in)
+		value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1);
+	else
+		value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1);
+	iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1);
+}
+
+static void fotg210_set_cxdone(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_DCFESR);
+
+	value |= DCFESR_CX_DONE;
+	iowrite32(value, fotg210->reg + FOTG210_DCFESR);
+}
+
+static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req,
+			int status)
+{
+	list_del_init(&req->queue);
+
+	/* don't modify queue heads during completion callback */
+	if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	spin_unlock(&ep->fotg210->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&ep->fotg210->lock);
+
+	if (ep->epnum) {
+		if (list_empty(&ep->queue))
+			fotg210_disable_fifo_int(ep);
+	} else {
+		fotg210_set_cxdone(ep->fotg210);
+	}
+}
+
+static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum,
+				u32 dir_in)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 val;
+
+	/* Driver should map an ep to a fifo and then map the fifo
+	 * to the ep. What a brain-damaged design!
+	 */
+
+	/* map a fifo to an ep */
+	val = ioread32(fotg210->reg + FOTG210_EPMAP);
+	val &= ~EPMAP_FIFONOMSK(epnum, dir_in);
+	val |= EPMAP_FIFONO(epnum, dir_in);
+	iowrite32(val, fotg210->reg + FOTG210_EPMAP);
+
+	/* map the ep to the fifo */
+	val = ioread32(fotg210->reg + FOTG210_FIFOMAP);
+	val &= ~FIFOMAP_EPNOMSK(epnum);
+	val |= FIFOMAP_EPNO(epnum);
+	iowrite32(val, fotg210->reg + FOTG210_FIFOMAP);
+
+	/* enable fifo */
+	val = ioread32(fotg210->reg + FOTG210_FIFOCF);
+	val |= FIFOCF_FIFO_EN(epnum - 1);
+	iowrite32(val, fotg210->reg + FOTG210_FIFOCF);
+}
+
+static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 val;
+
+	val = ioread32(fotg210->reg + FOTG210_FIFOMAP);
+	val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1));
+	iowrite32(val, fotg210->reg + FOTG210_FIFOMAP);
+}
+
+static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 val;
+
+	val = ioread32(fotg210->reg + FOTG210_FIFOCF);
+	val |= FIFOCF_TYPE(type, epnum - 1);
+	iowrite32(val, fotg210->reg + FOTG210_FIFOCF);
+}
+
+static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps,
+				u32 dir_in)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 val;
+	u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) :
+				FOTG210_OUTEPMPSR(epnum);
+
+	val = ioread32(fotg210->reg + offset);
+	val |= INOUTEPMPSR_MPS(mps);
+	iowrite32(val, fotg210->reg + offset);
+}
+
+static int fotg210_config_ep(struct fotg210_ep *ep,
+		     const struct usb_endpoint_descriptor *desc)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+
+	fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in);
+	fotg210_set_tfrtype(ep, ep->epnum, ep->type);
+	fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in);
+	fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in);
+
+	fotg210->ep[ep->epnum] = ep;
+
+	return 0;
+}
+
+static int fotg210_ep_enable(struct usb_ep *_ep,
+			  const struct usb_endpoint_descriptor *desc)
+{
+	struct fotg210_ep *ep;
+
+	ep = container_of(_ep, struct fotg210_ep, ep);
+
+	ep->desc = desc;
+	ep->epnum = usb_endpoint_num(desc);
+	ep->type = usb_endpoint_type(desc);
+	ep->dir_in = usb_endpoint_dir_in(desc);
+	ep->ep.maxpacket = usb_endpoint_maxp(desc);
+
+	return fotg210_config_ep(ep, desc);
+}
+
+static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum)
+{
+	struct fotg210_ep *ep = fotg210->ep[epnum];
+	u32 value;
+	void __iomem *reg;
+
+	reg = (ep->dir_in) ?
+		fotg210->reg + FOTG210_INEPMPSR(epnum) :
+		fotg210->reg + FOTG210_OUTEPMPSR(epnum);
+
+	/* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ
+	 *	 bit. Controller wouldn't clear this bit. WTF!!!
+	 */
+
+	value = ioread32(reg);
+	value |= INOUTEPMPSR_RESET_TSEQ;
+	iowrite32(value, reg);
+
+	value = ioread32(reg);
+	value &= ~INOUTEPMPSR_RESET_TSEQ;
+	iowrite32(value, reg);
+}
+
+static int fotg210_ep_release(struct fotg210_ep *ep)
+{
+	if (!ep->epnum)
+		return 0;
+	ep->epnum = 0;
+	ep->stall = 0;
+	ep->wedged = 0;
+
+	fotg210_reset_tseq(ep->fotg210, ep->epnum);
+
+	return 0;
+}
+
+static int fotg210_ep_disable(struct usb_ep *_ep)
+{
+	struct fotg210_ep *ep;
+	struct fotg210_request *req;
+	unsigned long flags;
+
+	BUG_ON(!_ep);
+
+	ep = container_of(_ep, struct fotg210_ep, ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+			struct fotg210_request, queue);
+		spin_lock_irqsave(&ep->fotg210->lock, flags);
+		fotg210_done(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+	}
+
+	return fotg210_ep_release(ep);
+}
+
+static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct fotg210_request *req;
+
+	req = kzalloc(sizeof(struct fotg210_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void fotg210_ep_free_request(struct usb_ep *_ep,
+					struct usb_request *_req)
+{
+	struct fotg210_request *req;
+
+	req = container_of(_req, struct fotg210_request, req);
+	kfree(req);
+}
+
+static void fotg210_enable_dma(struct fotg210_ep *ep,
+			      dma_addr_t d, u32 len)
+{
+	u32 value;
+	struct fotg210_udc *fotg210 = ep->fotg210;
+
+	/* set transfer length and direction */
+	value = ioread32(fotg210->reg + FOTG210_DMACPSR1);
+	value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1));
+	value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in);
+	iowrite32(value, fotg210->reg + FOTG210_DMACPSR1);
+
+	/* set device DMA target FIFO number */
+	value = ioread32(fotg210->reg + FOTG210_DMATFNR);
+	if (ep->epnum)
+		value |= DMATFNR_ACC_FN(ep->epnum - 1);
+	else
+		value |= DMATFNR_ACC_CXF;
+	iowrite32(value, fotg210->reg + FOTG210_DMATFNR);
+
+	/* set DMA memory address */
+	iowrite32(d, fotg210->reg + FOTG210_DMACPSR2);
+
+	/* enable MDMA_EROR and MDMA_CMPLT interrupt */
+	value = ioread32(fotg210->reg + FOTG210_DMISGR2);
+	value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR);
+	iowrite32(value, fotg210->reg + FOTG210_DMISGR2);
+
+	/* start DMA */
+	value = ioread32(fotg210->reg + FOTG210_DMACPSR1);
+	value |= DMACPSR1_DMA_START;
+	iowrite32(value, fotg210->reg + FOTG210_DMACPSR1);
+}
+
+static void fotg210_disable_dma(struct fotg210_ep *ep)
+{
+	iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR);
+}
+
+static void fotg210_wait_dma_done(struct fotg210_ep *ep)
+{
+	u32 value;
+
+	do {
+		value = ioread32(ep->fotg210->reg + FOTG210_DISGR2);
+		if ((value & DISGR2_USBRST_INT) ||
+		    (value & DISGR2_DMA_ERROR))
+			goto dma_reset;
+	} while (!(value & DISGR2_DMA_CMPLT));
+
+	value &= ~DISGR2_DMA_CMPLT;
+	iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2);
+	return;
+
+dma_reset:
+	value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1);
+	value |= DMACPSR1_DMA_ABORT;
+	iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1);
+
+	/* reset fifo */
+	if (ep->epnum) {
+		value = ioread32(ep->fotg210->reg +
+				FOTG210_FIBCR(ep->epnum - 1));
+		value |= FIBCR_FFRST;
+		iowrite32(value, ep->fotg210->reg +
+				FOTG210_FIBCR(ep->epnum - 1));
+	} else {
+		value = ioread32(ep->fotg210->reg + FOTG210_DCFESR);
+		value |= DCFESR_CX_CLR;
+		iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR);
+	}
+}
+
+static void fotg210_start_dma(struct fotg210_ep *ep,
+			struct fotg210_request *req)
+{
+	dma_addr_t d;
+	u8 *buffer;
+	u32 length;
+
+	if (ep->epnum) {
+		if (ep->dir_in) {
+			buffer = req->req.buf;
+			length = req->req.length;
+		} else {
+			buffer = req->req.buf + req->req.actual;
+			length = ioread32(ep->fotg210->reg +
+					FOTG210_FIBCR(ep->epnum - 1));
+			length &= FIBCR_BCFX;
+		}
+	} else {
+		buffer = req->req.buf + req->req.actual;
+		if (req->req.length - req->req.actual > ep->ep.maxpacket)
+			length = ep->ep.maxpacket;
+		else
+			length = req->req.length;
+	}
+
+	d = dma_map_single(NULL, buffer, length,
+			ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+	if (dma_mapping_error(NULL, d)) {
+		pr_err("dma_mapping_error\n");
+		return;
+	}
+
+	dma_sync_single_for_device(NULL, d, length,
+				   ep->dir_in ? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+
+	fotg210_enable_dma(ep, d, length);
+
+	/* check if dma is done */
+	fotg210_wait_dma_done(ep);
+
+	fotg210_disable_dma(ep);
+
+	/* update actual transfer length */
+	req->req.actual += length;
+
+	dma_unmap_single(NULL, d, length, DMA_TO_DEVICE);
+}
+
+static void fotg210_ep0_queue(struct fotg210_ep *ep,
+				struct fotg210_request *req)
+{
+	if (!req->req.length) {
+		fotg210_done(ep, req, 0);
+		return;
+	}
+	if (ep->dir_in) { /* if IN */
+		fotg210_start_dma(ep, req);
+		if ((req->req.length == req->req.actual) ||
+		    (req->req.actual < ep->ep.maxpacket))
+			fotg210_done(ep, req, 0);
+	} else { /* OUT */
+		u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0);
+
+		value &= ~DMISGR0_MCX_OUT_INT;
+		iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0);
+	}
+}
+
+static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				gfp_t gfp_flags)
+{
+	struct fotg210_ep *ep;
+	struct fotg210_request *req;
+	unsigned long flags;
+	int request = 0;
+
+	ep = container_of(_ep, struct fotg210_ep, ep);
+	req = container_of(_req, struct fotg210_request, req);
+
+	if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->fotg210->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (!ep->epnum) /* ep0 */
+		fotg210_ep0_queue(ep, req);
+	else if (request && !ep->stall)
+		fotg210_enable_fifo_int(ep);
+
+	spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+
+	return 0;
+}
+
+static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fotg210_ep *ep;
+	struct fotg210_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct fotg210_ep, ep);
+	req = container_of(_req, struct fotg210_request, req);
+
+	spin_lock_irqsave(&ep->fotg210->lock, flags);
+	if (!list_empty(&ep->queue))
+		fotg210_done(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+
+	return 0;
+}
+
+static void fotg210_set_epnstall(struct fotg210_ep *ep)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 value;
+	void __iomem *reg;
+
+	/* check if IN FIFO is empty before stall */
+	if (ep->dir_in) {
+		do {
+			value = ioread32(fotg210->reg + FOTG210_DCFESR);
+		} while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1)));
+	}
+
+	reg = (ep->dir_in) ?
+		fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+		fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+	value = ioread32(reg);
+	value |= INOUTEPMPSR_STL_EP;
+	iowrite32(value, reg);
+}
+
+static void fotg210_clear_epnstall(struct fotg210_ep *ep)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 value;
+	void __iomem *reg;
+
+	reg = (ep->dir_in) ?
+		fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+		fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+	value = ioread32(reg);
+	value &= ~INOUTEPMPSR_STL_EP;
+	iowrite32(value, reg);
+}
+
+static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
+{
+	struct fotg210_ep *ep;
+	struct fotg210_udc *fotg210;
+	unsigned long flags;
+	int ret = 0;
+
+	ep = container_of(_ep, struct fotg210_ep, ep);
+
+	fotg210 = ep->fotg210;
+
+	spin_lock_irqsave(&ep->fotg210->lock, flags);
+
+	if (value) {
+		fotg210_set_epnstall(ep);
+		ep->stall = 1;
+		if (wedge)
+			ep->wedged = 1;
+	} else {
+		fotg210_reset_tseq(fotg210, ep->epnum);
+		fotg210_clear_epnstall(ep);
+		ep->stall = 0;
+		ep->wedged = 0;
+		if (!list_empty(&ep->queue))
+			fotg210_enable_fifo_int(ep);
+	}
+
+	spin_unlock_irqrestore(&ep->fotg210->lock, flags);
+	return ret;
+}
+
+static int fotg210_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	return fotg210_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int fotg210_ep_set_wedge(struct usb_ep *_ep)
+{
+	return fotg210_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static void fotg210_ep_fifo_flush(struct usb_ep *_ep)
+{
+}
+
+static const struct usb_ep_ops fotg210_ep_ops = {
+	.enable		= fotg210_ep_enable,
+	.disable	= fotg210_ep_disable,
+
+	.alloc_request	= fotg210_ep_alloc_request,
+	.free_request	= fotg210_ep_free_request,
+
+	.queue		= fotg210_ep_queue,
+	.dequeue	= fotg210_ep_dequeue,
+
+	.set_halt	= fotg210_ep_set_halt,
+	.fifo_flush	= fotg210_ep_fifo_flush,
+	.set_wedge	= fotg210_ep_set_wedge,
+};
+
+static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE);
+
+	value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3
+		   | TX0BYTE_EP4);
+	iowrite32(value, fotg210->reg + FOTG210_TX0BYTE);
+}
+
+static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE);
+
+	value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3
+		   | RX0BYTE_EP4);
+	iowrite32(value, fotg210->reg + FOTG210_RX0BYTE);
+}
+
+/* read 8-byte setup packet only */
+static void fotg210_rdsetupp(struct fotg210_udc *fotg210,
+		   u8 *buffer)
+{
+	int i = 0;
+	u8 *tmp = buffer;
+	u32 data;
+	u32 length = 8;
+
+	iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR);
+
+	for (i = (length >> 2); i > 0; i--) {
+		data = ioread32(fotg210->reg + FOTG210_CXPORT);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		*(tmp + 3) = (data >> 24) & 0xFF;
+		tmp = tmp + 4;
+	}
+
+	switch (length % 4) {
+	case 1:
+		data = ioread32(fotg210->reg + FOTG210_CXPORT);
+		*tmp = data & 0xFF;
+		break;
+	case 2:
+		data = ioread32(fotg210->reg + FOTG210_CXPORT);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		break;
+	case 3:
+		data = ioread32(fotg210->reg + FOTG210_CXPORT);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		break;
+	default:
+		break;
+	}
+
+	iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR);
+}
+
+static void fotg210_set_configuration(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_DAR);
+
+	value |= DAR_AFT_CONF;
+	iowrite32(value, fotg210->reg + FOTG210_DAR);
+}
+
+static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_DAR);
+
+	value |= (addr & 0x7F);
+	iowrite32(value, fotg210->reg + FOTG210_DAR);
+}
+
+static void fotg210_set_cxstall(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_DCFESR);
+
+	value |= DCFESR_CX_STL;
+	iowrite32(value, fotg210->reg + FOTG210_DCFESR);
+}
+
+static void fotg210_request_error(struct fotg210_udc *fotg210)
+{
+	fotg210_set_cxstall(fotg210);
+	pr_err("request error!!\n");
+}
+
+static void fotg210_set_address(struct fotg210_udc *fotg210,
+				struct usb_ctrlrequest *ctrl)
+{
+	if (ctrl->wValue >= 0x0100) {
+		fotg210_request_error(fotg210);
+	} else {
+		fotg210_set_dev_addr(fotg210, ctrl->wValue);
+		fotg210_set_cxdone(fotg210);
+	}
+}
+
+static void fotg210_set_feature(struct fotg210_udc *fotg210,
+				struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		fotg210_set_cxdone(fotg210);
+		break;
+	case USB_RECIP_INTERFACE:
+		fotg210_set_cxdone(fotg210);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		u8 epnum;
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum)
+			fotg210_set_epnstall(fotg210->ep[epnum]);
+		else
+			fotg210_set_cxstall(fotg210);
+		fotg210_set_cxdone(fotg210);
+		}
+		break;
+	default:
+		fotg210_request_error(fotg210);
+		break;
+	}
+}
+
+static void fotg210_clear_feature(struct fotg210_udc *fotg210,
+				struct usb_ctrlrequest *ctrl)
+{
+	struct fotg210_ep *ep =
+		fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		fotg210_set_cxdone(fotg210);
+		break;
+	case USB_RECIP_INTERFACE:
+		fotg210_set_cxdone(fotg210);
+		break;
+	case USB_RECIP_ENDPOINT:
+		if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
+			if (ep->wedged) {
+				fotg210_set_cxdone(fotg210);
+				break;
+			}
+			if (ep->stall)
+				fotg210_set_halt_and_wedge(&ep->ep, 0, 0);
+		}
+		fotg210_set_cxdone(fotg210);
+		break;
+	default:
+		fotg210_request_error(fotg210);
+		break;
+	}
+}
+
+static int fotg210_is_epnstall(struct fotg210_ep *ep)
+{
+	struct fotg210_udc *fotg210 = ep->fotg210;
+	u32 value;
+	void __iomem *reg;
+
+	reg = (ep->dir_in) ?
+		fotg210->reg + FOTG210_INEPMPSR(ep->epnum) :
+		fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum);
+	value = ioread32(reg);
+	return value & INOUTEPMPSR_STL_EP ? 1 : 0;
+}
+
+static void fotg210_get_status(struct fotg210_udc *fotg210,
+				struct usb_ctrlrequest *ctrl)
+{
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		fotg210->ep0_data = 1 << USB_DEVICE_SELF_POWERED;
+		break;
+	case USB_RECIP_INTERFACE:
+		fotg210->ep0_data = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum)
+			fotg210->ep0_data =
+				fotg210_is_epnstall(fotg210->ep[epnum])
+				<< USB_ENDPOINT_HALT;
+		else
+			fotg210_request_error(fotg210);
+		break;
+
+	default:
+		fotg210_request_error(fotg210);
+		return;		/* exit */
+	}
+
+	fotg210->ep0_req->buf = &fotg210->ep0_data;
+	fotg210->ep0_req->length = 2;
+
+	spin_unlock(&fotg210->lock);
+	fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL);
+	spin_lock(&fotg210->lock);
+}
+
+static int fotg210_setup_packet(struct fotg210_udc *fotg210,
+				struct usb_ctrlrequest *ctrl)
+{
+	u8 *p = (u8 *)ctrl;
+	u8 ret = 0;
+
+	fotg210_rdsetupp(fotg210, p);
+
+	fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN;
+
+	if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) {
+		u32 value = ioread32(fotg210->reg + FOTG210_DMCR);
+		fotg210->gadget.speed = value & DMCR_HS_EN ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+	}
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			fotg210_get_status(fotg210, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			fotg210_clear_feature(fotg210, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			fotg210_set_feature(fotg210, ctrl);
+			break;
+		case USB_REQ_SET_ADDRESS:
+			fotg210_set_address(fotg210, ctrl);
+			break;
+		case USB_REQ_SET_CONFIGURATION:
+			fotg210_set_configuration(fotg210);
+			ret = 1;
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else {
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static void fotg210_ep0out(struct fotg210_udc *fotg210)
+{
+	struct fotg210_ep *ep = fotg210->ep[0];
+
+	if (!list_empty(&ep->queue) && !ep->dir_in) {
+		struct fotg210_request *req;
+
+		req = list_first_entry(&ep->queue,
+			struct fotg210_request, queue);
+
+		if (req->req.length)
+			fotg210_start_dma(ep, req);
+
+		if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+			fotg210_done(ep, req, 0);
+	} else {
+		pr_err("%s : empty queue\n", __func__);
+	}
+}
+
+static void fotg210_ep0in(struct fotg210_udc *fotg210)
+{
+	struct fotg210_ep *ep = fotg210->ep[0];
+
+	if ((!list_empty(&ep->queue)) && (ep->dir_in)) {
+		struct fotg210_request *req;
+
+		req = list_entry(ep->queue.next,
+				struct fotg210_request, queue);
+
+		if (req->req.length)
+			fotg210_start_dma(ep, req);
+
+		if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+			fotg210_done(ep, req, 0);
+	} else {
+		fotg210_set_cxdone(fotg210);
+	}
+}
+
+static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210)
+{
+	u32 value = ioread32(fotg210->reg + FOTG210_DISGR0);
+
+	value &= ~DISGR0_CX_COMABT_INT;
+	iowrite32(value, fotg210->reg + FOTG210_DISGR0);
+}
+
+static void fotg210_in_fifo_handler(struct fotg210_ep *ep)
+{
+	struct fotg210_request *req = list_entry(ep->queue.next,
+					struct fotg210_request, queue);
+
+	if (req->req.length)
+		fotg210_start_dma(ep, req);
+	fotg210_done(ep, req, 0);
+}
+
+static void fotg210_out_fifo_handler(struct fotg210_ep *ep)
+{
+	struct fotg210_request *req = list_entry(ep->queue.next,
+						 struct fotg210_request, queue);
+
+	fotg210_start_dma(ep, req);
+
+	/* finish out transfer */
+	if (req->req.length == req->req.actual ||
+	    req->req.actual < ep->ep.maxpacket)
+		fotg210_done(ep, req, 0);
+}
+
+static irqreturn_t fotg210_irq(int irq, void *_fotg210)
+{
+	struct fotg210_udc *fotg210 = _fotg210;
+	u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR);
+	u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR);
+
+	int_grp &= ~int_msk;
+
+	spin_lock(&fotg210->lock);
+
+	if (int_grp & DIGR_INT_G2) {
+		void __iomem *reg = fotg210->reg + FOTG210_DISGR2;
+		u32 int_grp2 = ioread32(reg);
+		u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2);
+		u32 value;
+
+		int_grp2 &= ~int_msk2;
+
+		if (int_grp2 & DISGR2_USBRST_INT) {
+			value = ioread32(reg);
+			value &= ~DISGR2_USBRST_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 udc reset\n");
+		}
+		if (int_grp2 & DISGR2_SUSP_INT) {
+			value = ioread32(reg);
+			value &= ~DISGR2_SUSP_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 udc suspend\n");
+		}
+		if (int_grp2 & DISGR2_RESM_INT) {
+			value = ioread32(reg);
+			value &= ~DISGR2_RESM_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 udc resume\n");
+		}
+		if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) {
+			value = ioread32(reg);
+			value &= ~DISGR2_ISO_SEQ_ERR_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 iso sequence error\n");
+		}
+		if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) {
+			value = ioread32(reg);
+			value &= ~DISGR2_ISO_SEQ_ABORT_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 iso sequence abort\n");
+		}
+		if (int_grp2 & DISGR2_TX0BYTE_INT) {
+			fotg210_clear_tx0byte(fotg210);
+			value = ioread32(reg);
+			value &= ~DISGR2_TX0BYTE_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 transferred 0 byte\n");
+		}
+		if (int_grp2 & DISGR2_RX0BYTE_INT) {
+			fotg210_clear_rx0byte(fotg210);
+			value = ioread32(reg);
+			value &= ~DISGR2_RX0BYTE_INT;
+			iowrite32(value, reg);
+			pr_info("fotg210 received 0 byte\n");
+		}
+		if (int_grp2 & DISGR2_DMA_ERROR) {
+			value = ioread32(reg);
+			value &= ~DISGR2_DMA_ERROR;
+			iowrite32(value, reg);
+		}
+	}
+
+	if (int_grp & DIGR_INT_G0) {
+		void __iomem *reg = fotg210->reg + FOTG210_DISGR0;
+		u32 int_grp0 = ioread32(reg);
+		u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0);
+		struct usb_ctrlrequest ctrl;
+
+		int_grp0 &= ~int_msk0;
+
+		/* the highest priority in this source register */
+		if (int_grp0 & DISGR0_CX_COMABT_INT) {
+			fotg210_clear_comabt_int(fotg210);
+			pr_info("fotg210 CX command abort\n");
+		}
+
+		if (int_grp0 & DISGR0_CX_SETUP_INT) {
+			if (fotg210_setup_packet(fotg210, &ctrl)) {
+				spin_unlock(&fotg210->lock);
+				if (fotg210->driver->setup(&fotg210->gadget,
+							   &ctrl) < 0)
+					fotg210_set_cxstall(fotg210);
+				spin_lock(&fotg210->lock);
+			}
+		}
+		if (int_grp0 & DISGR0_CX_COMEND_INT)
+			pr_info("fotg210 cmd end\n");
+
+		if (int_grp0 & DISGR0_CX_IN_INT)
+			fotg210_ep0in(fotg210);
+
+		if (int_grp0 & DISGR0_CX_OUT_INT)
+			fotg210_ep0out(fotg210);
+
+		if (int_grp0 & DISGR0_CX_COMFAIL_INT) {
+			fotg210_set_cxstall(fotg210);
+			pr_info("fotg210 ep0 fail\n");
+		}
+	}
+
+	if (int_grp & DIGR_INT_G1) {
+		void __iomem *reg = fotg210->reg + FOTG210_DISGR1;
+		u32 int_grp1 = ioread32(reg);
+		u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1);
+		int fifo;
+
+		int_grp1 &= ~int_msk1;
+
+		for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) {
+			if (int_grp1 & DISGR1_IN_INT(fifo))
+				fotg210_in_fifo_handler(fotg210->ep[fifo + 1]);
+
+			if ((int_grp1 & DISGR1_OUT_INT(fifo)) ||
+			    (int_grp1 & DISGR1_SPK_INT(fifo)))
+				fotg210_out_fifo_handler(fotg210->ep[fifo + 1]);
+		}
+	}
+
+	spin_unlock(&fotg210->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void fotg210_disable_unplug(struct fotg210_udc *fotg210)
+{
+	u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR);
+
+	reg &= ~PHYTMSR_UNPLUG;
+	iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR);
+}
+
+static int fotg210_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
+	u32 value;
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	fotg210->driver = driver;
+
+	/* enable device global interrupt */
+	value = ioread32(fotg210->reg + FOTG210_DMCR);
+	value |= DMCR_GLINT_EN;
+	iowrite32(value, fotg210->reg + FOTG210_DMCR);
+
+	return 0;
+}
+
+static void fotg210_init(struct fotg210_udc *fotg210)
+{
+	u32 value;
+
+	/* disable global interrupt and set int polarity to active high */
+	iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY,
+		  fotg210->reg + FOTG210_GMIR);
+
+	/* disable device global interrupt */
+	value = ioread32(fotg210->reg + FOTG210_DMCR);
+	value &= ~DMCR_GLINT_EN;
+	iowrite32(value, fotg210->reg + FOTG210_DMCR);
+
+	/* disable all fifo interrupt */
+	iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1);
+
+	/* disable cmd end */
+	value = ioread32(fotg210->reg + FOTG210_DMISGR0);
+	value |= DMISGR0_MCX_COMEND;
+	iowrite32(value, fotg210->reg + FOTG210_DMISGR0);
+}
+
+static int fotg210_udc_stop(struct usb_gadget *g)
+{
+	struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&fotg210->lock, flags);
+
+	fotg210_init(fotg210);
+	fotg210->driver = NULL;
+
+	spin_unlock_irqrestore(&fotg210->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops fotg210_gadget_ops = {
+	.udc_start		= fotg210_udc_start,
+	.udc_stop		= fotg210_udc_stop,
+};
+
+static int fotg210_udc_remove(struct platform_device *pdev)
+{
+	struct fotg210_udc *fotg210 = platform_get_drvdata(pdev);
+	int i;
+
+	usb_del_gadget_udc(&fotg210->gadget);
+	iounmap(fotg210->reg);
+	free_irq(platform_get_irq(pdev, 0), fotg210);
+
+	fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
+	for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
+		kfree(fotg210->ep[i]);
+	kfree(fotg210);
+
+	return 0;
+}
+
+static int fotg210_udc_probe(struct platform_device *pdev)
+{
+	struct resource *res, *ires;
+	struct fotg210_udc *fotg210 = NULL;
+	struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP];
+	int ret = 0;
+	int i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_err("platform_get_resource error.\n");
+		return -ENODEV;
+	}
+
+	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!ires) {
+		pr_err("platform_get_resource IORESOURCE_IRQ error.\n");
+		return -ENODEV;
+	}
+
+	ret = -ENOMEM;
+
+	/* initialize udc */
+	fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL);
+	if (fotg210 == NULL)
+		goto err;
+
+	for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
+		_ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
+		if (_ep[i] == NULL)
+			goto err_alloc;
+		fotg210->ep[i] = _ep[i];
+	}
+
+	fotg210->reg = ioremap(res->start, resource_size(res));
+	if (fotg210->reg == NULL) {
+		pr_err("ioremap error.\n");
+		goto err_alloc;
+	}
+
+	spin_lock_init(&fotg210->lock);
+
+	platform_set_drvdata(pdev, fotg210);
+
+	fotg210->gadget.ops = &fotg210_gadget_ops;
+
+	fotg210->gadget.max_speed = USB_SPEED_HIGH;
+	fotg210->gadget.dev.parent = &pdev->dev;
+	fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	fotg210->gadget.name = udc_name;
+
+	INIT_LIST_HEAD(&fotg210->gadget.ep_list);
+
+	for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
+		struct fotg210_ep *ep = fotg210->ep[i];
+
+		if (i) {
+			INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list);
+			list_add_tail(&fotg210->ep[i]->ep.ep_list,
+				      &fotg210->gadget.ep_list);
+		}
+		ep->fotg210 = fotg210;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = fotg210_ep_name[i];
+		ep->ep.ops = &fotg210_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
+
+		if (i == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+	usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40);
+	fotg210->gadget.ep0 = &fotg210->ep[0]->ep;
+	INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list);
+
+	fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep,
+				GFP_KERNEL);
+	if (fotg210->ep0_req == NULL)
+		goto err_map;
+
+	fotg210_init(fotg210);
+
+	fotg210_disable_unplug(fotg210);
+
+	ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED,
+			  udc_name, fotg210);
+	if (ret < 0) {
+		pr_err("request_irq error (%d)\n", ret);
+		goto err_req;
+	}
+
+	ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget);
+	if (ret)
+		goto err_add_udc;
+
+	dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+
+	return 0;
+
+err_add_udc:
+	free_irq(ires->start, fotg210);
+
+err_req:
+	fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
+
+err_map:
+	iounmap(fotg210->reg);
+
+err_alloc:
+	for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
+		kfree(fotg210->ep[i]);
+	kfree(fotg210);
+
+err:
+	return ret;
+}
+
+static struct platform_driver fotg210_driver = {
+	.driver		= {
+		.name =	(char *)udc_name,
+	},
+	.probe		= fotg210_udc_probe,
+	.remove		= fotg210_udc_remove,
+};
+
+module_platform_driver(fotg210_driver);
+
+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/gadget/udc/fotg210.h b/drivers/usb/gadget/udc/fotg210.h
new file mode 100644
index 0000000..08c3295
--- /dev/null
+++ b/drivers/usb/gadget/udc/fotg210.h
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Faraday FOTG210 USB OTG controller
+ *
+ * Copyright (C) 2013 Faraday Technology Corporation
+ * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
+ */
+
+#include <linux/kernel.h>
+
+#define FOTG210_MAX_NUM_EP	5 /* ep0...ep4 */
+#define FOTG210_MAX_FIFO_NUM	4 /* fifo0...fifo4 */
+
+/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */
+#define FOTG210_GMIR		0xC4
+#define GMIR_INT_POLARITY	0x8 /*Active High*/
+#define GMIR_MHC_INT		0x4
+#define GMIR_MOTG_INT		0x2
+#define GMIR_MDEV_INT		0x1
+
+/*  Device Main Control Register(0x100) */
+#define FOTG210_DMCR		0x100
+#define DMCR_HS_EN		(1 << 6)
+#define DMCR_CHIP_EN		(1 << 5)
+#define DMCR_SFRST		(1 << 4)
+#define DMCR_GOSUSP		(1 << 3)
+#define DMCR_GLINT_EN		(1 << 2)
+#define DMCR_HALF_SPEED		(1 << 1)
+#define DMCR_CAP_RMWAKUP	(1 << 0)
+
+/* Device Address Register(0x104) */
+#define FOTG210_DAR		0x104
+#define DAR_AFT_CONF		(1 << 7)
+
+/* Device Test Register(0x108) */
+#define FOTG210_DTR		0x108
+#define DTR_TST_CLRFF		(1 << 0)
+
+/* PHY Test Mode Selector register(0x114) */
+#define FOTG210_PHYTMSR		0x114
+#define PHYTMSR_TST_PKT		(1 << 4)
+#define PHYTMSR_TST_SE0NAK	(1 << 3)
+#define PHYTMSR_TST_KSTA	(1 << 2)
+#define PHYTMSR_TST_JSTA	(1 << 1)
+#define PHYTMSR_UNPLUG		(1 << 0)
+
+/* Cx configuration and FIFO Empty Status register(0x120) */
+#define FOTG210_DCFESR		0x120
+#define DCFESR_FIFO_EMPTY(fifo)	(1 << 8 << (fifo))
+#define DCFESR_CX_EMP		(1 << 5)
+#define DCFESR_CX_CLR		(1 << 3)
+#define DCFESR_CX_STL		(1 << 2)
+#define DCFESR_TST_PKDONE	(1 << 1)
+#define DCFESR_CX_DONE		(1 << 0)
+
+/* Device IDLE Counter Register(0x124) */
+#define FOTG210_DICR		0x124
+
+/* Device Mask of Interrupt Group Register (0x130) */
+#define FOTG210_DMIGR		0x130
+#define DMIGR_MINT_G0		(1 << 0)
+
+/* Device Mask of Interrupt Source Group 0(0x134) */
+#define FOTG210_DMISGR0		0x134
+#define DMISGR0_MCX_COMEND	(1 << 3)
+#define DMISGR0_MCX_OUT_INT	(1 << 2)
+#define DMISGR0_MCX_IN_INT	(1 << 1)
+#define DMISGR0_MCX_SETUP_INT	(1 << 0)
+
+/* Device Mask of Interrupt Source Group 1 Register(0x138)*/
+#define FOTG210_DMISGR1		0x138
+#define DMISGR1_MF3_IN_INT	(1 << 19)
+#define DMISGR1_MF2_IN_INT	(1 << 18)
+#define DMISGR1_MF1_IN_INT	(1 << 17)
+#define DMISGR1_MF0_IN_INT	(1 << 16)
+#define DMISGR1_MF_IN_INT(fifo)	(1 << (16 + (fifo)))
+#define DMISGR1_MF3_SPK_INT	(1 << 7)
+#define DMISGR1_MF3_OUT_INT	(1 << 6)
+#define DMISGR1_MF2_SPK_INT	(1 << 5)
+#define DMISGR1_MF2_OUT_INT	(1 << 4)
+#define DMISGR1_MF1_SPK_INT	(1 << 3)
+#define DMISGR1_MF1_OUT_INT	(1 << 2)
+#define DMISGR1_MF0_SPK_INT	(1 << 1)
+#define DMISGR1_MF0_OUT_INT	(1 << 0)
+#define DMISGR1_MF_OUTSPK_INT(fifo)	(0x3 << (fifo) * 2)
+
+/* Device Mask of Interrupt Source Group 2 Register (0x13C) */
+#define FOTG210_DMISGR2		0x13C
+#define DMISGR2_MDMA_ERROR	(1 << 8)
+#define DMISGR2_MDMA_CMPLT	(1 << 7)
+
+/* Device Interrupt group Register (0x140) */
+#define FOTG210_DIGR		0x140
+#define DIGR_INT_G2		(1 << 2)
+#define DIGR_INT_G1		(1 << 1)
+#define DIGR_INT_G0		(1 << 0)
+
+/* Device Interrupt Source Group 0 Register (0x144) */
+#define FOTG210_DISGR0		0x144
+#define DISGR0_CX_COMABT_INT	(1 << 5)
+#define DISGR0_CX_COMFAIL_INT	(1 << 4)
+#define DISGR0_CX_COMEND_INT	(1 << 3)
+#define DISGR0_CX_OUT_INT	(1 << 2)
+#define DISGR0_CX_IN_INT	(1 << 1)
+#define DISGR0_CX_SETUP_INT	(1 << 0)
+
+/* Device Interrupt Source Group 1 Register (0x148) */
+#define FOTG210_DISGR1		0x148
+#define DISGR1_OUT_INT(fifo)	(1 << ((fifo) * 2))
+#define DISGR1_SPK_INT(fifo)	(1 << 1 << ((fifo) * 2))
+#define DISGR1_IN_INT(fifo)	(1 << 16 << (fifo))
+
+/* Device Interrupt Source Group 2 Register (0x14C) */
+#define FOTG210_DISGR2		0x14C
+#define DISGR2_DMA_ERROR	(1 << 8)
+#define DISGR2_DMA_CMPLT	(1 << 7)
+#define DISGR2_RX0BYTE_INT	(1 << 6)
+#define DISGR2_TX0BYTE_INT	(1 << 5)
+#define DISGR2_ISO_SEQ_ABORT_INT	(1 << 4)
+#define DISGR2_ISO_SEQ_ERR_INT	(1 << 3)
+#define DISGR2_RESM_INT		(1 << 2)
+#define DISGR2_SUSP_INT		(1 << 1)
+#define DISGR2_USBRST_INT	(1 << 0)
+
+/* Device Receive Zero-Length Data Packet Register (0x150)*/
+#define FOTG210_RX0BYTE		0x150
+#define RX0BYTE_EP8		(1 << 7)
+#define RX0BYTE_EP7		(1 << 6)
+#define RX0BYTE_EP6		(1 << 5)
+#define RX0BYTE_EP5		(1 << 4)
+#define RX0BYTE_EP4		(1 << 3)
+#define RX0BYTE_EP3		(1 << 2)
+#define RX0BYTE_EP2		(1 << 1)
+#define RX0BYTE_EP1		(1 << 0)
+
+/* Device Transfer Zero-Length Data Packet Register (0x154)*/
+#define FOTG210_TX0BYTE		0x154
+#define TX0BYTE_EP8		(1 << 7)
+#define TX0BYTE_EP7		(1 << 6)
+#define TX0BYTE_EP6		(1 << 5)
+#define TX0BYTE_EP5		(1 << 4)
+#define TX0BYTE_EP4		(1 << 3)
+#define TX0BYTE_EP3		(1 << 2)
+#define TX0BYTE_EP2		(1 << 1)
+#define TX0BYTE_EP1		(1 << 0)
+
+/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */
+#define FOTG210_INEPMPSR(ep)	(0x160 + 4 * ((ep) - 1))
+#define INOUTEPMPSR_MPS(mps)	((mps) & 0x2FF)
+#define INOUTEPMPSR_STL_EP	(1 << 11)
+#define INOUTEPMPSR_RESET_TSEQ	(1 << 12)
+
+/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */
+#define FOTG210_OUTEPMPSR(ep)	(0x180 + 4 * ((ep) - 1))
+
+/* Device Endpoint 1~4 Map Register (0x1A0) */
+#define FOTG210_EPMAP		0x1A0
+#define EPMAP_FIFONO(ep, dir)		\
+	((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
+#define EPMAP_FIFONOMSK(ep, dir)	\
+	((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
+
+/* Device FIFO Map Register (0x1A8) */
+#define FOTG210_FIFOMAP		0x1A8
+#define FIFOMAP_DIROUT(fifo)	(0x0 << 4 << (fifo) * 8)
+#define FIFOMAP_DIRIN(fifo)	(0x1 << 4 << (fifo) * 8)
+#define FIFOMAP_BIDIR(fifo)	(0x2 << 4 << (fifo) * 8)
+#define FIFOMAP_NA(fifo)	(0x3 << 4 << (fifo) * 8)
+#define FIFOMAP_EPNO(ep)	((ep) << ((ep) - 1) * 8)
+#define FIFOMAP_EPNOMSK(ep)	(0xF << ((ep) - 1) * 8)
+
+/* Device FIFO Confuguration Register (0x1AC) */
+#define FOTG210_FIFOCF		0x1AC
+#define FIFOCF_TYPE(type, fifo)	((type) << (fifo) * 8)
+#define FIFOCF_BLK_SIN(fifo)	(0x0 << (fifo) * 8 << 2)
+#define FIFOCF_BLK_DUB(fifo)	(0x1 << (fifo) * 8 << 2)
+#define FIFOCF_BLK_TRI(fifo)	(0x2 << (fifo) * 8 << 2)
+#define FIFOCF_BLKSZ_512(fifo)	(0x0 << (fifo) * 8 << 4)
+#define FIFOCF_BLKSZ_1024(fifo)	(0x1 << (fifo) * 8 << 4)
+#define FIFOCF_FIFO_EN(fifo)	(0x1 << (fifo) * 8 << 5)
+
+/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */
+#define FOTG210_FIBCR(fifo)	(0x1B0 + (fifo) * 4)
+#define FIBCR_BCFX		0x7FF
+#define FIBCR_FFRST		(1 << 12)
+
+/* Device DMA Target FIFO Number Register (0x1C0) */
+#define FOTG210_DMATFNR		0x1C0
+#define DMATFNR_ACC_CXF		(1 << 4)
+#define DMATFNR_ACC_F3		(1 << 3)
+#define DMATFNR_ACC_F2		(1 << 2)
+#define DMATFNR_ACC_F1		(1 << 1)
+#define DMATFNR_ACC_F0		(1 << 0)
+#define DMATFNR_ACC_FN(fifo)	(1 << (fifo))
+#define DMATFNR_DISDMA		0
+
+/* Device DMA Controller Parameter setting 1 Register (0x1C8) */
+#define FOTG210_DMACPSR1	0x1C8
+#define DMACPSR1_DMA_LEN(len)	(((len) & 0xFFFF) << 8)
+#define DMACPSR1_DMA_ABORT	(1 << 3)
+#define DMACPSR1_DMA_TYPE(dir_in)	(((dir_in) ? 1 : 0) << 1)
+#define DMACPSR1_DMA_START	(1 << 0)
+
+/* Device DMA Controller Parameter setting 2 Register (0x1CC) */
+#define FOTG210_DMACPSR2	0x1CC
+
+/* Device DMA Controller Parameter setting 3 Register (0x1CC) */
+#define FOTG210_CXPORT		0x1D0
+
+struct fotg210_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+struct fotg210_ep {
+	struct usb_ep		ep;
+	struct fotg210_udc	*fotg210;
+
+	struct list_head	queue;
+	unsigned		stall:1;
+	unsigned		wedged:1;
+	unsigned		use_dma:1;
+
+	unsigned char		epnum;
+	unsigned char		type;
+	unsigned char		dir_in;
+	unsigned int		maxp;
+	const struct usb_endpoint_descriptor	*desc;
+};
+
+struct fotg210_udc {
+	spinlock_t		lock; /* protect the struct */
+	void __iomem		*reg;
+
+	unsigned long		irq_trigger;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct fotg210_ep	*ep[FOTG210_MAX_NUM_EP];
+
+	struct usb_request	*ep0_req;	/* for internal request */
+	__le16			ep0_data;
+	u8			ep0_dir;	/* 0/0x80  out/in */
+
+	u8			reenum;		/* if re-enumeration */
+};
+
+#define gadget_to_fotg210(g)	container_of((g), struct fotg210_udc, gadget)
diff --git a/drivers/usb/gadget/udc/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c
new file mode 100644
index 0000000..5a32199
--- /dev/null
+++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * Description:
+ * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
+ * driver to function correctly on these systems.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include "fsl_usb2_udc.h"
+
+static struct clk *mxc_ahb_clk;
+static struct clk *mxc_per_clk;
+static struct clk *mxc_ipg_clk;
+
+/* workaround ENGcm09152 for i.MX35 */
+#define MX35_USBPHYCTRL_OFFSET		0x600
+#define USBPHYCTRL_OTGBASE_OFFSET	0x8
+#define USBPHYCTRL_EVDO			(1 << 23)
+
+int fsl_udc_clk_init(struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata;
+	unsigned long freq;
+	int ret;
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(mxc_ipg_clk)) {
+		dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n");
+		return PTR_ERR(mxc_ipg_clk);
+	}
+
+	mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+	if (IS_ERR(mxc_ahb_clk)) {
+		dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n");
+		return PTR_ERR(mxc_ahb_clk);
+	}
+
+	mxc_per_clk = devm_clk_get(&pdev->dev, "per");
+	if (IS_ERR(mxc_per_clk)) {
+		dev_err(&pdev->dev, "clk_get(\"per\") failed\n");
+		return PTR_ERR(mxc_per_clk);
+	}
+
+	clk_prepare_enable(mxc_ipg_clk);
+	clk_prepare_enable(mxc_ahb_clk);
+	clk_prepare_enable(mxc_per_clk);
+
+	/* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
+	if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) {
+		freq = clk_get_rate(mxc_per_clk);
+		if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
+		    (freq < 59999000 || freq > 60001000)) {
+			dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
+			ret = -EINVAL;
+			goto eclkrate;
+		}
+	}
+
+	return 0;
+
+eclkrate:
+	clk_disable_unprepare(mxc_ipg_clk);
+	clk_disable_unprepare(mxc_ahb_clk);
+	clk_disable_unprepare(mxc_per_clk);
+	mxc_per_clk = NULL;
+	return ret;
+}
+
+int fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	int ret = 0;
+
+	/* workaround ENGcm09152 for i.MX35 */
+	if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
+		unsigned int v;
+		struct resource *res = platform_get_resource
+			(pdev, IORESOURCE_MEM, 0);
+		void __iomem *phy_regs = ioremap(res->start +
+						MX35_USBPHYCTRL_OFFSET, 512);
+		if (!phy_regs) {
+			dev_err(&pdev->dev, "ioremap for phy address fails\n");
+			ret = -EINVAL;
+			goto ioremap_err;
+		}
+
+		v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
+		writel(v | USBPHYCTRL_EVDO,
+			phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
+
+		iounmap(phy_regs);
+	}
+
+
+ioremap_err:
+	/* ULPI transceivers don't need usbpll */
+	if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
+		clk_disable_unprepare(mxc_per_clk);
+		mxc_per_clk = NULL;
+	}
+
+	return ret;
+}
+
+void fsl_udc_clk_release(void)
+{
+	if (mxc_per_clk)
+		clk_disable_unprepare(mxc_per_clk);
+	clk_disable_unprepare(mxc_ahb_clk);
+	clk_disable_unprepare(mxc_ipg_clk);
+}
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
new file mode 100644
index 0000000..2707be6
--- /dev/null
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -0,0 +1,2715 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * driver/usb/gadget/fsl_qe_udc.c
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 	Xie Xiaobo <X.Xie@freescale.com>
+ * 	Li Yang <leoli@freescale.com>
+ * 	Based on bareboard code from Shlomi Gridish.
+ *
+ * Description:
+ * Freescle QE/CPM USB Pheripheral Controller Driver
+ * The controller can be found on MPC8360, MPC8272, and etc.
+ * MPC8360 Rev 1.1 may need QE mircocode update
+ */
+
+#undef USB_TRACE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <soc/fsl/qe/qe.h>
+#include <asm/cpm.h>
+#include <asm/dma.h>
+#include <asm/reg.h>
+#include "fsl_qe_udc.h"
+
+#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
+#define DRIVER_AUTHOR   "Xie XiaoBo"
+#define DRIVER_VERSION  "1.0"
+
+#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl_qe_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*ep name is important in gadget, it should obey the convention of ep_match()*/
+static const char *const ep_name[] = {
+	"ep0-control", /* everyone has ep0 */
+	/* 3 configurable endpoints */
+	"ep1",
+	"ep2",
+	"ep3",
+};
+
+static const struct usb_endpoint_descriptor qe_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+/********************************************************************
+ *      Internal Used Function Start
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ *--------------------------------------------------------------*/
+static void done(struct qe_ep *ep, struct qe_req *req, int status)
+{
+	struct qe_udc *udc = ep->udc;
+	unsigned char stopped = ep->stopped;
+
+	/* the req->queue pointer is used by ep_queue() func, in which
+	 * the request will be added into a udc_ep->queue 'd tail
+	 * so here the req will be dropped from the ep->queue
+	 */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (req->mapped) {
+		dma_unmap_single(udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+
+	if (status && (status != -ESHUTDOWN))
+		dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&udc->lock);
+
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+
+	spin_lock(&udc->lock);
+
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ *--------------------------------------------------------------*/
+static void nuke(struct qe_ep *ep, int status)
+{
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct qe_req *req = NULL;
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		done(ep, req, status);
+	}
+}
+
+/*---------------------------------------------------------------------------*
+ * USB and Endpoint manipulate process, include parameter and register       *
+ *---------------------------------------------------------------------------*/
+/* @value: 1--set stall 0--clean stall */
+static int qe_eprx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_RHS_MASK;
+	if (value == 1)
+		tem_usep |= USB_RHS_STALL;
+	else if (ep->dir == USB_DIR_IN)
+		tem_usep |= USB_RHS_IGNORE_OUT;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+	return 0;
+}
+
+static int qe_eptx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_THS_MASK;
+	if (value == 1)
+		tem_usep |= USB_THS_STALL;
+	else if (ep->dir == USB_DIR_OUT)
+		tem_usep |= USB_THS_IGNORE_IN;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+
+	return 0;
+}
+
+static int qe_ep0_stall(struct qe_udc *udc)
+{
+	qe_eptx_stall_change(&udc->eps[0], 1);
+	qe_eprx_stall_change(&udc->eps[0], 1);
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = 0;
+	return 0;
+}
+
+static int qe_eprx_nack(struct qe_ep *ep)
+{
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_IDLE) {
+		/* Set the ep's nack */
+		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum],
+				USB_RHS_MASK, USB_RHS_NACK);
+
+		/* Mask Rx and Busy interrupts */
+		clrbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_NACK;
+	}
+	return 0;
+}
+
+static int qe_eprx_normal(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_NACK) {
+		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum],
+				USB_RTHS_MASK, USB_THS_IGNORE_IN);
+
+		/* Unmask RX interrupts */
+		out_be16(&udc->usb_regs->usb_usber,
+				USB_E_BSY_MASK | USB_E_RXB_MASK);
+		setbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_IDLE;
+		ep->has_data = 0;
+	}
+
+	return 0;
+}
+
+static int qe_ep_cmd_stoptx(struct qe_ep *ep)
+{
+	if (ep->udc->soc_type == PORT_CPM)
+		cpm_command(CPM_USB_STOP_TX | (ep->epnum << CPM_USB_EP_SHIFT),
+				CPM_USB_STOP_TX_OPCODE);
+	else
+		qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB,
+				ep->epnum, 0);
+
+	return 0;
+}
+
+static int qe_ep_cmd_restarttx(struct qe_ep *ep)
+{
+	if (ep->udc->soc_type == PORT_CPM)
+		cpm_command(CPM_USB_RESTART_TX | (ep->epnum <<
+				CPM_USB_EP_SHIFT), CPM_USB_RESTART_TX_OPCODE);
+	else
+		qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB,
+				ep->epnum, 0);
+
+	return 0;
+}
+
+static int qe_ep_flushtxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+	int i;
+
+	i = (int)ep->epnum;
+
+	qe_ep_cmd_stoptx(ep);
+	out_8(&udc->usb_regs->usb_uscom,
+		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	out_be16(&udc->ep_param[i]->tbptr, in_be16(&udc->ep_param[i]->tbase));
+	out_be32(&udc->ep_param[i]->tstate, 0);
+	out_be16(&udc->ep_param[i]->tbcnt, 0);
+
+	ep->c_txbd = ep->txbase;
+	ep->n_txbd = ep->txbase;
+	qe_ep_cmd_restarttx(ep);
+	return 0;
+}
+
+static int qe_ep_filltxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	out_8(&udc->usb_regs->usb_uscom,
+			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	return 0;
+}
+
+static int qe_epbds_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u32 bdring_len;
+	struct qe_bd __iomem *bd;
+	int i;
+
+	ep = &udc->eps[pipe_num];
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	bd = ep->rxbase;
+	for (i = 0; i < (bdring_len - 1); i++) {
+		out_be32((u32 __iomem *)bd, R_E | R_I);
+		bd++;
+	}
+	out_be32((u32 __iomem *)bd, R_E | R_I | R_W);
+
+	bd = ep->txbase;
+	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32((u32 __iomem *)bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u16 tmpusep;
+
+	ep = &udc->eps[pipe_num];
+	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]);
+	tmpusep &= ~USB_RTHS_MASK;
+
+	switch (ep->dir) {
+	case USB_DIR_BOTH:
+		qe_ep_flushtxfifo(ep);
+		break;
+	case USB_DIR_OUT:
+		tmpusep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		qe_ep_flushtxfifo(ep);
+		tmpusep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep);
+
+	qe_epbds_reset(udc, pipe_num);
+
+	return 0;
+}
+
+static int qe_ep_toggledata01(struct qe_ep *ep)
+{
+	ep->data01 ^= 0x1;
+	return 0;
+}
+
+static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	unsigned long tmp_addr = 0;
+	struct usb_ep_para __iomem *epparam;
+	int i;
+	struct qe_bd __iomem *bd;
+	int bdring_len;
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	epparam = udc->ep_param[pipe_num];
+	/* alloc multi-ram for BD rings and set the ep parameters */
+	tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len +
+				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
+	if (IS_ERR_VALUE(tmp_addr))
+		return -ENOMEM;
+
+	out_be16(&epparam->rbase, (u16)tmp_addr);
+	out_be16(&epparam->tbase, (u16)(tmp_addr +
+				(sizeof(struct qe_bd) * bdring_len)));
+
+	out_be16(&epparam->rbptr, in_be16(&epparam->rbase));
+	out_be16(&epparam->tbptr, in_be16(&epparam->tbase));
+
+	ep->rxbase = cpm_muram_addr(tmp_addr);
+	ep->txbase = cpm_muram_addr(tmp_addr + (sizeof(struct qe_bd)
+				* bdring_len));
+	ep->n_rxbd = ep->rxbase;
+	ep->e_rxbd = ep->rxbase;
+	ep->n_txbd = ep->txbase;
+	ep->c_txbd = ep->txbase;
+	ep->data01 = 0; /* data0 */
+
+	/* Init TX and RX bds */
+	bd = ep->rxbase;
+	for (i = 0; i < bdring_len - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32(&bd->buf, 0);
+	out_be32((u32 __iomem *)bd, R_W);
+
+	bd = ep->txbase;
+	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32(&bd->buf, 0);
+	out_be32((u32 __iomem *)bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_rxbd_update(struct qe_ep *ep)
+{
+	unsigned int size;
+	int i;
+	unsigned int tmp;
+	struct qe_bd __iomem *bd;
+	unsigned int bdring_len;
+
+	if (ep->rxbase == NULL)
+		return -EINVAL;
+
+	bd = ep->rxbase;
+
+	ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC);
+	if (!ep->rxframe)
+		return -ENOMEM;
+
+	qe_frame_init(ep->rxframe);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
+	ep->rxbuffer = kzalloc(size, GFP_ATOMIC);
+	if (!ep->rxbuffer) {
+		kfree(ep->rxframe);
+		return -ENOMEM;
+	}
+
+	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
+	if (ep->rxbuf_d == DMA_ADDR_INVALID) {
+		ep->rxbuf_d = dma_map_single(ep->udc->gadget.dev.parent,
+					ep->rxbuffer,
+					size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 0;
+	}
+
+	size = ep->ep.maxpacket + USB_CRC_SIZE + 2;
+	tmp = ep->rxbuf_d;
+	tmp = (u32)(((tmp >> 2) << 2) + 4);
+
+	for (i = 0; i < bdring_len - 1; i++) {
+		out_be32(&bd->buf, tmp);
+		out_be32((u32 __iomem *)bd, (R_E | R_I));
+		tmp = tmp + size;
+		bd++;
+	}
+	out_be32(&bd->buf, tmp);
+	out_be32((u32 __iomem *)bd, (R_E | R_I | R_W));
+
+	return 0;
+}
+
+static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	struct usb_ep_para __iomem *epparam;
+	u16 usep, logepnum;
+	u16 tmp;
+	u8 rtfcr = 0;
+
+	epparam = udc->ep_param[pipe_num];
+
+	usep = 0;
+	logepnum = (ep->ep.desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usep |= (logepnum << USB_EPNUM_SHIFT);
+
+	switch (ep->ep.desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_BULK:
+		usep |= USB_TRANS_BULK;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		usep |=  USB_TRANS_ISO;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		usep |= USB_TRANS_INT;
+		break;
+	default:
+		usep |= USB_TRANS_CTR;
+		break;
+	}
+
+	switch (ep->dir) {
+	case USB_DIR_OUT:
+		usep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		usep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep);
+
+	rtfcr = 0x30;
+	out_8(&epparam->rbmr, rtfcr);
+	out_8(&epparam->tbmr, rtfcr);
+
+	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE);
+	/* MRBLR must be divisble by 4 */
+	tmp = (u16)(((tmp >> 2) << 2) + 4);
+	out_be16(&epparam->mrblr, tmp);
+
+	return 0;
+}
+
+static int qe_ep_init(struct qe_udc *udc,
+		      unsigned char pipe_num,
+		      const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	unsigned long flags;
+	int reval = 0;
+	u16 max = 0;
+
+	max = usb_endpoint_maxp(desc);
+
+	/* check the max package size validate for this endpoint */
+	/* Refer to USB2.0 spec table 9-13,
+	*/
+	if (pipe_num != 0) {
+		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_BULK:
+			if (strstr(ep->ep.name, "-iso")
+					|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			if ((max == 128) || (max == 256) || (max == 512))
+				break;
+			default:
+				switch (max) {
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+				case USB_SPEED_LOW:
+					goto en_done;
+				}
+			}
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 64)
+					break;
+			default:
+				if (max <= 8)
+					break;
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			if (strstr(ep->ep.name, "-bulk")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 1023)
+					break;
+			default:
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_CONTROL:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			case USB_SPEED_FULL:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+					goto en_done;
+				}
+			case USB_SPEED_LOW:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					break;
+				default:
+					goto en_done;
+				}
+			default:
+				goto en_done;
+			}
+			break;
+
+		default:
+			goto en_done;
+		}
+	} /* if ep0*/
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* initialize ep structure */
+	ep->ep.maxpacket = max;
+	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	ep->ep.desc = desc;
+	ep->stopped = 0;
+	ep->init = 1;
+
+	if (pipe_num == 0) {
+		ep->dir = USB_DIR_BOTH;
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_SETUP;
+	} else	{
+		switch (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+		case USB_DIR_OUT:
+			ep->dir = USB_DIR_OUT;
+			break;
+		case USB_DIR_IN:
+			ep->dir = USB_DIR_IN;
+		default:
+			break;
+		}
+	}
+
+	/* hardware special operation */
+	qe_ep_bd_init(udc, pipe_num);
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) {
+		reval = qe_ep_rxbd_update(ep);
+		if (reval)
+			goto en_done1;
+	}
+
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
+		ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC);
+		if (!ep->txframe)
+			goto en_done2;
+		qe_frame_init(ep->txframe);
+	}
+
+	qe_ep_register_init(udc, pipe_num);
+
+	/* Now HW will be NAKing transfers to that EP,
+	 * until a buffer is queued to it. */
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+en_done2:
+	kfree(ep->rxbuffer);
+	kfree(ep->rxframe);
+en_done1:
+	spin_unlock_irqrestore(&udc->lock, flags);
+en_done:
+	dev_err(udc->dev, "failed to initialize %s\n", ep->ep.name);
+	return -ENODEV;
+}
+
+static inline void qe_usb_enable(struct qe_udc *udc)
+{
+	setbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN);
+}
+
+static inline void qe_usb_disable(struct qe_udc *udc)
+{
+	clrbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN);
+}
+
+/*----------------------------------------------------------------------------*
+ *		USB and EP basic manipulate function end		      *
+ *----------------------------------------------------------------------------*/
+
+
+/******************************************************************************
+		UDC transmit and receive process
+ ******************************************************************************/
+static void recycle_one_rxbd(struct qe_ep *ep)
+{
+	u32 bdstatus;
+
+	bdstatus = in_be32((u32 __iomem *)ep->e_rxbd);
+	bdstatus = R_I | R_E | (bdstatus & R_W);
+	out_be32((u32 __iomem *)ep->e_rxbd, bdstatus);
+
+	if (bdstatus & R_W)
+		ep->e_rxbd = ep->rxbase;
+	else
+		ep->e_rxbd++;
+}
+
+static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext)
+{
+	u32 bdstatus;
+	struct qe_bd __iomem *bd, *nextbd;
+	unsigned char stop = 0;
+
+	nextbd = ep->n_rxbd;
+	bd = ep->e_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+
+	while (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK) && !stop) {
+		bdstatus = R_E | R_I | (bdstatus & R_W);
+		out_be32((u32 __iomem *)bd, bdstatus);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		if (stopatnext && (bd == nextbd))
+			stop = 1;
+	}
+
+	ep->e_rxbd = bd;
+}
+
+static void ep_recycle_rxbds(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd = ep->n_rxbd;
+	u32 bdstatus;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	bdstatus = in_be32((u32 __iomem *)bd);
+	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) {
+		bd = ep->rxbase +
+				((in_be16(&udc->ep_param[epnum]->rbptr) -
+				  in_be16(&udc->ep_param[epnum]->rbase))
+				 >> 3);
+		bdstatus = in_be32((u32 __iomem *)bd);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		ep->e_rxbd = bd;
+		recycle_rxbds(ep, 0);
+		ep->e_rxbd = ep->n_rxbd;
+	} else
+		recycle_rxbds(ep, 1);
+
+	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK)
+		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK);
+
+	if (ep->has_data <= 0 && (!list_empty(&ep->queue)))
+		qe_eprx_normal(ep);
+
+	ep->localnack = 0;
+}
+
+static void setup_received_handle(struct qe_udc *udc,
+					struct usb_ctrlrequest *setup);
+static int qe_ep_rxframe_handle(struct qe_ep *ep);
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
+/* when BD PID is setup, handle the packet */
+static int ep0_setup_handle(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	struct qe_frame *pframe;
+	unsigned int fsize;
+	u8 *cp;
+
+	pframe = ep->rxframe;
+	if ((frame_get_info(pframe) & PID_SETUP)
+			&& (udc->ep0_state == WAIT_FOR_SETUP)) {
+		fsize = frame_get_length(pframe);
+		if (unlikely(fsize != 8))
+			return -EINVAL;
+		cp = (u8 *)&udc->local_setup_buff;
+		memcpy(cp, pframe->data, fsize);
+		ep->data01 = 1;
+
+		/* handle the usb command base on the usb_ctrlrequest */
+		setup_received_handle(udc, &udc->local_setup_buff);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int qe_ep0_rx(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	u32 bdstatus, length;
+	u32 vaddr;
+
+	pframe = ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		dev_err(udc->dev, "ep0 not a control endpoint\n");
+		return -EINVAL;
+	}
+
+	bd = ep->n_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus & R_E) && length) {
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+			&& !(bdstatus & R_ERROR)) {
+			if (length == USB_CRC_SIZE) {
+				udc->ep0_state = WAIT_FOR_SETUP;
+				dev_vdbg(udc->dev,
+					"receive a ZLP in status phase\n");
+			} else {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+
+				if ((bdstatus & R_PID) == R_PID_SETUP)
+					ep0_setup_handle(udc);
+				else
+					qe_ep_rxframe_handle(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address */
+		recycle_one_rxbd(ep);
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+	}
+
+	ep->n_rxbd = bd;
+
+	return 0;
+}
+
+static int qe_ep_rxframe_handle(struct qe_ep *ep)
+{
+	struct qe_frame *pframe;
+	u8 framepid = 0;
+	unsigned int fsize;
+	u8 *cp;
+	struct qe_req *req;
+
+	pframe = ep->rxframe;
+
+	if (frame_get_info(pframe) & PID_DATA1)
+		framepid = 0x1;
+
+	if (framepid != ep->data01) {
+		dev_err(ep->udc->dev, "the data01 error!\n");
+		return -EIO;
+	}
+
+	fsize = frame_get_length(pframe);
+	if (list_empty(&ep->queue)) {
+		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name);
+	} else {
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		cp = (u8 *)(req->req.buf) + req->req.actual;
+		if (cp) {
+			memcpy(cp, pframe->data, fsize);
+			req->req.actual += fsize;
+			if ((fsize < ep->ep.maxpacket) ||
+					(req->req.actual >= req->req.length)) {
+				if (ep->epnum == 0)
+					ep0_req_complete(ep->udc, req);
+				else
+					done(ep, req, 0);
+				if (list_empty(&ep->queue) && ep->epnum != 0)
+					qe_eprx_nack(ep);
+			}
+		}
+	}
+
+	qe_ep_toggledata01(ep);
+
+	return 0;
+}
+
+static void ep_rx_tasklet(unsigned long data)
+{
+	struct qe_udc *udc = (struct qe_udc *)data;
+	struct qe_ep *ep;
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	unsigned long flags;
+	u32 bdstatus, length;
+	u32 vaddr, i;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	for (i = 1; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &udc->eps[i];
+
+		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) {
+			dev_dbg(udc->dev,
+				"This is a transmit ep or disable tasklet!\n");
+			continue;
+		}
+
+		pframe = ep->rxframe;
+		bd = ep->n_rxbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+		while (!(bdstatus & R_E) && length) {
+			if (list_empty(&ep->queue)) {
+				qe_eprx_nack(ep);
+				dev_dbg(udc->dev,
+					"The rxep have noreq %d\n",
+					ep->has_data);
+				break;
+			}
+
+			if ((bdstatus & R_F) && (bdstatus & R_L)
+				&& !(bdstatus & R_ERROR)) {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+				/* handle the rx frame */
+				qe_ep_rxframe_handle(ep);
+			} else {
+				dev_err(udc->dev,
+					"error in received frame\n");
+			}
+			/* note: don't clear the rxbd's buffer address */
+			/*clear the length */
+			out_be32((u32 __iomem *)bd, bdstatus & BD_STATUS_MASK);
+			ep->has_data--;
+			if (!(ep->localnack))
+				recycle_one_rxbd(ep);
+
+			/* Get next BD */
+			if (bdstatus & R_W)
+				bd = ep->rxbase;
+			else
+				bd++;
+
+			bdstatus = in_be32((u32 __iomem *)bd);
+			length = bdstatus & BD_LENGTH_MASK;
+		}
+
+		ep->n_rxbd = bd;
+
+		if (ep->localnack)
+			ep_recycle_rxbds(ep);
+
+		ep->enable_tasklet = 0;
+	} /* for i=1 */
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static int qe_ep_rx(struct qe_ep *ep)
+{
+	struct qe_udc *udc;
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	u16 swoffs, ucoffs, emptybds;
+
+	udc = ep->udc;
+	pframe = ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		dev_err(udc->dev, "transmit ep in rx function\n");
+		return -EINVAL;
+	}
+
+	bd = ep->n_rxbd;
+
+	swoffs = (u16)(bd - ep->rxbase);
+	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) -
+			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3);
+	if (swoffs < ucoffs)
+		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs;
+	else
+		emptybds = swoffs - ucoffs;
+
+	if (emptybds < MIN_EMPTY_BDS) {
+		qe_eprx_nack(ep);
+		ep->localnack = 1;
+		dev_vdbg(udc->dev, "%d empty bds, send NACK\n", emptybds);
+	}
+	ep->has_data = USB_BDRING_LEN_RX - emptybds;
+
+	if (list_empty(&ep->queue)) {
+		qe_eprx_nack(ep);
+		dev_vdbg(udc->dev, "The rxep have no req queued with %d BDs\n",
+				ep->has_data);
+		return 0;
+	}
+
+	tasklet_schedule(&udc->rx_tasklet);
+	ep->enable_tasklet = 1;
+
+	return 0;
+}
+
+/* send data from a frame, no matter what tx_req */
+static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_udc *udc = ep->udc;
+	struct qe_bd __iomem *bd;
+	u16 saveusbmr;
+	u32 bdstatus, pidmask;
+	u32 paddr;
+
+	if (ep->dir == USB_DIR_OUT) {
+		dev_err(udc->dev, "receive ep passed to tx function\n");
+		return -EINVAL;
+	}
+
+	/* Disable the Tx interrupt */
+	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr);
+	out_be16(&udc->usb_regs->usb_usbmr,
+			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK));
+
+	bd = ep->n_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+
+	if (!(bdstatus & (T_R | BD_LENGTH_MASK))) {
+		if (frame_get_length(frame) == 0) {
+			frame_set_data(frame, udc->nullbuf);
+			frame_set_length(frame, 2);
+			frame->info |= (ZLP | NO_CRC);
+			dev_vdbg(udc->dev, "the frame size = 0\n");
+		}
+		paddr = virt_to_phys((void *)frame->data);
+		out_be32(&bd->buf, paddr);
+		bdstatus = (bdstatus&T_W);
+		if (!(frame_get_info(frame) & NO_CRC))
+			bdstatus |= T_R | T_I | T_L | T_TC
+					| frame_get_length(frame);
+		else
+			bdstatus |= T_R | T_I | T_L | frame_get_length(frame);
+
+		/* if the packet is a ZLP in status phase */
+		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP))
+			ep->data01 = 0x1;
+
+		if (ep->data01) {
+			pidmask = T_PID_DATA1;
+			frame->info |= PID_DATA1;
+		} else {
+			pidmask = T_PID_DATA0;
+			frame->info |= PID_DATA0;
+		}
+		bdstatus |= T_CNF;
+		bdstatus |= pidmask;
+		out_be32((u32 __iomem *)bd, bdstatus);
+		qe_ep_filltxfifo(ep);
+
+		/* enable the TX interrupt */
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+
+		qe_ep_toggledata01(ep);
+		if (bdstatus & T_W)
+			ep->n_txbd = ep->txbase;
+		else
+			ep->n_txbd++;
+
+		return 0;
+	} else {
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+		dev_vdbg(udc->dev, "The tx bd is not ready!\n");
+		return -EBUSY;
+	}
+}
+
+/* when a bd was transmitted, the function can
+ * handle the tx_req, not include ep0           */
+static int txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	if (ep->tx_req != NULL) {
+		struct qe_req *req = ep->tx_req;
+		unsigned zlp = 0, last_len = 0;
+
+		last_len = min_t(unsigned, req->req.length - ep->sent,
+				ep->ep.maxpacket);
+
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* zlp needed when req->re.zero is set */
+		if (req->req.zero) {
+			if (last_len == 0 ||
+				(req->req.length % ep->ep.maxpacket) != 0)
+				zlp = 0;
+			else
+				zlp = 1;
+		} else
+			zlp = 0;
+
+		/* a request already were transmitted completely */
+		if (((ep->tx_req->req.length - ep->sent) <= 0) && !zlp) {
+			done(ep, ep->tx_req, 0);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	/* we should gain a new tx_req fot this endpoint */
+	if (ep->tx_req == NULL) {
+		if (!list_empty(&ep->queue)) {
+			ep->tx_req = list_entry(ep->queue.next,	struct qe_req,
+							queue);
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	return 0;
+}
+
+/* give a frame and a tx_req, send some data */
+static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
+{
+	unsigned int size;
+	u8 *buf;
+
+	qe_frame_clean(frame);
+	size = min_t(u32, (ep->tx_req->req.length - ep->sent),
+				ep->ep.maxpacket);
+	buf = (u8 *)ep->tx_req->req.buf + ep->sent;
+	if (buf && size) {
+		ep->last = size;
+		ep->tx_req->req.actual += size;
+		frame_set_data(frame, buf);
+		frame_set_length(frame, size);
+		frame_set_status(frame, FRAME_OK);
+		frame_set_info(frame, 0);
+		return qe_ep_tx(ep, frame);
+	}
+	return -EIO;
+}
+
+/* give a frame struct,send a ZLP */
+static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (frame == NULL)
+		return -ENODEV;
+
+	qe_frame_clean(frame);
+	frame_set_data(frame, (u8 *)udc->nullbuf);
+	frame_set_length(frame, 2);
+	frame_set_status(frame, FRAME_OK);
+	frame_set_info(frame, (ZLP | NO_CRC | infor));
+
+	return qe_ep_tx(ep, frame);
+}
+
+static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_req *req = ep->tx_req;
+	int reval;
+
+	if (req == NULL)
+		return -ENODEV;
+
+	if ((req->req.length - ep->sent) > 0)
+		reval = qe_usb_senddata(ep, frame);
+	else
+		reval = sendnulldata(ep, frame, 0);
+
+	return reval;
+}
+
+/* if direction is DIR_IN, the status is Device->Host
+ * if direction is DIR_OUT, the status transaction is Device<-Host
+ * in status phase, udc create a request and gain status */
+static int ep0_prime_status(struct qe_udc *udc, int direction)
+{
+
+	struct qe_ep *ep = &udc->eps[0];
+
+	if (direction == USB_DIR_IN) {
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+		udc->ep0_dir = USB_DIR_IN;
+		sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ);
+	} else {
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+
+	return 0;
+}
+
+/* a request complete in ep0, whether gadget request or udc request */
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	/* because usb and ep's status already been set in ch9setaddress() */
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		done(ep, req, 0);
+		/* receive status phase */
+		if (ep0_prime_status(udc, USB_DIR_OUT))
+			qe_ep0_stall(udc);
+		break;
+
+	case DATA_STATE_NEED_ZLP:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case DATA_STATE_RECV:
+		done(ep, req, 0);
+		/* send status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			qe_ep0_stall(udc);
+		break;
+
+	case WAIT_FOR_OUT_STATUS:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case WAIT_FOR_SETUP:
+		dev_vdbg(udc->dev, "Unexpected interrupt\n");
+		break;
+
+	default:
+		qe_ep0_stall(udc);
+		break;
+	}
+}
+
+static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+	struct qe_frame *frame = ep->txframe;
+
+	if ((frame_get_info(frame) & (ZLP | NO_REQ)) == (ZLP | NO_REQ)) {
+		if (!restart)
+			ep->udc->ep0_state = WAIT_FOR_SETUP;
+		else
+			sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ);
+		return 0;
+	}
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			ep0_req_complete(ep->udc, ep->tx_req);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	} else {
+		dev_vdbg(ep->udc->dev, "the ep0_controller have no req\n");
+	}
+
+	return 0;
+}
+
+static int ep0_txframe_handle(struct qe_ep *ep)
+{
+	/* if have error, transmit again */
+	if (frame_get_status(ep->txframe) & FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		dev_vdbg(ep->udc->dev, "The EP0 transmit data have error!\n");
+		if (frame_get_info(ep->txframe) & PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		ep0_txcomplete(ep, 1);
+	} else
+		ep0_txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe);
+	return 0;
+}
+
+static int qe_ep0_txconf(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd;
+	struct qe_frame *pframe;
+	u32 bdstatus;
+
+	bd = ep->c_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+
+		/* clear and recycle the BD */
+		out_be32((u32 __iomem *)bd, bdstatus & T_W);
+		out_be32(&bd->buf, 0);
+		if (bdstatus & T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		if (ep->c_txbd == ep->n_txbd) {
+			if (bdstatus & DEVICE_T_ERROR) {
+				frame_set_status(pframe, FRAME_ERROR);
+				if (bdstatus & T_TO)
+					pframe->status |= TX_ER_TIMEOUT;
+				if (bdstatus & T_UN)
+					pframe->status |= TX_ER_UNDERUN;
+			}
+			ep0_txframe_handle(ep);
+		}
+
+		bd = ep->c_txbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+	}
+
+	return 0;
+}
+
+static int ep_txframe_handle(struct qe_ep *ep)
+{
+	if (frame_get_status(ep->txframe) & FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		dev_vdbg(ep->udc->dev, "The EP0 transmit data have error!\n");
+		if (frame_get_info(ep->txframe) & PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		txcomplete(ep, 1);
+	} else
+		txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe); /* send the data */
+	return 0;
+}
+
+/* confirm the already trainsmited bd */
+static int qe_ep_txconf(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+	unsigned char breakonrxinterrupt = 0;
+
+	bd = ep->c_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+		if (bdstatus & DEVICE_T_ERROR) {
+			frame_set_status(pframe, FRAME_ERROR);
+			if (bdstatus & T_TO)
+				pframe->status |= TX_ER_TIMEOUT;
+			if (bdstatus & T_UN)
+				pframe->status |= TX_ER_UNDERUN;
+		}
+
+		/* clear and recycle the BD */
+		out_be32((u32 __iomem *)bd, bdstatus & T_W);
+		out_be32(&bd->buf, 0);
+		if (bdstatus & T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		/* handle the tx frame */
+		ep_txframe_handle(ep);
+		bd = ep->c_txbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+	}
+	if (breakonrxinterrupt)
+		return -EIO;
+	else
+		return 0;
+}
+
+/* Add a request in queue, and try to transmit a packet */
+static int ep_req_send(struct qe_ep *ep, struct qe_req *req)
+{
+	int reval = 0;
+
+	if (ep->tx_req == NULL) {
+		ep->sent = 0;
+		ep->last = 0;
+		txcomplete(ep, 0); /* can gain a new tx_req */
+		reval = frame_create_tx(ep, ep->txframe);
+	}
+	return reval;
+}
+
+/* Maybe this is a good ideal */
+static int ep_req_rx(struct qe_ep *ep, struct qe_req *req)
+{
+	struct qe_udc *udc = ep->udc;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd __iomem *bd;
+	u32 bdstatus, length;
+	u32 vaddr, fsize;
+	u8 *cp;
+	u8 finish_req = 0;
+	u8 framepid;
+
+	if (list_empty(&ep->queue)) {
+		dev_vdbg(udc->dev, "the req already finish!\n");
+		return 0;
+	}
+	pframe = ep->rxframe;
+
+	bd = ep->n_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus & R_E) && length) {
+		if (finish_req)
+			break;
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+					&& !(bdstatus & R_ERROR)) {
+			qe_frame_clean(pframe);
+			vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+			frame_set_data(pframe, (u8 *)vaddr);
+			frame_set_length(pframe, (length - USB_CRC_SIZE));
+			frame_set_status(pframe, FRAME_OK);
+			switch (bdstatus & R_PID) {
+			case R_PID_DATA1:
+				frame_set_info(pframe, PID_DATA1); break;
+			default:
+				frame_set_info(pframe, PID_DATA0); break;
+			}
+			/* handle the rx frame */
+
+			if (frame_get_info(pframe) & PID_DATA1)
+				framepid = 0x1;
+			else
+				framepid = 0;
+
+			if (framepid != ep->data01) {
+				dev_vdbg(udc->dev, "the data01 error!\n");
+			} else {
+				fsize = frame_get_length(pframe);
+
+				cp = (u8 *)(req->req.buf) + req->req.actual;
+				if (cp) {
+					memcpy(cp, pframe->data, fsize);
+					req->req.actual += fsize;
+					if ((fsize < ep->ep.maxpacket)
+						|| (req->req.actual >=
+							req->req.length)) {
+						finish_req = 1;
+						done(ep, req, 0);
+						if (list_empty(&ep->queue))
+							qe_eprx_nack(ep);
+					}
+				}
+				qe_ep_toggledata01(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address *
+		 * only Clear the length */
+		out_be32((u32 __iomem *)bd, (bdstatus & BD_STATUS_MASK));
+		ep->has_data--;
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+	}
+
+	ep->n_rxbd = bd;
+	ep_recycle_rxbds(ep);
+
+	return 0;
+}
+
+/* only add the request in queue */
+static int ep_req_receive(struct qe_ep *ep, struct qe_req *req)
+{
+	if (ep->state == EP_STATE_NACK) {
+		if (ep->has_data <= 0) {
+			/* Enable rx and unmask rx interrupt */
+			qe_eprx_normal(ep);
+		} else {
+			/* Copy the exist BD data */
+			ep_req_rx(ep, req);
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************
+	Internal Used Function End
+********************************************************************/
+
+/*-----------------------------------------------------------------------
+	Endpoint Management Functions For Gadget
+ -----------------------------------------------------------------------*/
+static int qe_ep_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_udc *udc;
+	struct qe_ep *ep;
+	int retval = 0;
+	unsigned char epnum;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc || _ep->name == ep_name[0] ||
+			(desc->bDescriptorType != USB_DT_ENDPOINT))
+		return -EINVAL;
+
+	udc = ep->udc;
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	epnum = (u8)desc->bEndpointAddress & 0xF;
+
+	retval = qe_ep_init(udc, epnum, desc);
+	if (retval != 0) {
+		cpm_muram_free(cpm_muram_offset(ep->rxbase));
+		dev_dbg(udc->dev, "enable ep%d failed\n", ep->epnum);
+		return -EINVAL;
+	}
+	dev_dbg(udc->dev, "enable ep%d successful\n", ep->epnum);
+	return 0;
+}
+
+static int qe_ep_disable(struct usb_ep *_ep)
+{
+	struct qe_udc *udc;
+	struct qe_ep *ep;
+	unsigned long flags;
+	unsigned int size;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+	udc = ep->udc;
+
+	if (!_ep || !ep->ep.desc) {
+		dev_dbg(udc->dev, "%s not enabled\n", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+	ep->tx_req = NULL;
+	qe_ep_reset(udc, ep->epnum);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	cpm_muram_free(cpm_muram_offset(ep->rxbase));
+
+	if (ep->dir == USB_DIR_OUT)
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN_RX + 1);
+	else
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN + 1);
+
+	if (ep->dir != USB_DIR_IN) {
+		kfree(ep->rxframe);
+		if (ep->rxbufmap) {
+			dma_unmap_single(udc->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+			ep->rxbuf_d = DMA_ADDR_INVALID;
+		} else {
+			dma_sync_single_for_cpu(
+					udc->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		}
+		kfree(ep->rxbuffer);
+	}
+
+	if (ep->dir != USB_DIR_OUT)
+		kfree(ep->txframe);
+
+	dev_dbg(udc->dev, "disabled %s OK\n", _ep->name);
+	return 0;
+}
+
+static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags)
+{
+	struct qe_req *req;
+
+	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 qe_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req;
+
+	req = container_of(_req, struct qe_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+	struct qe_udc *udc;
+	int reval;
+
+	udc = ep->udc;
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		dev_dbg(udc->dev, "bad params\n");
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->ep.desc && ep_index(ep))) {
+		dev_dbg(udc->dev, "bad ep\n");
+		return -EINVAL;
+	}
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+					req->req.buf,
+					req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					req->req.dma, req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+
+	list_add_tail(&req->queue, &ep->queue);
+	dev_vdbg(udc->dev, "gadget have request in %s! %d\n",
+			ep->name, req->req.length);
+
+	/* push the request to device */
+	if (ep_is_in(ep))
+		reval = ep_req_send(ep, req);
+
+	/* EP0 */
+	if (ep_index(ep) == 0 && req->req.length > 0) {
+		if (ep_is_in(ep))
+			udc->ep0_state = DATA_STATE_XMIT;
+		else
+			udc->ep0_state = DATA_STATE_RECV;
+	}
+
+	if (ep->dir == USB_DIR_OUT)
+		reval = ep_req_receive(ep, req);
+
+	return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+		       gfp_t gfp_flags)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_udc *udc = ep->udc;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	ret = __qe_ep_queue(_ep, _req);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return ret;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req;
+	unsigned long flags;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually 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->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int qe_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct qe_ep *ep;
+	unsigned long flags;
+	int status = -EOPNOTSUPP;
+	struct qe_udc *udc;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+	if (!_ep || !ep->ep.desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	udc = ep->udc;
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	qe_eptx_stall_change(ep, value);
+	qe_eprx_stall_change(ep, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep->epnum == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+
+	/* set data toggle to DATA0 on clear halt */
+	if (value == 0)
+		ep->data01 = 0;
+out:
+	dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static const struct usb_ep_ops qe_ep_ops = {
+	.enable = qe_ep_enable,
+	.disable = qe_ep_disable,
+
+	.alloc_request = qe_alloc_request,
+	.free_request = qe_free_request,
+
+	.queue = qe_ep_queue,
+	.dequeue = qe_ep_dequeue,
+
+	.set_halt = qe_ep_set_halt,
+};
+
+/*------------------------------------------------------------------------
+	Gadget Driver Layer Operations
+ ------------------------------------------------------------------------*/
+
+/* Get the current frame number */
+static int qe_get_frame(struct usb_gadget *gadget)
+{
+	struct qe_udc *udc = container_of(gadget, struct qe_udc, gadget);
+	u16 tmp;
+
+	tmp = in_be16(&udc->usb_param->frame_n);
+	if (tmp & 0x8000)
+		return tmp & 0x07ff;
+	return -EINVAL;
+}
+
+static int fsl_qe_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver);
+static int fsl_qe_stop(struct usb_gadget *gadget);
+
+/* defined in usb_gadget.h */
+static const struct usb_gadget_ops qe_gadget_ops = {
+	.get_frame = qe_get_frame,
+	.udc_start = fsl_qe_start,
+	.udc_stop = fsl_qe_stop,
+};
+
+/*-------------------------------------------------------------------------
+	USB ep0 Setup process in BUS Enumeration
+ -------------------------------------------------------------------------*/
+static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe)
+{
+	struct qe_ep *ep = &udc->eps[pipe];
+
+	nuke(ep, -ECONNRESET);
+	ep->tx_req = NULL;
+	return 0;
+}
+
+static int reset_queues(struct qe_udc *udc)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	spin_unlock(&udc->lock);
+	usb_gadget_udc_reset(&udc->gadget, udc->driver);
+	spin_lock(&udc->lock);
+
+	return 0;
+}
+
+static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index,
+			u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+
+	/* Status phase , send a ZLP */
+	if (ep0_prime_status(udc, USB_DIR_IN))
+		qe_ep0_stall(udc);
+}
+
+static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+
+	req->req.buf = NULL;
+	kfree(req);
+}
+
+static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
+			u16 index, u16 length)
+{
+	u16 usb_status = 0;
+	struct qe_req *req;
+	struct qe_ep *ep;
+	int status = 0;
+
+	ep = &udc->eps[0];
+	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		/* Get device status */
+		usb_status = 1 << USB_DEVICE_SELF_POWERED;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+		/* Get interface status */
+		/* We don't have interface information in udc driver */
+		usb_status = 0;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+		/* Get endpoint status */
+		int pipe = index & USB_ENDPOINT_NUMBER_MASK;
+		struct qe_ep *target_ep = &udc->eps[pipe];
+		u16 usep;
+
+		/* stall if endpoint doesn't exist */
+		if (!target_ep->ep.desc)
+			goto stall;
+
+		usep = in_be16(&udc->usb_regs->usb_usep[pipe]);
+		if (index & USB_DIR_IN) {
+			if (target_ep->dir != USB_DIR_IN)
+				goto stall;
+			if ((usep & USB_THS_MASK) == USB_THS_STALL)
+				usb_status = 1 << USB_ENDPOINT_HALT;
+		} else {
+			if (target_ep->dir != USB_DIR_OUT)
+				goto stall;
+			if ((usep & USB_RHS_MASK) == USB_RHS_STALL)
+				usb_status = 1 << USB_ENDPOINT_HALT;
+		}
+	}
+
+	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
+					struct qe_req, req);
+	req->req.length = 2;
+	req->req.buf = udc->statusbuf;
+	*(u16 *)req->req.buf = cpu_to_le16(usb_status);
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = ownercomplete;
+
+	udc->ep0_dir = USB_DIR_IN;
+
+	/* data phase */
+	status = __qe_ep_queue(&ep->ep, &req->req);
+
+	if (status == 0)
+		return;
+stall:
+	dev_err(udc->dev, "Can't respond to getstatus request \n");
+	qe_ep0_stall(udc);
+}
+
+/* only handle the setup request, suppose the device in normal status */
+static void setup_received_handle(struct qe_udc *udc,
+				struct usb_ctrlrequest *setup)
+{
+	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	/* clear the previous request in the ep0 */
+	udc_reset_ep_queue(udc, 0);
+
+	if (setup->bRequestType & USB_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase form udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, setup->bRequestType, wValue, wIndex,
+					wLength);
+		return;
+
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+						USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Requests with no data phase, status phase from udc */
+		if ((setup->bRequestType & USB_TYPE_MASK)
+					!= USB_TYPE_STANDARD)
+			break;
+
+		if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK;
+			struct qe_ep *ep;
+
+			if (wValue != 0 || wLength != 0
+				|| pipe >= USB_MAX_ENDPOINTS)
+				break;
+			ep = &udc->eps[pipe];
+
+			spin_unlock(&udc->lock);
+			qe_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+		}
+
+		ep0_prime_status(udc, USB_DIR_IN);
+
+		return;
+
+	default:
+		break;
+	}
+
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		if (setup->bRequestType & USB_DIR_IN) {
+			udc->ep0_state = DATA_STATE_XMIT;
+			udc->ep0_dir = USB_DIR_IN;
+		} else {
+			udc->ep0_state = DATA_STATE_RECV;
+			udc->ep0_dir = USB_DIR_OUT;
+		}
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+	}
+}
+
+/*-------------------------------------------------------------------------
+	USB Interrupt handlers
+ -------------------------------------------------------------------------*/
+static void suspend_irq(struct qe_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver ,serial.c not support this*/
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void resume_irq(struct qe_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver , serial.c not support this*/
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+static void idle_irq(struct qe_udc *udc)
+{
+	u8 usbs;
+
+	usbs = in_8(&udc->usb_regs->usb_usbs);
+	if (usbs & USB_IDLE_STATUS_MASK) {
+		if ((udc->usb_state) != USB_STATE_SUSPENDED)
+			suspend_irq(udc);
+	} else {
+		if (udc->usb_state == USB_STATE_SUSPENDED)
+			resume_irq(udc);
+	}
+}
+
+static int reset_irq(struct qe_udc *udc)
+{
+	unsigned char i;
+
+	if (udc->usb_state == USB_STATE_DEFAULT)
+		return 0;
+
+	qe_usb_disable(udc);
+	out_8(&udc->usb_regs->usb_usadr, 0);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		if (udc->eps[i].init)
+			qe_ep_reset(udc, i);
+	}
+
+	reset_queues(udc);
+	udc->usb_state = USB_STATE_DEFAULT;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+	qe_usb_enable(udc);
+	return 0;
+}
+
+static int bsy_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+static int txe_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+/* ep0 tx interrupt also in here */
+static int tx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	struct qe_bd __iomem *bd;
+	int i, res = 0;
+
+	if ((udc->usb_state == USB_STATE_ADDRESS)
+		&& (in_8(&udc->usb_regs->usb_usadr) == 0))
+		out_8(&udc->usb_regs->usb_usadr, udc->device_address);
+
+	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) {
+		ep = &udc->eps[i];
+		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) {
+			bd = ep->c_txbd;
+			if (!(in_be32((u32 __iomem *)bd) & T_R)
+						&& (in_be32(&bd->buf))) {
+				/* confirm the transmitted bd */
+				if (ep->epnum == 0)
+					res = qe_ep0_txconf(ep);
+				else
+					res = qe_ep_txconf(ep);
+			}
+		}
+	}
+	return res;
+}
+
+
+/* setup packect's rx is handle in the function too */
+static void rx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	struct qe_bd __iomem *bd;
+	int i;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &udc->eps[i];
+		if (ep && ep->init && (ep->dir != USB_DIR_IN)) {
+			bd = ep->n_rxbd;
+			if (!(in_be32((u32 __iomem *)bd) & R_E)
+						&& (in_be32(&bd->buf))) {
+				if (ep->epnum == 0) {
+					qe_ep0_rx(udc);
+				} else {
+					/*non-setup package receive*/
+					qe_ep_rx(ep);
+				}
+			}
+		}
+	}
+}
+
+static irqreturn_t qe_udc_irq(int irq, void *_udc)
+{
+	struct qe_udc *udc = (struct qe_udc *)_udc;
+	u16 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	irq_src = in_be16(&udc->usb_regs->usb_usber) &
+		in_be16(&udc->usb_regs->usb_usbmr);
+	/* Clear notification bits */
+	out_be16(&udc->usb_regs->usb_usber, irq_src);
+	/* USB Interrupt */
+	if (irq_src & USB_E_IDLE_MASK) {
+		idle_irq(udc);
+		irq_src &= ~USB_E_IDLE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXB_MASK) {
+		tx_irq(udc);
+		irq_src &= ~USB_E_TXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RXB_MASK) {
+		rx_irq(udc);
+		irq_src &= ~USB_E_RXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RESET_MASK) {
+		reset_irq(udc);
+		irq_src &= ~USB_E_RESET_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_BSY_MASK) {
+		bsy_irq(udc);
+		irq_src &= ~USB_E_BSY_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXE_MASK) {
+		txe_irq(udc);
+		irq_src &= ~USB_E_TXE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------
+	Gadget driver probe and unregister.
+ --------------------------------------------------------------------------*/
+static int fsl_qe_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct qe_udc *udc;
+	unsigned long flags;
+
+	udc = container_of(gadget, struct qe_udc, gadget);
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc->lock, flags);
+
+	driver->driver.bus = NULL;
+	/* hook up the driver */
+	udc->driver = driver;
+	udc->gadget.speed = driver->max_speed;
+
+	/* Enable IRQ reg and Set usbcmd reg EN bit */
+	qe_usb_enable(udc);
+
+	out_be16(&udc->usb_regs->usb_usber, 0xffff);
+	out_be16(&udc->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
+	udc->usb_state = USB_STATE_ATTACHED;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int fsl_qe_stop(struct usb_gadget *gadget)
+{
+	struct qe_udc *udc;
+	struct qe_ep *loop_ep;
+	unsigned long flags;
+
+	udc = container_of(gadget, struct qe_udc, gadget);
+	/* stop usb controller, disable intr */
+	qe_usb_disable(udc);
+
+	/* in fact, no needed */
+	udc->usb_state = USB_STATE_ATTACHED;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc->gadget.ep_list, ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	udc->driver = NULL;
+
+	return 0;
+}
+
+/* udc structure's alloc and setup, include ep-param alloc */
+static struct qe_udc *qe_udc_config(struct platform_device *ofdev)
+{
+	struct qe_udc *udc;
+	struct device_node *np = ofdev->dev.of_node;
+	unsigned long tmp_addr = 0;
+	struct usb_device_para __iomem *usbpram;
+	unsigned int i;
+	u64 size;
+	u32 offset;
+
+	udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		goto cleanup;
+
+	udc->dev = &ofdev->dev;
+
+	/* get default address of usb parameter in MURAM from device tree */
+	offset = *of_get_address(np, 1, &size, NULL);
+	udc->usb_param = cpm_muram_addr(offset);
+	memset_io(udc->usb_param, 0, size);
+
+	usbpram = udc->usb_param;
+	out_be16(&usbpram->frame_n, 0);
+	out_be32(&usbpram->rstate, 0);
+
+	tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS *
+					sizeof(struct usb_ep_para)),
+					   USB_EP_PARA_ALIGNMENT);
+	if (IS_ERR_VALUE(tmp_addr))
+		goto cleanup;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		out_be16(&usbpram->epptr[i], (u16)tmp_addr);
+		udc->ep_param[i] = cpm_muram_addr(tmp_addr);
+		tmp_addr += 32;
+	}
+
+	memset_io(udc->ep_param[0], 0,
+			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para));
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+
+	spin_lock_init(&udc->lock);
+	return udc;
+
+cleanup:
+	kfree(udc);
+	return NULL;
+}
+
+/* USB Controller register init */
+static int qe_udc_reg_init(struct qe_udc *udc)
+{
+	struct usb_ctlr __iomem *qe_usbregs;
+	qe_usbregs = udc->usb_regs;
+
+	/* Spec says that we must enable the USB controller to change mode. */
+	out_8(&qe_usbregs->usb_usmod, 0x01);
+	/* Mode changed, now disable it, since muram isn't initialized yet. */
+	out_8(&qe_usbregs->usb_usmod, 0x00);
+
+	/* Initialize the rest. */
+	out_be16(&qe_usbregs->usb_usbmr, 0);
+	out_8(&qe_usbregs->usb_uscom, 0);
+	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR);
+
+	return 0;
+}
+
+static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+
+	ep->udc = udc;
+	strcpy(ep->name, ep_name[pipe_num]);
+	ep->ep.name = ep_name[pipe_num];
+
+	if (pipe_num == 0) {
+		ep->ep.caps.type_control = true;
+	} else {
+		ep->ep.caps.type_iso = true;
+		ep->ep.caps.type_bulk = true;
+		ep->ep.caps.type_int = true;
+	}
+
+	ep->ep.caps.dir_in = true;
+	ep->ep.caps.dir_out = true;
+
+	ep->ep.ops = &qe_ep_ops;
+	ep->stopped = 1;
+	usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
+	ep->ep.desc = NULL;
+	ep->dir = 0xff;
+	ep->epnum = (u8)pipe_num;
+	ep->sent = 0;
+	ep->last = 0;
+	ep->init = 0;
+	ep->rxframe = NULL;
+	ep->txframe = NULL;
+	ep->tx_req = NULL;
+	ep->state = EP_STATE_IDLE;
+	ep->has_data = 0;
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0*/
+	if (pipe_num != 0)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+	ep->gadget = &udc->gadget;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ *	UDC device Driver operation functions				*
+ *----------------------------------------------------------------------*/
+static void qe_udc_release(struct device *dev)
+{
+	struct qe_udc *udc = container_of(dev, struct qe_udc, gadget.dev);
+	int i;
+
+	complete(udc->done);
+	cpm_muram_free(cpm_muram_offset(udc->ep_param[0]));
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+		udc->ep_param[i] = NULL;
+
+	kfree(udc);
+}
+
+/* Driver probe functions */
+static const struct of_device_id qe_udc_match[];
+static int qe_udc_probe(struct platform_device *ofdev)
+{
+	struct qe_udc *udc;
+	const struct of_device_id *match;
+	struct device_node *np = ofdev->dev.of_node;
+	struct qe_ep *ep;
+	unsigned int ret = 0;
+	unsigned int i;
+	const void *prop;
+
+	match = of_match_device(qe_udc_match, &ofdev->dev);
+	if (!match)
+		return -EINVAL;
+
+	prop = of_get_property(np, "mode", NULL);
+	if (!prop || strcmp(prop, "peripheral"))
+		return -ENODEV;
+
+	/* Initialize the udc structure including QH member and other member */
+	udc = qe_udc_config(ofdev);
+	if (!udc) {
+		dev_err(&ofdev->dev, "failed to initialize\n");
+		return -ENOMEM;
+	}
+
+	udc->soc_type = (unsigned long)match->data;
+	udc->usb_regs = of_iomap(np, 0);
+	if (!udc->usb_regs) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	/* initialize usb hw reg except for regs for EP,
+	 * leave usbintr reg untouched*/
+	qe_udc_reg_init(udc);
+
+	/* here comes the stand operations for probe
+	 * set the qe_udc->gadget.xxx */
+	udc->gadget.ops = &qe_gadget_ops;
+
+	/* gadget.ep0 is a pointer */
+	udc->gadget.ep0 = &udc->eps[0].ep;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+	/* modify in register gadget process */
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* name: Identifies the controller hardware type. */
+	udc->gadget.name = driver_name;
+	udc->gadget.dev.parent = &ofdev->dev;
+
+	/* initialize qe_ep struct */
+	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
+		/* because the ep type isn't decide here so
+		 * qe_ep_init() should be called in ep_enable() */
+
+		/* setup the qe_ep struct and link ep.ep.list
+		 * into gadget.ep_list */
+		qe_ep_config(udc, (unsigned char)i);
+	}
+
+	/* ep0 initialization in here */
+	ret = qe_ep_init(udc, 0, &qe_ep0_desc);
+	if (ret)
+		goto err2;
+
+	/* create a buf for ZLP send, need to remain zeroed */
+	udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL);
+	if (udc->nullbuf == NULL) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	/* buffer for data of get_status request */
+	udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL);
+	if (udc->statusbuf == NULL) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	udc->nullp = virt_to_phys((void *)udc->nullbuf);
+	if (udc->nullp == DMA_ADDR_INVALID) {
+		udc->nullp = dma_map_single(
+					udc->gadget.dev.parent,
+					udc->nullbuf,
+					256,
+					DMA_TO_DEVICE);
+		udc->nullmap = 1;
+	} else {
+		dma_sync_single_for_device(udc->gadget.dev.parent,
+					udc->nullp, 256,
+					DMA_TO_DEVICE);
+	}
+
+	tasklet_init(&udc->rx_tasklet, ep_rx_tasklet,
+			(unsigned long)udc);
+	/* request irq and disable DR  */
+	udc->usb_irq = irq_of_parse_and_map(np, 0);
+	if (!udc->usb_irq) {
+		ret = -EINVAL;
+		goto err_noirq;
+	}
+
+	ret = request_irq(udc->usb_irq, qe_udc_irq, 0,
+				driver_name, udc);
+	if (ret) {
+		dev_err(udc->dev, "cannot request irq %d err %d\n",
+				udc->usb_irq, ret);
+		goto err4;
+	}
+
+	ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget,
+			qe_udc_release);
+	if (ret)
+		goto err5;
+
+	platform_set_drvdata(ofdev, udc);
+	dev_info(udc->dev,
+			"%s USB controller initialized as device\n",
+			(udc->soc_type == PORT_QE) ? "QE" : "CPM");
+	return 0;
+
+err5:
+	free_irq(udc->usb_irq, udc);
+err4:
+	irq_dispose_mapping(udc->usb_irq);
+err_noirq:
+	if (udc->nullmap) {
+		dma_unmap_single(udc->gadget.dev.parent,
+			udc->nullp, 256,
+				DMA_TO_DEVICE);
+			udc->nullp = DMA_ADDR_INVALID;
+	} else {
+		dma_sync_single_for_cpu(udc->gadget.dev.parent,
+			udc->nullp, 256,
+				DMA_TO_DEVICE);
+	}
+err3:
+	ep = &udc->eps[0];
+	cpm_muram_free(cpm_muram_offset(ep->rxbase));
+	kfree(ep->rxframe);
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+err2:
+	iounmap(udc->usb_regs);
+err1:
+	kfree(udc);
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int qe_udc_suspend(struct platform_device *dev, pm_message_t state)
+{
+	return -ENOTSUPP;
+}
+
+static int qe_udc_resume(struct platform_device *dev)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static int qe_udc_remove(struct platform_device *ofdev)
+{
+	struct qe_udc *udc = platform_get_drvdata(ofdev);
+	struct qe_ep *ep;
+	unsigned int size;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	usb_del_gadget_udc(&udc->gadget);
+
+	udc->done = &done;
+	tasklet_disable(&udc->rx_tasklet);
+
+	if (udc->nullmap) {
+		dma_unmap_single(udc->gadget.dev.parent,
+			udc->nullp, 256,
+				DMA_TO_DEVICE);
+			udc->nullp = DMA_ADDR_INVALID;
+	} else {
+		dma_sync_single_for_cpu(udc->gadget.dev.parent,
+			udc->nullp, 256,
+				DMA_TO_DEVICE);
+	}
+
+	ep = &udc->eps[0];
+	cpm_muram_free(cpm_muram_offset(ep->rxbase));
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (USB_BDRING_LEN + 1);
+
+	kfree(ep->rxframe);
+	if (ep->rxbufmap) {
+		dma_unmap_single(udc->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+		ep->rxbuf_d = DMA_ADDR_INVALID;
+	} else {
+		dma_sync_single_for_cpu(udc->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+	}
+
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+
+	free_irq(udc->usb_irq, udc);
+	irq_dispose_mapping(udc->usb_irq);
+
+	tasklet_kill(&udc->rx_tasklet);
+
+	iounmap(udc->usb_regs);
+
+	/* wait for release() of gadget.dev to free udc */
+	wait_for_completion(&done);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static const struct of_device_id qe_udc_match[] = {
+	{
+		.compatible = "fsl,mpc8323-qe-usb",
+		.data = (void *)PORT_QE,
+	},
+	{
+		.compatible = "fsl,mpc8360-qe-usb",
+		.data = (void *)PORT_QE,
+	},
+	{
+		.compatible = "fsl,mpc8272-cpm-usb",
+		.data = (void *)PORT_CPM,
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, qe_udc_match);
+
+static struct platform_driver udc_driver = {
+	.driver = {
+		.name = driver_name,
+		.of_match_table = qe_udc_match,
+	},
+	.probe          = qe_udc_probe,
+	.remove         = qe_udc_remove,
+#ifdef CONFIG_PM
+	.suspend        = qe_udc_suspend,
+	.resume         = qe_udc_resume,
+#endif
+};
+
+module_platform_driver(udc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h
new file mode 100644
index 0000000..2c537a9
--- /dev/null
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.h
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/usb/gadget/qe_udc.h
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 	Xiaobo Xie <X.Xie@freescale.com>
+ * 	Li Yang <leoli@freescale.com>
+ *
+ * Description:
+ * Freescale USB device/endpoint management registers
+ */
+
+#ifndef __FSL_QE_UDC_H
+#define __FSL_QE_UDC_H
+
+/* SoC type */
+#define PORT_CPM	0
+#define PORT_QE		1
+
+#define USB_MAX_ENDPOINTS               4
+#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
+#define USB_EP0_MAX_SIZE		64
+#define USB_MAX_CTRL_PAYLOAD            0x4000
+#define USB_BDRING_LEN			16
+#define USB_BDRING_LEN_RX		256
+#define USB_BDRING_LEN_TX		16
+#define MIN_EMPTY_BDS			128
+#define MAX_DATA_BDS			8
+#define USB_CRC_SIZE			2
+#define USB_DIR_BOTH			0x88
+#define R_BUF_MAXSIZE			0x800
+#define USB_EP_PARA_ALIGNMENT		32
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN		0x01
+#define USB_MODE_HOST		0x02
+#define USB_MODE_TEST		0x04
+#define USB_MODE_SFTE		0x08
+#define USB_MODE_RESUME		0x40
+#define USB_MODE_LSS		0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK	0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK		0xF000
+#define USB_EPNUM_SHIFT		12
+
+#define USB_TRANS_MODE_SHIFT	8
+#define USB_TRANS_CTR		0x0000
+#define USB_TRANS_INT		0x0100
+#define USB_TRANS_BULK		0x0200
+#define USB_TRANS_ISO		0x0300
+
+#define USB_EP_MF		0x0020
+#define USB_EP_RTE		0x0010
+
+#define USB_THS_SHIFT		2
+#define USB_THS_MASK		0x000c
+#define USB_THS_NORMAL		0x0
+#define USB_THS_IGNORE_IN	0x0004
+#define USB_THS_NACK		0x0008
+#define USB_THS_STALL		0x000c
+
+#define USB_RHS_SHIFT   	0
+#define USB_RHS_MASK		0x0003
+#define USB_RHS_NORMAL  	0x0
+#define USB_RHS_IGNORE_OUT	0x0001
+#define USB_RHS_NACK		0x0002
+#define USB_RHS_STALL		0x0003
+
+#define USB_RTHS_MASK		0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO	0x80
+#define USB_CMD_FLUSH_FIFO	0x40
+#define USB_CMD_ISFT		0x20
+#define USB_CMD_DSFT		0x10
+#define USB_CMD_EP_MASK		0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK		0x0800
+#define USB_E_SFT_MASK		0x0400
+#define USB_E_RESET_MASK	0x0200
+#define USB_E_IDLE_MASK		0x0100
+#define USB_E_TXE4_MASK		0x0080
+#define USB_E_TXE3_MASK		0x0040
+#define USB_E_TXE2_MASK		0x0020
+#define USB_E_TXE1_MASK		0x0010
+#define USB_E_SOF_MASK		0x0008
+#define USB_E_BSY_MASK		0x0004
+#define USB_E_TXB_MASK		0x0002
+#define USB_E_RXB_MASK		0x0001
+#define USBER_ALL_CLEAR 	0x0fff
+
+#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
+				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
+				USB_E_TXE1_MASK | USB_E_BSY_MASK | \
+				USB_E_TXB_MASK | USB_E_RXB_MASK)
+
+#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
+				 USB_E_TXE2_MASK | USB_E_TXE1_MASK)
+/* USB Status Register define */
+#define USB_IDLE_STATUS_MASK	0x01
+
+/* USB Start of Frame Timer */
+#define USB_USSFT_MASK		0x3FFF
+
+/* USB Frame Number Register */
+#define USB_USFRN_MASK		0xFFFF
+
+struct usb_device_para{
+	u16	epptr[4];
+	u32	rstate;
+	u32	rptr;
+	u16	frame_n;
+	u16	rbcnt;
+	u32	rtemp;
+	u32	rxusb_data;
+	u16	rxuptr;
+	u8	reso[2];
+	u32	softbl;
+	u8	sofucrctemp;
+};
+
+struct usb_ep_para{
+	u16	rbase;
+	u16	tbase;
+	u8	rbmr;
+	u8	tbmr;
+	u16	mrblr;
+	u16	rbptr;
+	u16	tbptr;
+	u32	tstate;
+	u32	tptr;
+	u16	tcrc;
+	u16	tbcnt;
+	u32	ttemp;
+	u16	txusbu_ptr;
+	u8	reserve[2];
+};
+
+#define USB_BUSMODE_GBL		0x20
+#define USB_BUSMODE_BO_MASK	0x18
+#define USB_BUSMODE_BO_SHIFT	0x3
+#define USB_BUSMODE_BE		0x2
+#define USB_BUSMODE_CETM	0x04
+#define USB_BUSMODE_DTB		0x02
+
+/* Endpoint basic handle */
+#define ep_index(EP)		((EP)->ep.desc->bEndpointAddress & 0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \
+			& USB_DIR_IN) == USB_DIR_IN)
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* ep tramsfer mode */
+#define USBP_TM_CTL	0
+#define USBP_TM_ISO	1
+#define USBP_TM_BULK	2
+#define USBP_TM_INT	3
+
+/*-----------------------------------------------------------------------------
+	USB RX And TX DATA Frame
+ -----------------------------------------------------------------------------*/
+struct qe_frame{
+	u8 *data;
+	u32 len;
+	u32 status;
+	u32 info;
+
+	void *privdata;
+	struct list_head node;
+};
+
+/* Frame structure, info field. */
+#define PID_DATA0              0x80000000 /* Data toggle zero */
+#define PID_DATA1              0x40000000 /* Data toggle one  */
+#define PID_SETUP              0x20000000 /* setup bit */
+#define SETUP_STATUS           0x10000000 /* setup status bit */
+#define SETADDR_STATUS         0x08000000 /* setupup address status bit */
+#define NO_REQ                 0x04000000 /* Frame without request */
+#define HOST_DATA              0x02000000 /* Host data frame */
+#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */
+#define TOKEN_FRAME            0x00800000 /* Host token frame */
+#define ZLP                    0x00400000 /* Zero length packet */
+#define IN_TOKEN_FRAME         0x00200000 /* In token package */
+#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */
+#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */
+#define STALL_FRAME            0x00040000 /* Stall handshake */
+#define NACK_FRAME             0x00020000 /* Nack handshake */
+#define NO_PID                 0x00010000 /* No send PID */
+#define NO_CRC                 0x00008000 /* No send CRC */
+#define HOST_COMMAND           0x00004000 /* Host command frame   */
+
+/* Frame status field */
+/* Receive side */
+#define FRAME_OK               0x00000000 /* Frame transmitted or received OK */
+#define FRAME_ERROR            0x80000000 /* Error occurred on frame */
+#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
+#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
+#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */
+#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet
+					     with bit stuff error */
+#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */
+#define RX_ER_OVERUN           0x02000000 /* Over-run occurred on reception */
+#define RX_ER_PID              0x01000000 /* Wrong PID received */
+/* Tranmit side */
+#define TX_ER_NAK              0x00800000 /* Received NAK handshake */
+#define TX_ER_STALL            0x00400000 /* Received STALL handshake */
+#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */
+#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */
+#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */
+#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */
+#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */
+
+/* QE USB frame operation functions */
+#define frame_get_length(frm) (frm->len)
+#define frame_set_length(frm, leng) (frm->len = leng)
+#define frame_get_data(frm) (frm->data)
+#define frame_set_data(frm, dat) (frm->data = dat)
+#define frame_get_info(frm) (frm->info)
+#define frame_set_info(frm, inf) (frm->info = inf)
+#define frame_get_status(frm) (frm->status)
+#define frame_set_status(frm, stat) (frm->status = stat)
+#define frame_get_privdata(frm) (frm->privdata)
+#define frame_set_privdata(frm, dat) (frm->privdata = dat)
+
+static inline void qe_frame_clean(struct qe_frame *frm)
+{
+	frame_set_data(frm, NULL);
+	frame_set_length(frm, 0);
+	frame_set_status(frm, FRAME_OK);
+	frame_set_info(frm, 0);
+	frame_set_privdata(frm, NULL);
+}
+
+static inline void qe_frame_init(struct qe_frame *frm)
+{
+	qe_frame_clean(frm);
+	INIT_LIST_HEAD(&(frm->node));
+}
+
+struct qe_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	 a request->queue into a udc_ep->queue 'd tail */
+	struct qe_ep *ep;
+	unsigned mapped:1;
+};
+
+struct qe_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct qe_udc *udc;
+	struct usb_gadget *gadget;
+
+	u8 state;
+
+	struct qe_bd __iomem *rxbase;
+	struct qe_bd __iomem *n_rxbd;
+	struct qe_bd __iomem *e_rxbd;
+
+	struct qe_bd __iomem *txbase;
+	struct qe_bd __iomem *n_txbd;
+	struct qe_bd __iomem *c_txbd;
+
+	struct qe_frame *rxframe;
+	u8 *rxbuffer;
+	dma_addr_t rxbuf_d;
+	u8 rxbufmap;
+	unsigned char localnack;
+	int has_data;
+
+	struct qe_frame *txframe;
+	struct qe_req *tx_req;
+	int sent;  /*data already sent */
+	int last;  /*data sent in the last time*/
+
+	u8 dir;
+	u8 epnum;
+	u8 tm; /* transfer mode */
+	u8 data01;
+	u8 init;
+
+	u8 already_seen;
+	u8 enable_tasklet;
+	u8 setup_stage;
+	u32 last_io;            /* timestamp */
+
+	char name[14];
+
+	unsigned double_buf:1;
+	unsigned stopped:1;
+	unsigned fnf:1;
+	unsigned has_dma:1;
+
+	u8 ackwait;
+	u8 dma_channel;
+	u16 dma_counter;
+	int lch;
+
+	struct timer_list timer;
+};
+
+struct qe_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct qe_ep eps[USB_MAX_ENDPOINTS];
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;	/* lock for set/config qe_udc */
+	unsigned long soc_type;		/* QE or CPM soc */
+
+	struct qe_req *status_req;	/* ep0 status request */
+
+	/* USB and EP Parameter Block pointer */
+	struct usb_device_para __iomem *usb_param;
+	struct usb_ep_para __iomem *ep_param[4];
+
+	u32 max_pipes;          /* Device max pipes */
+	u32 max_use_endpts;     /* Max endpointes to be used */
+	u32 bus_reset;          /* Device is bus reseting */
+	u32 resume_state;       /* USB state to resume*/
+	u32 usb_state;          /* USB current state */
+	u32 usb_next_state;     /* USB next state */
+	u32 ep0_state;          /* Enpoint zero state */
+	u32 ep0_dir;            /* Enpoint zero direction: can be
+				USB_DIR_IN or USB_DIR_OUT*/
+	u32 usb_sof_count;      /* SOF count */
+	u32 errors;             /* USB ERRORs count */
+
+	u8 *tmpbuf;
+	u32 c_start;
+	u32 c_end;
+
+	u8 *nullbuf;
+	u8 *statusbuf;
+	dma_addr_t nullp;
+	u8 nullmap;
+	u8 device_address;	/* Device USB address */
+
+	unsigned int usb_clock;
+	unsigned int usb_irq;
+	struct usb_ctlr __iomem *usb_regs;
+
+	struct tasklet_struct rx_tasklet;
+
+	struct completion *done;	/* to make sure release() is done */
+};
+
+#define EP_STATE_IDLE	0
+#define EP_STATE_NACK	1
+#define EP_STATE_STALL	2
+
+/*
+ * transmit BD's status
+ */
+#define T_R           0x80000000         /* ready bit */
+#define T_W           0x20000000         /* wrap bit */
+#define T_I           0x10000000         /* interrupt on completion */
+#define T_L           0x08000000         /* last */
+#define T_TC          0x04000000         /* transmit CRC */
+#define T_CNF         0x02000000         /* wait for  transmit confirm */
+#define T_LSP         0x01000000         /* Low-speed transaction */
+#define T_PID         0x00c00000         /* packet id */
+#define T_NAK         0x00100000         /* No ack. */
+#define T_STAL        0x00080000         /* Stall received */
+#define T_TO          0x00040000         /* time out */
+#define T_UN          0x00020000         /* underrun */
+
+#define DEVICE_T_ERROR    (T_UN | T_TO)
+#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL)
+#define DEVICE_T_BD_MASK  DEVICE_T_ERROR
+#define HOST_T_BD_MASK    HOST_T_ERROR
+
+#define T_PID_SHIFT   6
+#define T_PID_DATA0   0x00800000         /* Data 0 toggle */
+#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */
+
+/*
+ * receive BD's status
+ */
+#define R_E           0x80000000         /* buffer empty */
+#define R_W           0x20000000         /* wrap bit */
+#define R_I           0x10000000         /* interrupt on reception */
+#define R_L           0x08000000         /* last */
+#define R_F           0x04000000         /* first */
+#define R_PID         0x00c00000         /* packet id */
+#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */
+#define R_AB          0x00080000         /* Frame Aborted */
+#define R_CR          0x00040000         /* CRC Error */
+#define R_OV          0x00020000         /* Overrun */
+
+#define R_ERROR       (R_NO | R_AB | R_CR | R_OV)
+#define R_BD_MASK     R_ERROR
+
+#define R_PID_DATA0   0x00000000
+#define R_PID_DATA1   0x00400000
+#define R_PID_SETUP   0x00800000
+
+#define CPM_USB_STOP_TX 0x2e600000
+#define CPM_USB_RESTART_TX 0x2e600000
+#define CPM_USB_STOP_TX_OPCODE 0x0a
+#define CPM_USB_RESTART_TX_OPCODE 0x0b
+#define CPM_USB_EP_SHIFT 5
+
+#endif  /* __FSL_QE_UDC_H */
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
new file mode 100644
index 0000000..be59309
--- /dev/null
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -0,0 +1,2688 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Author: Li Yang <leoli@freescale.com>
+ *         Jiang Bo <tanya.jiang@freescale.com>
+ *
+ * Description:
+ * Freescale high-speed USB SOC DR module device controller driver.
+ * This can be found on MPC8349E/MPC8313E/MPC5121E cpus.
+ * The driver is previously named as mpc_udc.  Based on bare board
+ * code from Dave Liu and Shlomi Gridish.
+ */
+
+#undef VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dmapool.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+
+#include "fsl_usb2_udc.h"
+
+#define	DRIVER_DESC	"Freescale High-Speed USB SOC Device Controller driver"
+#define	DRIVER_AUTHOR	"Li Yang/Jiang Bo"
+#define	DRIVER_VERSION	"Apr 20, 2007"
+
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl-usb2-udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+static struct usb_dr_device __iomem *dr_regs;
+
+static struct usb_sys_interface __iomem *usb_sys_regs;
+
+/* it is initialized in probe()  */
+static struct fsl_udc *udc_controller = NULL;
+
+static const struct usb_endpoint_descriptor
+fsl_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep);
+
+#ifdef CONFIG_PPC32
+/*
+ * On some SoCs, the USB controller registers can be big or little endian,
+ * depending on the version of the chip. In order to be able to run the
+ * same kernel binary on 2 different versions of an SoC, the BE/LE decision
+ * must be made at run time. _fsl_readl and fsl_writel are pointers to the
+ * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel()
+ * call through those pointers. Platform code for SoCs that have BE USB
+ * registers should set pdata->big_endian_mmio flag.
+ *
+ * This also applies to controller-to-cpu accessors for the USB descriptors,
+ * since their endianness is also SoC dependant. Platform code for SoCs that
+ * have BE USB descriptors should set pdata->big_endian_desc flag.
+ */
+static u32 _fsl_readl_be(const unsigned __iomem *p)
+{
+	return in_be32(p);
+}
+
+static u32 _fsl_readl_le(const unsigned __iomem *p)
+{
+	return in_le32(p);
+}
+
+static void _fsl_writel_be(u32 v, unsigned __iomem *p)
+{
+	out_be32(p, v);
+}
+
+static void _fsl_writel_le(u32 v, unsigned __iomem *p)
+{
+	out_le32(p, v);
+}
+
+static u32 (*_fsl_readl)(const unsigned __iomem *p);
+static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
+
+#define fsl_readl(p)		(*_fsl_readl)((p))
+#define fsl_writel(v, p)	(*_fsl_writel)((v), (p))
+
+static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata)
+{
+	if (pdata->big_endian_mmio) {
+		_fsl_readl = _fsl_readl_be;
+		_fsl_writel = _fsl_writel_be;
+	} else {
+		_fsl_readl = _fsl_readl_le;
+		_fsl_writel = _fsl_writel_le;
+	}
+}
+
+static inline u32 cpu_to_hc32(const u32 x)
+{
+	return udc_controller->pdata->big_endian_desc
+		? (__force u32)cpu_to_be32(x)
+		: (__force u32)cpu_to_le32(x);
+}
+
+static inline u32 hc32_to_cpu(const u32 x)
+{
+	return udc_controller->pdata->big_endian_desc
+		? be32_to_cpu((__force __be32)x)
+		: le32_to_cpu((__force __le32)x);
+}
+#else /* !CONFIG_PPC32 */
+static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {}
+
+#define fsl_readl(addr)		readl(addr)
+#define fsl_writel(val32, addr) writel(val32, addr)
+#define cpu_to_hc32(x)		cpu_to_le32(x)
+#define hc32_to_cpu(x)		le32_to_cpu(x)
+#endif /* CONFIG_PPC32 */
+
+/********************************************************************
+ *	Internal Used Function
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ *	request is still in progress.
+ *--------------------------------------------------------------*/
+static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+__releases(ep->udc->lock)
+__acquires(ep->udc->lock)
+{
+	struct fsl_udc *udc = NULL;
+	unsigned char stopped = ep->stopped;
+	struct ep_td_struct *curr_td, *next_td;
+	int j;
+
+	udc = (struct fsl_udc *)ep->udc;
+	/* Removed the req from fsl_ep->queue */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	/* Free dtd for the request */
+	next_td = req->head;
+	for (j = 0; j < req->dtd_count; j++) {
+		curr_td = next_td;
+		if (j != req->dtd_count - 1) {
+			next_td = curr_td->next_td_virt;
+		}
+		dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+	}
+
+	usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+
+	if (status && (status != -ESHUTDOWN))
+		VDBG("complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	ep->stopped = 1;
+
+	spin_unlock(&ep->udc->lock);
+
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+
+	spin_lock(&ep->udc->lock);
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ * called with spinlock held
+ *--------------------------------------------------------------*/
+static void nuke(struct fsl_ep *ep, int status)
+{
+	ep->stopped = 1;
+
+	/* Flush fifo */
+	fsl_ep_fifo_flush(&ep->ep);
+
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct fsl_req *req = NULL;
+
+		req = list_entry(ep->queue.next, struct fsl_req, queue);
+		done(ep, req, status);
+	}
+}
+
+/*------------------------------------------------------------------
+	Internal Hardware related function
+ ------------------------------------------------------------------*/
+
+static int dr_controller_setup(struct fsl_udc *udc)
+{
+	unsigned int tmp, portctrl, ep_num;
+	unsigned int max_no_of_ep;
+	unsigned int ctrl;
+	unsigned long timeout;
+
+#define FSL_UDC_RESET_TIMEOUT 1000
+
+	/* Config PHY interface */
+	portctrl = fsl_readl(&dr_regs->portsc1);
+	portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+	switch (udc->phy_mode) {
+	case FSL_USB2_PHY_ULPI:
+		if (udc->pdata->have_sysif_regs) {
+			if (udc->pdata->controller_ver) {
+				/* controller version 1.6 or above */
+				ctrl = __raw_readl(&usb_sys_regs->control);
+				ctrl &= ~USB_CTRL_UTMI_PHY_EN;
+				ctrl |= USB_CTRL_USB_EN;
+				__raw_writel(ctrl, &usb_sys_regs->control);
+			}
+		}
+		portctrl |= PORTSCX_PTS_ULPI;
+		break;
+	case FSL_USB2_PHY_UTMI_WIDE:
+		portctrl |= PORTSCX_PTW_16BIT;
+		/* fall through */
+	case FSL_USB2_PHY_UTMI:
+	case FSL_USB2_PHY_UTMI_DUAL:
+		if (udc->pdata->have_sysif_regs) {
+			if (udc->pdata->controller_ver) {
+				/* controller version 1.6 or above */
+				ctrl = __raw_readl(&usb_sys_regs->control);
+				ctrl |= (USB_CTRL_UTMI_PHY_EN |
+					USB_CTRL_USB_EN);
+				__raw_writel(ctrl, &usb_sys_regs->control);
+				mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI
+					PHY CLK to become stable - 10ms*/
+			}
+		}
+		portctrl |= PORTSCX_PTS_UTMI;
+		break;
+	case FSL_USB2_PHY_SERIAL:
+		portctrl |= PORTSCX_PTS_FSLS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	fsl_writel(portctrl, &dr_regs->portsc1);
+
+	/* Stop and reset the usb controller */
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp |= USB_CMD_CTRL_RESET;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+
+	/* Wait for reset to complete */
+	timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+	while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+		if (time_after(jiffies, timeout)) {
+			ERR("udc reset timeout!\n");
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+	}
+
+	/* Set the controller as device mode */
+	tmp = fsl_readl(&dr_regs->usbmode);
+	tmp &= ~USB_MODE_CTRL_MODE_MASK;	/* clear mode bits */
+	tmp |= USB_MODE_CTRL_MODE_DEVICE;
+	/* Disable Setup Lockout */
+	tmp |= USB_MODE_SETUP_LOCK_OFF;
+	if (udc->pdata->es)
+		tmp |= USB_MODE_ES;
+	fsl_writel(tmp, &dr_regs->usbmode);
+
+	/* Clear the setup status */
+	fsl_writel(0, &dr_regs->usbsts);
+
+	tmp = udc->ep_qh_dma;
+	tmp &= USB_EP_LIST_ADDRESS_MASK;
+	fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+	VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+		udc->ep_qh, (int)tmp,
+		fsl_readl(&dr_regs->endpointlistaddr));
+
+	max_no_of_ep = (0x0000001F & fsl_readl(&dr_regs->dccparams));
+	for (ep_num = 1; ep_num < max_no_of_ep; ep_num++) {
+		tmp = fsl_readl(&dr_regs->endptctrl[ep_num]);
+		tmp &= ~(EPCTRL_TX_TYPE | EPCTRL_RX_TYPE);
+		tmp |= (EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT)
+		| (EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT);
+		fsl_writel(tmp, &dr_regs->endptctrl[ep_num]);
+	}
+	/* Config control enable i/o output, cpu endian register */
+#ifndef CONFIG_ARCH_MXC
+	if (udc->pdata->have_sysif_regs) {
+		ctrl = __raw_readl(&usb_sys_regs->control);
+		ctrl |= USB_CTRL_IOENB;
+		__raw_writel(ctrl, &usb_sys_regs->control);
+	}
+#endif
+
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+	/* Turn on cache snooping hardware, since some PowerPC platforms
+	 * wholly rely on hardware to deal with cache coherent. */
+
+	if (udc->pdata->have_sysif_regs) {
+		/* Setup Snooping for all the 4GB space */
+		tmp = SNOOP_SIZE_2GB;	/* starts from 0x0, size 2G */
+		__raw_writel(tmp, &usb_sys_regs->snoop1);
+		tmp |= 0x80000000;	/* starts from 0x8000000, size 2G */
+		__raw_writel(tmp, &usb_sys_regs->snoop2);
+	}
+#endif
+
+	return 0;
+}
+
+/* Enable DR irq and set controller to run state */
+static void dr_controller_run(struct fsl_udc *udc)
+{
+	u32 temp;
+
+	/* Enable DR irq reg */
+	temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+		| USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+		| USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+	fsl_writel(temp, &dr_regs->usbintr);
+
+	/* Clear stopped bit */
+	udc->stopped = 0;
+
+	/* Set the controller as device mode */
+	temp = fsl_readl(&dr_regs->usbmode);
+	temp |= USB_MODE_CTRL_MODE_DEVICE;
+	fsl_writel(temp, &dr_regs->usbmode);
+
+	/* Set controller to Run */
+	temp = fsl_readl(&dr_regs->usbcmd);
+	temp |= USB_CMD_RUN_STOP;
+	fsl_writel(temp, &dr_regs->usbcmd);
+}
+
+static void dr_controller_stop(struct fsl_udc *udc)
+{
+	unsigned int tmp;
+
+	pr_debug("%s\n", __func__);
+
+	/* if we're in OTG mode, and the Host is currently using the port,
+	 * stop now and don't rip the controller out from under the
+	 * ehci driver
+	 */
+	if (udc->gadget.is_otg) {
+		if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
+			pr_debug("udc: Leaving early\n");
+			return;
+		}
+	}
+
+	/* disable all INTR */
+	fsl_writel(0, &dr_regs->usbintr);
+
+	/* Set stopped bit for isr */
+	udc->stopped = 1;
+
+	/* disable IO output */
+/*	usb_sys_regs->control = 0; */
+
+	/* set controller to Stop */
+	tmp = fsl_readl(&dr_regs->usbcmd);
+	tmp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(tmp, &dr_regs->usbcmd);
+}
+
+static void dr_ep_setup(unsigned char ep_num, unsigned char dir,
+			unsigned char ep_type)
+{
+	unsigned int tmp_epctrl = 0;
+
+	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (dir) {
+		if (ep_num)
+			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+		tmp_epctrl |= EPCTRL_TX_ENABLE;
+		tmp_epctrl &= ~EPCTRL_TX_TYPE;
+		tmp_epctrl |= ((unsigned int)(ep_type)
+				<< EPCTRL_TX_EP_TYPE_SHIFT);
+	} else {
+		if (ep_num)
+			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+		tmp_epctrl |= EPCTRL_RX_ENABLE;
+		tmp_epctrl &= ~EPCTRL_RX_TYPE;
+		tmp_epctrl |= ((unsigned int)(ep_type)
+				<< EPCTRL_RX_EP_TYPE_SHIFT);
+	}
+
+	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+static void
+dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
+{
+	u32 tmp_epctrl = 0;
+
+	tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+
+	if (value) {
+		/* set the stall bit */
+		if (dir)
+			tmp_epctrl |= EPCTRL_TX_EP_STALL;
+		else
+			tmp_epctrl |= EPCTRL_RX_EP_STALL;
+	} else {
+		/* clear the stall bit and reset data toggle */
+		if (dir) {
+			tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+			tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+		} else {
+			tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+			tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+		}
+	}
+	fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+/* Get stall status of a specific ep
+   Return: 0: not stalled; 1:stalled */
+static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
+{
+	u32 epctrl;
+
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (dir)
+		return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
+	else
+		return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
+}
+
+/********************************************************************
+	Internal Structure Build up functions
+********************************************************************/
+
+/*------------------------------------------------------------------
+* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
+ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
+ * @mult: Mult field
+ ------------------------------------------------------------------*/
+static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
+		unsigned char dir, unsigned char ep_type,
+		unsigned int max_pkt_len,
+		unsigned int zlt, unsigned char mult)
+{
+	struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
+	unsigned int tmp = 0;
+
+	/* set the Endpoint Capabilites in QH */
+	switch (ep_type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		/* Interrupt On Setup (IOS). for control ep  */
+		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+			| EP_QUEUE_HEAD_IOS;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+			| (mult << EP_QUEUE_HEAD_MULT_POS);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+		break;
+	default:
+		VDBG("error ep type is %d", ep_type);
+		return;
+	}
+	if (zlt)
+		tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+
+	p_QH->max_pkt_length = cpu_to_hc32(tmp);
+	p_QH->next_dtd_ptr = 1;
+	p_QH->size_ioc_int_sts = 0;
+}
+
+/* Setup qh structure and ep register for ep0. */
+static void ep0_setup(struct fsl_udc *udc)
+{
+	/* the initialization of an ep includes: fields in QH, Regs,
+	 * fsl_ep struct */
+	struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+			USB_MAX_CTRL_PAYLOAD, 0, 0);
+	struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+			USB_MAX_CTRL_PAYLOAD, 0, 0);
+	dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+	dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+
+	return;
+
+}
+
+/***********************************************************************
+		Endpoint Management Functions
+***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ * when configurations are set, or when interface settings change
+ * for example the do_set_interface() in gadget layer,
+ * the driver will enable or disable the relevant endpoints
+ * ep0 doesn't use this routine. It is always enabled.
+-------------------------------------------------------------------------*/
+static int fsl_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct fsl_udc *udc = NULL;
+	struct fsl_ep *ep = NULL;
+	unsigned short max = 0;
+	unsigned char mult = 0, zlt;
+	int retval = -EINVAL;
+	unsigned long flags = 0;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc
+			|| (desc->bDescriptorType != USB_DT_ENDPOINT))
+		return -EINVAL;
+
+	udc = ep->udc;
+
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	max = usb_endpoint_maxp(desc);
+
+	/* Disable automatic zlp generation.  Driver is responsible to indicate
+	 * explicitly through req->req.zero.  This is needed to enable multi-td
+	 * request. */
+	zlt = 1;
+
+	/* Assume the max packet size from gadget is always correct */
+	switch (desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_CONTROL:
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		/* mult = 0.  Execute N Transactions as demonstrated by
+		 * the USB variable length packet protocol where N is
+		 * computed using the Maximum Packet Length (dQH) and
+		 * the Total Bytes field (dTD) */
+		mult = 0;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* Calculate transactions needed for high bandwidth iso */
+		mult = usb_endpoint_maxp_mult(desc);
+		/* 3 transactions at most */
+		if (mult > 3)
+			goto en_done;
+		break;
+	default:
+		goto en_done;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	ep->ep.maxpacket = max;
+	ep->ep.desc = desc;
+	ep->stopped = 0;
+
+	/* Controller related setup */
+	/* Init EPx Queue Head (Ep Capabilites field in QH
+	 * according to max, zlt, mult) */
+	struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
+			(unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+					?  USB_SEND : USB_RECV),
+			(unsigned char) (desc->bmAttributes
+					& USB_ENDPOINT_XFERTYPE_MASK),
+			max, zlt, mult);
+
+	/* Init endpoint ctrl register */
+	dr_ep_setup((unsigned char) ep_index(ep),
+			(unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+					? USB_SEND : USB_RECV),
+			(unsigned char) (desc->bmAttributes
+					& USB_ENDPOINT_XFERTYPE_MASK));
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	retval = 0;
+
+	VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
+			ep->ep.desc->bEndpointAddress & 0x0f,
+			(desc->bEndpointAddress & USB_DIR_IN)
+				? "in" : "out", max);
+en_done:
+	return retval;
+}
+
+/*---------------------------------------------------------------------
+ * @ep : the ep being unconfigured. May not be ep0
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+*---------------------------------------------------------------------*/
+static int fsl_ep_disable(struct usb_ep *_ep)
+{
+	struct fsl_udc *udc = NULL;
+	struct fsl_ep *ep = NULL;
+	unsigned long flags = 0;
+	u32 epctrl;
+	int ep_num;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+	if (!_ep || !ep->ep.desc) {
+		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	/* disable ep on controller */
+	ep_num = ep_index(ep);
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep)) {
+		epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE);
+		epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT;
+	} else {
+		epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE);
+		epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT;
+	}
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+	udc = (struct fsl_udc *)ep->udc;
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	VDBG("disabled %s OK", _ep->name);
+	return 0;
+}
+
+/*---------------------------------------------------------------------
+ * allocate a request object used by this endpoint
+ * the main operation is to insert the req->queue to the eq->queue
+ * Returns the request, or null if one could not be allocated
+*---------------------------------------------------------------------*/
+static struct usb_request *
+fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct fsl_req *req = NULL;
+
+	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 fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fsl_req *req = NULL;
+
+	req = container_of(_req, struct fsl_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+/* Actually add a dTD chain to an empty dQH and let go */
+static void fsl_prime_ep(struct fsl_ep *ep, struct ep_td_struct *td)
+{
+	struct ep_queue_head *qh = get_qh_by_ep(ep);
+
+	/* Write dQH next pointer and terminate bit to 0 */
+	qh->next_dtd_ptr = cpu_to_hc32(td->td_dma
+			& EP_QUEUE_HEAD_NEXT_POINTER_MASK);
+
+	/* Clear active and halt bit */
+	qh->size_ioc_int_sts &= cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+					| EP_QUEUE_HEAD_STATUS_HALT));
+
+	/* Ensure that updates to the QH will occur before priming. */
+	wmb();
+
+	/* Prime endpoint by writing correct bit to ENDPTPRIME */
+	fsl_writel(ep_is_in(ep) ? (1 << (ep_index(ep) + 16))
+			: (1 << (ep_index(ep))), &dr_regs->endpointprime);
+}
+
+/* Add dTD chain to the dQH of an EP */
+static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
+{
+	u32 temp, bitmask, tmp_stat;
+
+	/* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
+	VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
+
+	bitmask = ep_is_in(ep)
+		? (1 << (ep_index(ep) + 16))
+		: (1 << (ep_index(ep)));
+
+	/* check if the pipe is empty */
+	if (!(list_empty(&ep->queue)) && !(ep_index(ep) == 0)) {
+		/* Add td to the end */
+		struct fsl_req *lastreq;
+		lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
+		lastreq->tail->next_td_ptr =
+			cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
+		/* Ensure dTD's next dtd pointer to be updated */
+		wmb();
+		/* Read prime bit, if 1 goto done */
+		if (fsl_readl(&dr_regs->endpointprime) & bitmask)
+			return;
+
+		do {
+			/* Set ATDTW bit in USBCMD */
+			temp = fsl_readl(&dr_regs->usbcmd);
+			fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+			/* Read correct status bit */
+			tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
+
+		} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
+
+		/* Write ATDTW bit to 0 */
+		temp = fsl_readl(&dr_regs->usbcmd);
+		fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+		if (tmp_stat)
+			return;
+	}
+
+	fsl_prime_ep(ep, req->head);
+}
+
+/* Fill in the dTD structure
+ * @req: request that the transfer belongs to
+ * @length: return actually data length of the dTD
+ * @dma: return dma address of the dTD
+ * @is_last: return flag if it is the last dTD of the request
+ * return: pointer to the built dTD */
+static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
+		dma_addr_t *dma, int *is_last, gfp_t gfp_flags)
+{
+	u32 swap_temp;
+	struct ep_td_struct *dtd;
+
+	/* how big will this transfer be? */
+	*length = min(req->req.length - req->req.actual,
+			(unsigned)EP_MAX_LENGTH_TRANSFER);
+
+	dtd = dma_pool_alloc(udc_controller->td_pool, gfp_flags, dma);
+	if (dtd == NULL)
+		return dtd;
+
+	dtd->td_dma = *dma;
+	/* Clear reserved field */
+	swap_temp = hc32_to_cpu(dtd->size_ioc_sts);
+	swap_temp &= ~DTD_RESERVED_FIELDS;
+	dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
+
+	/* Init all of buffer page pointers */
+	swap_temp = (u32) (req->req.dma + req->req.actual);
+	dtd->buff_ptr0 = cpu_to_hc32(swap_temp);
+	dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000);
+	dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000);
+	dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000);
+	dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000);
+
+	req->req.actual += *length;
+
+	/* zlp is needed if req->req.zero is set */
+	if (req->req.zero) {
+		if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+			*is_last = 1;
+		else
+			*is_last = 0;
+	} else if (req->req.length == req->req.actual)
+		*is_last = 1;
+	else
+		*is_last = 0;
+
+	if ((*is_last) == 0)
+		VDBG("multi-dtd request!");
+	/* Fill in the transfer size; set active bit */
+	swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+	/* Enable interrupt for the last dtd of a request */
+	if (*is_last && !req->req.no_interrupt)
+		swap_temp |= DTD_IOC;
+
+	dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
+
+	mb();
+
+	VDBG("length = %d address= 0x%x", *length, (int)*dma);
+
+	return dtd;
+}
+
+/* Generate dtd chain for a request */
+static int fsl_req_to_dtd(struct fsl_req *req, gfp_t gfp_flags)
+{
+	unsigned	count;
+	int		is_last;
+	int		is_first =1;
+	struct ep_td_struct	*last_dtd = NULL, *dtd;
+	dma_addr_t dma;
+
+	do {
+		dtd = fsl_build_dtd(req, &count, &dma, &is_last, gfp_flags);
+		if (dtd == NULL)
+			return -ENOMEM;
+
+		if (is_first) {
+			is_first = 0;
+			req->head = dtd;
+		} else {
+			last_dtd->next_td_ptr = cpu_to_hc32(dma);
+			last_dtd->next_td_virt = dtd;
+		}
+		last_dtd = dtd;
+
+		req->dtd_count++;
+	} while (!is_last);
+
+	dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE);
+
+	req->tail = dtd;
+
+	return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+	struct fsl_req *req = container_of(_req, struct fsl_req, req);
+	struct fsl_udc *udc;
+	unsigned long flags;
+	int ret;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		VDBG("%s, bad params", __func__);
+		return -EINVAL;
+	}
+	if (unlikely(!_ep || !ep->ep.desc)) {
+		VDBG("%s, bad ep", __func__);
+		return -EINVAL;
+	}
+	if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
+		if (req->req.length > ep->ep.maxpacket)
+			return -EMSGSIZE;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+	if (ret)
+		return ret;
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->dtd_count = 0;
+
+	/* build dtds and push them to device queue */
+	if (!fsl_req_to_dtd(req, gfp_flags)) {
+		spin_lock_irqsave(&udc->lock, flags);
+		fsl_queue_td(ep, req);
+	} else {
+		return -ENOMEM;
+	}
+
+	/* irq handler advances the queue */
+	if (req != NULL)
+		list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+	struct fsl_req *req;
+	unsigned long flags;
+	int ep_num, stopped, ret = 0;
+	u32 epctrl;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	stopped = ep->stopped;
+
+	/* Stop the ep before we deal with the queue */
+	ep->stopped = 1;
+	ep_num = ep_index(ep);
+	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep))
+		epctrl &= ~EPCTRL_TX_ENABLE;
+	else
+		epctrl &= ~EPCTRL_RX_ENABLE;
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* The request is in progress, or completed but not dequeued */
+	if (ep->queue.next == &req->queue) {
+		_req->status = -ECONNRESET;
+		fsl_ep_fifo_flush(_ep);	/* flush current transfer */
+
+		/* The request isn't the last request in this ep queue */
+		if (req->queue.next != &ep->queue) {
+			struct fsl_req *next_req;
+
+			next_req = list_entry(req->queue.next, struct fsl_req,
+					queue);
+
+			/* prime with dTD of next request */
+			fsl_prime_ep(ep, next_req->head);
+		}
+	/* The request hasn't been processed, patch up the TD chain */
+	} else {
+		struct fsl_req *prev_req;
+
+		prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
+		prev_req->tail->next_td_ptr = req->tail->next_td_ptr;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	/* Enable EP */
+out:	epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+	if (ep_is_in(ep))
+		epctrl |= EPCTRL_TX_ENABLE;
+	else
+		epctrl |= EPCTRL_RX_ENABLE;
+	fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+	ep->stopped = stopped;
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct fsl_ep *ep = NULL;
+	unsigned long flags = 0;
+	int status = -EOPNOTSUPP;	/* operation not supported */
+	unsigned char ep_dir = 0, ep_num = 0;
+	struct fsl_udc *udc = NULL;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+	udc = ep->udc;
+	if (!_ep || !ep->ep.desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
+		status = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+	ep_num = (unsigned char)(ep_index(ep));
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	dr_ep_change_stall(ep_num, ep_dir, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep_index(ep) == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+out:
+	VDBG(" %s %s halt stat %d", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static int fsl_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct fsl_ep *ep;
+	struct fsl_udc *udc;
+	int size = 0;
+	u32 bitmask;
+	struct ep_queue_head *qh;
+
+	ep = container_of(_ep, struct fsl_ep, ep);
+	if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
+		return -ENODEV;
+
+	udc = (struct fsl_udc *)ep->udc;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	qh = get_qh_by_ep(ep);
+
+	bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
+	    (1 << (ep_index(ep)));
+
+	if (fsl_readl(&dr_regs->endptstatus) & bitmask)
+		size = (qh->size_ioc_int_sts & DTD_PACKET_SIZE)
+		    >> DTD_LENGTH_BIT_POS;
+
+	pr_debug("%s %u\n", __func__, size);
+	return size;
+}
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct fsl_ep *ep;
+	int ep_num, ep_dir;
+	u32 bits;
+	unsigned long timeout;
+#define FSL_UDC_FLUSH_TIMEOUT 1000
+
+	if (!_ep) {
+		return;
+	} else {
+		ep = container_of(_ep, struct fsl_ep, ep);
+		if (!ep->ep.desc)
+			return;
+	}
+	ep_num = ep_index(ep);
+	ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+	if (ep_num == 0)
+		bits = (1 << 16) | 1;
+	else if (ep_dir == USB_SEND)
+		bits = 1 << (16 + ep_num);
+	else
+		bits = 1 << ep_num;
+
+	timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
+	do {
+		fsl_writel(bits, &dr_regs->endptflush);
+
+		/* Wait until flush complete */
+		while (fsl_readl(&dr_regs->endptflush)) {
+			if (time_after(jiffies, timeout)) {
+				ERR("ep flush timeout\n");
+				return;
+			}
+			cpu_relax();
+		}
+		/* See if we need to flush again */
+	} while (fsl_readl(&dr_regs->endptstatus) & bits);
+}
+
+static const struct usb_ep_ops fsl_ep_ops = {
+	.enable = fsl_ep_enable,
+	.disable = fsl_ep_disable,
+
+	.alloc_request = fsl_alloc_request,
+	.free_request = fsl_free_request,
+
+	.queue = fsl_ep_queue,
+	.dequeue = fsl_ep_dequeue,
+
+	.set_halt = fsl_ep_set_halt,
+	.fifo_status = fsl_ep_fifo_status,
+	.fifo_flush = fsl_ep_fifo_flush,	/* flush fifo */
+};
+
+/*-------------------------------------------------------------------------
+		Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ * Get the current frame number (from DR frame_index Reg )
+ *----------------------------------------------------------------------*/
+static int fsl_get_frame(struct usb_gadget *gadget)
+{
+	return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ -----------------------------------------------------------------------*/
+static int fsl_wakeup(struct usb_gadget *gadget)
+{
+	struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
+	u32 portsc;
+
+	/* Remote wakeup feature not enabled by host */
+	if (!udc->remote_wakeup)
+		return -ENOTSUPP;
+
+	portsc = fsl_readl(&dr_regs->portsc1);
+	/* not suspended? */
+	if (!(portsc & PORTSCX_PORT_SUSPEND))
+		return 0;
+	/* trigger force resume */
+	portsc |= PORTSCX_PORT_FORCE_RESUME;
+	fsl_writel(portsc, &dr_regs->portsc1);
+	return 0;
+}
+
+static int can_pullup(struct fsl_udc *udc)
+{
+	return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+   detects VBUS sessions */
+static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct fsl_udc	*udc;
+	unsigned long	flags;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	VDBG("VBUS %s", is_active ? "on" : "off");
+	udc->vbus_active = (is_active != 0);
+	if (can_pullup(udc))
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	else
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	struct fsl_udc *udc;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		return usb_phy_set_power(udc->transceiver, mA);
+	return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int fsl_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct fsl_udc *udc;
+
+	udc = container_of(gadget, struct fsl_udc, gadget);
+
+	if (!udc->vbus_active)
+		return -EOPNOTSUPP;
+
+	udc->softconnect = (is_on != 0);
+	if (can_pullup(udc))
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+	else
+		fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+				&dr_regs->usbcmd);
+
+	return 0;
+}
+
+static int fsl_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int fsl_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops fsl_gadget_ops = {
+	.get_frame = fsl_get_frame,
+	.wakeup = fsl_wakeup,
+/*	.set_selfpowered = fsl_set_selfpowered,	*/ /* Always selfpowered */
+	.vbus_session = fsl_vbus_session,
+	.vbus_draw = fsl_vbus_draw,
+	.pullup = fsl_pullup,
+	.udc_start = fsl_udc_start,
+	.udc_stop = fsl_udc_stop,
+};
+
+/*
+ * Empty complete function used by this driver to fill in the req->complete
+ * field when creating a request since the complete field is mandatory.
+ */
+static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { }
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+   on new transaction */
+static void ep0stall(struct fsl_udc *udc)
+{
+	u32 tmp;
+
+	/* must set tx and rx to stall at the same time */
+	tmp = fsl_readl(&dr_regs->endptctrl[0]);
+	tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+	fsl_writel(tmp, &dr_regs->endptctrl[0]);
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = 0;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct fsl_udc *udc, int direction)
+{
+	struct fsl_req *req = udc->status_req;
+	struct fsl_ep *ep;
+	int ret;
+
+	if (direction == EP_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	ep = &udc->eps[0];
+	if (udc->ep0_state != DATA_STATE_XMIT)
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+	req->ep = ep;
+	req->req.length = 0;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = fsl_noop_complete;
+	req->dtd_count = 0;
+
+	ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+	if (ret)
+		return ret;
+
+	if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0)
+		fsl_queue_td(ep, req);
+	else
+		return -ENOMEM;
+
+	list_add_tail(&req->queue, &ep->queue);
+
+	return 0;
+}
+
+static void udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
+{
+	struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
+
+	if (ep->ep.name)
+		nuke(ep, -ESHUTDOWN);
+}
+
+/*
+ * ch9 Set address
+ */
+static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+	/* Status phase */
+	if (ep0_prime_status(udc, EP_DIR_IN))
+		ep0stall(udc);
+}
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
+		u16 index, u16 length)
+{
+	u16 tmp = 0;		/* Status, cpu endian */
+	struct fsl_req *req;
+	struct fsl_ep *ep;
+	int ret;
+
+	ep = &udc->eps[0];
+
+	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		/* Get device status */
+		tmp = udc->gadget.is_selfpowered;
+		tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+		/* Get interface status */
+		/* We don't have interface information in udc driver */
+		tmp = 0;
+	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+		/* Get endpoint status */
+		struct fsl_ep *target_ep;
+
+		target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
+
+		/* stall if endpoint doesn't exist */
+		if (!target_ep->ep.desc)
+			goto stall;
+		tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
+				<< USB_ENDPOINT_HALT;
+	}
+
+	udc->ep0_dir = USB_DIR_IN;
+	/* Borrow the per device status_req */
+	req = udc->status_req;
+	/* Fill in the reqest structure */
+	*((u16 *) req->req.buf) = cpu_to_le16(tmp);
+
+	req->ep = ep;
+	req->req.length = 2;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = fsl_noop_complete;
+	req->dtd_count = 0;
+
+	ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+	if (ret)
+		goto stall;
+
+	/* prime the data phase */
+	if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0))
+		fsl_queue_td(ep, req);
+	else			/* no mem */
+		goto stall;
+
+	list_add_tail(&req->queue, &ep->queue);
+	udc->ep0_state = DATA_STATE_XMIT;
+	if (ep0_prime_status(udc, EP_DIR_OUT))
+		ep0stall(udc);
+
+	return;
+stall:
+	ep0stall(udc);
+}
+
+static void setup_received_irq(struct fsl_udc *udc,
+		struct usb_ctrlrequest *setup)
+__releases(udc->lock)
+__acquires(udc->lock)
+{
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	udc_reset_ep_queue(udc, 0);
+
+	/* We process some stardard setup requests here */
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase from udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+						| USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Status phase from udc */
+	{
+		int rc = -EOPNOTSUPP;
+		u16 ptc = 0;
+
+		if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
+				== (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+			int pipe = get_pipe_by_windex(wIndex);
+			struct fsl_ep *ep;
+
+			if (wValue != 0 || wLength != 0 || pipe >= udc->max_ep)
+				break;
+			ep = get_ep_by_pipe(udc, pipe);
+
+			spin_unlock(&udc->lock);
+			rc = fsl_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+
+		} else if ((setup->bRequestType & (USB_RECIP_MASK
+				| USB_TYPE_MASK)) == (USB_RECIP_DEVICE
+				| USB_TYPE_STANDARD)) {
+			/* Note: The driver has not include OTG support yet.
+			 * This will be set when OTG support is added */
+			if (wValue == USB_DEVICE_TEST_MODE)
+				ptc = wIndex >> 8;
+			else if (gadget_is_otg(&udc->gadget)) {
+				if (setup->bRequest ==
+				    USB_DEVICE_B_HNP_ENABLE)
+					udc->gadget.b_hnp_enable = 1;
+				else if (setup->bRequest ==
+					 USB_DEVICE_A_HNP_SUPPORT)
+					udc->gadget.a_hnp_support = 1;
+				else if (setup->bRequest ==
+					 USB_DEVICE_A_ALT_HNP_SUPPORT)
+					udc->gadget.a_alt_hnp_support = 1;
+			}
+			rc = 0;
+		} else
+			break;
+
+		if (rc == 0) {
+			if (ep0_prime_status(udc, EP_DIR_IN))
+				ep0stall(udc);
+		}
+		if (ptc) {
+			u32 tmp;
+
+			mdelay(10);
+			tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16);
+			fsl_writel(tmp, &dr_regs->portsc1);
+			printk(KERN_INFO "udc: switch to test mode %d.\n", ptc);
+		}
+
+		return;
+	}
+
+	default:
+		break;
+	}
+
+	/* Requests handled by gadget */
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+				?  USB_DIR_IN : USB_DIR_OUT;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+				&udc->local_setup_buff) < 0)
+			ep0stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+				?  DATA_STATE_XMIT : DATA_STATE_RECV;
+		/*
+		 * If the data stage is IN, send status prime immediately.
+		 * See 2.0 Spec chapter 8.5.3.3 for detail.
+		 */
+		if (udc->ep0_state == DATA_STATE_XMIT)
+			if (ep0_prime_status(udc, EP_DIR_OUT))
+				ep0stall(udc);
+
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+				&udc->local_setup_buff) < 0)
+			ep0stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+}
+
+/* Process request for Data or Status phase of ep0
+ * prime status phase if needed */
+static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
+		struct fsl_req *req)
+{
+	if (udc->usb_state == USB_STATE_ADDRESS) {
+		/* Set the new address */
+		u32 new_address = (u32) udc->device_address;
+		fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
+				&dr_regs->deviceaddr);
+	}
+
+	done(ep0, req, 0);
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		/* already primed at setup_received_irq */
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+		break;
+	case DATA_STATE_RECV:
+		/* send status phase */
+		if (ep0_prime_status(udc, EP_DIR_IN))
+			ep0stall(udc);
+		break;
+	case WAIT_FOR_OUT_STATUS:
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+	case WAIT_FOR_SETUP:
+		ERR("Unexpected ep0 packets\n");
+		break;
+	default:
+		ep0stall(udc);
+		break;
+	}
+}
+
+/* Tripwire mechanism to ensure a setup packet payload is extracted without
+ * being corrupted by another incoming setup packet */
+static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+	u32 temp;
+	struct ep_queue_head *qh;
+	struct fsl_usb2_platform_data *pdata = udc->pdata;
+
+	qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+	/* Clear bit in ENDPTSETUPSTAT */
+	temp = fsl_readl(&dr_regs->endptsetupstat);
+	fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
+
+	/* while a hazard exists when setup package arrives */
+	do {
+		/* Set Setup Tripwire */
+		temp = fsl_readl(&dr_regs->usbcmd);
+		fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
+
+		/* Copy the setup packet to local buffer */
+		if (pdata->le_setup_buf) {
+			u32 *p = (u32 *)buffer_ptr;
+			u32 *s = (u32 *)qh->setup_buffer;
+
+			/* Convert little endian setup buffer to CPU endian */
+			*p++ = le32_to_cpu(*s++);
+			*p = le32_to_cpu(*s);
+		} else {
+			memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+		}
+	} while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
+
+	/* Clear Setup Tripwire */
+	temp = fsl_readl(&dr_regs->usbcmd);
+	fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
+}
+
+/* process-ep_req(): free the completed Tds for this req */
+static int process_ep_req(struct fsl_udc *udc, int pipe,
+		struct fsl_req *curr_req)
+{
+	struct ep_td_struct *curr_td;
+	int	td_complete, actual, remaining_length, j, tmp;
+	int	status = 0;
+	int	errors = 0;
+	struct  ep_queue_head *curr_qh = &udc->ep_qh[pipe];
+	int direction = pipe % 2;
+
+	curr_td = curr_req->head;
+	td_complete = 0;
+	actual = curr_req->req.length;
+
+	for (j = 0; j < curr_req->dtd_count; j++) {
+		remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts)
+					& DTD_PACKET_SIZE)
+				>> DTD_LENGTH_BIT_POS;
+		actual -= remaining_length;
+
+		errors = hc32_to_cpu(curr_td->size_ioc_sts);
+		if (errors & DTD_ERROR_MASK) {
+			if (errors & DTD_STATUS_HALTED) {
+				ERR("dTD error %08x QH=%d\n", errors, pipe);
+				/* Clear the errors and Halt condition */
+				tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
+				tmp &= ~errors;
+				curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp);
+				status = -EPIPE;
+				/* FIXME: continue with next queued TD? */
+
+				break;
+			}
+			if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+				VDBG("Transfer overflow");
+				status = -EPROTO;
+				break;
+			} else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+				VDBG("ISO error");
+				status = -EILSEQ;
+				break;
+			} else
+				ERR("Unknown error has occurred (0x%x)!\n",
+					errors);
+
+		} else if (hc32_to_cpu(curr_td->size_ioc_sts)
+				& DTD_STATUS_ACTIVE) {
+			VDBG("Request not complete");
+			status = REQ_UNCOMPLETE;
+			return status;
+		} else if (remaining_length) {
+			if (direction) {
+				VDBG("Transmit dTD remaining length not zero");
+				status = -EPROTO;
+				break;
+			} else {
+				td_complete++;
+				break;
+			}
+		} else {
+			td_complete++;
+			VDBG("dTD transmitted successful");
+		}
+
+		if (j != curr_req->dtd_count - 1)
+			curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
+	}
+
+	if (status)
+		return status;
+
+	curr_req->req.actual = actual;
+
+	return 0;
+}
+
+/* Process a DTD completion interrupt */
+static void dtd_complete_irq(struct fsl_udc *udc)
+{
+	u32 bit_pos;
+	int i, ep_num, direction, bit_mask, status;
+	struct fsl_ep *curr_ep;
+	struct fsl_req *curr_req, *temp_req;
+
+	/* Clear the bits in the register */
+	bit_pos = fsl_readl(&dr_regs->endptcomplete);
+	fsl_writel(bit_pos, &dr_regs->endptcomplete);
+
+	if (!bit_pos)
+		return;
+
+	for (i = 0; i < udc->max_ep; i++) {
+		ep_num = i >> 1;
+		direction = i % 2;
+
+		bit_mask = 1 << (ep_num + 16 * direction);
+
+		if (!(bit_pos & bit_mask))
+			continue;
+
+		curr_ep = get_ep_by_pipe(udc, i);
+
+		/* If the ep is configured */
+		if (!curr_ep->ep.name) {
+			WARNING("Invalid EP?");
+			continue;
+		}
+
+		/* process the req queue until an uncomplete request */
+		list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+				queue) {
+			status = process_ep_req(udc, i, curr_req);
+
+			VDBG("status of process_ep_req= %d, ep = %d",
+					status, ep_num);
+			if (status == REQ_UNCOMPLETE)
+				break;
+			/* write back status to req */
+			curr_req->req.status = status;
+
+			if (ep_num == 0) {
+				ep0_req_complete(udc, curr_ep, curr_req);
+				break;
+			} else
+				done(curr_ep, curr_req, status);
+		}
+	}
+}
+
+static inline enum usb_device_speed portscx_device_speed(u32 reg)
+{
+	switch (reg & PORTSCX_PORT_SPEED_MASK) {
+	case PORTSCX_PORT_SPEED_HIGH:
+		return USB_SPEED_HIGH;
+	case PORTSCX_PORT_SPEED_FULL:
+		return USB_SPEED_FULL;
+	case PORTSCX_PORT_SPEED_LOW:
+		return USB_SPEED_LOW;
+	default:
+		return USB_SPEED_UNKNOWN;
+	}
+}
+
+/* Process a port change interrupt */
+static void port_change_irq(struct fsl_udc *udc)
+{
+	if (udc->bus_reset)
+		udc->bus_reset = 0;
+
+	/* Bus resetting is finished */
+	if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET))
+		/* Get the speed */
+		udc->gadget.speed =
+			portscx_device_speed(fsl_readl(&dr_regs->portsc1));
+
+	/* Update USB state */
+	if (!udc->resume_state)
+		udc->usb_state = USB_STATE_DEFAULT;
+}
+
+/* Process suspend interrupt */
+static void suspend_irq(struct fsl_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver, serial.c does not support this */
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void bus_resume(struct fsl_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver, serial.c does not support this */
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+/* Clear up all ep queues */
+static int reset_queues(struct fsl_udc *udc, bool bus_reset)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < udc->max_pipes; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	spin_unlock(&udc->lock);
+	if (bus_reset)
+		usb_gadget_udc_reset(&udc->gadget, udc->driver);
+	else
+		udc->driver->disconnect(&udc->gadget);
+	spin_lock(&udc->lock);
+
+	return 0;
+}
+
+/* Process reset interrupt */
+static void reset_irq(struct fsl_udc *udc)
+{
+	u32 temp;
+	unsigned long timeout;
+
+	/* Clear the device address */
+	temp = fsl_readl(&dr_regs->deviceaddr);
+	fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
+
+	udc->device_address = 0;
+
+	/* Clear usb state */
+	udc->resume_state = 0;
+	udc->ep0_dir = 0;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->remote_wakeup = 0;	/* default to 0 on reset */
+	udc->gadget.b_hnp_enable = 0;
+	udc->gadget.a_hnp_support = 0;
+	udc->gadget.a_alt_hnp_support = 0;
+
+	/* Clear all the setup token semaphores */
+	temp = fsl_readl(&dr_regs->endptsetupstat);
+	fsl_writel(temp, &dr_regs->endptsetupstat);
+
+	/* Clear all the endpoint complete status bits */
+	temp = fsl_readl(&dr_regs->endptcomplete);
+	fsl_writel(temp, &dr_regs->endptcomplete);
+
+	timeout = jiffies + 100;
+	while (fsl_readl(&dr_regs->endpointprime)) {
+		/* Wait until all endptprime bits cleared */
+		if (time_after(jiffies, timeout)) {
+			ERR("Timeout for reset\n");
+			break;
+		}
+		cpu_relax();
+	}
+
+	/* Write 1s to the flush register */
+	fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+	if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+		VDBG("Bus reset");
+		/* Bus is reseting */
+		udc->bus_reset = 1;
+		/* Reset all the queues, include XD, dTD, EP queue
+		 * head and TR Queue */
+		reset_queues(udc, true);
+		udc->usb_state = USB_STATE_DEFAULT;
+	} else {
+		VDBG("Controller reset");
+		/* initialize usb hw reg except for regs for EP, not
+		 * touch usbintr reg */
+		dr_controller_setup(udc);
+
+		/* Reset all internal used Queues */
+		reset_queues(udc, false);
+
+		ep0_setup(udc);
+
+		/* Enable DR IRQ reg, Set Run bit, change udc state */
+		dr_controller_run(udc);
+		udc->usb_state = USB_STATE_ATTACHED;
+	}
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+	struct fsl_udc *udc = _udc;
+	u32 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+	/* Disable ISR for OTG host mode */
+	if (udc->stopped)
+		return IRQ_NONE;
+	spin_lock_irqsave(&udc->lock, flags);
+	irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+	/* Clear notification bits */
+	fsl_writel(irq_src, &dr_regs->usbsts);
+
+	/* VDBG("irq_src [0x%8x]", irq_src); */
+
+	/* Need to resume? */
+	if (udc->usb_state == USB_STATE_SUSPENDED)
+		if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
+			bus_resume(udc);
+
+	/* USB Interrupt */
+	if (irq_src & USB_STS_INT) {
+		VDBG("Packet int");
+		/* Setup package, we only support ep0 as control ep */
+		if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+			tripwire_handler(udc, 0,
+					(u8 *) (&udc->local_setup_buff));
+			setup_received_irq(udc, &udc->local_setup_buff);
+			status = IRQ_HANDLED;
+		}
+
+		/* completion of dtd */
+		if (fsl_readl(&dr_regs->endptcomplete)) {
+			dtd_complete_irq(udc);
+			status = IRQ_HANDLED;
+		}
+	}
+
+	/* SOF (for ISO transfer) */
+	if (irq_src & USB_STS_SOF) {
+		status = IRQ_HANDLED;
+	}
+
+	/* Port Change */
+	if (irq_src & USB_STS_PORT_CHANGE) {
+		port_change_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	/* Reset Received */
+	if (irq_src & USB_STS_RESET) {
+		VDBG("reset int");
+		reset_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	/* Sleep Enable (Suspend) */
+	if (irq_src & USB_STS_SUSPEND) {
+		suspend_irq(udc);
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+		VDBG("Error IRQ %x", irq_src);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+/*----------------------------------------------------------------*
+ * Hook to gadget drivers
+ * Called by initialization code of gadget drivers
+*----------------------------------------------------------------*/
+static int fsl_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	int retval = 0;
+	unsigned long flags = 0;
+
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+
+	driver->driver.bus = NULL;
+	/* hook up the driver */
+	udc_controller->driver = driver;
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+	g->is_selfpowered = 1;
+
+	if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
+		/* Suspend the controller until OTG enable it */
+		udc_controller->stopped = 1;
+		printk(KERN_INFO "Suspend udc for OTG auto detect\n");
+
+		/* connect to bus through transceiver */
+		if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
+			retval = otg_set_peripheral(
+					udc_controller->transceiver->otg,
+						    &udc_controller->gadget);
+			if (retval < 0) {
+				ERR("can't bind to transceiver\n");
+				udc_controller->driver = NULL;
+				return retval;
+			}
+		}
+	} else {
+		/* Enable DR IRQ reg and set USBCMD reg Run bit */
+		dr_controller_run(udc_controller);
+		udc_controller->usb_state = USB_STATE_ATTACHED;
+		udc_controller->ep0_state = WAIT_FOR_SETUP;
+		udc_controller->ep0_dir = 0;
+	}
+
+	return retval;
+}
+
+/* Disconnect from gadget driver */
+static int fsl_udc_stop(struct usb_gadget *g)
+{
+	struct fsl_ep *loop_ep;
+	unsigned long flags;
+
+	if (!IS_ERR_OR_NULL(udc_controller->transceiver))
+		otg_set_peripheral(udc_controller->transceiver->otg, NULL);
+
+	/* stop DR, disable intr */
+	dr_controller_stop(udc_controller);
+
+	/* in fact, no needed */
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc_controller->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+			ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	udc_controller->driver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------
+		PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/fsl_usb2_udc";
+
+static int fsl_proc_read(struct seq_file *m, void *v)
+{
+	unsigned long flags;
+	int i;
+	u32 tmp_reg;
+	struct fsl_ep *ep = NULL;
+	struct fsl_req *req;
+
+	struct fsl_udc *udc = udc_controller;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* ------basic driver information ---- */
+	seq_printf(m,
+			DRIVER_DESC "\n"
+			"%s version: %s\n"
+			"Gadget driver: %s\n\n",
+			driver_name, DRIVER_VERSION,
+			udc->driver ? udc->driver->driver.name : "(none)");
+
+	/* ------ DR Registers ----- */
+	tmp_reg = fsl_readl(&dr_regs->usbcmd);
+	seq_printf(m,
+			"USBCMD reg:\n"
+			"SetupTW: %d\n"
+			"Run/Stop: %s\n\n",
+			(tmp_reg & USB_CMD_SUTW) ? 1 : 0,
+			(tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
+
+	tmp_reg = fsl_readl(&dr_regs->usbsts);
+	seq_printf(m,
+			"USB Status Reg:\n"
+			"Dr Suspend: %d Reset Received: %d System Error: %s "
+			"USB Error Interrupt: %s\n\n",
+			(tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
+			(tmp_reg & USB_STS_RESET) ? 1 : 0,
+			(tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
+			(tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
+
+	tmp_reg = fsl_readl(&dr_regs->usbintr);
+	seq_printf(m,
+			"USB Interrupt Enable Reg:\n"
+			"Sleep Enable: %d SOF Received Enable: %d "
+			"Reset Enable: %d\n"
+			"System Error Enable: %d "
+			"Port Change Dectected Enable: %d\n"
+			"USB Error Intr Enable: %d USB Intr Enable: %d\n\n",
+			(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
+			(tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
+			(tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
+
+	tmp_reg = fsl_readl(&dr_regs->frindex);
+	seq_printf(m,
+			"USB Frame Index Reg: Frame Number is 0x%x\n\n",
+			(tmp_reg & USB_FRINDEX_MASKS));
+
+	tmp_reg = fsl_readl(&dr_regs->deviceaddr);
+	seq_printf(m,
+			"USB Device Address Reg: Device Addr is 0x%x\n\n",
+			(tmp_reg & USB_DEVICE_ADDRESS_MASK));
+
+	tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
+	seq_printf(m,
+			"USB Endpoint List Address Reg: "
+			"Device Addr is 0x%x\n\n",
+			(tmp_reg & USB_EP_LIST_ADDRESS_MASK));
+
+	tmp_reg = fsl_readl(&dr_regs->portsc1);
+	seq_printf(m,
+		"USB Port Status&Control Reg:\n"
+		"Port Transceiver Type : %s Port Speed: %s\n"
+		"PHY Low Power Suspend: %s Port Reset: %s "
+		"Port Suspend Mode: %s\n"
+		"Over-current Change: %s "
+		"Port Enable/Disable Change: %s\n"
+		"Port Enabled/Disabled: %s "
+		"Current Connect Status: %s\n\n", ( {
+			const char *s;
+			switch (tmp_reg & PORTSCX_PTS_FSLS) {
+			case PORTSCX_PTS_UTMI:
+				s = "UTMI"; break;
+			case PORTSCX_PTS_ULPI:
+				s = "ULPI "; break;
+			case PORTSCX_PTS_FSLS:
+				s = "FS/LS Serial"; break;
+			default:
+				s = "None"; break;
+			}
+			s;} ),
+		usb_speed_string(portscx_device_speed(tmp_reg)),
+		(tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
+		"Normal PHY mode" : "Low power mode",
+		(tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
+		"Not in Reset",
+		(tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
+		(tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
+		"No",
+		(tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
+		"Not change",
+		(tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
+		"Not correct",
+		(tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
+		"Attached" : "Not-Att");
+
+	tmp_reg = fsl_readl(&dr_regs->usbmode);
+	seq_printf(m,
+			"USB Mode Reg: Controller Mode is: %s\n\n", ( {
+				const char *s;
+				switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
+				case USB_MODE_CTRL_MODE_IDLE:
+					s = "Idle"; break;
+				case USB_MODE_CTRL_MODE_DEVICE:
+					s = "Device Controller"; break;
+				case USB_MODE_CTRL_MODE_HOST:
+					s = "Host Controller"; break;
+				default:
+					s = "None"; break;
+				}
+				s;
+			} ));
+
+	tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
+	seq_printf(m,
+			"Endpoint Setup Status Reg: SETUP on ep 0x%x\n\n",
+			(tmp_reg & EP_SETUP_STATUS_MASK));
+
+	for (i = 0; i < udc->max_ep / 2; i++) {
+		tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
+		seq_printf(m, "EP Ctrl Reg [0x%x]: = [0x%x]\n", i, tmp_reg);
+	}
+	tmp_reg = fsl_readl(&dr_regs->endpointprime);
+	seq_printf(m, "EP Prime Reg = [0x%x]\n\n", tmp_reg);
+
+#ifndef CONFIG_ARCH_MXC
+	if (udc->pdata->have_sysif_regs) {
+		tmp_reg = usb_sys_regs->snoop1;
+		seq_printf(m, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
+
+		tmp_reg = usb_sys_regs->control;
+		seq_printf(m, "General Control Reg : = [0x%x]\n\n", tmp_reg);
+	}
+#endif
+
+	/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
+	ep = &udc->eps[0];
+	seq_printf(m, "For %s Maxpkt is 0x%x index is 0x%x\n",
+			ep->ep.name, ep_maxpacket(ep), ep_index(ep));
+
+	if (list_empty(&ep->queue)) {
+		seq_puts(m, "its req queue is empty\n\n");
+	} else {
+		list_for_each_entry(req, &ep->queue, queue) {
+			seq_printf(m,
+				"req %p actual 0x%x length 0x%x buf %p\n",
+				&req->req, req->req.actual,
+				req->req.length, req->req.buf);
+		}
+	}
+	/* other gadget->eplist ep */
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		if (ep->ep.desc) {
+			seq_printf(m,
+					"\nFor %s Maxpkt is 0x%x "
+					"index is 0x%x\n",
+					ep->ep.name, ep_maxpacket(ep),
+					ep_index(ep));
+
+			if (list_empty(&ep->queue)) {
+				seq_puts(m, "its req queue is empty\n\n");
+			} else {
+				list_for_each_entry(req, &ep->queue, queue) {
+					seq_printf(m,
+						"req %p actual 0x%x length "
+						"0x%x  buf %p\n",
+						&req->req, req->req.actual,
+						req->req.length, req->req.buf);
+				}	/* end for each_entry of ep req */
+			}	/* end for else */
+		}	/* end for if(ep->queue) */
+	}	/* end (ep->desc) */
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+#define create_proc_file() \
+	proc_create_single(proc_filename, 0, NULL, fsl_proc_read)
+#define remove_proc_file()	remove_proc_entry(proc_filename, NULL)
+
+#else				/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file()	do {} while (0)
+#define remove_proc_file()	do {} while (0)
+
+#endif				/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/* Release udc structures */
+static void fsl_udc_release(struct device *dev)
+{
+	complete(udc_controller->done);
+	dma_free_coherent(dev->parent, udc_controller->ep_qh_size,
+			udc_controller->ep_qh, udc_controller->ep_qh_dma);
+	kfree(udc_controller);
+}
+
+/******************************************************************
+	Internal structure setup functions
+*******************************************************************/
+/*------------------------------------------------------------------
+ * init resource for globle controller
+ * Return the udc handle on success or NULL on failure
+ ------------------------------------------------------------------*/
+static int struct_udc_setup(struct fsl_udc *udc,
+		struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata;
+	size_t size;
+
+	pdata = dev_get_platdata(&pdev->dev);
+	udc->phy_mode = pdata->phy_mode;
+
+	udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL);
+	if (!udc->eps)
+		return -1;
+
+	/* initialized QHs, take care of alignment */
+	size = udc->max_ep * sizeof(struct ep_queue_head);
+	if (size < QH_ALIGNMENT)
+		size = QH_ALIGNMENT;
+	else if ((size % QH_ALIGNMENT) != 0) {
+		size += QH_ALIGNMENT + 1;
+		size &= ~(QH_ALIGNMENT - 1);
+	}
+	udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+					&udc->ep_qh_dma, GFP_KERNEL);
+	if (!udc->ep_qh) {
+		ERR("malloc QHs for udc failed\n");
+		kfree(udc->eps);
+		return -1;
+	}
+
+	udc->ep_qh_size = size;
+
+	/* Initialize ep0 status request structure */
+	/* FIXME: fsl_alloc_request() ignores ep argument */
+	udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+			struct fsl_req, req);
+	/* allocate a small amount of memory to get valid address */
+	udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+	udc->remote_wakeup = 0;	/* default to 0 on reset */
+
+	return 0;
+}
+
+/*----------------------------------------------------------------
+ * Setup the fsl_ep struct for eps
+ * Link fsl_ep->ep to gadget->ep_list
+ * ep0out is not used so do nothing here
+ * ep0in should be taken care
+ *--------------------------------------------------------------*/
+static int struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+		char *name, int link)
+{
+	struct fsl_ep *ep = &udc->eps[index];
+
+	ep->udc = udc;
+	strcpy(ep->name, name);
+	ep->ep.name = ep->name;
+
+	ep->ep.ops = &fsl_ep_ops;
+	ep->stopped = 0;
+
+	if (index == 0) {
+		ep->ep.caps.type_control = true;
+	} else {
+		ep->ep.caps.type_iso = true;
+		ep->ep.caps.type_bulk = true;
+		ep->ep.caps.type_int = true;
+	}
+
+	if (index & 1)
+		ep->ep.caps.dir_in = true;
+	else
+		ep->ep.caps.dir_out = true;
+
+	/* for ep0: maxP defined in desc
+	 * for other eps, maxP is set by epautoconfig() called by gadget layer
+	 */
+	usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0 */
+	if (link)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+	ep->gadget = &udc->gadget;
+	ep->qh = &udc->ep_qh[index];
+
+	return 0;
+}
+
+/* Driver probe function
+ * all initialization operations implemented here except enabling usb_intr reg
+ * board setup should have been done in the platform code
+ */
+static int fsl_udc_probe(struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata;
+	struct resource *res;
+	int ret = -ENODEV;
+	unsigned int i;
+	u32 dccparams;
+
+	udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+	if (udc_controller == NULL)
+		return -ENOMEM;
+
+	pdata = dev_get_platdata(&pdev->dev);
+	udc_controller->pdata = pdata;
+	spin_lock_init(&udc_controller->lock);
+	udc_controller->stopped = 1;
+
+#ifdef CONFIG_USB_OTG
+	if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+		udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+		if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
+			ERR("Can't find OTG driver!\n");
+			ret = -ENODEV;
+			goto err_kfree;
+		}
+	}
+#endif
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENXIO;
+		goto err_kfree;
+	}
+
+	if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
+		if (!request_mem_region(res->start, resource_size(res),
+					driver_name)) {
+			ERR("request mem region for %s failed\n", pdev->name);
+			ret = -EBUSY;
+			goto err_kfree;
+		}
+	}
+
+	dr_regs = ioremap(res->start, resource_size(res));
+	if (!dr_regs) {
+		ret = -ENOMEM;
+		goto err_release_mem_region;
+	}
+
+	pdata->regs = (void __iomem *)dr_regs;
+
+	/*
+	 * do platform specific init: check the clock, grab/config pins, etc.
+	 */
+	if (pdata->init && pdata->init(pdev)) {
+		ret = -ENODEV;
+		goto err_iounmap_noclk;
+	}
+
+	/* Set accessors only after pdata->init() ! */
+	fsl_set_accessors(pdata);
+
+#ifndef CONFIG_ARCH_MXC
+	if (pdata->have_sysif_regs)
+		usb_sys_regs = (void *)dr_regs + USB_DR_SYS_OFFSET;
+#endif
+
+	/* Initialize USB clocks */
+	ret = fsl_udc_clk_init(pdev);
+	if (ret < 0)
+		goto err_iounmap_noclk;
+
+	/* Read Device Controller Capability Parameters register */
+	dccparams = fsl_readl(&dr_regs->dccparams);
+	if (!(dccparams & DCCPARAMS_DC)) {
+		ERR("This SOC doesn't support device role\n");
+		ret = -ENODEV;
+		goto err_iounmap;
+	}
+	/* Get max device endpoints */
+	/* DEN is bidirectional ep number, max_ep doubles the number */
+	udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
+
+	udc_controller->irq = platform_get_irq(pdev, 0);
+	if (!udc_controller->irq) {
+		ret = -ENODEV;
+		goto err_iounmap;
+	}
+
+	ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
+			driver_name, udc_controller);
+	if (ret != 0) {
+		ERR("cannot request irq %d err %d\n",
+				udc_controller->irq, ret);
+		goto err_iounmap;
+	}
+
+	/* Initialize the udc structure including QH member and other member */
+	if (struct_udc_setup(udc_controller, pdev)) {
+		ERR("Can't initialize udc data structure\n");
+		ret = -ENOMEM;
+		goto err_free_irq;
+	}
+
+	if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
+		/* initialize usb hw reg except for regs for EP,
+		 * leave usbintr reg untouched */
+		dr_controller_setup(udc_controller);
+	}
+
+	ret = fsl_udc_clk_finalize(pdev);
+	if (ret)
+		goto err_free_irq;
+
+	/* Setup gadget structure */
+	udc_controller->gadget.ops = &fsl_gadget_ops;
+	udc_controller->gadget.max_speed = USB_SPEED_HIGH;
+	udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	udc_controller->gadget.name = driver_name;
+
+	/* Setup gadget.dev and register with kernel */
+	dev_set_name(&udc_controller->gadget.dev, "gadget");
+	udc_controller->gadget.dev.of_node = pdev->dev.of_node;
+
+	if (!IS_ERR_OR_NULL(udc_controller->transceiver))
+		udc_controller->gadget.is_otg = 1;
+
+	/* setup QH and epctrl for ep0 */
+	ep0_setup(udc_controller);
+
+	/* setup udc->eps[] for ep0 */
+	struct_ep_setup(udc_controller, 0, "ep0", 0);
+	/* for ep0: the desc defined here;
+	 * for other eps, gadget layer called ep_enable with defined desc
+	 */
+	udc_controller->eps[0].ep.desc = &fsl_ep0_desc;
+	usb_ep_set_maxpacket_limit(&udc_controller->eps[0].ep,
+				   USB_MAX_CTRL_PAYLOAD);
+
+	/* setup the udc->eps[] for non-control endpoints and link
+	 * to gadget.ep_list */
+	for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
+		char name[14];
+
+		sprintf(name, "ep%dout", i);
+		struct_ep_setup(udc_controller, i * 2, name, 1);
+		sprintf(name, "ep%din", i);
+		struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
+	}
+
+	/* use dma_pool for TD management */
+	udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
+			sizeof(struct ep_td_struct),
+			DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
+	if (udc_controller->td_pool == NULL) {
+		ret = -ENOMEM;
+		goto err_free_irq;
+	}
+
+	ret = usb_add_gadget_udc_release(&pdev->dev, &udc_controller->gadget,
+			fsl_udc_release);
+	if (ret)
+		goto err_del_udc;
+
+	create_proc_file();
+	return 0;
+
+err_del_udc:
+	dma_pool_destroy(udc_controller->td_pool);
+err_free_irq:
+	free_irq(udc_controller->irq, udc_controller);
+err_iounmap:
+	if (pdata->exit)
+		pdata->exit(pdev);
+	fsl_udc_clk_release();
+err_iounmap_noclk:
+	iounmap(dr_regs);
+err_release_mem_region:
+	if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+		release_mem_region(res->start, resource_size(res));
+err_kfree:
+	kfree(udc_controller);
+	udc_controller = NULL;
+	return ret;
+}
+
+/* Driver removal function
+ * Free resources and finish pending transactions
+ */
+static int fsl_udc_remove(struct platform_device *pdev)
+{
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	udc_controller->done = &done;
+	usb_del_gadget_udc(&udc_controller->gadget);
+
+	fsl_udc_clk_release();
+
+	/* DR has been stopped in usb_gadget_unregister_driver() */
+	remove_proc_file();
+
+	/* Free allocated memory */
+	kfree(udc_controller->status_req->req.buf);
+	kfree(udc_controller->status_req);
+	kfree(udc_controller->eps);
+
+	dma_pool_destroy(udc_controller->td_pool);
+	free_irq(udc_controller->irq, udc_controller);
+	iounmap(dr_regs);
+	if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+		release_mem_region(res->start, resource_size(res));
+
+	/* free udc --wait for the release() finished */
+	wait_for_completion(&done);
+
+	/*
+	 * do platform specific un-initialization:
+	 * release iomux pins, etc.
+	 */
+	if (pdata->exit)
+		pdata->exit(pdev);
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Modify Power management attributes
+ * Used by OTG statemachine to disable gadget temporarily
+ -----------------------------------------------------------------*/
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	dr_controller_stop(udc_controller);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ *-----------------------------------------------------------------*/
+static int fsl_udc_resume(struct platform_device *pdev)
+{
+	/* Enable DR irq reg and set controller Run */
+	if (udc_controller->stopped) {
+		dr_controller_setup(udc_controller);
+		dr_controller_run(udc_controller);
+	}
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	return 0;
+}
+
+static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state)
+{
+	struct fsl_udc *udc = udc_controller;
+	u32 mode, usbcmd;
+
+	mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
+
+	pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);
+
+	/*
+	 * If the controller is already stopped, then this must be a
+	 * PM suspend.  Remember this fact, so that we will leave the
+	 * controller stopped at PM resume time.
+	 */
+	if (udc->stopped) {
+		pr_debug("gadget already stopped, leaving early\n");
+		udc->already_stopped = 1;
+		return 0;
+	}
+
+	if (mode != USB_MODE_CTRL_MODE_DEVICE) {
+		pr_debug("gadget not in device mode, leaving early\n");
+		return 0;
+	}
+
+	/* stop the controller */
+	usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP;
+	fsl_writel(usbcmd, &dr_regs->usbcmd);
+
+	udc->stopped = 1;
+
+	pr_info("USB Gadget suspended\n");
+
+	return 0;
+}
+
+static int fsl_udc_otg_resume(struct device *dev)
+{
+	pr_debug("%s(): stopped %d  already_stopped %d\n", __func__,
+		 udc_controller->stopped, udc_controller->already_stopped);
+
+	/*
+	 * If the controller was stopped at suspend time, then
+	 * don't resume it now.
+	 */
+	if (udc_controller->already_stopped) {
+		udc_controller->already_stopped = 0;
+		pr_debug("gadget was already stopped, leaving early\n");
+		return 0;
+	}
+
+	pr_info("USB Gadget resume\n");
+
+	return fsl_udc_resume(NULL);
+}
+/*-------------------------------------------------------------------------
+	Register entry point for the peripheral controller driver
+--------------------------------------------------------------------------*/
+static const struct platform_device_id fsl_udc_devtype[] = {
+	{
+		.name = "imx-udc-mx27",
+	}, {
+		.name = "imx-udc-mx51",
+	}, {
+		.name = "fsl-usb2-udc",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
+static struct platform_driver udc_driver = {
+	.remove		= fsl_udc_remove,
+	/* Just for FSL i.mx SoC currently */
+	.id_table	= fsl_udc_devtype,
+	/* these suspend and resume are not usb suspend and resume */
+	.suspend	= fsl_udc_suspend,
+	.resume		= fsl_udc_resume,
+	.driver		= {
+			.name = driver_name,
+			/* udc suspend/resume called from OTG driver */
+			.suspend = fsl_udc_otg_suspend,
+			.resume  = fsl_udc_otg_resume,
+	},
+};
+
+module_platform_driver_probe(udc_driver, fsl_udc_probe);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:fsl-usb2-udc");
diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
new file mode 100644
index 0000000..4ba651a
--- /dev/null
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2004,2012 Freescale Semiconductor, Inc
+ * All rights reserved.
+ *
+ * Freescale USB device/endpoint management registers
+ */
+#ifndef __FSL_USB2_UDC_H
+#define __FSL_USB2_UDC_H
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* ### define USB registers here
+ */
+#define USB_MAX_CTRL_PAYLOAD		64
+#define USB_DR_SYS_OFFSET		0x400
+
+ /* USB DR device mode registers (Little Endian) */
+struct usb_dr_device {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structural Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 deviceaddr;		/* Device Address */
+	u32 endpointlistaddr;	/* Endpoint List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[24];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc1;		/* Port 1 Status and Control Register */
+	u8 res7[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+};
+
+ /* USB DR host mode registers (Little Endian) */
+struct usb_dr_host {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structural Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 periodiclistbase;	/* Periodic Frame List Base Address Register */
+	u32 asynclistaddr;	/* Current Asynchronous List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[24];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc1;		/* Port 1 Status and Control Register */
+	u8 res7[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+};
+
+ /* non-EHCI USB system interface registers (Big Endian) */
+struct usb_sys_interface {
+	u32 snoop1;
+	u32 snoop2;
+	u32 age_cnt_thresh;	/* Age Count Threshold Register */
+	u32 pri_ctrl;		/* Priority Control Register */
+	u32 si_ctrl;		/* System Interface Control Register */
+	u8 res[236];
+	u32 control;		/* General Purpose Control Register */
+};
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* Device Controller Capability Parameter register */
+#define DCCPARAMS_DC				0x00000080
+#define DCCPARAMS_DEN_MASK			0x0000001f
+
+/* Frame Index Register Bit Masks */
+#define	USB_FRINDEX_MASKS			0x3fff
+/* USB CMD  Register Bit Masks */
+#define  USB_CMD_RUN_STOP                     0x00000001
+#define  USB_CMD_CTRL_RESET                   0x00000002
+#define  USB_CMD_PERIODIC_SCHEDULE_EN         0x00000010
+#define  USB_CMD_ASYNC_SCHEDULE_EN            0x00000020
+#define  USB_CMD_INT_AA_DOORBELL              0x00000040
+#define  USB_CMD_ASP                          0x00000300
+#define  USB_CMD_ASYNC_SCH_PARK_EN            0x00000800
+#define  USB_CMD_SUTW                         0x00002000
+#define  USB_CMD_ATDTW                        0x00004000
+#define  USB_CMD_ITC                          0x00FF0000
+
+/* bit 15,3,2 are frame list size */
+#define  USB_CMD_FRAME_SIZE_1024              0x00000000
+#define  USB_CMD_FRAME_SIZE_512               0x00000004
+#define  USB_CMD_FRAME_SIZE_256               0x00000008
+#define  USB_CMD_FRAME_SIZE_128               0x0000000C
+#define  USB_CMD_FRAME_SIZE_64                0x00008000
+#define  USB_CMD_FRAME_SIZE_32                0x00008004
+#define  USB_CMD_FRAME_SIZE_16                0x00008008
+#define  USB_CMD_FRAME_SIZE_8                 0x0000800C
+
+/* bit 9-8 are async schedule park mode count */
+#define  USB_CMD_ASP_00                       0x00000000
+#define  USB_CMD_ASP_01                       0x00000100
+#define  USB_CMD_ASP_10                       0x00000200
+#define  USB_CMD_ASP_11                       0x00000300
+#define  USB_CMD_ASP_BIT_POS                  8
+
+/* bit 23-16 are interrupt threshold control */
+#define  USB_CMD_ITC_NO_THRESHOLD             0x00000000
+#define  USB_CMD_ITC_1_MICRO_FRM              0x00010000
+#define  USB_CMD_ITC_2_MICRO_FRM              0x00020000
+#define  USB_CMD_ITC_4_MICRO_FRM              0x00040000
+#define  USB_CMD_ITC_8_MICRO_FRM              0x00080000
+#define  USB_CMD_ITC_16_MICRO_FRM             0x00100000
+#define  USB_CMD_ITC_32_MICRO_FRM             0x00200000
+#define  USB_CMD_ITC_64_MICRO_FRM             0x00400000
+#define  USB_CMD_ITC_BIT_POS                  16
+
+/* USB STS Register Bit Masks */
+#define  USB_STS_INT                          0x00000001
+#define  USB_STS_ERR                          0x00000002
+#define  USB_STS_PORT_CHANGE                  0x00000004
+#define  USB_STS_FRM_LST_ROLL                 0x00000008
+#define  USB_STS_SYS_ERR                      0x00000010
+#define  USB_STS_IAA                          0x00000020
+#define  USB_STS_RESET                        0x00000040
+#define  USB_STS_SOF                          0x00000080
+#define  USB_STS_SUSPEND                      0x00000100
+#define  USB_STS_HC_HALTED                    0x00001000
+#define  USB_STS_RCL                          0x00002000
+#define  USB_STS_PERIODIC_SCHEDULE            0x00004000
+#define  USB_STS_ASYNC_SCHEDULE               0x00008000
+
+/* USB INTR Register Bit Masks */
+#define  USB_INTR_INT_EN                      0x00000001
+#define  USB_INTR_ERR_INT_EN                  0x00000002
+#define  USB_INTR_PTC_DETECT_EN               0x00000004
+#define  USB_INTR_FRM_LST_ROLL_EN             0x00000008
+#define  USB_INTR_SYS_ERR_EN                  0x00000010
+#define  USB_INTR_ASYN_ADV_EN                 0x00000020
+#define  USB_INTR_RESET_EN                    0x00000040
+#define  USB_INTR_SOF_EN                      0x00000080
+#define  USB_INTR_DEVICE_SUSPEND              0x00000100
+
+/* Device Address bit masks */
+#define  USB_DEVICE_ADDRESS_MASK              0xFE000000
+#define  USB_DEVICE_ADDRESS_BIT_POS           25
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK              0xfffff800
+
+/* PORTSCX  Register Bit Masks */
+#define  PORTSCX_CURRENT_CONNECT_STATUS       0x00000001
+#define  PORTSCX_CONNECT_STATUS_CHANGE        0x00000002
+#define  PORTSCX_PORT_ENABLE                  0x00000004
+#define  PORTSCX_PORT_EN_DIS_CHANGE           0x00000008
+#define  PORTSCX_OVER_CURRENT_ACT             0x00000010
+#define  PORTSCX_OVER_CURRENT_CHG             0x00000020
+#define  PORTSCX_PORT_FORCE_RESUME            0x00000040
+#define  PORTSCX_PORT_SUSPEND                 0x00000080
+#define  PORTSCX_PORT_RESET                   0x00000100
+#define  PORTSCX_LINE_STATUS_BITS             0x00000C00
+#define  PORTSCX_PORT_POWER                   0x00001000
+#define  PORTSCX_PORT_INDICTOR_CTRL           0x0000C000
+#define  PORTSCX_PORT_TEST_CTRL               0x000F0000
+#define  PORTSCX_WAKE_ON_CONNECT_EN           0x00100000
+#define  PORTSCX_WAKE_ON_CONNECT_DIS          0x00200000
+#define  PORTSCX_WAKE_ON_OVER_CURRENT         0x00400000
+#define  PORTSCX_PHY_LOW_POWER_SPD            0x00800000
+#define  PORTSCX_PORT_FORCE_FULL_SPEED        0x01000000
+#define  PORTSCX_PORT_SPEED_MASK              0x0C000000
+#define  PORTSCX_PORT_WIDTH                   0x10000000
+#define  PORTSCX_PHY_TYPE_SEL                 0xC0000000
+
+/* bit 11-10 are line status */
+#define  PORTSCX_LINE_STATUS_SE0              0x00000000
+#define  PORTSCX_LINE_STATUS_JSTATE           0x00000400
+#define  PORTSCX_LINE_STATUS_KSTATE           0x00000800
+#define  PORTSCX_LINE_STATUS_UNDEF            0x00000C00
+#define  PORTSCX_LINE_STATUS_BIT_POS          10
+
+/* bit 15-14 are port indicator control */
+#define  PORTSCX_PIC_OFF                      0x00000000
+#define  PORTSCX_PIC_AMBER                    0x00004000
+#define  PORTSCX_PIC_GREEN                    0x00008000
+#define  PORTSCX_PIC_UNDEF                    0x0000C000
+#define  PORTSCX_PIC_BIT_POS                  14
+
+/* bit 19-16 are port test control */
+#define  PORTSCX_PTC_DISABLE                  0x00000000
+#define  PORTSCX_PTC_JSTATE                   0x00010000
+#define  PORTSCX_PTC_KSTATE                   0x00020000
+#define  PORTSCX_PTC_SEQNAK                   0x00030000
+#define  PORTSCX_PTC_PACKET                   0x00040000
+#define  PORTSCX_PTC_FORCE_EN                 0x00050000
+#define  PORTSCX_PTC_BIT_POS                  16
+
+/* bit 27-26 are port speed */
+#define  PORTSCX_PORT_SPEED_FULL              0x00000000
+#define  PORTSCX_PORT_SPEED_LOW               0x04000000
+#define  PORTSCX_PORT_SPEED_HIGH              0x08000000
+#define  PORTSCX_PORT_SPEED_UNDEF             0x0C000000
+#define  PORTSCX_SPEED_BIT_POS                26
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define  PORTSCX_PTW                          0x10000000
+#define  PORTSCX_PTW_8BIT                     0x00000000
+#define  PORTSCX_PTW_16BIT                    0x10000000
+
+/* bit 31-30 are port transceiver select */
+#define  PORTSCX_PTS_UTMI                     0x00000000
+#define  PORTSCX_PTS_ULPI                     0x80000000
+#define  PORTSCX_PTS_FSLS                     0xC0000000
+#define  PORTSCX_PTS_BIT_POS                  30
+
+/* otgsc Register Bit Masks */
+#define  OTGSC_CTRL_VUSB_DISCHARGE            0x00000001
+#define  OTGSC_CTRL_VUSB_CHARGE               0x00000002
+#define  OTGSC_CTRL_OTG_TERM                  0x00000008
+#define  OTGSC_CTRL_DATA_PULSING              0x00000010
+#define  OTGSC_STS_USB_ID                     0x00000100
+#define  OTGSC_STS_A_VBUS_VALID               0x00000200
+#define  OTGSC_STS_A_SESSION_VALID            0x00000400
+#define  OTGSC_STS_B_SESSION_VALID            0x00000800
+#define  OTGSC_STS_B_SESSION_END              0x00001000
+#define  OTGSC_STS_1MS_TOGGLE                 0x00002000
+#define  OTGSC_STS_DATA_PULSING               0x00004000
+#define  OTGSC_INTSTS_USB_ID                  0x00010000
+#define  OTGSC_INTSTS_A_VBUS_VALID            0x00020000
+#define  OTGSC_INTSTS_A_SESSION_VALID         0x00040000
+#define  OTGSC_INTSTS_B_SESSION_VALID         0x00080000
+#define  OTGSC_INTSTS_B_SESSION_END           0x00100000
+#define  OTGSC_INTSTS_1MS                     0x00200000
+#define  OTGSC_INTSTS_DATA_PULSING            0x00400000
+#define  OTGSC_INTR_USB_ID                    0x01000000
+#define  OTGSC_INTR_A_VBUS_VALID              0x02000000
+#define  OTGSC_INTR_A_SESSION_VALID           0x04000000
+#define  OTGSC_INTR_B_SESSION_VALID           0x08000000
+#define  OTGSC_INTR_B_SESSION_END             0x10000000
+#define  OTGSC_INTR_1MS_TIMER                 0x20000000
+#define  OTGSC_INTR_DATA_PULSING              0x40000000
+
+/* USB MODE Register Bit Masks */
+#define  USB_MODE_CTRL_MODE_IDLE              0x00000000
+#define  USB_MODE_CTRL_MODE_DEVICE            0x00000002
+#define  USB_MODE_CTRL_MODE_HOST              0x00000003
+#define  USB_MODE_CTRL_MODE_MASK              0x00000003
+#define  USB_MODE_CTRL_MODE_RSV               0x00000001
+#define  USB_MODE_ES                          0x00000004 /* Endian Select */
+#define  USB_MODE_SETUP_LOCK_OFF              0x00000008
+#define  USB_MODE_STREAM_DISABLE              0x00000010
+/* Endpoint Flush Register */
+#define EPFLUSH_TX_OFFSET		      0x00010000
+#define EPFLUSH_RX_OFFSET		      0x00000000
+
+/* Endpoint Setup Status bit masks */
+#define  EP_SETUP_STATUS_MASK                 0x0000003F
+#define  EP_SETUP_STATUS_EP0		      0x00000001
+
+/* ENDPOINTCTRLx  Register Bit Masks */
+#define  EPCTRL_TX_ENABLE                     0x00800000
+#define  EPCTRL_TX_DATA_TOGGLE_RST            0x00400000	/* Not EP0 */
+#define  EPCTRL_TX_DATA_TOGGLE_INH            0x00200000	/* Not EP0 */
+#define  EPCTRL_TX_TYPE                       0x000C0000
+#define  EPCTRL_TX_DATA_SOURCE                0x00020000	/* Not EP0 */
+#define  EPCTRL_TX_EP_STALL                   0x00010000
+#define  EPCTRL_RX_ENABLE                     0x00000080
+#define  EPCTRL_RX_DATA_TOGGLE_RST            0x00000040	/* Not EP0 */
+#define  EPCTRL_RX_DATA_TOGGLE_INH            0x00000020	/* Not EP0 */
+#define  EPCTRL_RX_TYPE                       0x0000000C
+#define  EPCTRL_RX_DATA_SINK                  0x00000002	/* Not EP0 */
+#define  EPCTRL_RX_EP_STALL                   0x00000001
+
+/* bit 19-18 and 3-2 are endpoint type */
+#define  EPCTRL_EP_TYPE_CONTROL               0
+#define  EPCTRL_EP_TYPE_ISO                   1
+#define  EPCTRL_EP_TYPE_BULK                  2
+#define  EPCTRL_EP_TYPE_INTERRUPT             3
+#define  EPCTRL_TX_EP_TYPE_SHIFT              18
+#define  EPCTRL_RX_EP_TYPE_SHIFT              2
+
+/* SNOOPn Register Bit Masks */
+#define  SNOOP_ADDRESS_MASK                   0xFFFFF000
+#define  SNOOP_SIZE_ZERO                      0x00	/* snooping disable */
+#define  SNOOP_SIZE_4KB                       0x0B	/* 4KB snoop size */
+#define  SNOOP_SIZE_8KB                       0x0C
+#define  SNOOP_SIZE_16KB                      0x0D
+#define  SNOOP_SIZE_32KB                      0x0E
+#define  SNOOP_SIZE_64KB                      0x0F
+#define  SNOOP_SIZE_128KB                     0x10
+#define  SNOOP_SIZE_256KB                     0x11
+#define  SNOOP_SIZE_512KB                     0x12
+#define  SNOOP_SIZE_1MB                       0x13
+#define  SNOOP_SIZE_2MB                       0x14
+#define  SNOOP_SIZE_4MB                       0x15
+#define  SNOOP_SIZE_8MB                       0x16
+#define  SNOOP_SIZE_16MB                      0x17
+#define  SNOOP_SIZE_32MB                      0x18
+#define  SNOOP_SIZE_64MB                      0x19
+#define  SNOOP_SIZE_128MB                     0x1A
+#define  SNOOP_SIZE_256MB                     0x1B
+#define  SNOOP_SIZE_512MB                     0x1C
+#define  SNOOP_SIZE_1GB                       0x1D
+#define  SNOOP_SIZE_2GB                       0x1E	/* 2GB snoop size */
+
+/* pri_ctrl Register Bit Masks */
+#define  PRI_CTRL_PRI_LVL1                    0x0000000C
+#define  PRI_CTRL_PRI_LVL0                    0x00000003
+
+/* si_ctrl Register Bit Masks */
+#define  SI_CTRL_ERR_DISABLE                  0x00000010
+#define  SI_CTRL_IDRC_DISABLE                 0x00000008
+#define  SI_CTRL_RD_SAFE_EN                   0x00000004
+#define  SI_CTRL_RD_PREFETCH_DISABLE          0x00000002
+#define  SI_CTRL_RD_PREFEFETCH_VAL            0x00000001
+
+/* control Register Bit Masks */
+#define  USB_CTRL_IOENB                       0x00000004
+#define  USB_CTRL_ULPI_INT0EN                 0x00000001
+#define USB_CTRL_UTMI_PHY_EN		      0x00000200
+#define USB_CTRL_USB_EN			      0x00000004
+#define USB_CTRL_ULPI_PHY_CLK_SEL	      0x00000400
+
+/* Endpoint Queue Head data struct
+ * Rem: all the variables of qh are LittleEndian Mode
+ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
+ */
+struct ep_queue_head {
+	u32 max_pkt_length;	/* Mult(31-30) , Zlt(29) , Max Pkt len
+				   and IOS(15) */
+	u32 curr_dtd_ptr;	/* Current dTD Pointer(31-5) */
+	u32 next_dtd_ptr;	/* Next dTD Pointer(31-5), T(0) */
+	u32 size_ioc_int_sts;	/* Total bytes (30-16), IOC (15),
+				   MultO(11-10), STS (7-0)  */
+	u32 buff_ptr0;		/* Buffer pointer Page 0 (31-12) */
+	u32 buff_ptr1;		/* Buffer pointer Page 1 (31-12) */
+	u32 buff_ptr2;		/* Buffer pointer Page 2 (31-12) */
+	u32 buff_ptr3;		/* Buffer pointer Page 3 (31-12) */
+	u32 buff_ptr4;		/* Buffer pointer Page 4 (31-12) */
+	u32 res1;
+	u8 setup_buffer[8];	/* Setup data 8 bytes */
+	u32 res2[4];
+};
+
+/* Endpoint Queue Head Bit Masks */
+#define  EP_QUEUE_HEAD_MULT_POS               30
+#define  EP_QUEUE_HEAD_ZLT_SEL                0x20000000
+#define  EP_QUEUE_HEAD_MAX_PKT_LEN_POS        16
+#define  EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info)   (((ep_info)>>16)&0x07ff)
+#define  EP_QUEUE_HEAD_IOS                    0x00008000
+#define  EP_QUEUE_HEAD_NEXT_TERMINATE         0x00000001
+#define  EP_QUEUE_HEAD_IOC                    0x00008000
+#define  EP_QUEUE_HEAD_MULTO                  0x00000C00
+#define  EP_QUEUE_HEAD_STATUS_HALT	      0x00000040
+#define  EP_QUEUE_HEAD_STATUS_ACTIVE          0x00000080
+#define  EP_QUEUE_CURRENT_OFFSET_MASK         0x00000FFF
+#define  EP_QUEUE_HEAD_NEXT_POINTER_MASK      0xFFFFFFE0
+#define  EP_QUEUE_FRINDEX_MASK                0x000007FF
+#define  EP_MAX_LENGTH_TRANSFER               0x4000
+
+/* Endpoint Transfer Descriptor data struct */
+/* Rem: all the variables of td are LittleEndian Mode */
+struct ep_td_struct {
+	u32 next_td_ptr;	/* Next TD pointer(31-5), T(0) set
+				   indicate invalid */
+	u32 size_ioc_sts;	/* Total bytes (30-16), IOC (15),
+				   MultO(11-10), STS (7-0)  */
+	u32 buff_ptr0;		/* Buffer pointer Page 0 */
+	u32 buff_ptr1;		/* Buffer pointer Page 1 */
+	u32 buff_ptr2;		/* Buffer pointer Page 2 */
+	u32 buff_ptr3;		/* Buffer pointer Page 3 */
+	u32 buff_ptr4;		/* Buffer pointer Page 4 */
+	u32 res;
+	/* 32 bytes */
+	dma_addr_t td_dma;	/* dma address for this td */
+	/* virtual address of next td specified in next_td_ptr */
+	struct ep_td_struct *next_td_virt;
+};
+
+/* Endpoint Transfer Descriptor bit Masks */
+#define  DTD_NEXT_TERMINATE                   0x00000001
+#define  DTD_IOC                              0x00008000
+#define  DTD_STATUS_ACTIVE                    0x00000080
+#define  DTD_STATUS_HALTED                    0x00000040
+#define  DTD_STATUS_DATA_BUFF_ERR             0x00000020
+#define  DTD_STATUS_TRANSACTION_ERR           0x00000008
+#define  DTD_RESERVED_FIELDS                  0x80007300
+#define  DTD_ADDR_MASK                        0xFFFFFFE0
+#define  DTD_PACKET_SIZE                      0x7FFF0000
+#define  DTD_LENGTH_BIT_POS                   16
+#define  DTD_ERROR_MASK                       (DTD_STATUS_HALTED | \
+                                               DTD_STATUS_DATA_BUFF_ERR | \
+                                               DTD_STATUS_TRANSACTION_ERR)
+/* Alignment requirements; must be a power of two */
+#define DTD_ALIGNMENT				0x20
+#define QH_ALIGNMENT				2048
+
+/* Controller dma boundary */
+#define UDC_DMA_BOUNDARY			0x1000
+
+/*-------------------------------------------------------------------------*/
+
+/* ### driver private data
+ */
+struct fsl_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	   a request->queue into a udc_ep->queue 'd tail */
+	struct fsl_ep *ep;
+	unsigned mapped:1;
+
+	struct ep_td_struct *head, *tail;	/* For dTD List
+						   cpu endian Virtual addr */
+	unsigned int dtd_count;
+};
+
+#define REQ_UNCOMPLETE			1
+
+struct fsl_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct fsl_udc *udc;
+	struct ep_queue_head *qh;
+	struct usb_gadget *gadget;
+
+	char name[14];
+	unsigned stopped:1;
+};
+
+#define EP_DIR_IN	1
+#define EP_DIR_OUT	0
+
+struct fsl_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct fsl_usb2_platform_data *pdata;
+	struct completion *done;	/* to make sure release() is done */
+	struct fsl_ep *eps;
+	unsigned int max_ep;
+	unsigned int irq;
+
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;
+	struct usb_phy *transceiver;
+	unsigned softconnect:1;
+	unsigned vbus_active:1;
+	unsigned stopped:1;
+	unsigned remote_wakeup:1;
+	unsigned already_stopped:1;
+	unsigned big_endian_desc:1;
+
+	struct ep_queue_head *ep_qh;	/* Endpoints Queue-Head */
+	struct fsl_req *status_req;	/* ep0 status request */
+	struct dma_pool *td_pool;	/* dma pool for DTD */
+	enum fsl_usb2_phy_modes phy_mode;
+
+	size_t ep_qh_size;		/* size after alignment adjustment*/
+	dma_addr_t ep_qh_dma;		/* dma address of QH */
+
+	u32 max_pipes;          /* Device max pipes */
+	u32 bus_reset;		/* Device is bus resetting */
+	u32 resume_state;	/* USB state to resume */
+	u32 usb_state;		/* USB current state */
+	u32 ep0_state;		/* Endpoint zero state */
+	u32 ep0_dir;		/* Endpoint zero direction: can be
+				   USB_DIR_IN or USB_DIR_OUT */
+	u8 device_address;	/* Device USB address */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(fmt, args...) 	printk(KERN_DEBUG "[%s]  " fmt "\n", \
+				__func__, ## args)
+#else
+#define DBG(fmt, args...)	do{}while(0)
+#endif
+
+#if 0
+static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+{
+	unsigned int start, num, i;
+	char line[52], *p;
+
+	if (length >= 512)
+		return;
+	DBG("%s, length %u:\n", label, length);
+	start = 0;
+	while (length > 0) {
+		num = min(length, 16u);
+		p = line;
+		for (i = 0; i < num; ++i) {
+			if (i == 8)
+				*p++ = ' ';
+			sprintf(p, " %02x", buf[i]);
+			p += 3;
+		}
+		*p = 0;
+		printk(KERN_DEBUG "%6x: %s\n", start, line);
+		buf += num;
+		start += num;
+		length -= num;
+	}
+}
+#endif
+
+#ifdef VERBOSE
+#define VDBG		DBG
+#else
+#define VDBG(stuff...)	do{}while(0)
+#endif
+
+#define ERR(stuff...)		pr_err("udc: " stuff)
+#define WARNING(stuff...)	pr_warn("udc: " stuff)
+#define INFO(stuff...)		pr_info("udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* ### Add board specific defines here
+ */
+
+/*
+ * ### pipe direction macro from device view
+ */
+#define USB_RECV	0	/* OUT EP */
+#define USB_SEND	1	/* IN EP */
+
+/*
+ * ### internal used help routines.
+ */
+#define ep_index(EP)		((EP)->ep.desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \
+			& USB_DIR_IN)==USB_DIR_IN)
+#define get_ep_by_pipe(udc, pipe)	((pipe == 1)? &udc->eps[0]: \
+					&udc->eps[pipe])
+#define get_pipe_by_windex(windex)	((windex & USB_ENDPOINT_NUMBER_MASK) \
+					* 2 + ((windex & USB_DIR_IN) ? 1 : 0))
+#define get_pipe_by_ep(EP)	(ep_index(EP) * 2 + ep_is_in(EP))
+
+static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
+{
+	/* we only have one ep0 structure but two queue heads */
+	if (ep_index(ep) != 0)
+		return ep->qh;
+	else
+		return &ep->udc->ep_qh[(ep->udc->ep0_dir ==
+				USB_DIR_IN) ? 1 : 0];
+}
+
+struct platform_device;
+#ifdef CONFIG_ARCH_MXC
+int fsl_udc_clk_init(struct platform_device *pdev);
+int fsl_udc_clk_finalize(struct platform_device *pdev);
+void fsl_udc_clk_release(void);
+#else
+static inline int fsl_udc_clk_init(struct platform_device *pdev)
+{
+	return 0;
+}
+static inline int fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+	return 0;
+}
+static inline void fsl_udc_clk_release(void)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
new file mode 100644
index 0000000..263804d
--- /dev/null
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -0,0 +1,1509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Fusb300 UDC (USB gadget)
+ *
+ * Copyright (C) 2010 Faraday Technology Corp.
+ *
+ * Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
+ */
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "fusb300_udc.h"
+
+MODULE_DESCRIPTION("FUSB300  USB gadget driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
+MODULE_ALIAS("platform:fusb300_udc");
+
+#define DRIVER_VERSION	"20 October 2010"
+
+static const char udc_name[] = "fusb300_udc";
+static const char * const fusb300_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9",
+	"ep10", "ep11", "ep12", "ep13", "ep14", "ep15"
+};
+
+static void done(struct fusb300_ep *ep, struct fusb300_request *req,
+		 int status);
+
+static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset,
+			       u32 value)
+{
+	u32 reg = ioread32(fusb300->reg + offset);
+
+	reg |= value;
+	iowrite32(reg, fusb300->reg + offset);
+}
+
+static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset,
+				u32 value)
+{
+	u32 reg = ioread32(fusb300->reg + offset);
+
+	reg &= ~value;
+	iowrite32(reg, fusb300->reg + offset);
+}
+
+
+static void fusb300_ep_setting(struct fusb300_ep *ep,
+			       struct fusb300_ep_info info)
+{
+	ep->epnum = info.epnum;
+	ep->type = info.type;
+}
+
+static int fusb300_ep_release(struct fusb300_ep *ep)
+{
+	if (!ep->epnum)
+		return 0;
+	ep->epnum = 0;
+	ep->stall = 0;
+	ep->wedged = 0;
+	return 0;
+}
+
+static void fusb300_set_fifo_entry(struct fusb300 *fusb300,
+				   u32 ep)
+{
+	u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+
+	val &= ~FUSB300_EPSET1_FIFOENTRY_MSK;
+	val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM);
+	iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+}
+
+static void fusb300_set_start_entry(struct fusb300 *fusb300,
+				    u8 ep)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+	u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM;
+
+	reg &= ~FUSB300_EPSET1_START_ENTRY_MSK	;
+	reg |= FUSB300_EPSET1_START_ENTRY(start_entry);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+	if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) {
+		fusb300->fifo_entry_num = 0;
+		fusb300->addrofs = 0;
+		pr_err("fifo entry is over the maximum number!\n");
+	} else
+		fusb300->fifo_entry_num++;
+}
+
+/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */
+static void fusb300_set_epaddrofs(struct fusb300 *fusb300,
+				  struct fusb300_ep_info info)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+
+	reg &= ~FUSB300_EPSET2_ADDROFS_MSK;
+	reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+	fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM;
+}
+
+static void ep_fifo_setting(struct fusb300 *fusb300,
+			    struct fusb300_ep_info info)
+{
+	fusb300_set_fifo_entry(fusb300, info.epnum);
+	fusb300_set_start_entry(fusb300, info.epnum);
+	fusb300_set_epaddrofs(fusb300, info);
+}
+
+static void fusb300_set_eptype(struct fusb300 *fusb300,
+			       struct fusb300_ep_info info)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+	reg &= ~FUSB300_EPSET1_TYPE_MSK;
+	reg |= FUSB300_EPSET1_TYPE(info.type);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_epdir(struct fusb300 *fusb300,
+			      struct fusb300_ep_info info)
+{
+	u32 reg;
+
+	if (!info.dir_in)
+		return;
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+	reg &= ~FUSB300_EPSET1_DIR_MSK;
+	reg |= FUSB300_EPSET1_DIRIN;
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_ep_active(struct fusb300 *fusb300,
+			  u8 ep)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+
+	reg |= FUSB300_EPSET1_ACTEN;
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+}
+
+static void fusb300_set_epmps(struct fusb300 *fusb300,
+			      struct fusb300_ep_info info)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+
+	reg &= ~FUSB300_EPSET2_MPS_MSK;
+	reg |= FUSB300_EPSET2_MPS(info.maxpacket);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+}
+
+static void fusb300_set_interval(struct fusb300 *fusb300,
+				 struct fusb300_ep_info info)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+	reg &= ~FUSB300_EPSET1_INTERVAL(0x7);
+	reg |= FUSB300_EPSET1_INTERVAL(info.interval);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_bwnum(struct fusb300 *fusb300,
+			      struct fusb300_ep_info info)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+	reg &= ~FUSB300_EPSET1_BWNUM(0x3);
+	reg |= FUSB300_EPSET1_BWNUM(info.bw_num);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void set_ep_reg(struct fusb300 *fusb300,
+		      struct fusb300_ep_info info)
+{
+	fusb300_set_eptype(fusb300, info);
+	fusb300_set_epdir(fusb300, info);
+	fusb300_set_epmps(fusb300, info);
+
+	if (info.interval)
+		fusb300_set_interval(fusb300, info);
+
+	if (info.bw_num)
+		fusb300_set_bwnum(fusb300, info);
+
+	fusb300_set_ep_active(fusb300, info.epnum);
+}
+
+static int config_ep(struct fusb300_ep *ep,
+		     const struct usb_endpoint_descriptor *desc)
+{
+	struct fusb300 *fusb300 = ep->fusb300;
+	struct fusb300_ep_info info;
+
+	ep->ep.desc = desc;
+
+	info.interval = 0;
+	info.addrofs = 0;
+	info.bw_num = 0;
+
+	info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
+	info.maxpacket = usb_endpoint_maxp(desc);
+	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+	if ((info.type == USB_ENDPOINT_XFER_INT) ||
+	   (info.type == USB_ENDPOINT_XFER_ISOC)) {
+		info.interval = desc->bInterval;
+		if (info.type == USB_ENDPOINT_XFER_ISOC)
+			info.bw_num = usb_endpoint_maxp_mult(desc);
+	}
+
+	ep_fifo_setting(fusb300, info);
+
+	set_ep_reg(fusb300, info);
+
+	fusb300_ep_setting(ep, info);
+
+	fusb300->ep[info.epnum] = ep;
+
+	return 0;
+}
+
+static int fusb300_enable(struct usb_ep *_ep,
+			  const struct usb_endpoint_descriptor *desc)
+{
+	struct fusb300_ep *ep;
+
+	ep = container_of(_ep, struct fusb300_ep, ep);
+
+	if (ep->fusb300->reenum) {
+		ep->fusb300->fifo_entry_num = 0;
+		ep->fusb300->addrofs = 0;
+		ep->fusb300->reenum = 0;
+	}
+
+	return config_ep(ep, desc);
+}
+
+static int fusb300_disable(struct usb_ep *_ep)
+{
+	struct fusb300_ep *ep;
+	struct fusb300_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct fusb300_ep, ep);
+
+	BUG_ON(!ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct fusb300_request, queue);
+		spin_lock_irqsave(&ep->fusb300->lock, flags);
+		done(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+	}
+
+	return fusb300_ep_release(ep);
+}
+
+static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct fusb300_request *req;
+
+	req = kzalloc(sizeof(struct fusb300_request), gfp_flags);
+	if (!req)
+		return NULL;
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fusb300_request *req;
+
+	req = container_of(_req, struct fusb300_request, req);
+	kfree(req);
+}
+
+static int enable_fifo_int(struct fusb300_ep *ep)
+{
+	struct fusb300 *fusb300 = ep->fusb300;
+
+	if (ep->epnum) {
+		fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0,
+			FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
+	} else {
+		pr_err("can't enable_fifo_int ep0\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int disable_fifo_int(struct fusb300_ep *ep)
+{
+	struct fusb300 *fusb300 = ep->fusb300;
+
+	if (ep->epnum) {
+		fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0,
+			FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
+	} else {
+		pr_err("can't disable_fifo_int ep0\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length)
+{
+	u32 reg;
+
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
+	reg &= ~FUSB300_CSR_LEN_MSK;
+	reg |= FUSB300_CSR_LEN(length);
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR);
+}
+
+/* write data to cx fifo */
+static void fusb300_wrcxf(struct fusb300_ep *ep,
+		   struct fusb300_request *req)
+{
+	int i = 0;
+	u8 *tmp;
+	u32 data;
+	struct fusb300 *fusb300 = ep->fusb300;
+	u32 length = req->req.length - req->req.actual;
+
+	tmp = req->req.buf + req->req.actual;
+
+	if (length > SS_CTL_MAX_PACKET_SIZE) {
+		fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE);
+		for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) {
+			data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
+				*(tmp + 3) << 24;
+			iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+			tmp += 4;
+		}
+		req->req.actual += SS_CTL_MAX_PACKET_SIZE;
+	} else { /* length is less than max packet size */
+		fusb300_set_cxlen(fusb300, length);
+		for (i = length >> 2; i > 0; i--) {
+			data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
+				*(tmp + 3) << 24;
+			printk(KERN_DEBUG "    0x%x\n", data);
+			iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+			tmp = tmp + 4;
+		}
+		switch (length % 4) {
+		case 1:
+			data = *tmp;
+			printk(KERN_DEBUG "    0x%x\n", data);
+			iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+			break;
+		case 2:
+			data = *tmp | *(tmp + 1) << 8;
+			printk(KERN_DEBUG "    0x%x\n", data);
+			iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+			break;
+		case 3:
+			data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
+			printk(KERN_DEBUG "    0x%x\n", data);
+			iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+			break;
+		default:
+			break;
+		}
+		req->req.actual += length;
+	}
+}
+
+static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep)
+{
+	fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
+		FUSB300_EPSET0_STL);
+}
+
+static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+
+	if (reg & FUSB300_EPSET0_STL) {
+		printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep);
+		reg |= FUSB300_EPSET0_STL_CLR;
+		iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+	}
+}
+
+static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req)
+{
+	if (ep->fusb300->ep0_dir) { /* if IN */
+		if (req->req.length) {
+			fusb300_wrcxf(ep, req);
+		} else
+			printk(KERN_DEBUG "%s : req->req.length = 0x%x\n",
+				__func__, req->req.length);
+		if ((req->req.length == req->req.actual) ||
+		    (req->req.actual < ep->ep.maxpacket))
+			done(ep, req, 0);
+	} else { /* OUT */
+		if (!req->req.length)
+			done(ep, req, 0);
+		else
+			fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1,
+				FUSB300_IGER1_CX_OUT_INT);
+	}
+}
+
+static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req,
+			 gfp_t gfp_flags)
+{
+	struct fusb300_ep *ep;
+	struct fusb300_request *req;
+	unsigned long flags;
+	int request  = 0;
+
+	ep = container_of(_ep, struct fusb300_ep, ep);
+	req = container_of(_req, struct fusb300_request, req);
+
+	if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->fusb300->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (ep->ep.desc == NULL) /* ep0 */
+		ep0_queue(ep, req);
+	else if (request && !ep->stall)
+		enable_fifo_int(ep);
+
+	spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+
+	return 0;
+}
+
+static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct fusb300_ep *ep;
+	struct fusb300_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct fusb300_ep, ep);
+	req = container_of(_req, struct fusb300_request, req);
+
+	spin_lock_irqsave(&ep->fusb300->lock, flags);
+	if (!list_empty(&ep->queue))
+		done(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+
+	return 0;
+}
+
+static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
+{
+	struct fusb300_ep *ep;
+	struct fusb300 *fusb300;
+	unsigned long flags;
+	int ret = 0;
+
+	ep = container_of(_ep, struct fusb300_ep, ep);
+
+	fusb300 = ep->fusb300;
+
+	spin_lock_irqsave(&ep->fusb300->lock, flags);
+
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	if (value) {
+		fusb300_set_epnstall(fusb300, ep->epnum);
+		ep->stall = 1;
+		if (wedge)
+			ep->wedged = 1;
+	} else {
+		fusb300_clear_epnstall(fusb300, ep->epnum);
+		ep->stall = 0;
+		ep->wedged = 0;
+	}
+
+out:
+	spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+	return ret;
+}
+
+static int fusb300_set_halt(struct usb_ep *_ep, int value)
+{
+	return fusb300_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int fusb300_set_wedge(struct usb_ep *_ep)
+{
+	return fusb300_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static void fusb300_fifo_flush(struct usb_ep *_ep)
+{
+}
+
+static const struct usb_ep_ops fusb300_ep_ops = {
+	.enable		= fusb300_enable,
+	.disable	= fusb300_disable,
+
+	.alloc_request	= fusb300_alloc_request,
+	.free_request	= fusb300_free_request,
+
+	.queue		= fusb300_queue,
+	.dequeue	= fusb300_dequeue,
+
+	.set_halt	= fusb300_set_halt,
+	.fifo_flush	= fusb300_fifo_flush,
+	.set_wedge	= fusb300_set_wedge,
+};
+
+/*****************************************************************************/
+static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset,
+		       u32 value)
+{
+	iowrite32(value, fusb300->reg + offset);
+}
+
+static void fusb300_reset(void)
+{
+}
+
+static void fusb300_set_cxstall(struct fusb300 *fusb300)
+{
+	fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
+			   FUSB300_CSR_STL);
+}
+
+static void fusb300_set_cxdone(struct fusb300 *fusb300)
+{
+	fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
+			   FUSB300_CSR_DONE);
+}
+
+/* read data from cx fifo */
+static void fusb300_rdcxf(struct fusb300 *fusb300,
+		   u8 *buffer, u32 length)
+{
+	int i = 0;
+	u8 *tmp;
+	u32 data;
+
+	tmp = buffer;
+
+	for (i = (length >> 2); i > 0; i--) {
+		data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+		printk(KERN_DEBUG "    0x%x\n", data);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		*(tmp + 3) = (data >> 24) & 0xFF;
+		tmp = tmp + 4;
+	}
+
+	switch (length % 4) {
+	case 1:
+		data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+		printk(KERN_DEBUG "    0x%x\n", data);
+		*tmp = data & 0xFF;
+		break;
+	case 2:
+		data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+		printk(KERN_DEBUG "    0x%x\n", data);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		break;
+	case 3:
+		data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+		printk(KERN_DEBUG "    0x%x\n", data);
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		break;
+	default:
+		break;
+	}
+}
+
+static void fusb300_rdfifo(struct fusb300_ep *ep,
+			  struct fusb300_request *req,
+			  u32 length)
+{
+	int i = 0;
+	u8 *tmp;
+	u32 data, reg;
+	struct fusb300 *fusb300 = ep->fusb300;
+
+	tmp = req->req.buf + req->req.actual;
+	req->req.actual += length;
+
+	if (req->req.actual > req->req.length)
+		printk(KERN_DEBUG "req->req.actual > req->req.length\n");
+
+	for (i = (length >> 2); i > 0; i--) {
+		data = ioread32(fusb300->reg +
+			FUSB300_OFFSET_EPPORT(ep->epnum));
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		*(tmp + 3) = (data >> 24) & 0xFF;
+		tmp = tmp + 4;
+	}
+
+	switch (length % 4) {
+	case 1:
+		data = ioread32(fusb300->reg +
+			FUSB300_OFFSET_EPPORT(ep->epnum));
+		*tmp = data & 0xFF;
+		break;
+	case 2:
+		data = ioread32(fusb300->reg +
+			FUSB300_OFFSET_EPPORT(ep->epnum));
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		break;
+	case 3:
+		data = ioread32(fusb300->reg +
+			FUSB300_OFFSET_EPPORT(ep->epnum));
+		*tmp = data & 0xFF;
+		*(tmp + 1) = (data >> 8) & 0xFF;
+		*(tmp + 2) = (data >> 16) & 0xFF;
+		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;
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+
+	value = reg & FUSB300_EPSET0_STL;
+
+	return value;
+}
+
+static u8 fusb300_get_cxstall(struct fusb300 *fusb300)
+{
+	u8 value;
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
+
+	value = (reg & FUSB300_CSR_STL) >> 1;
+
+	return value;
+}
+
+static void request_error(struct fusb300 *fusb300)
+{
+	fusb300_set_cxstall(fusb300);
+	printk(KERN_DEBUG "request error!!\n");
+}
+
+static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+__releases(fusb300->lock)
+__acquires(fusb300->lock)
+{
+	u8 ep;
+	u16 status = 0;
+	u16 w_index = ctrl->wIndex;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		status = 1 << USB_DEVICE_SELF_POWERED;
+		break;
+	case USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = w_index & USB_ENDPOINT_NUMBER_MASK;
+		if (ep) {
+			if (fusb300_get_epnstall(fusb300, ep))
+				status = 1 << USB_ENDPOINT_HALT;
+		} else {
+			if (fusb300_get_cxstall(fusb300))
+				status = 0;
+		}
+		break;
+
+	default:
+		request_error(fusb300);
+		return;		/* exit */
+	}
+
+	fusb300->ep0_data = cpu_to_le16(status);
+	fusb300->ep0_req->buf = &fusb300->ep0_data;
+	fusb300->ep0_req->length = 2;
+
+	spin_unlock(&fusb300->lock);
+	fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL);
+	spin_lock(&fusb300->lock);
+}
+
+static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+	u8 ep;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		fusb300_set_cxdone(fusb300);
+		break;
+	case USB_RECIP_INTERFACE:
+		fusb300_set_cxdone(fusb300);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+		ep = w_index & USB_ENDPOINT_NUMBER_MASK;
+		if (ep)
+			fusb300_set_epnstall(fusb300, ep);
+		else
+			fusb300_set_cxstall(fusb300);
+		fusb300_set_cxdone(fusb300);
+		}
+		break;
+	default:
+		request_error(fusb300);
+		break;
+	}
+}
+
+static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep)
+{
+	fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
+			    FUSB300_EPSET0_CLRSEQNUM);
+}
+
+static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+	struct fusb300_ep *ep =
+		fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		fusb300_set_cxdone(fusb300);
+		break;
+	case USB_RECIP_INTERFACE:
+		fusb300_set_cxdone(fusb300);
+		break;
+	case USB_RECIP_ENDPOINT:
+		if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
+			if (ep->wedged) {
+				fusb300_set_cxdone(fusb300);
+				break;
+			}
+			if (ep->stall) {
+				ep->stall = 0;
+				fusb300_clear_seqnum(fusb300, ep->epnum);
+				fusb300_clear_epnstall(fusb300, ep->epnum);
+				if (!list_empty(&ep->queue))
+					enable_fifo_int(ep);
+			}
+		}
+		fusb300_set_cxdone(fusb300);
+		break;
+	default:
+		request_error(fusb300);
+		break;
+	}
+}
+
+static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR);
+
+	reg &= ~FUSB300_DAR_DRVADDR_MSK;
+	reg |= FUSB300_DAR_DRVADDR(addr);
+
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR);
+}
+
+static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+	if (ctrl->wValue >= 0x0100)
+		request_error(fusb300);
+	else {
+		fusb300_set_dev_addr(fusb300, ctrl->wValue);
+		fusb300_set_cxdone(fusb300);
+	}
+}
+
+#define UVC_COPY_DESCRIPTORS(mem, src) \
+	do { \
+		const struct usb_descriptor_header * const *__src; \
+		for (__src = src; *__src; ++__src) { \
+			memcpy(mem, *__src, (*__src)->bLength); \
+			mem += (*__src)->bLength; \
+		} \
+	} while (0)
+
+static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+	u8 *p = (u8 *)ctrl;
+	u8 ret = 0;
+	u8 i = 0;
+
+	fusb300_rdcxf(fusb300, p, 8);
+	fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN;
+	fusb300->ep0_length = ctrl->wLength;
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			get_status(fusb300, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			clear_feature(fusb300, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			set_feature(fusb300, ctrl);
+			break;
+		case USB_REQ_SET_ADDRESS:
+			set_address(fusb300, ctrl);
+			break;
+		case USB_REQ_SET_CONFIGURATION:
+			fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR,
+					   FUSB300_DAR_SETCONFG);
+			/* clear sequence number */
+			for (i = 1; i <= FUSB300_MAX_NUM_EP; i++)
+				fusb300_clear_seqnum(fusb300, i);
+			fusb300->reenum = 1;
+			ret = 1;
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else
+		ret = 1;
+
+	return ret;
+}
+
+static void done(struct fusb300_ep *ep, struct fusb300_request *req,
+		 int status)
+{
+	list_del_init(&req->queue);
+
+	/* don't modify queue heads during completion callback */
+	if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	spin_unlock(&ep->fusb300->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&ep->fusb300->lock);
+
+	if (ep->epnum) {
+		disable_fifo_int(ep);
+		if (!list_empty(&ep->queue))
+			enable_fifo_int(ep);
+	} else
+		fusb300_set_cxdone(ep->fusb300);
+}
+
+static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d,
+		u32 len)
+{
+	u32 value;
+	u32 reg;
+
+	/* wait SW owner */
+	do {
+		reg = ioread32(ep->fusb300->reg +
+			FUSB300_OFFSET_EPPRD_W0(ep->epnum));
+		reg &= FUSB300_EPPRD0_H;
+	} while (reg);
+
+	iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum));
+
+	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));
+
+	iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum));
+
+	fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY,
+		FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum));
+}
+
+static void fusb300_wait_idma_finished(struct fusb300_ep *ep)
+{
+	u32 reg;
+
+	do {
+		reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1);
+		if ((reg & FUSB300_IGR1_VBUS_CHG_INT) ||
+		    (reg & FUSB300_IGR1_WARM_RST_INT) ||
+		    (reg & FUSB300_IGR1_HOT_RST_INT) ||
+		    (reg & FUSB300_IGR1_USBRST_INT)
+		)
+			goto IDMA_RESET;
+		reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0);
+		reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum);
+	} while (!reg);
+
+	fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0,
+		FUSB300_IGR0_EPn_PRD_INT(ep->epnum));
+	return;
+
+IDMA_RESET:
+	reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0);
+	reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum);
+	iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0);
+}
+
+static void fusb300_set_idma(struct fusb300_ep *ep,
+			struct fusb300_request *req)
+{
+	int ret;
+
+	ret = usb_gadget_map_request(&ep->fusb300->gadget,
+			&req->req, DMA_TO_DEVICE);
+	if (ret)
+		return;
+
+	fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0,
+		FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
+
+	fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length);
+	/* check idma is done */
+	fusb300_wait_idma_finished(ep);
+
+	usb_gadget_unmap_request(&ep->fusb300->gadget,
+			&req->req, DMA_TO_DEVICE);
+}
+
+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)
+		fusb300_set_idma(ep, req);
+	done(ep, req, 0);
+}
+
+static void out_ep_fifo_handler(struct fusb300_ep *ep)
+{
+	struct fusb300 *fusb300 = ep->fusb300;
+	struct fusb300_request *req = list_entry(ep->queue.next,
+						 struct fusb300_request, queue);
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
+	u32 length = reg & FUSB300_FFR_BYCNT;
+
+	fusb300_rdfifo(ep, req, length);
+
+	/* finish out transfer */
+	if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket))
+		done(ep, req, 0);
+}
+
+static void check_device_mode(struct fusb300 *fusb300)
+{
+	u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR);
+
+	switch (reg & FUSB300_GCR_DEVEN_MSK) {
+	case FUSB300_GCR_DEVEN_SS:
+		fusb300->gadget.speed = USB_SPEED_SUPER;
+		break;
+	case FUSB300_GCR_DEVEN_HS:
+		fusb300->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case FUSB300_GCR_DEVEN_FS:
+		fusb300->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		fusb300->gadget.speed = USB_SPEED_UNKNOWN;
+		break;
+	}
+	printk(KERN_INFO "dev_mode = %d\n", (reg & FUSB300_GCR_DEVEN_MSK));
+}
+
+
+static void fusb300_ep0out(struct fusb300 *fusb300)
+{
+	struct fusb300_ep *ep = fusb300->ep[0];
+	u32 reg;
+
+	if (!list_empty(&ep->queue)) {
+		struct fusb300_request *req;
+
+		req = list_first_entry(&ep->queue,
+			struct fusb300_request, queue);
+		if (req->req.length)
+			fusb300_rdcxf(ep->fusb300, req->req.buf,
+				req->req.length);
+		done(ep, req, 0);
+		reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
+		reg &= ~FUSB300_IGER1_CX_OUT_INT;
+		iowrite32(reg, fusb300->reg + FUSB300_OFFSET_IGER1);
+	} else
+		pr_err("%s : empty queue\n", __func__);
+}
+
+static void fusb300_ep0in(struct fusb300 *fusb300)
+{
+	struct fusb300_request *req;
+	struct fusb300_ep *ep = fusb300->ep[0];
+
+	if ((!list_empty(&ep->queue)) && (fusb300->ep0_dir)) {
+		req = list_entry(ep->queue.next,
+				struct fusb300_request, queue);
+		if (req->req.length)
+			fusb300_wrcxf(ep, req);
+		if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+			done(ep, req, 0);
+	} else
+		fusb300_set_cxdone(fusb300);
+}
+
+static void fusb300_grp2_handler(void)
+{
+}
+
+static void fusb300_grp3_handler(void)
+{
+}
+
+static void fusb300_grp4_handler(void)
+{
+}
+
+static void fusb300_grp5_handler(void)
+{
+}
+
+static irqreturn_t fusb300_irq(int irq, void *_fusb300)
+{
+	struct fusb300 *fusb300 = _fusb300;
+	u32 int_grp1 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
+	u32 int_grp1_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
+	u32 int_grp0 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR0);
+	u32 int_grp0_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER0);
+	struct usb_ctrlrequest ctrl;
+	u8 in;
+	u32 reg;
+	int i;
+
+	spin_lock(&fusb300->lock);
+
+	int_grp1 &= int_grp1_en;
+	int_grp0 &= int_grp0_en;
+
+	if (int_grp1 & FUSB300_IGR1_WARM_RST_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_WARM_RST_INT);
+		printk(KERN_INFO"fusb300_warmreset\n");
+		fusb300_reset();
+	}
+
+	if (int_grp1 & FUSB300_IGR1_HOT_RST_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_HOT_RST_INT);
+		printk(KERN_INFO"fusb300_hotreset\n");
+		fusb300_reset();
+	}
+
+	if (int_grp1 & FUSB300_IGR1_USBRST_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_USBRST_INT);
+		fusb300_reset();
+	}
+	/* COMABT_INT has a highest priority */
+
+	if (int_grp1 & FUSB300_IGR1_CX_COMABT_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_CX_COMABT_INT);
+		printk(KERN_INFO"fusb300_ep0abt\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_VBUS_CHG_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_VBUS_CHG_INT);
+		printk(KERN_INFO"fusb300_vbus_change\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U3_EXIT_FAIL_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U3_EXIT_FAIL_INT);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U2_EXIT_FAIL_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U2_EXIT_FAIL_INT);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U1_EXIT_FAIL_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U1_EXIT_FAIL_INT);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U2_ENTRY_FAIL_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U2_ENTRY_FAIL_INT);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U1_ENTRY_FAIL_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U1_ENTRY_FAIL_INT);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U3_EXIT_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U3_EXIT_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U3_EXIT_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U2_EXIT_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U2_EXIT_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U2_EXIT_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U1_EXIT_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U1_EXIT_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U1_EXIT_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U3_ENTRY_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U3_ENTRY_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U3_ENTRY_INT\n");
+		fusb300_enable_bit(fusb300, FUSB300_OFFSET_SSCR1,
+				   FUSB300_SSCR1_GO_U3_DONE);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U2_ENTRY_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U2_ENTRY_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U2_ENTRY_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_U1_ENTRY_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_U1_ENTRY_INT);
+		printk(KERN_INFO "FUSB300_IGR1_U1_ENTRY_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_RESM_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_RESM_INT);
+		printk(KERN_INFO "fusb300_resume\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_SUSP_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_SUSP_INT);
+		printk(KERN_INFO "fusb300_suspend\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_HS_LPM_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_HS_LPM_INT);
+		printk(KERN_INFO "fusb300_HS_LPM_INT\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_DEV_MODE_CHG_INT) {
+		fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+				  FUSB300_IGR1_DEV_MODE_CHG_INT);
+		check_device_mode(fusb300);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_CX_COMFAIL_INT) {
+		fusb300_set_cxstall(fusb300);
+		printk(KERN_INFO "fusb300_ep0fail\n");
+	}
+
+	if (int_grp1 & FUSB300_IGR1_CX_SETUP_INT) {
+		printk(KERN_INFO "fusb300_ep0setup\n");
+		if (setup_packet(fusb300, &ctrl)) {
+			spin_unlock(&fusb300->lock);
+			if (fusb300->driver->setup(&fusb300->gadget, &ctrl) < 0)
+				fusb300_set_cxstall(fusb300);
+			spin_lock(&fusb300->lock);
+		}
+	}
+
+	if (int_grp1 & FUSB300_IGR1_CX_CMDEND_INT)
+		printk(KERN_INFO "fusb300_cmdend\n");
+
+
+	if (int_grp1 & FUSB300_IGR1_CX_OUT_INT) {
+		printk(KERN_INFO "fusb300_cxout\n");
+		fusb300_ep0out(fusb300);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_CX_IN_INT) {
+		printk(KERN_INFO "fusb300_cxin\n");
+		fusb300_ep0in(fusb300);
+	}
+
+	if (int_grp1 & FUSB300_IGR1_INTGRP5)
+		fusb300_grp5_handler();
+
+	if (int_grp1 & FUSB300_IGR1_INTGRP4)
+		fusb300_grp4_handler();
+
+	if (int_grp1 & FUSB300_IGR1_INTGRP3)
+		fusb300_grp3_handler();
+
+	if (int_grp1 & FUSB300_IGR1_INTGRP2)
+		fusb300_grp2_handler();
+
+	if (int_grp0) {
+		for (i = 1; i < FUSB300_MAX_NUM_EP; i++) {
+			if (int_grp0 & FUSB300_IGR0_EPn_FIFO_INT(i)) {
+				reg = ioread32(fusb300->reg +
+					FUSB300_OFFSET_EPSET1(i));
+				in = (reg & FUSB300_EPSET1_DIRIN) ? 1 : 0;
+				if (in)
+					in_ep_fifo_handler(fusb300->ep[i]);
+				else
+					out_ep_fifo_handler(fusb300->ep[i]);
+			}
+		}
+	}
+
+	spin_unlock(&fusb300->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void fusb300_set_u2_timeout(struct fusb300 *fusb300,
+				   u32 time)
+{
+	u32 reg;
+
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
+	reg &= ~0xff;
+	reg |= FUSB300_SSCR2_U2TIMEOUT(time);
+
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
+}
+
+static void fusb300_set_u1_timeout(struct fusb300 *fusb300,
+				   u32 time)
+{
+	u32 reg;
+
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
+	reg &= ~(0xff << 8);
+	reg |= FUSB300_SSCR2_U1TIMEOUT(time);
+
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
+}
+
+static void init_controller(struct fusb300 *fusb300)
+{
+	u32 reg;
+	u32 mask = 0;
+	u32 val = 0;
+
+	/* split on */
+	mask = val = FUSB300_AHBBCR_S0_SPLIT_ON | FUSB300_AHBBCR_S1_SPLIT_ON;
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_AHBCR);
+	reg &= ~mask;
+	reg |= val;
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_AHBCR);
+
+	/* enable high-speed LPM */
+	mask = val = FUSB300_HSCR_HS_LPM_PERMIT;
+	reg = ioread32(fusb300->reg + FUSB300_OFFSET_HSCR);
+	reg &= ~mask;
+	reg |= val;
+	iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR);
+
+	/*set u1 u2 timmer*/
+	fusb300_set_u2_timeout(fusb300, 0xff);
+	fusb300_set_u1_timeout(fusb300, 0xff);
+
+	/* enable all grp1 interrupt */
+	iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1);
+}
+/*------------------------------------------------------------------------*/
+static int fusb300_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct fusb300 *fusb300 = to_fusb300(g);
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	fusb300->driver = driver;
+
+	return 0;
+}
+
+static int fusb300_udc_stop(struct usb_gadget *g)
+{
+	struct fusb300 *fusb300 = to_fusb300(g);
+
+	init_controller(fusb300);
+	fusb300->driver = NULL;
+
+	return 0;
+}
+/*--------------------------------------------------------------------------*/
+
+static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	return 0;
+}
+
+static const struct usb_gadget_ops fusb300_gadget_ops = {
+	.pullup		= fusb300_udc_pullup,
+	.udc_start	= fusb300_udc_start,
+	.udc_stop	= fusb300_udc_stop,
+};
+
+static int fusb300_remove(struct platform_device *pdev)
+{
+	struct fusb300 *fusb300 = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&fusb300->gadget);
+	iounmap(fusb300->reg);
+	free_irq(platform_get_irq(pdev, 0), fusb300);
+
+	fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
+	kfree(fusb300);
+
+	return 0;
+}
+
+static int fusb300_probe(struct platform_device *pdev)
+{
+	struct resource *res, *ires, *ires1;
+	void __iomem *reg = NULL;
+	struct fusb300 *fusb300 = NULL;
+	struct fusb300_ep *_ep[FUSB300_MAX_NUM_EP];
+	int ret = 0;
+	int i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		pr_err("platform_get_resource error.\n");
+		goto clean_up;
+	}
+
+	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!ires) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev,
+			"platform_get_resource IORESOURCE_IRQ error.\n");
+		goto clean_up;
+	}
+
+	ires1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	if (!ires1) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev,
+			"platform_get_resource IORESOURCE_IRQ 1 error.\n");
+		goto clean_up;
+	}
+
+	reg = ioremap(res->start, resource_size(res));
+	if (reg == NULL) {
+		ret = -ENOMEM;
+		pr_err("ioremap error.\n");
+		goto clean_up;
+	}
+
+	/* initialize udc */
+	fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
+	if (fusb300 == NULL) {
+		ret = -ENOMEM;
+		goto clean_up;
+	}
+
+	for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
+		_ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
+		if (_ep[i] == NULL) {
+			ret = -ENOMEM;
+			goto clean_up;
+		}
+		fusb300->ep[i] = _ep[i];
+	}
+
+	spin_lock_init(&fusb300->lock);
+
+	platform_set_drvdata(pdev, fusb300);
+
+	fusb300->gadget.ops = &fusb300_gadget_ops;
+
+	fusb300->gadget.max_speed = USB_SPEED_HIGH;
+	fusb300->gadget.name = udc_name;
+	fusb300->reg = reg;
+
+	ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED,
+			  udc_name, fusb300);
+	if (ret < 0) {
+		pr_err("request_irq error (%d)\n", ret);
+		goto clean_up;
+	}
+
+	ret = request_irq(ires1->start, fusb300_irq,
+			IRQF_SHARED, udc_name, fusb300);
+	if (ret < 0) {
+		pr_err("request_irq1 error (%d)\n", ret);
+		goto clean_up;
+	}
+
+	INIT_LIST_HEAD(&fusb300->gadget.ep_list);
+
+	for (i = 0; i < FUSB300_MAX_NUM_EP ; i++) {
+		struct fusb300_ep *ep = fusb300->ep[i];
+
+		if (i != 0) {
+			INIT_LIST_HEAD(&fusb300->ep[i]->ep.ep_list);
+			list_add_tail(&fusb300->ep[i]->ep.ep_list,
+				     &fusb300->gadget.ep_list);
+		}
+		ep->fusb300 = fusb300;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = fusb300_ep_name[i];
+		ep->ep.ops = &fusb300_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE);
+
+		if (i == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+	usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE);
+	fusb300->ep[0]->epnum = 0;
+	fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
+	INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
+
+	fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep,
+				GFP_KERNEL);
+	if (fusb300->ep0_req == NULL) {
+		ret = -ENOMEM;
+		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);
+
+clean_up:
+	if (fusb300) {
+		if (fusb300->ep0_req)
+			fusb300_free_request(&fusb300->ep[0]->ep,
+				fusb300->ep0_req);
+		kfree(fusb300);
+	}
+	if (reg)
+		iounmap(reg);
+
+	return ret;
+}
+
+static struct platform_driver fusb300_driver = {
+	.remove =	fusb300_remove,
+	.driver		= {
+		.name =	(char *) udc_name,
+	},
+};
+
+module_platform_driver_probe(fusb300_driver, fusb300_probe);
diff --git a/drivers/usb/gadget/udc/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h
new file mode 100644
index 0000000..eb3d6d3
--- /dev/null
+++ b/drivers/usb/gadget/udc/fusb300_udc.h
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Fusb300 UDC (USB gadget)
+ *
+ * Copyright (C) 2010 Faraday Technology Corp.
+ *
+ * Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
+ */
+
+
+#ifndef __FUSB300_UDC_H__
+#define __FUSB300_UDC_H__
+
+#include <linux/kernel.h>
+
+#define FUSB300_OFFSET_GCR		0x00
+#define FUSB300_OFFSET_GTM		0x04
+#define FUSB300_OFFSET_DAR		0x08
+#define FUSB300_OFFSET_CSR		0x0C
+#define FUSB300_OFFSET_CXPORT		0x10
+#define FUSB300_OFFSET_EPSET0(n)	(0x20 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSET1(n)	(0x24 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSET2(n)	(0x28 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPFFR(n)		(0x2c + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSTRID(n)	(0x40 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_HSPTM		0x300
+#define FUSB300_OFFSET_HSCR		0x304
+#define FUSB300_OFFSET_SSCR0		0x308
+#define FUSB300_OFFSET_SSCR1		0x30C
+#define FUSB300_OFFSET_TT		0x310
+#define FUSB300_OFFSET_DEVNOTF		0x314
+#define FUSB300_OFFSET_DNC1		0x318
+#define FUSB300_OFFSET_CS		0x31C
+#define FUSB300_OFFSET_SOF		0x324
+#define FUSB300_OFFSET_EFCS		0x328
+#define FUSB300_OFFSET_IGR0		0x400
+#define FUSB300_OFFSET_IGR1		0x404
+#define FUSB300_OFFSET_IGR2		0x408
+#define FUSB300_OFFSET_IGR3		0x40C
+#define FUSB300_OFFSET_IGR4		0x410
+#define FUSB300_OFFSET_IGR5		0x414
+#define FUSB300_OFFSET_IGER0		0x420
+#define FUSB300_OFFSET_IGER1		0x424
+#define FUSB300_OFFSET_IGER2		0x428
+#define FUSB300_OFFSET_IGER3		0x42C
+#define FUSB300_OFFSET_IGER4		0x430
+#define FUSB300_OFFSET_IGER5		0x434
+#define FUSB300_OFFSET_DMAHMER		0x500
+#define FUSB300_OFFSET_EPPRDRDY		0x504
+#define FUSB300_OFFSET_DMAEPMR		0x508
+#define FUSB300_OFFSET_DMAENR		0x50C
+#define FUSB300_OFFSET_DMAAPR		0x510
+#define FUSB300_OFFSET_AHBCR		0x514
+#define FUSB300_OFFSET_EPPRD_W0(n)	(0x520 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPPRD_W1(n)	(0x524 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPPRD_W2(n)	(0x528 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPRD_PTR(n)	(0x52C + (n - 1) * 0x10)
+#define FUSB300_OFFSET_BUFDBG_START	0x800
+#define FUSB300_OFFSET_BUFDBG_END	0xBFC
+#define FUSB300_OFFSET_EPPORT(n)	(0x1010 + (n - 1) * 0x10)
+
+/*
+ * *	Global Control Register (offset = 000H)
+ * */
+#define FUSB300_GCR_SF_RST		(1 << 8)
+#define FUSB300_GCR_VBUS_STATUS		(1 << 7)
+#define FUSB300_GCR_FORCE_HS_SUSP	(1 << 6)
+#define FUSB300_GCR_SYNC_FIFO1_CLR	(1 << 5)
+#define FUSB300_GCR_SYNC_FIFO0_CLR	(1 << 4)
+#define FUSB300_GCR_FIFOCLR		(1 << 3)
+#define FUSB300_GCR_GLINTEN		(1 << 2)
+#define FUSB300_GCR_DEVEN_FS		0x3
+#define FUSB300_GCR_DEVEN_HS		0x2
+#define FUSB300_GCR_DEVEN_SS		0x1
+#define FUSB300_GCR_DEVDIS		0x0
+#define FUSB300_GCR_DEVEN_MSK		0x3
+
+
+/*
+ * *Global Test Mode (offset = 004H)
+ * */
+#define FUSB300_GTM_TST_DIS_SOFGEN	(1 << 16)
+#define FUSB300_GTM_TST_CUR_EP_ENTRY(n)	((n & 0xF) << 12)
+#define FUSB300_GTM_TST_EP_ENTRY(n)	((n & 0xF) << 8)
+#define FUSB300_GTM_TST_EP_NUM(n)	((n & 0xF) << 4)
+#define FUSB300_GTM_TST_FIFO_DEG	(1 << 1)
+#define FUSB300_GTM_TSTMODE		(1 << 0)
+
+/*
+ * * Device Address Register (offset = 008H)
+ * */
+#define FUSB300_DAR_SETCONFG	(1 << 7)
+#define FUSB300_DAR_DRVADDR(x)	(x & 0x7F)
+#define FUSB300_DAR_DRVADDR_MSK	0x7F
+
+/*
+ * *Control Transfer Configuration and Status Register
+ * (CX_Config_Status, offset = 00CH)
+ * */
+#define FUSB300_CSR_LEN(x)	((x & 0xFFFF) << 8)
+#define FUSB300_CSR_LEN_MSK	(0xFFFF << 8)
+#define FUSB300_CSR_EMP		(1 << 4)
+#define FUSB300_CSR_FUL		(1 << 3)
+#define FUSB300_CSR_CLR		(1 << 2)
+#define FUSB300_CSR_STL		(1 << 1)
+#define FUSB300_CSR_DONE	(1 << 0)
+
+/*
+ * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 )
+ * */
+#define FUSB300_EPSET0_STL_CLR		(1 << 3)
+#define FUSB300_EPSET0_CLRSEQNUM	(1 << 2)
+#define FUSB300_EPSET0_STL		(1 << 0)
+
+/*
+ * * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_EPSET1_START_ENTRY(x)	((x & 0xFF) << 24)
+#define FUSB300_EPSET1_START_ENTRY_MSK	(0xFF << 24)
+#define FUSB300_EPSET1_FIFOENTRY(x)	((x & 0x1F) << 12)
+#define FUSB300_EPSET1_FIFOENTRY_MSK	(0x1f << 12)
+#define FUSB300_EPSET1_INTERVAL(x)	((x & 0x7) << 6)
+#define FUSB300_EPSET1_BWNUM(x)		((x & 0x3) << 4)
+#define FUSB300_EPSET1_TYPEISO		(1 << 2)
+#define FUSB300_EPSET1_TYPEBLK		(2 << 2)
+#define FUSB300_EPSET1_TYPEINT		(3 << 2)
+#define FUSB300_EPSET1_TYPE(x)		((x & 0x3) << 2)
+#define FUSB300_EPSET1_TYPE_MSK		(0x3 << 2)
+#define FUSB300_EPSET1_DIROUT		(0 << 1)
+#define FUSB300_EPSET1_DIRIN		(1 << 1)
+#define FUSB300_EPSET1_DIR(x)		((x & 0x1) << 1)
+#define FUSB300_EPSET1_DIRIN		(1 << 1)
+#define FUSB300_EPSET1_DIR_MSK		((0x1) << 1)
+#define FUSB300_EPSET1_ACTDIS		0
+#define FUSB300_EPSET1_ACTEN		1
+
+/*
+ * *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_EPSET2_ADDROFS(x)	((x & 0x7FFF) << 16)
+#define FUSB300_EPSET2_ADDROFS_MSK	(0x7fff << 16)
+#define FUSB300_EPSET2_MPS(x)		(x & 0x7FF)
+#define FUSB300_EPSET2_MPS_MSK		0x7FF
+
+/*
+ * * EPn FIFO Register (offset = 2cH+(n-1)*30H)
+ * */
+#define FUSB300_FFR_RST		(1 << 31)
+#define FUSB300_FF_FUL		(1 << 30)
+#define FUSB300_FF_EMPTY	(1 << 29)
+#define FUSB300_FFR_BYCNT	0x1FFFF
+
+/*
+ * *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_STRID_STREN	(1 << 16)
+#define FUSB300_STRID_STRID(x)	(x & 0xFFFF)
+
+/*
+ * *HS PHY Test Mode (offset = 300H)
+ * */
+#define FUSB300_HSPTM_TSTPKDONE		(1 << 4)
+#define FUSB300_HSPTM_TSTPKT		(1 << 3)
+#define FUSB300_HSPTM_TSTSET0NAK	(1 << 2)
+#define FUSB300_HSPTM_TSTKSTA		(1 << 1)
+#define FUSB300_HSPTM_TSTJSTA		(1 << 0)
+
+/*
+ * *HS Control Register (offset = 304H)
+ * */
+#define FUSB300_HSCR_HS_LPM_PERMIT	(1 << 8)
+#define FUSB300_HSCR_HS_LPM_RMWKUP	(1 << 7)
+#define FUSB300_HSCR_CAP_LPM_RMWKUP	(1 << 6)
+#define FUSB300_HSCR_HS_GOSUSP		(1 << 5)
+#define FUSB300_HSCR_HS_GORMWKU		(1 << 4)
+#define FUSB300_HSCR_CAP_RMWKUP		(1 << 3)
+#define FUSB300_HSCR_IDLECNT_0MS	0
+#define FUSB300_HSCR_IDLECNT_1MS	1
+#define FUSB300_HSCR_IDLECNT_2MS	2
+#define FUSB300_HSCR_IDLECNT_3MS	3
+#define FUSB300_HSCR_IDLECNT_4MS	4
+#define FUSB300_HSCR_IDLECNT_5MS	5
+#define FUSB300_HSCR_IDLECNT_6MS	6
+#define FUSB300_HSCR_IDLECNT_7MS	7
+
+/*
+ * * SS Controller Register 0 (offset = 308H)
+ * */
+#define FUSB300_SSCR0_MAX_INTERVAL(x)	((x & 0x7) << 4)
+#define FUSB300_SSCR0_U2_FUN_EN		(1 << 1)
+#define FUSB300_SSCR0_U1_FUN_EN		(1 << 0)
+
+/*
+ * * SS Controller Register 1 (offset = 30CH)
+ * */
+#define FUSB300_SSCR1_GO_U3_DONE	(1 << 8)
+#define FUSB300_SSCR1_TXDEEMPH_LEVEL	(1 << 7)
+#define FUSB300_SSCR1_DIS_SCRMB		(1 << 6)
+#define FUSB300_SSCR1_FORCE_RECOVERY	(1 << 5)
+#define FUSB300_SSCR1_U3_WAKEUP_EN	(1 << 4)
+#define FUSB300_SSCR1_U2_EXIT_EN	(1 << 3)
+#define FUSB300_SSCR1_U1_EXIT_EN	(1 << 2)
+#define FUSB300_SSCR1_U2_ENTRY_EN	(1 << 1)
+#define FUSB300_SSCR1_U1_ENTRY_EN	(1 << 0)
+
+/*
+ * *SS Controller Register 2  (offset = 310H)
+ * */
+#define FUSB300_SSCR2_SS_TX_SWING		(1 << 25)
+#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT	(1 << 24)
+#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x)	((x & 0xFF) << 16)
+#define FUSB300_SSCR2_U1TIMEOUT(x)		((x & 0xFF) << 8)
+#define FUSB300_SSCR2_U2TIMEOUT(x)		(x & 0xFF)
+
+/*
+ * *SS Device Notification Control (DEV_NOTF, offset = 314H)
+ * */
+#define FUSB300_DEVNOTF_CONTEXT0(x)		((x & 0xFFFFFF) << 8)
+#define FUSB300_DEVNOTF_TYPE_DIS		0
+#define FUSB300_DEVNOTF_TYPE_FUNCWAKE		1
+#define FUSB300_DEVNOTF_TYPE_LTM		2
+#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG	3
+
+/*
+ * *BFM Arbiter Priority Register (BFM_ARB offset = 31CH)
+ * */
+#define FUSB300_BFMARB_ARB_M1	(1 << 3)
+#define FUSB300_BFMARB_ARB_M0	(1 << 2)
+#define FUSB300_BFMARB_ARB_S1	(1 << 1)
+#define FUSB300_BFMARB_ARB_S0	1
+
+/*
+ * *Vendor Specific IO Control Register (offset = 320H)
+ * */
+#define FUSB300_VSIC_VCTLOAD_N	(1 << 8)
+#define FUSB300_VSIC_VCTL(x)	(x & 0x3F)
+
+/*
+ * *SOF Mask Timer (offset = 324H)
+ * */
+#define FUSB300_SOF_MASK_TIMER_HS	0x044c
+#define FUSB300_SOF_MASK_TIMER_FS	0x2710
+
+/*
+ * *Error Flag and Control Status (offset = 328H)
+ * */
+#define FUSB300_EFCS_PM_STATE_U3	3
+#define FUSB300_EFCS_PM_STATE_U2	2
+#define FUSB300_EFCS_PM_STATE_U1	1
+#define FUSB300_EFCS_PM_STATE_U0	0
+
+/*
+ * *Interrupt Group 0 Register (offset = 400H)
+ * */
+#define FUSB300_IGR0_EP15_PRD_INT	(1 << 31)
+#define FUSB300_IGR0_EP14_PRD_INT	(1 << 30)
+#define FUSB300_IGR0_EP13_PRD_INT	(1 << 29)
+#define FUSB300_IGR0_EP12_PRD_INT	(1 << 28)
+#define FUSB300_IGR0_EP11_PRD_INT	(1 << 27)
+#define FUSB300_IGR0_EP10_PRD_INT	(1 << 26)
+#define FUSB300_IGR0_EP9_PRD_INT	(1 << 25)
+#define FUSB300_IGR0_EP8_PRD_INT	(1 << 24)
+#define FUSB300_IGR0_EP7_PRD_INT	(1 << 23)
+#define FUSB300_IGR0_EP6_PRD_INT	(1 << 22)
+#define FUSB300_IGR0_EP5_PRD_INT	(1 << 21)
+#define FUSB300_IGR0_EP4_PRD_INT	(1 << 20)
+#define FUSB300_IGR0_EP3_PRD_INT	(1 << 19)
+#define FUSB300_IGR0_EP2_PRD_INT	(1 << 18)
+#define FUSB300_IGR0_EP1_PRD_INT	(1 << 17)
+#define FUSB300_IGR0_EPn_PRD_INT(n)	(1 << (n + 16))
+
+#define FUSB300_IGR0_EP15_FIFO_INT	(1 << 15)
+#define FUSB300_IGR0_EP14_FIFO_INT	(1 << 14)
+#define FUSB300_IGR0_EP13_FIFO_INT	(1 << 13)
+#define FUSB300_IGR0_EP12_FIFO_INT	(1 << 12)
+#define FUSB300_IGR0_EP11_FIFO_INT	(1 << 11)
+#define FUSB300_IGR0_EP10_FIFO_INT	(1 << 10)
+#define FUSB300_IGR0_EP9_FIFO_INT	(1 << 9)
+#define FUSB300_IGR0_EP8_FIFO_INT	(1 << 8)
+#define FUSB300_IGR0_EP7_FIFO_INT	(1 << 7)
+#define FUSB300_IGR0_EP6_FIFO_INT	(1 << 6)
+#define FUSB300_IGR0_EP5_FIFO_INT	(1 << 5)
+#define FUSB300_IGR0_EP4_FIFO_INT	(1 << 4)
+#define FUSB300_IGR0_EP3_FIFO_INT	(1 << 3)
+#define FUSB300_IGR0_EP2_FIFO_INT	(1 << 2)
+#define FUSB300_IGR0_EP1_FIFO_INT	(1 << 1)
+#define FUSB300_IGR0_EPn_FIFO_INT(n)	(1 << n)
+
+/*
+ * *Interrupt Group 1 Register (offset = 404H)
+ * */
+#define FUSB300_IGR1_INTGRP5		(1 << 31)
+#define FUSB300_IGR1_VBUS_CHG_INT	(1 << 30)
+#define FUSB300_IGR1_SYNF1_EMPTY_INT	(1 << 29)
+#define FUSB300_IGR1_SYNF0_EMPTY_INT	(1 << 28)
+#define FUSB300_IGR1_U3_EXIT_FAIL_INT	(1 << 27)
+#define FUSB300_IGR1_U2_EXIT_FAIL_INT	(1 << 26)
+#define FUSB300_IGR1_U1_EXIT_FAIL_INT	(1 << 25)
+#define FUSB300_IGR1_U2_ENTRY_FAIL_INT	(1 << 24)
+#define FUSB300_IGR1_U1_ENTRY_FAIL_INT	(1 << 23)
+#define FUSB300_IGR1_U3_EXIT_INT	(1 << 22)
+#define FUSB300_IGR1_U2_EXIT_INT	(1 << 21)
+#define FUSB300_IGR1_U1_EXIT_INT	(1 << 20)
+#define FUSB300_IGR1_U3_ENTRY_INT	(1 << 19)
+#define FUSB300_IGR1_U2_ENTRY_INT	(1 << 18)
+#define FUSB300_IGR1_U1_ENTRY_INT	(1 << 17)
+#define FUSB300_IGR1_HOT_RST_INT	(1 << 16)
+#define FUSB300_IGR1_WARM_RST_INT	(1 << 15)
+#define FUSB300_IGR1_RESM_INT		(1 << 14)
+#define FUSB300_IGR1_SUSP_INT		(1 << 13)
+#define FUSB300_IGR1_HS_LPM_INT		(1 << 12)
+#define FUSB300_IGR1_USBRST_INT		(1 << 11)
+#define FUSB300_IGR1_DEV_MODE_CHG_INT	(1 << 9)
+#define FUSB300_IGR1_CX_COMABT_INT	(1 << 8)
+#define FUSB300_IGR1_CX_COMFAIL_INT	(1 << 7)
+#define FUSB300_IGR1_CX_CMDEND_INT	(1 << 6)
+#define FUSB300_IGR1_CX_OUT_INT		(1 << 5)
+#define FUSB300_IGR1_CX_IN_INT		(1 << 4)
+#define FUSB300_IGR1_CX_SETUP_INT	(1 << 3)
+#define FUSB300_IGR1_INTGRP4		(1 << 2)
+#define FUSB300_IGR1_INTGRP3		(1 << 1)
+#define FUSB300_IGR1_INTGRP2		(1 << 0)
+
+/*
+ * *Interrupt Group 2 Register (offset = 408H)
+ * */
+#define FUSB300_IGR2_EP6_STR_ACCEPT_INT		(1 << 29)
+#define FUSB300_IGR2_EP6_STR_RESUME_INT		(1 << 28)
+#define FUSB300_IGR2_EP6_STR_REQ_INT		(1 << 27)
+#define FUSB300_IGR2_EP6_STR_NOTRDY_INT		(1 << 26)
+#define FUSB300_IGR2_EP6_STR_PRIME_INT		(1 << 25)
+#define FUSB300_IGR2_EP5_STR_ACCEPT_INT		(1 << 24)
+#define FUSB300_IGR2_EP5_STR_RESUME_INT		(1 << 23)
+#define FUSB300_IGR2_EP5_STR_REQ_INT		(1 << 22)
+#define FUSB300_IGR2_EP5_STR_NOTRDY_INT		(1 << 21)
+#define FUSB300_IGR2_EP5_STR_PRIME_INT		(1 << 20)
+#define FUSB300_IGR2_EP4_STR_ACCEPT_INT		(1 << 19)
+#define FUSB300_IGR2_EP4_STR_RESUME_INT		(1 << 18)
+#define FUSB300_IGR2_EP4_STR_REQ_INT		(1 << 17)
+#define FUSB300_IGR2_EP4_STR_NOTRDY_INT		(1 << 16)
+#define FUSB300_IGR2_EP4_STR_PRIME_INT		(1 << 15)
+#define FUSB300_IGR2_EP3_STR_ACCEPT_INT		(1 << 14)
+#define FUSB300_IGR2_EP3_STR_RESUME_INT		(1 << 13)
+#define FUSB300_IGR2_EP3_STR_REQ_INT		(1 << 12)
+#define FUSB300_IGR2_EP3_STR_NOTRDY_INT		(1 << 11)
+#define FUSB300_IGR2_EP3_STR_PRIME_INT		(1 << 10)
+#define FUSB300_IGR2_EP2_STR_ACCEPT_INT		(1 << 9)
+#define FUSB300_IGR2_EP2_STR_RESUME_INT		(1 << 8)
+#define FUSB300_IGR2_EP2_STR_REQ_INT		(1 << 7)
+#define FUSB300_IGR2_EP2_STR_NOTRDY_INT		(1 << 6)
+#define FUSB300_IGR2_EP2_STR_PRIME_INT		(1 << 5)
+#define FUSB300_IGR2_EP1_STR_ACCEPT_INT		(1 << 4)
+#define FUSB300_IGR2_EP1_STR_RESUME_INT		(1 << 3)
+#define FUSB300_IGR2_EP1_STR_REQ_INT		(1 << 2)
+#define FUSB300_IGR2_EP1_STR_NOTRDY_INT		(1 << 1)
+#define FUSB300_IGR2_EP1_STR_PRIME_INT		(1 << 0)
+
+#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n)	(1 << (5 * n - 1))
+#define FUSB300_IGR2_EP_STR_RESUME_INT(n)	(1 << (5 * n - 2))
+#define FUSB300_IGR2_EP_STR_REQ_INT(n)		(1 << (5 * n - 3))
+#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n)	(1 << (5 * n - 4))
+#define FUSB300_IGR2_EP_STR_PRIME_INT(n)	(1 << (5 * n - 5))
+
+/*
+ * *Interrupt Group 3 Register (offset = 40CH)
+ * */
+#define FUSB300_IGR3_EP12_STR_ACCEPT_INT	(1 << 29)
+#define FUSB300_IGR3_EP12_STR_RESUME_INT	(1 << 28)
+#define FUSB300_IGR3_EP12_STR_REQ_INT		(1 << 27)
+#define FUSB300_IGR3_EP12_STR_NOTRDY_INT	(1 << 26)
+#define FUSB300_IGR3_EP12_STR_PRIME_INT		(1 << 25)
+#define FUSB300_IGR3_EP11_STR_ACCEPT_INT	(1 << 24)
+#define FUSB300_IGR3_EP11_STR_RESUME_INT	(1 << 23)
+#define FUSB300_IGR3_EP11_STR_REQ_INT		(1 << 22)
+#define FUSB300_IGR3_EP11_STR_NOTRDY_INT	(1 << 21)
+#define FUSB300_IGR3_EP11_STR_PRIME_INT		(1 << 20)
+#define FUSB300_IGR3_EP10_STR_ACCEPT_INT	(1 << 19)
+#define FUSB300_IGR3_EP10_STR_RESUME_INT	(1 << 18)
+#define FUSB300_IGR3_EP10_STR_REQ_INT		(1 << 17)
+#define FUSB300_IGR3_EP10_STR_NOTRDY_INT	(1 << 16)
+#define FUSB300_IGR3_EP10_STR_PRIME_INT		(1 << 15)
+#define FUSB300_IGR3_EP9_STR_ACCEPT_INT		(1 << 14)
+#define FUSB300_IGR3_EP9_STR_RESUME_INT		(1 << 13)
+#define FUSB300_IGR3_EP9_STR_REQ_INT		(1 << 12)
+#define FUSB300_IGR3_EP9_STR_NOTRDY_INT		(1 << 11)
+#define FUSB300_IGR3_EP9_STR_PRIME_INT		(1 << 10)
+#define FUSB300_IGR3_EP8_STR_ACCEPT_INT		(1 << 9)
+#define FUSB300_IGR3_EP8_STR_RESUME_INT		(1 << 8)
+#define FUSB300_IGR3_EP8_STR_REQ_INT		(1 << 7)
+#define FUSB300_IGR3_EP8_STR_NOTRDY_INT		(1 << 6)
+#define FUSB300_IGR3_EP8_STR_PRIME_INT		(1 << 5)
+#define FUSB300_IGR3_EP7_STR_ACCEPT_INT		(1 << 4)
+#define FUSB300_IGR3_EP7_STR_RESUME_INT		(1 << 3)
+#define FUSB300_IGR3_EP7_STR_REQ_INT		(1 << 2)
+#define FUSB300_IGR3_EP7_STR_NOTRDY_INT		(1 << 1)
+#define FUSB300_IGR3_EP7_STR_PRIME_INT		(1 << 0)
+
+#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n)	(1 << (5 * (n - 6) - 1))
+#define FUSB300_IGR3_EP_STR_RESUME_INT(n)	(1 << (5 * (n - 6) - 2))
+#define FUSB300_IGR3_EP_STR_REQ_INT(n)		(1 << (5 * (n - 6) - 3))
+#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n)	(1 << (5 * (n - 6) - 4))
+#define FUSB300_IGR3_EP_STR_PRIME_INT(n)	(1 << (5 * (n - 6) - 5))
+
+/*
+ * *Interrupt Group 4 Register (offset = 410H)
+ * */
+#define FUSB300_IGR4_EP15_RX0_INT		(1 << 31)
+#define FUSB300_IGR4_EP14_RX0_INT		(1 << 30)
+#define FUSB300_IGR4_EP13_RX0_INT		(1 << 29)
+#define FUSB300_IGR4_EP12_RX0_INT		(1 << 28)
+#define FUSB300_IGR4_EP11_RX0_INT		(1 << 27)
+#define FUSB300_IGR4_EP10_RX0_INT		(1 << 26)
+#define FUSB300_IGR4_EP9_RX0_INT		(1 << 25)
+#define FUSB300_IGR4_EP8_RX0_INT		(1 << 24)
+#define FUSB300_IGR4_EP7_RX0_INT		(1 << 23)
+#define FUSB300_IGR4_EP6_RX0_INT		(1 << 22)
+#define FUSB300_IGR4_EP5_RX0_INT		(1 << 21)
+#define FUSB300_IGR4_EP4_RX0_INT		(1 << 20)
+#define FUSB300_IGR4_EP3_RX0_INT		(1 << 19)
+#define FUSB300_IGR4_EP2_RX0_INT		(1 << 18)
+#define FUSB300_IGR4_EP1_RX0_INT		(1 << 17)
+#define FUSB300_IGR4_EP_RX0_INT(x)		(1 << (x + 16))
+#define FUSB300_IGR4_EP15_STR_ACCEPT_INT	(1 << 14)
+#define FUSB300_IGR4_EP15_STR_RESUME_INT	(1 << 13)
+#define FUSB300_IGR4_EP15_STR_REQ_INT		(1 << 12)
+#define FUSB300_IGR4_EP15_STR_NOTRDY_INT	(1 << 11)
+#define FUSB300_IGR4_EP15_STR_PRIME_INT		(1 << 10)
+#define FUSB300_IGR4_EP14_STR_ACCEPT_INT	(1 << 9)
+#define FUSB300_IGR4_EP14_STR_RESUME_INT	(1 << 8)
+#define FUSB300_IGR4_EP14_STR_REQ_INT		(1 << 7)
+#define FUSB300_IGR4_EP14_STR_NOTRDY_INT	(1 << 6)
+#define FUSB300_IGR4_EP14_STR_PRIME_INT		(1 << 5)
+#define FUSB300_IGR4_EP13_STR_ACCEPT_INT	(1 << 4)
+#define FUSB300_IGR4_EP13_STR_RESUME_INT	(1 << 3)
+#define FUSB300_IGR4_EP13_STR_REQ_INT		(1 << 2)
+#define FUSB300_IGR4_EP13_STR_NOTRDY_INT	(1 << 1)
+#define FUSB300_IGR4_EP13_STR_PRIME_INT		(1 << 0)
+
+#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n)	(1 << (5 * (n - 12) - 1))
+#define FUSB300_IGR4_EP_STR_RESUME_INT(n)	(1 << (5 * (n - 12) - 2))
+#define FUSB300_IGR4_EP_STR_REQ_INT(n)		(1 << (5 * (n - 12) - 3))
+#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n)	(1 << (5 * (n - 12) - 4))
+#define FUSB300_IGR4_EP_STR_PRIME_INT(n)	(1 << (5 * (n - 12) - 5))
+
+/*
+ * *Interrupt Group 5 Register (offset = 414H)
+ * */
+#define FUSB300_IGR5_EP_STL_INT(n)	(1 << n)
+
+/*
+ * *Interrupt Enable Group 0 Register (offset = 420H)
+ * */
+#define FUSB300_IGER0_EEP15_PRD_INT	(1 << 31)
+#define FUSB300_IGER0_EEP14_PRD_INT	(1 << 30)
+#define FUSB300_IGER0_EEP13_PRD_INT	(1 << 29)
+#define FUSB300_IGER0_EEP12_PRD_INT	(1 << 28)
+#define FUSB300_IGER0_EEP11_PRD_INT	(1 << 27)
+#define FUSB300_IGER0_EEP10_PRD_INT	(1 << 26)
+#define FUSB300_IGER0_EEP9_PRD_INT	(1 << 25)
+#define FUSB300_IGER0_EP8_PRD_INT	(1 << 24)
+#define FUSB300_IGER0_EEP7_PRD_INT	(1 << 23)
+#define FUSB300_IGER0_EEP6_PRD_INT	(1 << 22)
+#define FUSB300_IGER0_EEP5_PRD_INT	(1 << 21)
+#define FUSB300_IGER0_EEP4_PRD_INT	(1 << 20)
+#define FUSB300_IGER0_EEP3_PRD_INT	(1 << 19)
+#define FUSB300_IGER0_EEP2_PRD_INT	(1 << 18)
+#define FUSB300_IGER0_EEP1_PRD_INT	(1 << 17)
+#define FUSB300_IGER0_EEPn_PRD_INT(n)	(1 << (n + 16))
+
+#define FUSB300_IGER0_EEP15_FIFO_INT	(1 << 15)
+#define FUSB300_IGER0_EEP14_FIFO_INT	(1 << 14)
+#define FUSB300_IGER0_EEP13_FIFO_INT	(1 << 13)
+#define FUSB300_IGER0_EEP12_FIFO_INT	(1 << 12)
+#define FUSB300_IGER0_EEP11_FIFO_INT	(1 << 11)
+#define FUSB300_IGER0_EEP10_FIFO_INT	(1 << 10)
+#define FUSB300_IGER0_EEP9_FIFO_INT	(1 << 9)
+#define FUSB300_IGER0_EEP8_FIFO_INT	(1 << 8)
+#define FUSB300_IGER0_EEP7_FIFO_INT	(1 << 7)
+#define FUSB300_IGER0_EEP6_FIFO_INT	(1 << 6)
+#define FUSB300_IGER0_EEP5_FIFO_INT	(1 << 5)
+#define FUSB300_IGER0_EEP4_FIFO_INT	(1 << 4)
+#define FUSB300_IGER0_EEP3_FIFO_INT	(1 << 3)
+#define FUSB300_IGER0_EEP2_FIFO_INT	(1 << 2)
+#define FUSB300_IGER0_EEP1_FIFO_INT	(1 << 1)
+#define FUSB300_IGER0_EEPn_FIFO_INT(n)	(1 << n)
+
+/*
+ * *Interrupt Enable Group 1 Register (offset = 424H)
+ * */
+#define FUSB300_IGER1_EINT_GRP5		(1 << 31)
+#define FUSB300_IGER1_VBUS_CHG_INT	(1 << 30)
+#define FUSB300_IGER1_SYNF1_EMPTY_INT	(1 << 29)
+#define FUSB300_IGER1_SYNF0_EMPTY_INT	(1 << 28)
+#define FUSB300_IGER1_U3_EXIT_FAIL_INT	(1 << 27)
+#define FUSB300_IGER1_U2_EXIT_FAIL_INT	(1 << 26)
+#define FUSB300_IGER1_U1_EXIT_FAIL_INT	(1 << 25)
+#define FUSB300_IGER1_U2_ENTRY_FAIL_INT	(1 << 24)
+#define FUSB300_IGER1_U1_ENTRY_FAIL_INT	(1 << 23)
+#define FUSB300_IGER1_U3_EXIT_INT	(1 << 22)
+#define FUSB300_IGER1_U2_EXIT_INT	(1 << 21)
+#define FUSB300_IGER1_U1_EXIT_INT	(1 << 20)
+#define FUSB300_IGER1_U3_ENTRY_INT	(1 << 19)
+#define FUSB300_IGER1_U2_ENTRY_INT	(1 << 18)
+#define FUSB300_IGER1_U1_ENTRY_INT	(1 << 17)
+#define FUSB300_IGER1_HOT_RST_INT	(1 << 16)
+#define FUSB300_IGER1_WARM_RST_INT	(1 << 15)
+#define FUSB300_IGER1_RESM_INT		(1 << 14)
+#define FUSB300_IGER1_SUSP_INT		(1 << 13)
+#define FUSB300_IGER1_LPM_INT		(1 << 12)
+#define FUSB300_IGER1_HS_RST_INT	(1 << 11)
+#define FUSB300_IGER1_EDEV_MODE_CHG_INT	(1 << 9)
+#define FUSB300_IGER1_CX_COMABT_INT	(1 << 8)
+#define FUSB300_IGER1_CX_COMFAIL_INT	(1 << 7)
+#define FUSB300_IGER1_CX_CMDEND_INT	(1 << 6)
+#define FUSB300_IGER1_CX_OUT_INT	(1 << 5)
+#define FUSB300_IGER1_CX_IN_INT		(1 << 4)
+#define FUSB300_IGER1_CX_SETUP_INT	(1 << 3)
+#define FUSB300_IGER1_INTGRP4		(1 << 2)
+#define FUSB300_IGER1_INTGRP3		(1 << 1)
+#define FUSB300_IGER1_INTGRP2		(1 << 0)
+
+/*
+ * *Interrupt Enable Group 2 Register (offset = 428H)
+ * */
+#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n)	(1 << (5 * n - 1))
+#define FUSB300_IGER2_EEP_STR_RESUME_INT(n)	(1 << (5 * n - 2))
+#define FUSB300_IGER2_EEP_STR_REQ_INT(n)	(1 << (5 * n - 3))
+#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n)	(1 << (5 * n - 4))
+#define FUSB300_IGER2_EEP_STR_PRIME_INT(n)	(1 << (5 * n - 5))
+
+/*
+ * *Interrupt Enable Group 3 Register (offset = 42CH)
+ * */
+
+#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n)	(1 << (5 * (n - 6) - 1))
+#define FUSB300_IGER3_EEP_STR_RESUME_INT(n)	(1 << (5 * (n - 6) - 2))
+#define FUSB300_IGER3_EEP_STR_REQ_INT(n)	(1 << (5 * (n - 6) - 3))
+#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n)	(1 << (5 * (n - 6) - 4))
+#define FUSB300_IGER3_EEP_STR_PRIME_INT(n)	(1 << (5 * (n - 6) - 5))
+
+/*
+ * *Interrupt Enable Group 4 Register (offset = 430H)
+ * */
+
+#define FUSB300_IGER4_EEP_RX0_INT(n)		(1 << (n + 16))
+#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n)	(1 << (5 * (n - 6) - 1))
+#define FUSB300_IGER4_EEP_STR_RESUME_INT(n)	(1 << (5 * (n - 6) - 2))
+#define FUSB300_IGER4_EEP_STR_REQ_INT(n)	(1 << (5 * (n - 6) - 3))
+#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n)	(1 << (5 * (n - 6) - 4))
+#define FUSB300_IGER4_EEP_STR_PRIME_INT(n)	(1 << (5 * (n - 6) - 5))
+
+/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */
+
+#define FUSB300_EPPRDR_EP15_PRD_RDY		(1 << 15)
+#define FUSB300_EPPRDR_EP14_PRD_RDY		(1 << 14)
+#define FUSB300_EPPRDR_EP13_PRD_RDY		(1 << 13)
+#define FUSB300_EPPRDR_EP12_PRD_RDY		(1 << 12)
+#define FUSB300_EPPRDR_EP11_PRD_RDY		(1 << 11)
+#define FUSB300_EPPRDR_EP10_PRD_RDY		(1 << 10)
+#define FUSB300_EPPRDR_EP9_PRD_RDY		(1 << 9)
+#define FUSB300_EPPRDR_EP8_PRD_RDY		(1 << 8)
+#define FUSB300_EPPRDR_EP7_PRD_RDY		(1 << 7)
+#define FUSB300_EPPRDR_EP6_PRD_RDY		(1 << 6)
+#define FUSB300_EPPRDR_EP5_PRD_RDY		(1 << 5)
+#define FUSB300_EPPRDR_EP4_PRD_RDY		(1 << 4)
+#define FUSB300_EPPRDR_EP3_PRD_RDY		(1 << 3)
+#define FUSB300_EPPRDR_EP2_PRD_RDY		(1 << 2)
+#define FUSB300_EPPRDR_EP1_PRD_RDY		(1 << 1)
+#define FUSB300_EPPRDR_EP_PRD_RDY(n)		(1 << n)
+
+/* AHB Bus Control Register (offset = 514H) */
+#define FUSB300_AHBBCR_S1_SPLIT_ON		(1 << 17)
+#define FUSB300_AHBBCR_S0_SPLIT_ON		(1 << 16)
+#define FUSB300_AHBBCR_S1_1entry		(0 << 12)
+#define FUSB300_AHBBCR_S1_4entry		(3 << 12)
+#define FUSB300_AHBBCR_S1_8entry		(5 << 12)
+#define FUSB300_AHBBCR_S1_16entry		(7 << 12)
+#define FUSB300_AHBBCR_S0_1entry		(0 << 8)
+#define FUSB300_AHBBCR_S0_4entry		(3 << 8)
+#define FUSB300_AHBBCR_S0_8entry		(5 << 8)
+#define FUSB300_AHBBCR_S0_16entry		(7 << 8)
+#define FUSB300_AHBBCR_M1_BURST_SINGLE		(0 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR		(1 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR4		(3 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR8		(5 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR16		(7 << 4)
+#define FUSB300_AHBBCR_M0_BURST_SINGLE		0
+#define FUSB300_AHBBCR_M0_BURST_INCR		1
+#define FUSB300_AHBBCR_M0_BURST_INCR4		3
+#define FUSB300_AHBBCR_M0_BURST_INCR8		5
+#define FUSB300_AHBBCR_M0_BURST_INCR16		7
+#define FUSB300_IGER5_EEP_STL_INT(n)		(1 << n)
+
+/* WORD 0 Data Structure of PRD Table */
+#define FUSB300_EPPRD0_M			(1 << 30)
+#define FUSB300_EPPRD0_O			(1 << 29)
+/* The finished prd */
+#define FUSB300_EPPRD0_F			(1 << 28)
+#define FUSB300_EPPRD0_I			(1 << 27)
+#define FUSB300_EPPRD0_A			(1 << 26)
+/* To decide HW point to first prd at next time */
+#define FUSB300_EPPRD0_L			(1 << 25)
+#define FUSB300_EPPRD0_H			(1 << 24)
+#define FUSB300_EPPRD0_BTC(n)			(n & 0xFFFFFF)
+
+/*----------------------------------------------------------------------*/
+#define FUSB300_MAX_NUM_EP		16
+
+#define FUSB300_FIFO_ENTRY_NUM		8
+#define FUSB300_MAX_FIFO_ENTRY		8
+
+#define SS_CTL_MAX_PACKET_SIZE		0x200
+#define SS_BULK_MAX_PACKET_SIZE		0x400
+#define SS_INT_MAX_PACKET_SIZE		0x400
+#define SS_ISO_MAX_PACKET_SIZE		0x400
+
+#define HS_BULK_MAX_PACKET_SIZE		0x200
+#define HS_CTL_MAX_PACKET_SIZE		0x40
+#define HS_INT_MAX_PACKET_SIZE		0x400
+#define HS_ISO_MAX_PACKET_SIZE		0x400
+
+struct fusb300_ep_info {
+	u8	epnum;
+	u8	type;
+	u8	interval;
+	u8	dir_in;
+	u16	maxpacket;
+	u16	addrofs;
+	u16	bw_num;
+};
+
+struct fusb300_request {
+
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+
+struct fusb300_ep {
+	struct usb_ep		ep;
+	struct fusb300		*fusb300;
+
+	struct list_head	queue;
+	unsigned		stall:1;
+	unsigned		wedged:1;
+	unsigned		use_dma:1;
+
+	unsigned char		epnum;
+	unsigned char		type;
+};
+
+struct fusb300 {
+	spinlock_t		lock;
+	void __iomem		*reg;
+
+	unsigned long		irq_trigger;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct fusb300_ep	*ep[FUSB300_MAX_NUM_EP];
+
+	struct usb_request	*ep0_req;	/* for internal request */
+	__le16			ep0_data;
+	u32			ep0_length;	/* for internal request */
+	u8			ep0_dir;	/* 0/0x80  out/in */
+
+	u8			fifo_entry_num;	/* next start fifo entry */
+	u32			addrofs;	/* next fifo address offset */
+	u8			reenum;		/* if re-enumeration */
+};
+
+#define to_fusb300(g)		(container_of((g), struct fusb300, gadget))
+
+#endif
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
new file mode 100644
index 0000000..c372122
--- /dev/null
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -0,0 +1,1856 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toshiba TC86C001 ("Goku-S") USB Device Controller driver
+ *
+ * Copyright (C) 2000-2002 Lineo
+ *      by Stuart Lynne, Tom Rushworth, and Bruce Balden
+ * Copyright (C) 2002 Toshiba Corporation
+ * Copyright (C) 2003 MontaVista Software (source@mvista.com)
+ */
+
+/*
+ * This device has ep0 and three semi-configurable bulk/interrupt endpoints.
+ *
+ *  - Endpoint numbering is fixed: ep{1,2,3}-bulk
+ *  - Gadget drivers can choose ep maxpacket (8/16/32/64)
+ *  - Gadget drivers can choose direction (IN, OUT)
+ *  - DMA works with ep1 (OUT transfers) and ep2 (IN transfers).
+ */
+
+// #define	VERBOSE		/* extra debug messages (success too) */
+// #define	USB_TRACE	/* packet-level success messages */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+
+#include "goku_udc.h"
+
+#define	DRIVER_DESC		"TC86C001 USB Device Controller"
+#define	DRIVER_VERSION		"30-Oct 2003"
+
+static const char driver_name [] = "goku_udc";
+static const char driver_desc [] = DRIVER_DESC;
+
+MODULE_AUTHOR("source@mvista.com");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+
+/*
+ * IN dma behaves ok under testing, though the IN-dma abort paths don't
+ * seem to behave quite as expected.  Used by default.
+ *
+ * OUT dma documents design problems handling the common "short packet"
+ * transfer termination policy; it couldn't be enabled by default, even
+ * if the OUT-dma abort problems had a resolution.
+ */
+static unsigned use_dma = 1;
+
+#if 0
+//#include <linux/moduleparam.h>
+/* "modprobe goku_udc use_dma=1" etc
+ *	0 to disable dma
+ *	1 to use IN dma only (normal operation)
+ *	2 to use IN and OUT dma
+ */
+module_param(use_dma, uint, S_IRUGO);
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void nuke(struct goku_ep *, int status);
+
+static inline void
+command(struct goku_udc_regs __iomem *regs, int command, unsigned epnum)
+{
+	writel(COMMAND_EP(epnum) | command, &regs->Command);
+	udelay(300);
+}
+
+static int
+goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct goku_udc	*dev;
+	struct goku_ep	*ep;
+	u32		mode;
+	u16		max;
+	unsigned long	flags;
+
+	ep = container_of(_ep, struct goku_ep, ep);
+	if (!_ep || !desc
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (ep == &dev->ep[0])
+		return -EINVAL;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+	if (ep->num != usb_endpoint_num(desc))
+		return -EINVAL;
+
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if ((readl(ep->reg_status) & EPxSTATUS_EP_MASK)
+			!= EPxSTATUS_EP_INVALID)
+		return -EBUSY;
+
+	/* enabling the no-toggle interrupt mode would need an api hook */
+	mode = 0;
+	max = get_unaligned_le16(&desc->wMaxPacketSize);
+	switch (max) {
+	case 64:
+		mode++; /* fall through */
+	case 32:
+		mode++; /* fall through */
+	case 16:
+		mode++; /* fall through */
+	case 8:
+		mode <<= 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mode |= 2 << 1;		/* bulk, or intr-with-toggle */
+
+	/* ep1/ep2 dma direction is chosen early; it works in the other
+	 * direction, with pio.  be cautious with out-dma.
+	 */
+	ep->is_in = usb_endpoint_dir_in(desc);
+	if (ep->is_in) {
+		mode |= 1;
+		ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
+	} else {
+		ep->dma = (use_dma == 2) && (ep->num == UDC_MSTWR_ENDPOINT);
+		if (ep->dma)
+			DBG(dev, "%s out-dma hides short packets\n",
+				ep->ep.name);
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	/* ep1 and ep2 can do double buffering and/or dma */
+	if (ep->num < 3) {
+		struct goku_udc_regs __iomem	*regs = ep->dev->regs;
+		u32				tmp;
+
+		/* double buffer except (for now) with pio in */
+		tmp = ((ep->dma || !ep->is_in)
+				? 0x10	/* double buffered */
+				: 0x11	/* single buffer */
+			) << ep->num;
+		tmp |= readl(&regs->EPxSingle);
+		writel(tmp, &regs->EPxSingle);
+
+		tmp = (ep->dma ? 0x10/*dma*/ : 0x11/*pio*/) << ep->num;
+		tmp |= readl(&regs->EPxBCS);
+		writel(tmp, &regs->EPxBCS);
+	}
+	writel(mode, ep->reg_mode);
+	command(ep->dev->regs, COMMAND_RESET, ep->num);
+	ep->ep.maxpacket = max;
+	ep->stopped = 0;
+	ep->ep.desc = desc;
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DBG(dev, "enable %s %s %s maxpacket %u\n", ep->ep.name,
+		ep->is_in ? "IN" : "OUT",
+		ep->dma ? "dma" : "pio",
+		max);
+
+	return 0;
+}
+
+static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep)
+{
+	struct goku_udc		*dev = ep->dev;
+
+	if (regs) {
+		command(regs, COMMAND_INVALID, ep->num);
+		if (ep->num) {
+			if (ep->num == UDC_MSTWR_ENDPOINT)
+				dev->int_enable &= ~(INT_MSTWREND
+							|INT_MSTWRTMOUT);
+			else if (ep->num == UDC_MSTRD_ENDPOINT)
+				dev->int_enable &= ~INT_MSTRDEND;
+			dev->int_enable &= ~INT_EPxDATASET (ep->num);
+		} else
+			dev->int_enable &= ~INT_EP0;
+		writel(dev->int_enable, &regs->int_enable);
+		readl(&regs->int_enable);
+		if (ep->num < 3) {
+			struct goku_udc_regs __iomem	*r = ep->dev->regs;
+			u32				tmp;
+
+			tmp = readl(&r->EPxSingle);
+			tmp &= ~(0x11 << ep->num);
+			writel(tmp, &r->EPxSingle);
+
+			tmp = readl(&r->EPxBCS);
+			tmp &= ~(0x11 << ep->num);
+			writel(tmp, &r->EPxBCS);
+		}
+		/* reset dma in case we're still using it */
+		if (ep->dma) {
+			u32	master;
+
+			master = readl(&regs->dma_master) & MST_RW_BITS;
+			if (ep->num == UDC_MSTWR_ENDPOINT) {
+				master &= ~MST_W_BITS;
+				master |= MST_WR_RESET;
+			} else {
+				master &= ~MST_R_BITS;
+				master |= MST_RD_RESET;
+			}
+			writel(master, &regs->dma_master);
+		}
+	}
+
+	usb_ep_set_maxpacket_limit(&ep->ep, MAX_FIFO_SIZE);
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+	ep->irqs = 0;
+	ep->dma = 0;
+}
+
+static int goku_ep_disable(struct usb_ep *_ep)
+{
+	struct goku_ep	*ep;
+	struct goku_udc	*dev;
+	unsigned long	flags;
+
+	ep = container_of(_ep, struct goku_ep, ep);
+	if (!_ep || !ep->ep.desc)
+		return -ENODEV;
+	dev = ep->dev;
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	VDBG(dev, "disable %s\n", _ep->name);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	nuke(ep, -ESHUTDOWN);
+	ep_reset(dev->regs, ep);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct goku_request	*req;
+
+	if (!_ep)
+		return NULL;
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+static void
+goku_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct goku_request	*req;
+
+	if (!_ep || !_req)
+		return;
+
+	req = container_of(_req, struct goku_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+done(struct goku_ep *ep, struct goku_request *req, int status)
+{
+	struct goku_udc		*dev;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+
+	if (ep->dma)
+		usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+
+#ifndef USB_TRACE
+	if (status && status != -ESHUTDOWN)
+#endif
+		VDBG(dev, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&dev->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&dev->lock);
+	ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+write_packet(u32 __iomem *fifo, u8 *buf, struct goku_request *req, unsigned max)
+{
+	unsigned	length, count;
+
+	length = min(req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+	count = length;
+	while (likely(count--))
+		writel(*buf++, fifo);
+	return length;
+}
+
+// return:  0 = still running, 1 = completed, negative = errno
+static int write_fifo(struct goku_ep *ep, struct goku_request *req)
+{
+	struct goku_udc	*dev = ep->dev;
+	u32		tmp;
+	u8		*buf;
+	unsigned	count;
+	int		is_last;
+
+	tmp = readl(&dev->regs->DataSet);
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	dev = ep->dev;
+	if (unlikely(ep->num == 0 && dev->ep0state != EP0_IN))
+		return -EL2HLT;
+
+	/* NOTE:  just single-buffered PIO-IN for now.  */
+	if (unlikely((tmp & DATASET_A(ep->num)) != 0))
+		return 0;
+
+	/* clear our "packet available" irq */
+	if (ep->num != 0)
+		writel(~INT_EPxDATASET(ep->num), &dev->regs->int_status);
+
+	count = write_packet(ep->reg_fifo, buf, req, ep->ep.maxpacket);
+
+	/* last packet often short (sometimes a zlp, especially on ep0) */
+	if (unlikely(count != ep->ep.maxpacket)) {
+		writel(~(1<<ep->num), &dev->regs->EOP);
+		if (ep->num == 0) {
+			dev->ep[0].stopped = 1;
+			dev->ep0state = EP0_STATUS;
+		}
+		is_last = 1;
+	} else {
+		if (likely(req->req.length != req->req.actual)
+				|| req->req.zero)
+			is_last = 0;
+		else
+			is_last = 1;
+	}
+#if 0		/* printk seemed to trash is_last...*/
+//#ifdef USB_TRACE
+	VDBG(dev, "wrote %s %u bytes%s IN %u left %p\n",
+		ep->ep.name, count, is_last ? "/last" : "",
+		req->req.length - req->req.actual, req);
+#endif
+
+	/* requests complete when all IN data is in the FIFO,
+	 * or sometimes later, if a zlp was needed.
+	 */
+	if (is_last) {
+		done(ep, req, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int read_fifo(struct goku_ep *ep, struct goku_request *req)
+{
+	struct goku_udc_regs __iomem	*regs;
+	u32				size, set;
+	u8				*buf;
+	unsigned			bufferspace, is_short, dbuff;
+
+	regs = ep->dev->regs;
+top:
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+
+	if (unlikely(ep->num == 0 && ep->dev->ep0state != EP0_OUT))
+		return -EL2HLT;
+
+	dbuff = (ep->num == 1 || ep->num == 2);
+	do {
+		/* ack dataset irq matching the status we'll handle */
+		if (ep->num != 0)
+			writel(~INT_EPxDATASET(ep->num), &regs->int_status);
+
+		set = readl(&regs->DataSet) & DATASET_AB(ep->num);
+		size = readl(&regs->EPxSizeLA[ep->num]);
+		bufferspace = req->req.length - req->req.actual;
+
+		/* usually do nothing without an OUT packet */
+		if (likely(ep->num != 0 || bufferspace != 0)) {
+			if (unlikely(set == 0))
+				break;
+			/* use ep1/ep2 double-buffering for OUT */
+			if (!(size & PACKET_ACTIVE))
+				size = readl(&regs->EPxSizeLB[ep->num]);
+			if (!(size & PACKET_ACTIVE))	/* "can't happen" */
+				break;
+			size &= DATASIZE;	/* EPxSizeH == 0 */
+
+		/* ep0out no-out-data case for set_config, etc */
+		} else
+			size = 0;
+
+		/* read all bytes from this packet */
+		req->req.actual += size;
+		is_short = (size < ep->ep.maxpacket);
+#ifdef USB_TRACE
+		VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n",
+			ep->ep.name, size, is_short ? "/S" : "",
+			req, req->req.actual, req->req.length);
+#endif
+		while (likely(size-- != 0)) {
+			u8	byte = (u8) readl(ep->reg_fifo);
+
+			if (unlikely(bufferspace == 0)) {
+				/* this happens when the driver's buffer
+				 * is smaller than what the host sent.
+				 * discard the extra data in this packet.
+				 */
+				if (req->req.status != -EOVERFLOW)
+					DBG(ep->dev, "%s overflow %u\n",
+						ep->ep.name, size);
+				req->req.status = -EOVERFLOW;
+			} else {
+				*buf++ = byte;
+				bufferspace--;
+			}
+		}
+
+		/* completion */
+		if (unlikely(is_short || req->req.actual == req->req.length)) {
+			if (unlikely(ep->num == 0)) {
+				/* non-control endpoints now usable? */
+				if (ep->dev->req_config)
+					writel(ep->dev->configured
+							? USBSTATE_CONFIGURED
+							: 0,
+						&regs->UsbState);
+				/* ep0out status stage */
+				writel(~(1<<0), &regs->EOP);
+				ep->stopped = 1;
+				ep->dev->ep0state = EP0_STATUS;
+			}
+			done(ep, req, 0);
+
+			/* empty the second buffer asap */
+			if (dbuff && !list_empty(&ep->queue)) {
+				req = list_entry(ep->queue.next,
+						struct goku_request, queue);
+				goto top;
+			}
+			return 1;
+		}
+	} while (dbuff);
+	return 0;
+}
+
+static inline void
+pio_irq_enable(struct goku_udc *dev,
+		struct goku_udc_regs __iomem *regs, int epnum)
+{
+	dev->int_enable |= INT_EPxDATASET (epnum);
+	writel(dev->int_enable, &regs->int_enable);
+	/* write may still be posted */
+}
+
+static inline void
+pio_irq_disable(struct goku_udc *dev,
+		struct goku_udc_regs __iomem *regs, int epnum)
+{
+	dev->int_enable &= ~INT_EPxDATASET (epnum);
+	writel(dev->int_enable, &regs->int_enable);
+	/* write may still be posted */
+}
+
+static inline void
+pio_advance(struct goku_ep *ep)
+{
+	struct goku_request	*req;
+
+	if (unlikely(list_empty (&ep->queue)))
+		return;
+	req = list_entry(ep->queue.next, struct goku_request, queue);
+	(ep->is_in ? write_fifo : read_fifo)(ep, req);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+// return:  0 = q running, 1 = q stopped, negative = errno
+static int start_dma(struct goku_ep *ep, struct goku_request *req)
+{
+	struct goku_udc_regs __iomem	*regs = ep->dev->regs;
+	u32				master;
+	u32				start = req->req.dma;
+	u32				end = start + req->req.length - 1;
+
+	master = readl(&regs->dma_master) & MST_RW_BITS;
+
+	/* re-init the bits affecting IN dma; careful with zlps */
+	if (likely(ep->is_in)) {
+		if (unlikely(master & MST_RD_ENA)) {
+			DBG (ep->dev, "start, IN active dma %03x!!\n",
+				master);
+//			return -EL2HLT;
+		}
+		writel(end, &regs->in_dma_end);
+		writel(start, &regs->in_dma_start);
+
+		master &= ~MST_R_BITS;
+		if (unlikely(req->req.length == 0))
+			master = MST_RD_ENA | MST_RD_EOPB;
+		else if ((req->req.length % ep->ep.maxpacket) != 0
+					|| req->req.zero)
+			master = MST_RD_ENA | MST_EOPB_ENA;
+		else
+			master = MST_RD_ENA | MST_EOPB_DIS;
+
+		ep->dev->int_enable |= INT_MSTRDEND;
+
+	/* Goku DMA-OUT merges short packets, which plays poorly with
+	 * protocols where short packets mark the transfer boundaries.
+	 * The chip supports a nonstandard policy with INT_MSTWRTMOUT,
+	 * ending transfers after 3 SOFs; we don't turn it on.
+	 */
+	} else {
+		if (unlikely(master & MST_WR_ENA)) {
+			DBG (ep->dev, "start, OUT active dma %03x!!\n",
+				master);
+//			return -EL2HLT;
+		}
+		writel(end, &regs->out_dma_end);
+		writel(start, &regs->out_dma_start);
+
+		master &= ~MST_W_BITS;
+		master |= MST_WR_ENA | MST_TIMEOUT_DIS;
+
+		ep->dev->int_enable |= INT_MSTWREND|INT_MSTWRTMOUT;
+	}
+
+	writel(master, &regs->dma_master);
+	writel(ep->dev->int_enable, &regs->int_enable);
+	return 0;
+}
+
+static void dma_advance(struct goku_udc *dev, struct goku_ep *ep)
+{
+	struct goku_request		*req;
+	struct goku_udc_regs __iomem	*regs = ep->dev->regs;
+	u32				master;
+
+	master = readl(&regs->dma_master);
+
+	if (unlikely(list_empty(&ep->queue))) {
+stop:
+		if (ep->is_in)
+			dev->int_enable &= ~INT_MSTRDEND;
+		else
+			dev->int_enable &= ~(INT_MSTWREND|INT_MSTWRTMOUT);
+		writel(dev->int_enable, &regs->int_enable);
+		return;
+	}
+	req = list_entry(ep->queue.next, struct goku_request, queue);
+
+	/* normal hw dma completion (not abort) */
+	if (likely(ep->is_in)) {
+		if (unlikely(master & MST_RD_ENA))
+			return;
+		req->req.actual = readl(&regs->in_dma_current);
+	} else {
+		if (unlikely(master & MST_WR_ENA))
+			return;
+
+		/* hardware merges short packets, and also hides packet
+		 * overruns.  a partial packet MAY be in the fifo here.
+		 */
+		req->req.actual = readl(&regs->out_dma_current);
+	}
+	req->req.actual -= req->req.dma;
+	req->req.actual++;
+
+#ifdef USB_TRACE
+	VDBG(dev, "done %s %s dma, %u/%u bytes, req %p\n",
+		ep->ep.name, ep->is_in ? "IN" : "OUT",
+		req->req.actual, req->req.length, req);
+#endif
+	done(ep, req, 0);
+	if (list_empty(&ep->queue))
+		goto stop;
+	req = list_entry(ep->queue.next, struct goku_request, queue);
+	(void) start_dma(ep, req);
+}
+
+static void abort_dma(struct goku_ep *ep, int status)
+{
+	struct goku_udc_regs __iomem	*regs = ep->dev->regs;
+	struct goku_request		*req;
+	u32				curr, master;
+
+	/* NAK future host requests, hoping the implicit delay lets the
+	 * dma engine finish reading (or writing) its latest packet and
+	 * empty the dma buffer (up to 16 bytes).
+	 *
+	 * This avoids needing to clean up a partial packet in the fifo;
+	 * we can't do that for IN without side effects to HALT and TOGGLE.
+	 */
+	command(regs, COMMAND_FIFO_DISABLE, ep->num);
+	req = list_entry(ep->queue.next, struct goku_request, queue);
+	master = readl(&regs->dma_master) & MST_RW_BITS;
+
+	/* FIXME using these resets isn't usably documented. this may
+	 * not work unless it's followed by disabling the endpoint.
+	 *
+	 * FIXME the OUT reset path doesn't even behave consistently.
+	 */
+	if (ep->is_in) {
+		if (unlikely((readl(&regs->dma_master) & MST_RD_ENA) == 0))
+			goto finished;
+		curr = readl(&regs->in_dma_current);
+
+		writel(curr, &regs->in_dma_end);
+		writel(curr, &regs->in_dma_start);
+
+		master &= ~MST_R_BITS;
+		master |= MST_RD_RESET;
+		writel(master, &regs->dma_master);
+
+		if (readl(&regs->dma_master) & MST_RD_ENA)
+			DBG(ep->dev, "IN dma active after reset!\n");
+
+	} else {
+		if (unlikely((readl(&regs->dma_master) & MST_WR_ENA) == 0))
+			goto finished;
+		curr = readl(&regs->out_dma_current);
+
+		writel(curr, &regs->out_dma_end);
+		writel(curr, &regs->out_dma_start);
+
+		master &= ~MST_W_BITS;
+		master |= MST_WR_RESET;
+		writel(master, &regs->dma_master);
+
+		if (readl(&regs->dma_master) & MST_WR_ENA)
+			DBG(ep->dev, "OUT dma active after reset!\n");
+	}
+	req->req.actual = (curr - req->req.dma) + 1;
+	req->req.status = status;
+
+	VDBG(ep->dev, "%s %s %s %d/%d\n", __func__, ep->ep.name,
+		ep->is_in ? "IN" : "OUT",
+		req->req.actual, req->req.length);
+
+	command(regs, COMMAND_FIFO_ENABLE, ep->num);
+
+	return;
+
+finished:
+	/* dma already completed; no abort needed */
+	command(regs, COMMAND_FIFO_ENABLE, ep->num);
+	req->req.actual = req->req.length;
+	req->req.status = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct goku_request	*req;
+	struct goku_ep		*ep;
+	struct goku_udc		*dev;
+	unsigned long		flags;
+	int			status;
+
+	/* always require a cpu-view buffer so pio works */
+	req = container_of(_req, struct goku_request, req);
+	if (unlikely(!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue)))
+		return -EINVAL;
+	ep = container_of(_ep, struct goku_ep, ep);
+	if (unlikely(!_ep || (!ep->ep.desc && ep->num != 0)))
+		return -EINVAL;
+	dev = ep->dev;
+	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	/* can't touch registers when suspended */
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	/* set up dma mapping in case the caller didn't */
+	if (ep->dma) {
+		status = usb_gadget_map_request(&dev->gadget, &req->req,
+				ep->is_in);
+		if (status)
+			return status;
+	}
+
+#ifdef USB_TRACE
+	VDBG(dev, "%s queue req %p, len %u buf %p\n",
+			_ep->name, _req, _req->length, _req->buf);
+#endif
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* for ep0 IN without premature status, zlp is required and
+	 * writing EOP starts the status stage (OUT).
+	 */
+	if (unlikely(ep->num == 0 && ep->is_in))
+		_req->zero = 1;
+
+	/* kickstart this i/o queue? */
+	status = 0;
+	if (list_empty(&ep->queue) && likely(!ep->stopped)) {
+		/* dma:  done after dma completion IRQ (or error)
+		 * pio:  done after last fifo operation
+		 */
+		if (ep->dma)
+			status = start_dma(ep, req);
+		else
+			status = (ep->is_in ? write_fifo : read_fifo)(ep, req);
+
+		if (unlikely(status != 0)) {
+			if (status > 0)
+				status = 0;
+			req = NULL;
+		}
+
+	} /* else pio or dma irq handler advances the queue. */
+
+	if (likely(req != NULL))
+		list_add_tail(&req->queue, &ep->queue);
+
+	if (likely(!list_empty(&ep->queue))
+			&& likely(ep->num != 0)
+			&& !ep->dma
+			&& !(dev->int_enable & INT_EPxDATASET (ep->num)))
+		pio_irq_enable(dev, dev->regs, ep->num);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* pci writes may still be posted */
+	return status;
+}
+
+/* dequeue ALL requests */
+static void nuke(struct goku_ep *ep, int status)
+{
+	struct goku_request	*req;
+
+	ep->stopped = 1;
+	if (list_empty(&ep->queue))
+		return;
+	if (ep->dma)
+		abort_dma(ep, status);
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct goku_request, queue);
+		done(ep, req, status);
+	}
+}
+
+/* dequeue JUST ONE request */
+static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct goku_request	*req;
+	struct goku_ep		*ep;
+	struct goku_udc		*dev;
+	unsigned long		flags;
+
+	ep = container_of(_ep, struct goku_ep, ep);
+	if (!_ep || !_req || (!ep->ep.desc && ep->num != 0))
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver)
+		return -ESHUTDOWN;
+
+	/* we can't touch (dma) registers when suspended */
+	if (dev->ep0state == EP0_SUSPEND)
+		return -EBUSY;
+
+	VDBG(dev, "%s %s %s %s %p\n", __func__, _ep->name,
+		ep->is_in ? "IN" : "OUT",
+		ep->dma ? "dma" : "pio",
+		_req);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore (&dev->lock, flags);
+		return -EINVAL;
+	}
+
+	if (ep->dma && ep->queue.next == &req->queue && !ep->stopped) {
+		abort_dma(ep, -ECONNRESET);
+		done(ep, req, -ECONNRESET);
+		dma_advance(dev, ep);
+	} else if (!list_empty(&req->queue))
+		done(ep, req, -ECONNRESET);
+	else
+		req = NULL;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return req ? 0 : -EOPNOTSUPP;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void goku_clear_halt(struct goku_ep *ep)
+{
+	// assert (ep->num !=0)
+	VDBG(ep->dev, "%s clear halt\n", ep->ep.name);
+	command(ep->dev->regs, COMMAND_SETDATA0, ep->num);
+	command(ep->dev->regs, COMMAND_STALL_CLEAR, ep->num);
+	if (ep->stopped) {
+		ep->stopped = 0;
+		if (ep->dma) {
+			struct goku_request	*req;
+
+			if (list_empty(&ep->queue))
+				return;
+			req = list_entry(ep->queue.next, struct goku_request,
+						queue);
+			(void) start_dma(ep, req);
+		} else
+			pio_advance(ep);
+	}
+}
+
+static int goku_set_halt(struct usb_ep *_ep, int value)
+{
+	struct goku_ep	*ep;
+	unsigned long	flags;
+	int		retval = 0;
+
+	if (!_ep)
+		return -ENODEV;
+	ep = container_of (_ep, struct goku_ep, ep);
+
+	if (ep->num == 0) {
+		if (value) {
+			ep->dev->ep0state = EP0_STALL;
+			ep->dev->ep[0].stopped = 1;
+		} else
+			return -EINVAL;
+
+	/* don't change EPxSTATUS_EP_INVALID to READY */
+	} else if (!ep->ep.desc) {
+		DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	if (!list_empty(&ep->queue))
+		retval = -EAGAIN;
+	else if (ep->is_in && value
+			/* data in (either) packet buffer? */
+			&& (readl(&ep->dev->regs->DataSet)
+					& DATASET_AB(ep->num)))
+		retval = -EAGAIN;
+	else if (!value)
+		goku_clear_halt(ep);
+	else {
+		ep->stopped = 1;
+		VDBG(ep->dev, "%s set halt\n", ep->ep.name);
+		command(ep->dev->regs, COMMAND_STALL, ep->num);
+		readl(ep->reg_status);
+	}
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return retval;
+}
+
+static int goku_fifo_status(struct usb_ep *_ep)
+{
+	struct goku_ep			*ep;
+	struct goku_udc_regs __iomem	*regs;
+	u32				size;
+
+	if (!_ep)
+		return -ENODEV;
+	ep = container_of(_ep, struct goku_ep, ep);
+
+	/* size is only reported sanely for OUT */
+	if (ep->is_in)
+		return -EOPNOTSUPP;
+
+	/* ignores 16-byte dma buffer; SizeH == 0 */
+	regs = ep->dev->regs;
+	size = readl(&regs->EPxSizeLA[ep->num]) & DATASIZE;
+	size += readl(&regs->EPxSizeLB[ep->num]) & DATASIZE;
+	VDBG(ep->dev, "%s %s %u\n", __func__, ep->ep.name, size);
+	return size;
+}
+
+static void goku_fifo_flush(struct usb_ep *_ep)
+{
+	struct goku_ep			*ep;
+	struct goku_udc_regs __iomem	*regs;
+	u32				size;
+
+	if (!_ep)
+		return;
+	ep = container_of(_ep, struct goku_ep, ep);
+	VDBG(ep->dev, "%s %s\n", __func__, ep->ep.name);
+
+	/* don't change EPxSTATUS_EP_INVALID to READY */
+	if (!ep->ep.desc && ep->num != 0) {
+		DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name);
+		return;
+	}
+
+	regs = ep->dev->regs;
+	size = readl(&regs->EPxSizeLA[ep->num]);
+	size &= DATASIZE;
+
+	/* Non-desirable behavior:  FIFO_CLEAR also clears the
+	 * endpoint halt feature.  For OUT, we _could_ just read
+	 * the bytes out (PIO, if !ep->dma); for in, no choice.
+	 */
+	if (size)
+		command(regs, COMMAND_FIFO_CLEAR, ep->num);
+}
+
+static const struct usb_ep_ops goku_ep_ops = {
+	.enable		= goku_ep_enable,
+	.disable	= goku_ep_disable,
+
+	.alloc_request	= goku_alloc_request,
+	.free_request	= goku_free_request,
+
+	.queue		= goku_queue,
+	.dequeue	= goku_dequeue,
+
+	.set_halt	= goku_set_halt,
+	.fifo_status	= goku_fifo_status,
+	.fifo_flush	= goku_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int goku_get_frame(struct usb_gadget *_gadget)
+{
+	return -EOPNOTSUPP;
+}
+
+static struct usb_ep *goku_match_ep(struct usb_gadget *g,
+		struct usb_endpoint_descriptor *desc,
+		struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+	struct goku_udc	*dev = to_goku_udc(g);
+	struct usb_ep *ep;
+
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_INT:
+		/* single buffering is enough */
+		ep = &dev->ep[3].ep;
+		if (usb_gadget_ep_match_desc(g, ep, desc, ep_comp))
+			return ep;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		if (usb_endpoint_dir_in(desc)) {
+			/* DMA may be available */
+			ep = &dev->ep[2].ep;
+			if (usb_gadget_ep_match_desc(g, ep, desc, ep_comp))
+				return ep;
+		}
+		break;
+	default:
+		/* nothing */ ;
+	}
+
+	return NULL;
+}
+
+static int goku_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int goku_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops goku_ops = {
+	.get_frame	= goku_get_frame,
+	.udc_start	= goku_udc_start,
+	.udc_stop	= goku_udc_stop,
+	.match_ep	= goku_match_ep,
+	// no remote wakeup
+	// not selfpowered
+};
+
+/*-------------------------------------------------------------------------*/
+
+static inline const char *dmastr(void)
+{
+	if (use_dma == 0)
+		return "(dma disabled)";
+	else if (use_dma == 2)
+		return "(dma IN and OUT)";
+	else
+		return "(dma IN)";
+}
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static const char proc_node_name [] = "driver/udc";
+
+#define FOURBITS "%s%s%s%s"
+#define EIGHTBITS FOURBITS FOURBITS
+
+static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
+{
+	/* int_status is the same format ... */
+	seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
+		   label, mask,
+		   (mask & INT_PWRDETECT) ? " power" : "",
+		   (mask & INT_SYSERROR) ? " sys" : "",
+		   (mask & INT_MSTRDEND) ? " in-dma" : "",
+		   (mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
+
+		   (mask & INT_MSTWREND) ? " out-dma" : "",
+		   (mask & INT_MSTWRSET) ? " wrset" : "",
+		   (mask & INT_ERR) ? " err" : "",
+		   (mask & INT_SOF) ? " sof" : "",
+
+		   (mask & INT_EP3NAK) ? " ep3nak" : "",
+		   (mask & INT_EP2NAK) ? " ep2nak" : "",
+		   (mask & INT_EP1NAK) ? " ep1nak" : "",
+		   (mask & INT_EP3DATASET) ? " ep3" : "",
+
+		   (mask & INT_EP2DATASET) ? " ep2" : "",
+		   (mask & INT_EP1DATASET) ? " ep1" : "",
+		   (mask & INT_STATUSNAK) ? " ep0snak" : "",
+		   (mask & INT_STATUS) ? " ep0status" : "",
+
+		   (mask & INT_SETUP) ? " setup" : "",
+		   (mask & INT_ENDPOINT0) ? " ep0" : "",
+		   (mask & INT_USBRESET) ? " reset" : "",
+		   (mask & INT_SUSPEND) ? " suspend" : "");
+}
+
+static const char *udc_ep_state(enum ep0state state)
+{
+	switch (state) {
+	case EP0_DISCONNECT:
+		return "ep0_disconnect";
+	case EP0_IDLE:
+		return "ep0_idle";
+	case EP0_IN:
+		return "ep0_in";
+	case EP0_OUT:
+		return "ep0_out";
+	case EP0_STATUS:
+		return "ep0_status";
+	case EP0_STALL:
+		return "ep0_stall";
+	case EP0_SUSPEND:
+		return "ep0_suspend";
+	}
+
+	return "ep0_?";
+}
+
+static const char *udc_ep_status(u32 status)
+{
+	switch (status & EPxSTATUS_EP_MASK) {
+	case EPxSTATUS_EP_READY:
+		return "ready";
+	case EPxSTATUS_EP_DATAIN:
+		return "packet";
+	case EPxSTATUS_EP_FULL:
+		return "full";
+	case EPxSTATUS_EP_TX_ERR:	/* host will retry */
+		return "tx_err";
+	case EPxSTATUS_EP_RX_ERR:
+		return "rx_err";
+	case EPxSTATUS_EP_BUSY:		/* ep0 only */
+		return "busy";
+	case EPxSTATUS_EP_STALL:
+		return "stall";
+	case EPxSTATUS_EP_INVALID:	/* these "can't happen" */
+		return "invalid";
+	}
+
+	return "?";
+}
+
+static int udc_proc_read(struct seq_file *m, void *v)
+{
+	struct goku_udc			*dev = m->private;
+	struct goku_udc_regs __iomem	*regs = dev->regs;
+	unsigned long			flags;
+	int				i, is_usb_connected;
+	u32				tmp;
+
+	local_irq_save(flags);
+
+	/* basic device status */
+	tmp = readl(&regs->power_detect);
+	is_usb_connected = tmp & PW_DETECT;
+	seq_printf(m,
+		   "%s - %s\n"
+		   "%s version: %s %s\n"
+		   "Gadget driver: %s\n"
+		   "Host %s, %s\n"
+		   "\n",
+		   pci_name(dev->pdev), driver_desc,
+		   driver_name, DRIVER_VERSION, dmastr(),
+		   dev->driver ? dev->driver->driver.name : "(none)",
+		   is_usb_connected
+			   ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
+			   : "disconnected",
+		   udc_ep_state(dev->ep0state));
+
+	dump_intmask(m, "int_status", readl(&regs->int_status));
+	dump_intmask(m, "int_enable", readl(&regs->int_enable));
+
+	if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0)
+		goto done;
+
+	/* registers for (active) device and ep0 */
+	seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
+		   dev->irqs, readl(&regs->DataSet),
+		   readl(&regs->EPxSingle), readl(&regs->EPxBCS),
+		   readl(&regs->UsbState),
+		   readl(&regs->address));
+	if (seq_has_overflowed(m))
+		goto done;
+
+	tmp = readl(&regs->dma_master);
+	seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
+		   tmp,
+		   (tmp & MST_EOPB_DIS) ? " eopb-" : "",
+		   (tmp & MST_EOPB_ENA) ? " eopb+" : "",
+		   (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
+		   (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
+
+		   (tmp & MST_RD_EOPB) ? " eopb" : "",
+		   (tmp & MST_RD_RESET) ? " in_reset" : "",
+		   (tmp & MST_WR_RESET) ? " out_reset" : "",
+		   (tmp & MST_RD_ENA) ? " IN" : "",
+
+		   (tmp & MST_WR_ENA) ? " OUT" : "",
+		   (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
+	if (seq_has_overflowed(m))
+		goto done;
+
+	/* dump endpoint queues */
+	for (i = 0; i < 4; i++) {
+		struct goku_ep		*ep = &dev->ep [i];
+		struct goku_request	*req;
+
+		if (i && !ep->ep.desc)
+			continue;
+
+		tmp = readl(ep->reg_status);
+		seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
+			   ep->ep.name,
+			   ep->is_in ? "in" : "out",
+			   ep->ep.maxpacket,
+			   ep->dma ? "dma" : "pio",
+			   ep->irqs,
+			   tmp, udc_ep_status(tmp),
+			   (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
+			   (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
+			   (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
+			   (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
+		if (seq_has_overflowed(m))
+			goto done;
+
+		if (list_empty(&ep->queue)) {
+			seq_puts(m, "\t(nothing queued)\n");
+			if (seq_has_overflowed(m))
+				goto done;
+			continue;
+		}
+		list_for_each_entry(req, &ep->queue, queue) {
+			if (ep->dma && req->queue.prev == &ep->queue) {
+				if (i == UDC_MSTRD_ENDPOINT)
+					tmp = readl(&regs->in_dma_current);
+				else
+					tmp = readl(&regs->out_dma_current);
+				tmp -= req->req.dma;
+				tmp++;
+			} else
+				tmp = req->req.actual;
+
+			seq_printf(m, "\treq %p len %u/%u buf %p\n",
+				   &req->req, tmp, req->req.length,
+				   req->req.buf);
+			if (seq_has_overflowed(m))
+				goto done;
+		}
+	}
+
+done:
+	local_irq_restore(flags);
+	return 0;
+}
+#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+static void udc_reinit (struct goku_udc *dev)
+{
+	static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" };
+
+	unsigned i;
+
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	dev->gadget.ep0 = &dev->ep [0].ep;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->ep0state = EP0_DISCONNECT;
+	dev->irqs = 0;
+
+	for (i = 0; i < 4; i++) {
+		struct goku_ep	*ep = &dev->ep[i];
+
+		ep->num = i;
+		ep->ep.name = names[i];
+		ep->reg_fifo = &dev->regs->ep_fifo [i];
+		ep->reg_status = &dev->regs->ep_status [i];
+		ep->reg_mode = &dev->regs->ep_mode[i];
+
+		ep->ep.ops = &goku_ep_ops;
+		list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+		ep->dev = dev;
+		INIT_LIST_HEAD (&ep->queue);
+
+		ep_reset(NULL, ep);
+
+		if (i == 0)
+			ep->ep.caps.type_control = true;
+		else
+			ep->ep.caps.type_bulk = true;
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+
+	dev->ep[0].reg_mode = NULL;
+	usb_ep_set_maxpacket_limit(&dev->ep[0].ep, MAX_EP0_SIZE);
+	list_del_init (&dev->ep[0].ep.ep_list);
+}
+
+static void udc_reset(struct goku_udc *dev)
+{
+	struct goku_udc_regs __iomem	*regs = dev->regs;
+
+	writel(0, &regs->power_detect);
+	writel(0, &regs->int_enable);
+	readl(&regs->int_enable);
+	dev->int_enable = 0;
+
+	/* deassert reset, leave USB D+ at hi-Z (no pullup)
+	 * don't let INT_PWRDETECT sequence begin
+	 */
+	udelay(250);
+	writel(PW_RESETB, &regs->power_detect);
+	readl(&regs->int_enable);
+}
+
+static void ep0_start(struct goku_udc *dev)
+{
+	struct goku_udc_regs __iomem	*regs = dev->regs;
+	unsigned			i;
+
+	VDBG(dev, "%s\n", __func__);
+
+	udc_reset(dev);
+	udc_reinit (dev);
+	//writel(MST_EOPB_ENA | MST_TIMEOUT_ENA, &regs->dma_master);
+
+	/* hw handles set_address, set_feature, get_status; maybe more */
+	writel(   G_REQMODE_SET_INTF | G_REQMODE_GET_INTF
+		| G_REQMODE_SET_CONF | G_REQMODE_GET_CONF
+		| G_REQMODE_GET_DESC
+		| G_REQMODE_CLEAR_FEAT
+		, &regs->reqmode);
+
+	for (i = 0; i < 4; i++)
+		dev->ep[i].irqs = 0;
+
+	/* can't modify descriptors after writing UsbReady */
+	for (i = 0; i < DESC_LEN; i++)
+		writel(0, &regs->descriptors[i]);
+	writel(0, &regs->UsbReady);
+
+	/* expect ep0 requests when the host drops reset */
+	writel(PW_RESETB | PW_PULLUP, &regs->power_detect);
+	dev->int_enable = INT_DEVWIDE | INT_EP0;
+	writel(dev->int_enable, &dev->regs->int_enable);
+	readl(&regs->int_enable);
+	dev->gadget.speed = USB_SPEED_FULL;
+	dev->ep0state = EP0_IDLE;
+}
+
+static void udc_enable(struct goku_udc *dev)
+{
+	/* start enumeration now, or after power detect irq */
+	if (readl(&dev->regs->power_detect) & PW_DETECT)
+		ep0_start(dev);
+	else {
+		DBG(dev, "%s\n", __func__);
+		dev->int_enable = INT_PWRDETECT;
+		writel(dev->int_enable, &dev->regs->int_enable);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ */
+
+/* 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 goku_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct goku_udc	*dev = to_goku_udc(g);
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+
+	/*
+	 * then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	udc_enable(dev);
+
+	return 0;
+}
+
+static void stop_activity(struct goku_udc *dev)
+{
+	unsigned	i;
+
+	DBG (dev, "%s\n", __func__);
+
+	/* disconnect gadget driver after quiesceing hw and the driver */
+	udc_reset (dev);
+	for (i = 0; i < 4; i++)
+		nuke(&dev->ep [i], -ESHUTDOWN);
+
+	if (dev->driver)
+		udc_enable(dev);
+}
+
+static int goku_udc_stop(struct usb_gadget *g)
+{
+	struct goku_udc	*dev = to_goku_udc(g);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->driver = NULL;
+	stop_activity(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ep0_setup(struct goku_udc *dev)
+{
+	struct goku_udc_regs __iomem	*regs = dev->regs;
+	struct usb_ctrlrequest		ctrl;
+	int				tmp;
+
+	/* read SETUP packet and enter DATA stage */
+	ctrl.bRequestType = readl(&regs->bRequestType);
+	ctrl.bRequest = readl(&regs->bRequest);
+	ctrl.wValue  = cpu_to_le16((readl(&regs->wValueH)  << 8)
+					| readl(&regs->wValueL));
+	ctrl.wIndex  = cpu_to_le16((readl(&regs->wIndexH)  << 8)
+					| readl(&regs->wIndexL));
+	ctrl.wLength = cpu_to_le16((readl(&regs->wLengthH) << 8)
+					| readl(&regs->wLengthL));
+	writel(0, &regs->SetupRecv);
+
+	nuke(&dev->ep[0], 0);
+	dev->ep[0].stopped = 0;
+	if (likely(ctrl.bRequestType & USB_DIR_IN)) {
+		dev->ep[0].is_in = 1;
+		dev->ep0state = EP0_IN;
+		/* detect early status stages */
+		writel(ICONTROL_STATUSNAK, &dev->regs->IntControl);
+	} else {
+		dev->ep[0].is_in = 0;
+		dev->ep0state = EP0_OUT;
+
+		/* NOTE:  CLEAR_FEATURE is done in software so that we can
+		 * synchronize transfer restarts after bulk IN stalls.  data
+		 * won't even enter the fifo until the halt is cleared.
+		 */
+		switch (ctrl.bRequest) {
+		case USB_REQ_CLEAR_FEATURE:
+			switch (ctrl.bRequestType) {
+			case USB_RECIP_ENDPOINT:
+				tmp = le16_to_cpu(ctrl.wIndex) & 0x0f;
+				/* active endpoint */
+				if (tmp > 3 ||
+				    (!dev->ep[tmp].ep.desc && tmp != 0))
+					goto stall;
+				if (ctrl.wIndex & cpu_to_le16(
+						USB_DIR_IN)) {
+					if (!dev->ep[tmp].is_in)
+						goto stall;
+				} else {
+					if (dev->ep[tmp].is_in)
+						goto stall;
+				}
+				if (ctrl.wValue != cpu_to_le16(
+						USB_ENDPOINT_HALT))
+					goto stall;
+				if (tmp)
+					goku_clear_halt(&dev->ep[tmp]);
+succeed:
+				/* start ep0out status stage */
+				writel(~(1<<0), &regs->EOP);
+				dev->ep[0].stopped = 1;
+				dev->ep0state = EP0_STATUS;
+				return;
+			case USB_RECIP_DEVICE:
+				/* device remote wakeup: always clear */
+				if (ctrl.wValue != cpu_to_le16(1))
+					goto stall;
+				VDBG(dev, "clear dev remote wakeup\n");
+				goto succeed;
+			case USB_RECIP_INTERFACE:
+				goto stall;
+			default:		/* pass to gadget driver */
+				break;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+#ifdef USB_TRACE
+	VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+		ctrl.bRequestType, ctrl.bRequest,
+		le16_to_cpu(ctrl.wValue), le16_to_cpu(ctrl.wIndex),
+		le16_to_cpu(ctrl.wLength));
+#endif
+
+	/* hw wants to know when we're configured (or not) */
+	dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION
+				&& ctrl.bRequestType == USB_RECIP_DEVICE);
+	if (unlikely(dev->req_config))
+		dev->configured = (ctrl.wValue != cpu_to_le16(0));
+
+	/* delegate everything to the gadget driver.
+	 * it may respond after this irq handler returns.
+	 */
+	spin_unlock (&dev->lock);
+	tmp = dev->driver->setup(&dev->gadget, &ctrl);
+	spin_lock (&dev->lock);
+	if (unlikely(tmp < 0)) {
+stall:
+#ifdef USB_TRACE
+		VDBG(dev, "req %02x.%02x protocol STALL; err %d\n",
+				ctrl.bRequestType, ctrl.bRequest, tmp);
+#endif
+		command(regs, COMMAND_STALL, 0);
+		dev->ep[0].stopped = 1;
+		dev->ep0state = EP0_STALL;
+	}
+
+	/* expect at least one data or status stage irq */
+}
+
+#define ACK(irqbit) { \
+		stat &= ~irqbit; \
+		writel(~irqbit, &regs->int_status); \
+		handled = 1; \
+		}
+
+static irqreturn_t goku_irq(int irq, void *_dev)
+{
+	struct goku_udc			*dev = _dev;
+	struct goku_udc_regs __iomem	*regs = dev->regs;
+	struct goku_ep			*ep;
+	u32				stat, handled = 0;
+	unsigned			i, rescans = 5;
+
+	spin_lock(&dev->lock);
+
+rescan:
+	stat = readl(&regs->int_status) & dev->int_enable;
+        if (!stat)
+		goto done;
+	dev->irqs++;
+
+	/* device-wide irqs */
+	if (unlikely(stat & INT_DEVWIDE)) {
+		if (stat & INT_SYSERROR) {
+			ERROR(dev, "system error\n");
+			stop_activity(dev);
+			stat = 0;
+			handled = 1;
+			// FIXME have a neater way to prevent re-enumeration
+			dev->driver = NULL;
+			goto done;
+		}
+		if (stat & INT_PWRDETECT) {
+			writel(~stat, &regs->int_status);
+			if (readl(&dev->regs->power_detect) & PW_DETECT) {
+				VDBG(dev, "connect\n");
+				ep0_start(dev);
+			} else {
+				DBG(dev, "disconnect\n");
+				if (dev->gadget.speed == USB_SPEED_FULL)
+					stop_activity(dev);
+				dev->ep0state = EP0_DISCONNECT;
+				dev->int_enable = INT_DEVWIDE;
+				writel(dev->int_enable, &dev->regs->int_enable);
+			}
+			stat = 0;
+			handled = 1;
+			goto done;
+		}
+		if (stat & INT_SUSPEND) {
+			ACK(INT_SUSPEND);
+			if (readl(&regs->ep_status[0]) & EPxSTATUS_SUSPEND) {
+				switch (dev->ep0state) {
+				case EP0_DISCONNECT:
+				case EP0_SUSPEND:
+					goto pm_next;
+				default:
+					break;
+				}
+				DBG(dev, "USB suspend\n");
+				dev->ep0state = EP0_SUSPEND;
+				if (dev->gadget.speed != USB_SPEED_UNKNOWN
+						&& dev->driver
+						&& dev->driver->suspend) {
+					spin_unlock(&dev->lock);
+					dev->driver->suspend(&dev->gadget);
+					spin_lock(&dev->lock);
+				}
+			} else {
+				if (dev->ep0state != EP0_SUSPEND) {
+					DBG(dev, "bogus USB resume %d\n",
+						dev->ep0state);
+					goto pm_next;
+				}
+				DBG(dev, "USB resume\n");
+				dev->ep0state = EP0_IDLE;
+				if (dev->gadget.speed != USB_SPEED_UNKNOWN
+						&& dev->driver
+						&& dev->driver->resume) {
+					spin_unlock(&dev->lock);
+					dev->driver->resume(&dev->gadget);
+					spin_lock(&dev->lock);
+				}
+			}
+		}
+pm_next:
+		if (stat & INT_USBRESET) {		/* hub reset done */
+			ACK(INT_USBRESET);
+			INFO(dev, "USB reset done, gadget %s\n",
+				dev->driver->driver.name);
+		}
+		// and INT_ERR on some endpoint's crc/bitstuff/... problem
+	}
+
+	/* progress ep0 setup, data, or status stages.
+	 * no transition {EP0_STATUS, EP0_STALL} --> EP0_IDLE; saves irqs
+	 */
+	if (stat & INT_SETUP) {
+		ACK(INT_SETUP);
+		dev->ep[0].irqs++;
+		ep0_setup(dev);
+	}
+        if (stat & INT_STATUSNAK) {
+		ACK(INT_STATUSNAK|INT_ENDPOINT0);
+		if (dev->ep0state == EP0_IN) {
+			ep = &dev->ep[0];
+			ep->irqs++;
+			nuke(ep, 0);
+			writel(~(1<<0), &regs->EOP);
+			dev->ep0state = EP0_STATUS;
+		}
+	}
+        if (stat & INT_ENDPOINT0) {
+		ACK(INT_ENDPOINT0);
+		ep = &dev->ep[0];
+		ep->irqs++;
+		pio_advance(ep);
+        }
+
+	/* dma completion */
+        if (stat & INT_MSTRDEND) {	/* IN */
+		ACK(INT_MSTRDEND);
+		ep = &dev->ep[UDC_MSTRD_ENDPOINT];
+		ep->irqs++;
+		dma_advance(dev, ep);
+        }
+        if (stat & INT_MSTWREND) {	/* OUT */
+		ACK(INT_MSTWREND);
+		ep = &dev->ep[UDC_MSTWR_ENDPOINT];
+		ep->irqs++;
+		dma_advance(dev, ep);
+        }
+        if (stat & INT_MSTWRTMOUT) {	/* OUT */
+		ACK(INT_MSTWRTMOUT);
+		ep = &dev->ep[UDC_MSTWR_ENDPOINT];
+		ep->irqs++;
+		ERROR(dev, "%s write timeout ?\n", ep->ep.name);
+		// reset dma? then dma_advance()
+        }
+
+	/* pio */
+	for (i = 1; i < 4; i++) {
+		u32		tmp = INT_EPxDATASET(i);
+
+		if (!(stat & tmp))
+			continue;
+		ep = &dev->ep[i];
+		pio_advance(ep);
+		if (list_empty (&ep->queue))
+			pio_irq_disable(dev, regs, i);
+		stat &= ~tmp;
+		handled = 1;
+		ep->irqs++;
+	}
+
+	if (rescans--)
+		goto rescan;
+
+done:
+	(void)readl(&regs->int_enable);
+	spin_unlock(&dev->lock);
+	if (stat)
+		DBG(dev, "unhandled irq status: %05x (%05x, %05x)\n", stat,
+				readl(&regs->int_status), dev->int_enable);
+	return IRQ_RETVAL(handled);
+}
+
+#undef ACK
+
+/*-------------------------------------------------------------------------*/
+
+static void gadget_release(struct device *_dev)
+{
+	struct goku_udc	*dev = dev_get_drvdata(_dev);
+
+	kfree(dev);
+}
+
+/* tear down the binding between this driver and the pci device */
+
+static void goku_remove(struct pci_dev *pdev)
+{
+	struct goku_udc		*dev = pci_get_drvdata(pdev);
+
+	DBG(dev, "%s\n", __func__);
+
+	usb_del_gadget_udc(&dev->gadget);
+
+	BUG_ON(dev->driver);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	remove_proc_entry(proc_node_name, NULL);
+#endif
+	if (dev->regs)
+		udc_reset(dev);
+	if (dev->got_irq)
+		free_irq(pdev->irq, dev);
+	if (dev->regs)
+		iounmap(dev->regs);
+	if (dev->got_region)
+		release_mem_region(pci_resource_start (pdev, 0),
+				pci_resource_len (pdev, 0));
+	if (dev->enabled)
+		pci_disable_device(pdev);
+
+	dev->regs = NULL;
+
+	INFO(dev, "unbind\n");
+}
+
+/* wrap this driver around the specified pci device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct goku_udc		*dev = NULL;
+	unsigned long		resource, len;
+	void __iomem		*base = NULL;
+	int			retval;
+
+	if (!pdev->irq) {
+		printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev));
+		retval = -ENODEV;
+		goto err;
+	}
+
+	/* alloc, and start init */
+	dev = kzalloc (sizeof *dev, GFP_KERNEL);
+	if (!dev) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	spin_lock_init(&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &goku_ops;
+	dev->gadget.max_speed = USB_SPEED_FULL;
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	dev->gadget.name = driver_name;
+
+	/* now all the pci goodies ... */
+	retval = pci_enable_device(pdev);
+	if (retval < 0) {
+		DBG(dev, "can't enable, %d\n", retval);
+		goto err;
+	}
+	dev->enabled = 1;
+
+	resource = pci_resource_start(pdev, 0);
+	len = pci_resource_len(pdev, 0);
+	if (!request_mem_region(resource, len, driver_name)) {
+		DBG(dev, "controller already in use\n");
+		retval = -EBUSY;
+		goto err;
+	}
+	dev->got_region = 1;
+
+	base = ioremap_nocache(resource, len);
+	if (base == NULL) {
+		DBG(dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto err;
+	}
+	dev->regs = (struct goku_udc_regs __iomem *) base;
+
+	pci_set_drvdata(pdev, dev);
+	INFO(dev, "%s\n", driver_desc);
+	INFO(dev, "version: " DRIVER_VERSION " %s\n", dmastr());
+	INFO(dev, "irq %d, pci mem %p\n", pdev->irq, base);
+
+	/* init to known state, then setup irqs */
+	udc_reset(dev);
+	udc_reinit (dev);
+	if (request_irq(pdev->irq, goku_irq, IRQF_SHARED,
+			driver_name, dev) != 0) {
+		DBG(dev, "request interrupt %d failed\n", pdev->irq);
+		retval = -EBUSY;
+		goto err;
+	}
+	dev->got_irq = 1;
+	if (use_dma)
+		pci_set_master(pdev);
+
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	proc_create_single_data(proc_node_name, 0, NULL, udc_proc_read, dev);
+#endif
+
+	retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+			gadget_release);
+	if (retval)
+		goto err;
+
+	return 0;
+
+err:
+	if (dev)
+		goku_remove (pdev);
+	/* gadget_release is not registered yet, kfree explicitly */
+	kfree(dev);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+	.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+	.class_mask =	~0,
+	.vendor =	0x102f,		/* Toshiba */
+	.device =	0x0107,		/* this UDC */
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+static struct pci_driver goku_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+
+	.probe =	goku_probe,
+	.remove =	goku_remove,
+
+	/* FIXME add power management support */
+};
+
+module_pci_driver(goku_pci_driver);
diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
new file mode 100644
index 0000000..70023d4
--- /dev/null
+++ b/drivers/usb/gadget/udc/goku_udc.h
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toshiba TC86C001 ("Goku-S") USB Device Controller driver
+ *
+ * Copyright (C) 2000-2002 Lineo
+ *      by Stuart Lynne, Tom Rushworth, and Bruce Balden
+ * Copyright (C) 2002 Toshiba Corporation
+ * Copyright (C) 2003 MontaVista Software (source@mvista.com)
+ */
+
+/*
+ * PCI BAR 0 points to these registers.
+ */
+struct goku_udc_regs {
+	/* irq management */
+	u32	int_status;		/* 0x000 */
+	u32	int_enable;
+#define INT_SUSPEND		0x00001		/* or resume */
+#define INT_USBRESET		0x00002
+#define INT_ENDPOINT0		0x00004
+#define INT_SETUP		0x00008
+#define INT_STATUS		0x00010
+#define INT_STATUSNAK		0x00020
+#define INT_EPxDATASET(n)	(0x00020 << (n))	/* 0 < n < 4 */
+#	define INT_EP1DATASET		0x00040
+#	define INT_EP2DATASET		0x00080
+#	define INT_EP3DATASET		0x00100
+#define INT_EPnNAK(n)		(0x00100 << (n))	/* 0 < n < 4 */
+#	define INT_EP1NAK		0x00200
+#	define INT_EP2NAK		0x00400
+#	define INT_EP3NAK		0x00800
+#define INT_SOF			0x01000
+#define INT_ERR			0x02000
+#define INT_MSTWRSET		0x04000
+#define INT_MSTWREND		0x08000
+#define INT_MSTWRTMOUT		0x10000
+#define INT_MSTRDEND		0x20000
+#define INT_SYSERROR		0x40000
+#define INT_PWRDETECT		0x80000
+
+#define	INT_DEVWIDE \
+	(INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
+#define	INT_EP0 \
+	(INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
+
+	u32	dma_master;
+#define MST_EOPB_DIS		0x0800
+#define MST_EOPB_ENA		0x0400
+#define MST_TIMEOUT_DIS		0x0200
+#define MST_TIMEOUT_ENA		0x0100
+#define MST_RD_EOPB		0x0080		/* write-only */
+#define MST_RD_RESET		0x0040
+#define MST_WR_RESET		0x0020
+#define MST_RD_ENA		0x0004		/* 1:start, 0:ignore */
+#define MST_WR_ENA		0x0002		/* 1:start, 0:ignore */
+#define MST_CONNECTION		0x0001		/* 0 for ep1out/ep2in */
+
+#define MST_R_BITS		(MST_EOPB_DIS|MST_EOPB_ENA \
+					|MST_RD_ENA|MST_RD_RESET)
+#define MST_W_BITS		(MST_TIMEOUT_DIS|MST_TIMEOUT_ENA \
+					|MST_WR_ENA|MST_WR_RESET)
+#define MST_RW_BITS		(MST_R_BITS|MST_W_BITS \
+					|MST_CONNECTION)
+
+/* these values assume (dma_master & MST_CONNECTION) == 0 */
+#define UDC_MSTWR_ENDPOINT        1
+#define UDC_MSTRD_ENDPOINT        2
+
+	/* dma master write */
+	u32	out_dma_start;
+	u32	out_dma_end;
+	u32	out_dma_current;
+
+	/* dma master read */
+	u32	in_dma_start;
+	u32	in_dma_end;
+	u32	in_dma_current;
+
+	u32	power_detect;
+#define PW_DETECT		0x04
+#define PW_RESETB		0x02
+#define PW_PULLUP		0x01
+
+	u8	_reserved0 [0x1d8];
+
+	/* endpoint registers */
+	u32	ep_fifo [4];		/* 0x200 */
+	u8	_reserved1 [0x10];
+	u32	ep_mode [4];		/* only 1-3 valid */
+	u8	_reserved2 [0x10];
+
+	u32	ep_status [4];
+#define EPxSTATUS_TOGGLE	0x40
+#define EPxSTATUS_SUSPEND	0x20
+#define EPxSTATUS_EP_MASK	(0x07<<2)
+#	define EPxSTATUS_EP_READY	(0<<2)
+#	define EPxSTATUS_EP_DATAIN	(1<<2)
+#	define EPxSTATUS_EP_FULL	(2<<2)
+#	define EPxSTATUS_EP_TX_ERR	(3<<2)
+#	define EPxSTATUS_EP_RX_ERR	(4<<2)
+#	define EPxSTATUS_EP_BUSY	(5<<2)
+#	define EPxSTATUS_EP_STALL	(6<<2)
+#	define EPxSTATUS_EP_INVALID	(7<<2)
+#define EPxSTATUS_FIFO_DISABLE	0x02
+#define EPxSTATUS_STAGE_ERROR	0x01
+
+	u8	_reserved3 [0x10];
+	u32	EPxSizeLA[4];
+#define PACKET_ACTIVE		(1<<7)
+#define DATASIZE		0x7f
+	u8	_reserved3a [0x10];
+	u32	EPxSizeLB[4];		/* only 1,2 valid */
+	u8	_reserved3b [0x10];
+	u32	EPxSizeHA[4];		/* only 1-3 valid */
+	u8	_reserved3c [0x10];
+	u32	EPxSizeHB[4];		/* only 1,2 valid */
+	u8	_reserved4[0x30];
+
+	/* SETUP packet contents */
+	u32	bRequestType;		/* 0x300 */
+	u32	bRequest;
+	u32	wValueL;
+	u32	wValueH;
+	u32	wIndexL;
+	u32	wIndexH;
+	u32	wLengthL;
+	u32	wLengthH;
+
+	/* command interaction/handshaking */
+	u32	SetupRecv;		/* 0x320 */
+	u32	CurrConfig;
+	u32	StdRequest;
+	u32	Request;
+	u32	DataSet;
+#define DATASET_A(epnum)	(1<<(2*(epnum)))
+#define DATASET_B(epnum)	(2<<(2*(epnum)))
+#define DATASET_AB(epnum)	(3<<(2*(epnum)))
+	u8	_reserved5[4];
+
+	u32	UsbState;
+#define USBSTATE_CONFIGURED	0x04
+#define USBSTATE_ADDRESSED	0x02
+#define USBSTATE_DEFAULT	0x01
+
+	u32	EOP;
+
+	u32	Command;		/* 0x340 */
+#define COMMAND_SETDATA0	2
+#define COMMAND_RESET		3
+#define COMMAND_STALL		4
+#define COMMAND_INVALID		5
+#define COMMAND_FIFO_DISABLE	7
+#define COMMAND_FIFO_ENABLE	8
+#define COMMAND_INIT_DESCRIPTOR	9
+#define COMMAND_FIFO_CLEAR	10	/* also stall */
+#define COMMAND_STALL_CLEAR	11
+#define COMMAND_EP(n)		((n) << 4)
+
+	u32	EPxSingle;
+	u8	_reserved6[4];
+	u32	EPxBCS;
+	u8	_reserved7[8];
+	u32	IntControl;
+#define ICONTROL_STATUSNAK	1
+	u8	_reserved8[4];
+
+	u32	reqmode;	// 0x360 standard request mode, low 8 bits
+#define G_REQMODE_SET_INTF	(1<<7)
+#define G_REQMODE_GET_INTF	(1<<6)
+#define G_REQMODE_SET_CONF	(1<<5)
+#define G_REQMODE_GET_CONF	(1<<4)
+#define G_REQMODE_GET_DESC	(1<<3)
+#define G_REQMODE_SET_FEAT	(1<<2)
+#define G_REQMODE_CLEAR_FEAT	(1<<1)
+#define G_REQMODE_GET_STATUS	(1<<0)
+
+	u32	ReqMode;
+	u8	_reserved9[0x18];
+	u32	PortStatus;		/* 0x380 */
+	u8	_reserved10[8];
+	u32	address;
+	u32	buff_test;
+	u8	_reserved11[4];
+	u32	UsbReady;
+	u8	_reserved12[4];
+	u32	SetDescStall;		/* 0x3a0 */
+	u8	_reserved13[0x45c];
+
+	/* hardware could handle limited GET_DESCRIPTOR duties */
+#define	DESC_LEN	0x80
+	u32	descriptors[DESC_LEN];	/* 0x800 */
+	u8	_reserved14[0x600];
+
+} __attribute__ ((packed));
+
+#define	MAX_FIFO_SIZE	64
+#define	MAX_EP0_SIZE	8		/* ep0 fifo is bigger, though */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct goku_ep {
+	struct usb_ep				ep;
+	struct goku_udc				*dev;
+	unsigned long				irqs;
+
+	unsigned				num:8,
+						dma:1,
+						is_in:1,
+						stopped:1;
+
+	/* analogous to a host-side qh */
+	struct list_head			queue;
+
+	u32 __iomem				*reg_fifo;
+	u32 __iomem				*reg_mode;
+	u32 __iomem				*reg_status;
+};
+
+struct goku_request {
+	struct usb_request		req;
+	struct list_head		queue;
+
+	unsigned			mapped:1;
+};
+
+enum ep0state {
+	EP0_DISCONNECT,		/* no host */
+	EP0_IDLE,		/* between STATUS ack and SETUP report */
+	EP0_IN, EP0_OUT,	/* data stage */
+	EP0_STATUS,		/* status stage */
+	EP0_STALL,		/* data or status stages */
+	EP0_SUSPEND,		/* usb suspend */
+};
+
+struct goku_udc {
+	/* each pci device provides one gadget, several endpoints */
+	struct usb_gadget		gadget;
+	spinlock_t			lock;
+	struct goku_ep			ep[4];
+	struct usb_gadget_driver	*driver;
+
+	enum ep0state			ep0state;
+	unsigned			got_irq:1,
+					got_region:1,
+					req_config:1,
+					configured:1,
+					enabled:1;
+
+	/* pci state used to access those endpoints */
+	struct pci_dev			*pdev;
+	struct goku_udc_regs __iomem	*regs;
+	u32				int_enable;
+
+	/* statistics... */
+	unsigned long			irqs;
+};
+#define to_goku_udc(g)		(container_of((g), struct goku_udc, gadget))
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(dev,level,fmt,args...) \
+	printk(level "%s %s: " fmt , driver_name , \
+			pci_name(dev->pdev) , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(dev,fmt,args...) \
+	do { } while (0)
+#endif	/* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARNING(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
new file mode 100644
index 0000000..729e60e
--- /dev/null
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -0,0 +1,2257 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports GRUSBDC USB Device Controller cores available in the
+ * GRLIB VHDL IP core library.
+ *
+ * Full documentation of the GRUSBDC core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * Contributors:
+ * - Andreas Larsson <andreas@gaisler.com>
+ * - Marko Isomaki
+ */
+
+/*
+ * A GRUSBDC core can have up to 16 IN endpoints and 16 OUT endpoints each
+ * individually configurable to any of the four USB transfer types. This driver
+ * only supports cores in DMA mode.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include <asm/byteorder.h>
+
+#include "gr_udc.h"
+
+#define	DRIVER_NAME	"gr_udc"
+#define	DRIVER_DESC	"Aeroflex Gaisler GRUSBDC USB Peripheral Controller"
+
+static const char driver_name[] = DRIVER_NAME;
+static const char driver_desc[] = DRIVER_DESC;
+
+#define gr_read32(x) (ioread32be((x)))
+#define gr_write32(x, v) (iowrite32be((v), (x)))
+
+/* USB speed and corresponding string calculated from status register value */
+#define GR_SPEED(status) \
+	((status & GR_STATUS_SP) ? USB_SPEED_FULL : USB_SPEED_HIGH)
+#define GR_SPEED_STR(status) usb_speed_string(GR_SPEED(status))
+
+/* Size of hardware buffer calculated from epctrl register value */
+#define GR_BUFFER_SIZE(epctrl)					      \
+	((((epctrl) & GR_EPCTRL_BUFSZ_MASK) >> GR_EPCTRL_BUFSZ_POS) * \
+	 GR_EPCTRL_BUFSZ_SCALER)
+
+/* ---------------------------------------------------------------------- */
+/* Debug printout functionality */
+
+static const char * const gr_modestring[] = {"control", "iso", "bulk", "int"};
+
+static const char *gr_ep0state_string(enum gr_ep0state state)
+{
+	static const char *const names[] = {
+		[GR_EP0_DISCONNECT] = "disconnect",
+		[GR_EP0_SETUP] = "setup",
+		[GR_EP0_IDATA] = "idata",
+		[GR_EP0_ODATA] = "odata",
+		[GR_EP0_ISTATUS] = "istatus",
+		[GR_EP0_OSTATUS] = "ostatus",
+		[GR_EP0_STALL] = "stall",
+		[GR_EP0_SUSPEND] = "suspend",
+	};
+
+	if (state < 0 || state >= ARRAY_SIZE(names))
+		return "UNKNOWN";
+
+	return names[state];
+}
+
+#ifdef VERBOSE_DEBUG
+
+static void gr_dbgprint_request(const char *str, struct gr_ep *ep,
+				struct gr_request *req)
+{
+	int buflen = ep->is_in ? req->req.length : req->req.actual;
+	int rowlen = 32;
+	int plen = min(rowlen, buflen);
+
+	dev_dbg(ep->dev->dev, "%s: 0x%p, %d bytes data%s:\n", str, req, buflen,
+		(buflen > plen ? " (truncated)" : ""));
+	print_hex_dump_debug("   ", DUMP_PREFIX_NONE,
+			     rowlen, 4, req->req.buf, plen, false);
+}
+
+static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request,
+			       u16 value, u16 index, u16 length)
+{
+	dev_vdbg(dev->dev, "REQ: %02x.%02x v%04x i%04x l%04x\n",
+		 type, request, value, index, length);
+}
+#else /* !VERBOSE_DEBUG */
+
+static void gr_dbgprint_request(const char *str, struct gr_ep *ep,
+				struct gr_request *req) {}
+
+static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request,
+			       u16 value, u16 index, u16 length) {}
+
+#endif /* VERBOSE_DEBUG */
+
+/* ---------------------------------------------------------------------- */
+/* Debugfs functionality */
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+
+static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep)
+{
+	u32 epctrl = gr_read32(&ep->regs->epctrl);
+	u32 epstat = gr_read32(&ep->regs->epstat);
+	int mode = (epctrl & GR_EPCTRL_TT_MASK) >> GR_EPCTRL_TT_POS;
+	struct gr_request *req;
+
+	seq_printf(seq, "%s:\n", ep->ep.name);
+	seq_printf(seq, "  mode = %s\n", gr_modestring[mode]);
+	seq_printf(seq, "  halted: %d\n", !!(epctrl & GR_EPCTRL_EH));
+	seq_printf(seq, "  disabled: %d\n", !!(epctrl & GR_EPCTRL_ED));
+	seq_printf(seq, "  valid: %d\n", !!(epctrl & GR_EPCTRL_EV));
+	seq_printf(seq, "  dma_start = %d\n", ep->dma_start);
+	seq_printf(seq, "  stopped = %d\n", ep->stopped);
+	seq_printf(seq, "  wedged = %d\n", ep->wedged);
+	seq_printf(seq, "  callback = %d\n", ep->callback);
+	seq_printf(seq, "  maxpacket = %d\n", ep->ep.maxpacket);
+	seq_printf(seq, "  maxpacket_limit = %d\n", ep->ep.maxpacket_limit);
+	seq_printf(seq, "  bytes_per_buffer = %d\n", ep->bytes_per_buffer);
+	if (mode == 1 || mode == 3)
+		seq_printf(seq, "  nt = %d\n",
+			   (epctrl & GR_EPCTRL_NT_MASK) >> GR_EPCTRL_NT_POS);
+
+	seq_printf(seq, "  Buffer 0: %s %s%d\n",
+		   epstat & GR_EPSTAT_B0 ? "valid" : "invalid",
+		   epstat & GR_EPSTAT_BS ? " " : "selected ",
+		   (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS);
+	seq_printf(seq, "  Buffer 1: %s %s%d\n",
+		   epstat & GR_EPSTAT_B1 ? "valid" : "invalid",
+		   epstat & GR_EPSTAT_BS ? "selected " : " ",
+		   (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS);
+
+	if (list_empty(&ep->queue)) {
+		seq_puts(seq, "  Queue: empty\n\n");
+		return;
+	}
+
+	seq_puts(seq, "  Queue:\n");
+	list_for_each_entry(req, &ep->queue, queue) {
+		struct gr_dma_desc *desc;
+		struct gr_dma_desc *next;
+
+		seq_printf(seq, "    0x%p: 0x%p %d %d\n", req,
+			   &req->req.buf, req->req.actual, req->req.length);
+
+		next = req->first_desc;
+		do {
+			desc = next;
+			next = desc->next_desc;
+			seq_printf(seq, "    %c 0x%p (0x%08x): 0x%05x 0x%08x\n",
+				   desc == req->curr_desc ? 'c' : ' ',
+				   desc, desc->paddr, desc->ctrl, desc->data);
+		} while (desc != req->last_desc);
+	}
+	seq_puts(seq, "\n");
+}
+
+static int gr_dfs_show(struct seq_file *seq, void *v)
+{
+	struct gr_udc *dev = seq->private;
+	u32 control = gr_read32(&dev->regs->control);
+	u32 status = gr_read32(&dev->regs->status);
+	struct gr_ep *ep;
+
+	seq_printf(seq, "usb state = %s\n",
+		   usb_state_string(dev->gadget.state));
+	seq_printf(seq, "address = %d\n",
+		   (control & GR_CONTROL_UA_MASK) >> GR_CONTROL_UA_POS);
+	seq_printf(seq, "speed = %s\n", GR_SPEED_STR(status));
+	seq_printf(seq, "ep0state = %s\n", gr_ep0state_string(dev->ep0state));
+	seq_printf(seq, "irq_enabled = %d\n", dev->irq_enabled);
+	seq_printf(seq, "remote_wakeup = %d\n", dev->remote_wakeup);
+	seq_printf(seq, "test_mode = %d\n", dev->test_mode);
+	seq_puts(seq, "\n");
+
+	list_for_each_entry(ep, &dev->ep_list, ep_list)
+		gr_seq_ep_show(seq, ep);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(gr_dfs);
+
+static void gr_dfs_create(struct gr_udc *dev)
+{
+	const char *name = "gr_udc_state";
+
+	dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
+	debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops);
+}
+
+static void gr_dfs_delete(struct gr_udc *dev)
+{
+	debugfs_remove_recursive(dev->dfs_root);
+}
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FS */
+
+static void gr_dfs_create(struct gr_udc *dev) {}
+static void gr_dfs_delete(struct gr_udc *dev) {}
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FS */
+
+/* ---------------------------------------------------------------------- */
+/* DMA and request handling */
+
+/* Allocates a new struct gr_dma_desc, sets paddr and zeroes the rest */
+static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags)
+{
+	dma_addr_t paddr;
+	struct gr_dma_desc *dma_desc;
+
+	dma_desc = dma_pool_zalloc(ep->dev->desc_pool, gfp_flags, &paddr);
+	if (!dma_desc) {
+		dev_err(ep->dev->dev, "Could not allocate from DMA pool\n");
+		return NULL;
+	}
+
+	dma_desc->paddr = paddr;
+
+	return dma_desc;
+}
+
+static inline void gr_free_dma_desc(struct gr_udc *dev,
+				    struct gr_dma_desc *desc)
+{
+	dma_pool_free(dev->desc_pool, desc, (dma_addr_t)desc->paddr);
+}
+
+/* Frees the chain of struct gr_dma_desc for the given request */
+static void gr_free_dma_desc_chain(struct gr_udc *dev, struct gr_request *req)
+{
+	struct gr_dma_desc *desc;
+	struct gr_dma_desc *next;
+
+	next = req->first_desc;
+	if (!next)
+		return;
+
+	do {
+		desc = next;
+		next = desc->next_desc;
+		gr_free_dma_desc(dev, desc);
+	} while (desc != req->last_desc);
+
+	req->first_desc = NULL;
+	req->curr_desc = NULL;
+	req->last_desc = NULL;
+}
+
+static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req);
+
+/*
+ * Frees allocated resources and calls the appropriate completion function/setup
+ * package handler for a finished request.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
+			      int status)
+	__releases(&dev->lock)
+	__acquires(&dev->lock)
+{
+	struct gr_udc *dev;
+
+	list_del_init(&req->queue);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+	gr_free_dma_desc_chain(dev, req);
+
+	if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */
+		req->req.actual = req->req.length;
+	} else if (req->oddlen && req->req.actual > req->evenlen) {
+		/*
+		 * Copy to user buffer in this case where length was not evenly
+		 * divisible by ep->ep.maxpacket and the last descriptor was
+		 * actually used.
+		 */
+		char *buftail = ((char *)req->req.buf + req->evenlen);
+
+		memcpy(buftail, ep->tailbuf, req->oddlen);
+
+		if (req->req.actual > req->req.length) {
+			/* We got more data than was requested */
+			dev_dbg(ep->dev->dev, "Overflow for ep %s\n",
+				ep->ep.name);
+			gr_dbgprint_request("OVFL", ep, req);
+			req->req.status = -EOVERFLOW;
+		}
+	}
+
+	if (!status) {
+		if (ep->is_in)
+			gr_dbgprint_request("SENT", ep, req);
+		else
+			gr_dbgprint_request("RECV", ep, req);
+	}
+
+	/* Prevent changes to ep->queue during callback */
+	ep->callback = 1;
+	if (req == dev->ep0reqo && !status) {
+		if (req->setup)
+			gr_ep0_setup(dev, req);
+		else
+			dev_err(dev->dev,
+				"Unexpected non setup packet on ep0in\n");
+	} else if (req->req.complete) {
+		spin_unlock(&dev->lock);
+
+		usb_gadget_giveback_request(&ep->ep, &req->req);
+
+		spin_lock(&dev->lock);
+	}
+	ep->callback = 0;
+}
+
+static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct gr_request *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+/*
+ * Starts DMA for endpoint ep if there are requests in the queue.
+ *
+ * Must be called with dev->lock held and with !ep->stopped.
+ */
+static void gr_start_dma(struct gr_ep *ep)
+{
+	struct gr_request *req;
+	u32 dmactrl;
+
+	if (list_empty(&ep->queue)) {
+		ep->dma_start = 0;
+		return;
+	}
+
+	req = list_first_entry(&ep->queue, struct gr_request, queue);
+
+	/* A descriptor should already have been allocated */
+	BUG_ON(!req->curr_desc);
+
+	/*
+	 * The DMA controller can not handle smaller OUT buffers than
+	 * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly
+	 * long packet are received. Therefore an internal bounce buffer gets
+	 * used when such a request gets enabled.
+	 */
+	if (!ep->is_in && req->oddlen)
+		req->last_desc->data = ep->tailbuf_paddr;
+
+	wmb(); /* Make sure all is settled before handing it over to DMA */
+
+	/* Set the descriptor pointer in the hardware */
+	gr_write32(&ep->regs->dmaaddr, req->curr_desc->paddr);
+
+	/* Announce available descriptors */
+	dmactrl = gr_read32(&ep->regs->dmactrl);
+	gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_DA);
+
+	ep->dma_start = 1;
+}
+
+/*
+ * Finishes the first request in the ep's queue and, if available, starts the
+ * next request in queue.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static void gr_dma_advance(struct gr_ep *ep, int status)
+{
+	struct gr_request *req;
+
+	req = list_first_entry(&ep->queue, struct gr_request, queue);
+	gr_finish_request(ep, req, status);
+	gr_start_dma(ep); /* Regardless of ep->dma_start */
+}
+
+/*
+ * Abort DMA for an endpoint. Sets the abort DMA bit which causes an ongoing DMA
+ * transfer to be canceled and clears GR_DMACTRL_DA.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_abort_dma(struct gr_ep *ep)
+{
+	u32 dmactrl;
+
+	dmactrl = gr_read32(&ep->regs->dmactrl);
+	gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_AD);
+}
+
+/*
+ * Allocates and sets up a struct gr_dma_desc and putting it on the descriptor
+ * chain.
+ *
+ * Size is not used for OUT endpoints. Hardware can not be instructed to handle
+ * smaller buffer than MAXPL in the OUT direction.
+ */
+static int gr_add_dma_desc(struct gr_ep *ep, struct gr_request *req,
+			   dma_addr_t data, unsigned size, gfp_t gfp_flags)
+{
+	struct gr_dma_desc *desc;
+
+	desc = gr_alloc_dma_desc(ep, gfp_flags);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->data = data;
+	if (ep->is_in)
+		desc->ctrl =
+			(GR_DESC_IN_CTRL_LEN_MASK & size) | GR_DESC_IN_CTRL_EN;
+	else
+		desc->ctrl = GR_DESC_OUT_CTRL_IE;
+
+	if (!req->first_desc) {
+		req->first_desc = desc;
+		req->curr_desc = desc;
+	} else {
+		req->last_desc->next_desc = desc;
+		req->last_desc->next = desc->paddr;
+		req->last_desc->ctrl |= GR_DESC_OUT_CTRL_NX;
+	}
+	req->last_desc = desc;
+
+	return 0;
+}
+
+/*
+ * Sets up a chain of struct gr_dma_descriptors pointing to buffers that
+ * together covers req->req.length bytes of the buffer at DMA address
+ * req->req.dma for the OUT direction.
+ *
+ * The first descriptor in the chain is enabled, the rest disabled. The
+ * interrupt handler will later enable them one by one when needed so we can
+ * find out when the transfer is finished. For OUT endpoints, all descriptors
+ * therefore generate interrutps.
+ */
+static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req,
+				  gfp_t gfp_flags)
+{
+	u16 bytes_left; /* Bytes left to provide descriptors for */
+	u16 bytes_used; /* Bytes accommodated for */
+	int ret = 0;
+
+	req->first_desc = NULL; /* Signals that no allocation is done yet */
+	bytes_left = req->req.length;
+	bytes_used = 0;
+	while (bytes_left > 0) {
+		dma_addr_t start = req->req.dma + bytes_used;
+		u16 size = min(bytes_left, ep->bytes_per_buffer);
+
+		if (size < ep->bytes_per_buffer) {
+			/* Prepare using bounce buffer */
+			req->evenlen = req->req.length - bytes_left;
+			req->oddlen = size;
+		}
+
+		ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
+		if (ret)
+			goto alloc_err;
+
+		bytes_left -= size;
+		bytes_used += size;
+	}
+
+	req->first_desc->ctrl |= GR_DESC_OUT_CTRL_EN;
+
+	return 0;
+
+alloc_err:
+	gr_free_dma_desc_chain(ep->dev, req);
+
+	return ret;
+}
+
+/*
+ * Sets up a chain of struct gr_dma_descriptors pointing to buffers that
+ * together covers req->req.length bytes of the buffer at DMA address
+ * req->req.dma for the IN direction.
+ *
+ * When more data is provided than the maximum payload size, the hardware splits
+ * this up into several payloads automatically. Moreover, ep->bytes_per_buffer
+ * is always set to a multiple of the maximum payload (restricted to the valid
+ * number of maximum payloads during high bandwidth isochronous or interrupt
+ * transfers)
+ *
+ * All descriptors are enabled from the beginning and we only generate an
+ * interrupt for the last one indicating that the entire request has been pushed
+ * to hardware.
+ */
+static int gr_setup_in_desc_list(struct gr_ep *ep, struct gr_request *req,
+				 gfp_t gfp_flags)
+{
+	u16 bytes_left; /* Bytes left in req to provide descriptors for */
+	u16 bytes_used; /* Bytes in req accommodated for */
+	int ret = 0;
+
+	req->first_desc = NULL; /* Signals that no allocation is done yet */
+	bytes_left = req->req.length;
+	bytes_used = 0;
+	do { /* Allow for zero length packets */
+		dma_addr_t start = req->req.dma + bytes_used;
+		u16 size = min(bytes_left, ep->bytes_per_buffer);
+
+		ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
+		if (ret)
+			goto alloc_err;
+
+		bytes_left -= size;
+		bytes_used += size;
+	} while (bytes_left > 0);
+
+	/*
+	 * Send an extra zero length packet to indicate that no more data is
+	 * available when req->req.zero is set and the data length is even
+	 * multiples of ep->ep.maxpacket.
+	 */
+	if (req->req.zero && (req->req.length % ep->ep.maxpacket == 0)) {
+		ret = gr_add_dma_desc(ep, req, 0, 0, gfp_flags);
+		if (ret)
+			goto alloc_err;
+	}
+
+	/*
+	 * For IN packets we only want to know when the last packet has been
+	 * transmitted (not just put into internal buffers).
+	 */
+	req->last_desc->ctrl |= GR_DESC_IN_CTRL_PI;
+
+	return 0;
+
+alloc_err:
+	gr_free_dma_desc_chain(ep->dev, req);
+
+	return ret;
+}
+
+/* Must be called with dev->lock held */
+static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags)
+{
+	struct gr_udc *dev = ep->dev;
+	int ret;
+
+	if (unlikely(!ep->ep.desc && ep->num != 0)) {
+		dev_err(dev->dev, "No ep descriptor for %s\n", ep->ep.name);
+		return -EINVAL;
+	}
+
+	if (unlikely(!req->req.buf || !list_empty(&req->queue))) {
+		dev_err(dev->dev,
+			"Invalid request for %s: buf=%p list_empty=%d\n",
+			ep->ep.name, req->req.buf, list_empty(&req->queue));
+		return -EINVAL;
+	}
+
+	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		dev_err(dev->dev, "-ESHUTDOWN");
+		return -ESHUTDOWN;
+	}
+
+	/* Can't touch registers when suspended */
+	if (dev->ep0state == GR_EP0_SUSPEND) {
+		dev_err(dev->dev, "-EBUSY");
+		return -EBUSY;
+	}
+
+	/* Set up DMA mapping in case the caller didn't */
+	ret = usb_gadget_map_request(&dev->gadget, &req->req, ep->is_in);
+	if (ret) {
+		dev_err(dev->dev, "usb_gadget_map_request");
+		return ret;
+	}
+
+	if (ep->is_in)
+		ret = gr_setup_in_desc_list(ep, req, gfp_flags);
+	else
+		ret = gr_setup_out_desc_list(ep, req, gfp_flags);
+	if (ret)
+		return ret;
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	list_add_tail(&req->queue, &ep->queue);
+
+	/* Start DMA if not started, otherwise interrupt handler handles it */
+	if (!ep->dma_start && likely(!ep->stopped))
+		gr_start_dma(ep);
+
+	return 0;
+}
+
+/*
+ * Queue a request from within the driver.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_queue_int(struct gr_ep *ep, struct gr_request *req,
+			       gfp_t gfp_flags)
+{
+	if (ep->is_in)
+		gr_dbgprint_request("RESP", ep, req);
+
+	return gr_queue(ep, req, gfp_flags);
+}
+
+/* ---------------------------------------------------------------------- */
+/* General helper functions */
+
+/*
+ * Dequeue ALL requests.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_ep_nuke(struct gr_ep *ep)
+{
+	struct gr_request *req;
+
+	ep->stopped = 1;
+	ep->dma_start = 0;
+	gr_abort_dma(ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_first_entry(&ep->queue, struct gr_request, queue);
+		gr_finish_request(ep, req, -ESHUTDOWN);
+	}
+}
+
+/*
+ * Reset the hardware state of this endpoint.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_ep_reset(struct gr_ep *ep)
+{
+	gr_write32(&ep->regs->epctrl, 0);
+	gr_write32(&ep->regs->dmactrl, 0);
+
+	ep->ep.maxpacket = MAX_CTRL_PL_SIZE;
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+	ep->dma_start = 0;
+}
+
+/*
+ * Generate STALL on ep0in/out.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_control_stall(struct gr_udc *dev)
+{
+	u32 epctrl;
+
+	epctrl = gr_read32(&dev->epo[0].regs->epctrl);
+	gr_write32(&dev->epo[0].regs->epctrl, epctrl | GR_EPCTRL_CS);
+	epctrl = gr_read32(&dev->epi[0].regs->epctrl);
+	gr_write32(&dev->epi[0].regs->epctrl, epctrl | GR_EPCTRL_CS);
+
+	dev->ep0state = GR_EP0_STALL;
+}
+
+/*
+ * Halts, halts and wedges, or clears halt for an endpoint.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_ep_halt_wedge(struct gr_ep *ep, int halt, int wedge, int fromhost)
+{
+	u32 epctrl;
+	int retval = 0;
+
+	if (ep->num && !ep->ep.desc)
+		return -EINVAL;
+
+	if (ep->num && ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+		return -EOPNOTSUPP;
+
+	/* Never actually halt ep0, and therefore never clear halt for ep0 */
+	if (!ep->num) {
+		if (halt && !fromhost) {
+			/* ep0 halt from gadget - generate protocol stall */
+			gr_control_stall(ep->dev);
+			dev_dbg(ep->dev->dev, "EP: stall ep0\n");
+			return 0;
+		}
+		return -EINVAL;
+	}
+
+	dev_dbg(ep->dev->dev, "EP: %s halt %s\n",
+		(halt ? (wedge ? "wedge" : "set") : "clear"), ep->ep.name);
+
+	epctrl = gr_read32(&ep->regs->epctrl);
+	if (halt) {
+		/* Set HALT */
+		gr_write32(&ep->regs->epctrl, epctrl | GR_EPCTRL_EH);
+		ep->stopped = 1;
+		if (wedge)
+			ep->wedged = 1;
+	} else {
+		gr_write32(&ep->regs->epctrl, epctrl & ~GR_EPCTRL_EH);
+		ep->stopped = 0;
+		ep->wedged = 0;
+
+		/* Things might have been queued up in the meantime */
+		if (!ep->dma_start)
+			gr_start_dma(ep);
+	}
+
+	return retval;
+}
+
+/* Must be called with dev->lock held */
+static inline void gr_set_ep0state(struct gr_udc *dev, enum gr_ep0state value)
+{
+	if (dev->ep0state != value)
+		dev_vdbg(dev->dev, "STATE:  ep0state=%s\n",
+			 gr_ep0state_string(value));
+	dev->ep0state = value;
+}
+
+/*
+ * Should only be called when endpoints can not generate interrupts.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_disable_interrupts_and_pullup(struct gr_udc *dev)
+{
+	gr_write32(&dev->regs->control, 0);
+	wmb(); /* Make sure that we do not deny one of our interrupts */
+	dev->irq_enabled = 0;
+}
+
+/*
+ * Stop all device activity and disable data line pullup.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_stop_activity(struct gr_udc *dev)
+{
+	struct gr_ep *ep;
+
+	list_for_each_entry(ep, &dev->ep_list, ep_list)
+		gr_ep_nuke(ep);
+
+	gr_disable_interrupts_and_pullup(dev);
+
+	gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+	usb_gadget_set_state(&dev->gadget, USB_STATE_NOTATTACHED);
+}
+
+/* ---------------------------------------------------------------------- */
+/* ep0 setup packet handling */
+
+static void gr_ep0_testmode_complete(struct usb_ep *_ep,
+				     struct usb_request *_req)
+{
+	struct gr_ep *ep;
+	struct gr_udc *dev;
+	u32 control;
+
+	ep = container_of(_ep, struct gr_ep, ep);
+	dev = ep->dev;
+
+	spin_lock(&dev->lock);
+
+	control = gr_read32(&dev->regs->control);
+	control |= GR_CONTROL_TM | (dev->test_mode << GR_CONTROL_TS_POS);
+	gr_write32(&dev->regs->control, control);
+
+	spin_unlock(&dev->lock);
+}
+
+static void gr_ep0_dummy_complete(struct usb_ep *_ep, struct usb_request *_req)
+{
+	/* Nothing needs to be done here */
+}
+
+/*
+ * Queue a response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_ep0_respond(struct gr_udc *dev, u8 *buf, int length,
+			  void (*complete)(struct usb_ep *ep,
+					   struct usb_request *req))
+{
+	u8 *reqbuf = dev->ep0reqi->req.buf;
+	int status;
+	int i;
+
+	for (i = 0; i < length; i++)
+		reqbuf[i] = buf[i];
+	dev->ep0reqi->req.length = length;
+	dev->ep0reqi->req.complete = complete;
+
+	status = gr_queue_int(&dev->epi[0], dev->ep0reqi, GFP_ATOMIC);
+	if (status < 0)
+		dev_err(dev->dev,
+			"Could not queue ep0in setup response: %d\n", status);
+
+	return status;
+}
+
+/*
+ * Queue a 2 byte response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_ep0_respond_u16(struct gr_udc *dev, u16 response)
+{
+	__le16 le_response = cpu_to_le16(response);
+
+	return gr_ep0_respond(dev, (u8 *)&le_response, 2,
+			      gr_ep0_dummy_complete);
+}
+
+/*
+ * Queue a ZLP response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_ep0_respond_empty(struct gr_udc *dev)
+{
+	return gr_ep0_respond(dev, NULL, 0, gr_ep0_dummy_complete);
+}
+
+/*
+ * This is run when a SET_ADDRESS request is received. First writes
+ * the new address to the control register which is updated internally
+ * when the next IN packet is ACKED.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_set_address(struct gr_udc *dev, u8 address)
+{
+	u32 control;
+
+	control = gr_read32(&dev->regs->control) & ~GR_CONTROL_UA_MASK;
+	control |= (address << GR_CONTROL_UA_POS) & GR_CONTROL_UA_MASK;
+	control |= GR_CONTROL_SU;
+	gr_write32(&dev->regs->control, control);
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_device_request(struct gr_udc *dev, u8 type, u8 request,
+			     u16 value, u16 index)
+{
+	u16 response;
+	u8 test;
+
+	switch (request) {
+	case USB_REQ_SET_ADDRESS:
+		dev_dbg(dev->dev, "STATUS: address %d\n", value & 0xff);
+		gr_set_address(dev, value & 0xff);
+		if (value)
+			usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS);
+		else
+			usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT);
+		return gr_ep0_respond_empty(dev);
+
+	case USB_REQ_GET_STATUS:
+		/* Self powered | remote wakeup */
+		response = 0x0001 | (dev->remote_wakeup ? 0x0002 : 0);
+		return gr_ep0_respond_u16(dev, response);
+
+	case USB_REQ_SET_FEATURE:
+		switch (value) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			/* Allow remote wakeup */
+			dev->remote_wakeup = 1;
+			return gr_ep0_respond_empty(dev);
+
+		case USB_DEVICE_TEST_MODE:
+			/* The hardware does not support TEST_FORCE_EN */
+			test = index >> 8;
+			if (test >= TEST_J && test <= TEST_PACKET) {
+				dev->test_mode = test;
+				return gr_ep0_respond(dev, NULL, 0,
+						      gr_ep0_testmode_complete);
+			}
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		switch (value) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			/* Disallow remote wakeup */
+			dev->remote_wakeup = 0;
+			return gr_ep0_respond_empty(dev);
+		}
+		break;
+	}
+
+	return 1; /* Delegate the rest */
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_interface_request(struct gr_udc *dev, u8 type, u8 request,
+				u16 value, u16 index)
+{
+	if (dev->gadget.state != USB_STATE_CONFIGURED)
+		return -1;
+
+	/*
+	 * Should return STALL for invalid interfaces, but udc driver does not
+	 * know anything about that. However, many gadget drivers do not handle
+	 * GET_STATUS so we need to take care of that.
+	 */
+
+	switch (request) {
+	case USB_REQ_GET_STATUS:
+		return gr_ep0_respond_u16(dev, 0x0000);
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		/*
+		 * No possible valid standard requests. Still let gadget drivers
+		 * have a go at it.
+		 */
+		break;
+	}
+
+	return 1; /* Delegate the rest */
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_endpoint_request(struct gr_udc *dev, u8 type, u8 request,
+			       u16 value, u16 index)
+{
+	struct gr_ep *ep;
+	int status;
+	int halted;
+	u8 epnum = index & USB_ENDPOINT_NUMBER_MASK;
+	u8 is_in = index & USB_ENDPOINT_DIR_MASK;
+
+	if ((is_in && epnum >= dev->nepi) || (!is_in && epnum >= dev->nepo))
+		return -1;
+
+	if (dev->gadget.state != USB_STATE_CONFIGURED && epnum != 0)
+		return -1;
+
+	ep = (is_in ? &dev->epi[epnum] : &dev->epo[epnum]);
+
+	switch (request) {
+	case USB_REQ_GET_STATUS:
+		halted = gr_read32(&ep->regs->epctrl) & GR_EPCTRL_EH;
+		return gr_ep0_respond_u16(dev, halted ? 0x0001 : 0);
+
+	case USB_REQ_SET_FEATURE:
+		switch (value) {
+		case USB_ENDPOINT_HALT:
+			status = gr_ep_halt_wedge(ep, 1, 0, 1);
+			if (status >= 0)
+				status = gr_ep0_respond_empty(dev);
+			return status;
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		switch (value) {
+		case USB_ENDPOINT_HALT:
+			if (ep->wedged)
+				return -1;
+			status = gr_ep_halt_wedge(ep, 0, 0, 1);
+			if (status >= 0)
+				status = gr_ep0_respond_empty(dev);
+			return status;
+		}
+		break;
+	}
+
+	return 1; /* Delegate the rest */
+}
+
+/* Must be called with dev->lock held */
+static void gr_ep0out_requeue(struct gr_udc *dev)
+{
+	int ret = gr_queue_int(&dev->epo[0], dev->ep0reqo, GFP_ATOMIC);
+
+	if (ret)
+		dev_err(dev->dev, "Could not queue ep0out setup request: %d\n",
+			ret);
+}
+
+/*
+ * The main function dealing with setup requests on ep0.
+ *
+ * Must be called with dev->lock held and irqs disabled
+ */
+static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req)
+	__releases(&dev->lock)
+	__acquires(&dev->lock)
+{
+	union {
+		struct usb_ctrlrequest ctrl;
+		u8 raw[8];
+		u32 word[2];
+	} u;
+	u8 type;
+	u8 request;
+	u16 value;
+	u16 index;
+	u16 length;
+	int i;
+	int status;
+
+	/* Restore from ep0 halt */
+	if (dev->ep0state == GR_EP0_STALL) {
+		gr_set_ep0state(dev, GR_EP0_SETUP);
+		if (!req->req.actual)
+			goto out;
+	}
+
+	if (dev->ep0state == GR_EP0_ISTATUS) {
+		gr_set_ep0state(dev, GR_EP0_SETUP);
+		if (req->req.actual > 0)
+			dev_dbg(dev->dev,
+				"Unexpected setup packet at state %s\n",
+				gr_ep0state_string(GR_EP0_ISTATUS));
+		else
+			goto out; /* Got expected ZLP */
+	} else if (dev->ep0state != GR_EP0_SETUP) {
+		dev_info(dev->dev,
+			 "Unexpected ep0out request at state %s - stalling\n",
+			 gr_ep0state_string(dev->ep0state));
+		gr_control_stall(dev);
+		gr_set_ep0state(dev, GR_EP0_SETUP);
+		goto out;
+	} else if (!req->req.actual) {
+		dev_dbg(dev->dev, "Unexpected ZLP at state %s\n",
+			gr_ep0state_string(dev->ep0state));
+		goto out;
+	}
+
+	/* Handle SETUP packet */
+	for (i = 0; i < req->req.actual; i++)
+		u.raw[i] = ((u8 *)req->req.buf)[i];
+
+	type = u.ctrl.bRequestType;
+	request = u.ctrl.bRequest;
+	value = le16_to_cpu(u.ctrl.wValue);
+	index = le16_to_cpu(u.ctrl.wIndex);
+	length = le16_to_cpu(u.ctrl.wLength);
+
+	gr_dbgprint_devreq(dev, type, request, value, index, length);
+
+	/* Check for data stage */
+	if (length) {
+		if (type & USB_DIR_IN)
+			gr_set_ep0state(dev, GR_EP0_IDATA);
+		else
+			gr_set_ep0state(dev, GR_EP0_ODATA);
+	}
+
+	status = 1; /* Positive status flags delegation */
+	if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (type & USB_RECIP_MASK) {
+		case USB_RECIP_DEVICE:
+			status = gr_device_request(dev, type, request,
+						   value, index);
+			break;
+		case USB_RECIP_ENDPOINT:
+			status =  gr_endpoint_request(dev, type, request,
+						      value, index);
+			break;
+		case USB_RECIP_INTERFACE:
+			status = gr_interface_request(dev, type, request,
+						      value, index);
+			break;
+		}
+	}
+
+	if (status > 0) {
+		spin_unlock(&dev->lock);
+
+		dev_vdbg(dev->dev, "DELEGATE\n");
+		status = dev->driver->setup(&dev->gadget, &u.ctrl);
+
+		spin_lock(&dev->lock);
+	}
+
+	/* Generate STALL on both ep0out and ep0in if requested */
+	if (unlikely(status < 0)) {
+		dev_vdbg(dev->dev, "STALL\n");
+		gr_control_stall(dev);
+	}
+
+	if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
+	    request == USB_REQ_SET_CONFIGURATION) {
+		if (!value) {
+			dev_dbg(dev->dev, "STATUS: deconfigured\n");
+			usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS);
+		} else if (status >= 0) {
+			/* Not configured unless gadget OK:s it */
+			dev_dbg(dev->dev, "STATUS: configured: %d\n", value);
+			usb_gadget_set_state(&dev->gadget,
+					     USB_STATE_CONFIGURED);
+		}
+	}
+
+	/* Get ready for next stage */
+	if (dev->ep0state == GR_EP0_ODATA)
+		gr_set_ep0state(dev, GR_EP0_OSTATUS);
+	else if (dev->ep0state == GR_EP0_IDATA)
+		gr_set_ep0state(dev, GR_EP0_ISTATUS);
+	else
+		gr_set_ep0state(dev, GR_EP0_SETUP);
+
+out:
+	gr_ep0out_requeue(dev);
+}
+
+/* ---------------------------------------------------------------------- */
+/* VBUS and USB reset handling */
+
+/* Must be called with dev->lock held and irqs disabled  */
+static void gr_vbus_connected(struct gr_udc *dev, u32 status)
+{
+	u32 control;
+
+	dev->gadget.speed = GR_SPEED(status);
+	usb_gadget_set_state(&dev->gadget, USB_STATE_POWERED);
+
+	/* Turn on full interrupts and pullup */
+	control = (GR_CONTROL_SI | GR_CONTROL_UI | GR_CONTROL_VI |
+		   GR_CONTROL_SP | GR_CONTROL_EP);
+	gr_write32(&dev->regs->control, control);
+}
+
+/* Must be called with dev->lock held */
+static void gr_enable_vbus_detect(struct gr_udc *dev)
+{
+	u32 status;
+
+	dev->irq_enabled = 1;
+	wmb(); /* Make sure we do not ignore an interrupt */
+	gr_write32(&dev->regs->control, GR_CONTROL_VI);
+
+	/* Take care of the case we are already plugged in at this point */
+	status = gr_read32(&dev->regs->status);
+	if (status & GR_STATUS_VB)
+		gr_vbus_connected(dev, status);
+}
+
+/* Must be called with dev->lock held and irqs disabled */
+static void gr_vbus_disconnected(struct gr_udc *dev)
+{
+	gr_stop_activity(dev);
+
+	/* Report disconnect */
+	if (dev->driver && dev->driver->disconnect) {
+		spin_unlock(&dev->lock);
+
+		dev->driver->disconnect(&dev->gadget);
+
+		spin_lock(&dev->lock);
+	}
+
+	gr_enable_vbus_detect(dev);
+}
+
+/* Must be called with dev->lock held and irqs disabled */
+static void gr_udc_usbreset(struct gr_udc *dev, u32 status)
+{
+	gr_set_address(dev, 0);
+	gr_set_ep0state(dev, GR_EP0_SETUP);
+	usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT);
+	dev->gadget.speed = GR_SPEED(status);
+
+	gr_ep_nuke(&dev->epo[0]);
+	gr_ep_nuke(&dev->epi[0]);
+	dev->epo[0].stopped = 0;
+	dev->epi[0].stopped = 0;
+	gr_ep0out_requeue(dev);
+}
+
+/* ---------------------------------------------------------------------- */
+/* Irq handling */
+
+/*
+ * Handles interrupts from in endpoints. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static int gr_handle_in_ep(struct gr_ep *ep)
+{
+	struct gr_request *req;
+
+	req = list_first_entry(&ep->queue, struct gr_request, queue);
+	if (!req->last_desc)
+		return 0;
+
+	if (READ_ONCE(req->last_desc->ctrl) & GR_DESC_IN_CTRL_EN)
+		return 0; /* Not put in hardware buffers yet */
+
+	if (gr_read32(&ep->regs->epstat) & (GR_EPSTAT_B1 | GR_EPSTAT_B0))
+		return 0; /* Not transmitted yet, still in hardware buffers */
+
+	/* Write complete */
+	gr_dma_advance(ep, 0);
+
+	return 1;
+}
+
+/*
+ * Handles interrupts from out endpoints. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static int gr_handle_out_ep(struct gr_ep *ep)
+{
+	u32 ep_dmactrl;
+	u32 ctrl;
+	u16 len;
+	struct gr_request *req;
+	struct gr_udc *dev = ep->dev;
+
+	req = list_first_entry(&ep->queue, struct gr_request, queue);
+	if (!req->curr_desc)
+		return 0;
+
+	ctrl = READ_ONCE(req->curr_desc->ctrl);
+	if (ctrl & GR_DESC_OUT_CTRL_EN)
+		return 0; /* Not received yet */
+
+	/* Read complete */
+	len = ctrl & GR_DESC_OUT_CTRL_LEN_MASK;
+	req->req.actual += len;
+	if (ctrl & GR_DESC_OUT_CTRL_SE)
+		req->setup = 1;
+
+	if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) {
+		/* Short packet or >= expected size - we are done */
+
+		if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) {
+			/*
+			 * Send a status stage ZLP to ack the DATA stage in the
+			 * OUT direction. This needs to be done before
+			 * gr_dma_advance as that can lead to a call to
+			 * ep0_setup that can change dev->ep0state.
+			 */
+			gr_ep0_respond_empty(dev);
+			gr_set_ep0state(dev, GR_EP0_SETUP);
+		}
+
+		gr_dma_advance(ep, 0);
+	} else {
+		/* Not done yet. Enable the next descriptor to receive more. */
+		req->curr_desc = req->curr_desc->next_desc;
+		req->curr_desc->ctrl |= GR_DESC_OUT_CTRL_EN;
+
+		ep_dmactrl = gr_read32(&ep->regs->dmactrl);
+		gr_write32(&ep->regs->dmactrl, ep_dmactrl | GR_DMACTRL_DA);
+	}
+
+	return 1;
+}
+
+/*
+ * Handle state changes. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static int gr_handle_state_changes(struct gr_udc *dev)
+{
+	u32 status = gr_read32(&dev->regs->status);
+	int handled = 0;
+	int powstate = !(dev->gadget.state == USB_STATE_NOTATTACHED ||
+			 dev->gadget.state == USB_STATE_ATTACHED);
+
+	/* VBUS valid detected */
+	if (!powstate && (status & GR_STATUS_VB)) {
+		dev_dbg(dev->dev, "STATUS: vbus valid detected\n");
+		gr_vbus_connected(dev, status);
+		handled = 1;
+	}
+
+	/* Disconnect */
+	if (powstate && !(status & GR_STATUS_VB)) {
+		dev_dbg(dev->dev, "STATUS: vbus invalid detected\n");
+		gr_vbus_disconnected(dev);
+		handled = 1;
+	}
+
+	/* USB reset detected */
+	if (status & GR_STATUS_UR) {
+		dev_dbg(dev->dev, "STATUS: USB reset - speed is %s\n",
+			GR_SPEED_STR(status));
+		gr_write32(&dev->regs->status, GR_STATUS_UR);
+		gr_udc_usbreset(dev, status);
+		handled = 1;
+	}
+
+	/* Speed change */
+	if (dev->gadget.speed != GR_SPEED(status)) {
+		dev_dbg(dev->dev, "STATUS: USB Speed change to %s\n",
+			GR_SPEED_STR(status));
+		dev->gadget.speed = GR_SPEED(status);
+		handled = 1;
+	}
+
+	/* Going into suspend */
+	if ((dev->ep0state != GR_EP0_SUSPEND) && !(status & GR_STATUS_SU)) {
+		dev_dbg(dev->dev, "STATUS: USB suspend\n");
+		gr_set_ep0state(dev, GR_EP0_SUSPEND);
+		dev->suspended_from = dev->gadget.state;
+		usb_gadget_set_state(&dev->gadget, USB_STATE_SUSPENDED);
+
+		if ((dev->gadget.speed != USB_SPEED_UNKNOWN) &&
+		    dev->driver && dev->driver->suspend) {
+			spin_unlock(&dev->lock);
+
+			dev->driver->suspend(&dev->gadget);
+
+			spin_lock(&dev->lock);
+		}
+		handled = 1;
+	}
+
+	/* Coming out of suspend */
+	if ((dev->ep0state == GR_EP0_SUSPEND) && (status & GR_STATUS_SU)) {
+		dev_dbg(dev->dev, "STATUS: USB resume\n");
+		if (dev->suspended_from == USB_STATE_POWERED)
+			gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+		else
+			gr_set_ep0state(dev, GR_EP0_SETUP);
+		usb_gadget_set_state(&dev->gadget, dev->suspended_from);
+
+		if ((dev->gadget.speed != USB_SPEED_UNKNOWN) &&
+		    dev->driver && dev->driver->resume) {
+			spin_unlock(&dev->lock);
+
+			dev->driver->resume(&dev->gadget);
+
+			spin_lock(&dev->lock);
+		}
+		handled = 1;
+	}
+
+	return handled;
+}
+
+/* Non-interrupt context irq handler */
+static irqreturn_t gr_irq_handler(int irq, void *_dev)
+{
+	struct gr_udc *dev = _dev;
+	struct gr_ep *ep;
+	int handled = 0;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!dev->irq_enabled)
+		goto out;
+
+	/*
+	 * Check IN ep interrupts. We check these before the OUT eps because
+	 * some gadgets reuse the request that might already be currently
+	 * outstanding and needs to be completed (mainly setup requests).
+	 */
+	for (i = 0; i < dev->nepi; i++) {
+		ep = &dev->epi[i];
+		if (!ep->stopped && !ep->callback && !list_empty(&ep->queue))
+			handled = gr_handle_in_ep(ep) || handled;
+	}
+
+	/* Check OUT ep interrupts */
+	for (i = 0; i < dev->nepo; i++) {
+		ep = &dev->epo[i];
+		if (!ep->stopped && !ep->callback && !list_empty(&ep->queue))
+			handled = gr_handle_out_ep(ep) || handled;
+	}
+
+	/* Check status interrupts */
+	handled = gr_handle_state_changes(dev) || handled;
+
+	/*
+	 * Check AMBA DMA errors. Only check if we didn't find anything else to
+	 * handle because this shouldn't happen if we did everything right.
+	 */
+	if (!handled) {
+		list_for_each_entry(ep, &dev->ep_list, ep_list) {
+			if (gr_read32(&ep->regs->dmactrl) & GR_DMACTRL_AE) {
+				dev_err(dev->dev,
+					"AMBA Error occurred for %s\n",
+					ep->ep.name);
+				handled = 1;
+			}
+		}
+	}
+
+out:
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* Interrupt context irq handler */
+static irqreturn_t gr_irq(int irq, void *_dev)
+{
+	struct gr_udc *dev = _dev;
+
+	if (!dev->irq_enabled)
+		return IRQ_NONE;
+
+	return IRQ_WAKE_THREAD;
+}
+
+/* ---------------------------------------------------------------------- */
+/* USB ep ops */
+
+/* Enable endpoint. Not for ep0in and ep0out that are handled separately. */
+static int gr_ep_enable(struct usb_ep *_ep,
+			const struct usb_endpoint_descriptor *desc)
+{
+	struct gr_udc *dev;
+	struct gr_ep *ep;
+	u8 mode;
+	u8 nt;
+	u16 max;
+	u16 buffer_size = 0;
+	u32 epctrl;
+
+	ep = container_of(_ep, struct gr_ep, ep);
+	if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+
+	dev = ep->dev;
+
+	/* 'ep0' IN and OUT are reserved */
+	if (ep == &dev->epo[0] || ep == &dev->epi[0])
+		return -EINVAL;
+
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* Make sure we are clear for enabling */
+	epctrl = gr_read32(&ep->regs->epctrl);
+	if (epctrl & GR_EPCTRL_EV)
+		return -EBUSY;
+
+	/* Check that directions match */
+	if (!ep->is_in != !usb_endpoint_dir_in(desc))
+		return -EINVAL;
+
+	/* Check ep num */
+	if ((!ep->is_in && ep->num >= dev->nepo) ||
+	    (ep->is_in && ep->num >= dev->nepi))
+		return -EINVAL;
+
+	if (usb_endpoint_xfer_control(desc)) {
+		mode = 0;
+	} else if (usb_endpoint_xfer_isoc(desc)) {
+		mode = 1;
+	} else if (usb_endpoint_xfer_bulk(desc)) {
+		mode = 2;
+	} else if (usb_endpoint_xfer_int(desc)) {
+		mode = 3;
+	} else {
+		dev_err(dev->dev, "Unknown transfer type for %s\n",
+			ep->ep.name);
+		return -EINVAL;
+	}
+
+	/*
+	 * Bits 10-0 set the max payload. 12-11 set the number of
+	 * additional transactions.
+	 */
+	max = usb_endpoint_maxp(desc);
+	nt = usb_endpoint_maxp_mult(desc) - 1;
+	buffer_size = GR_BUFFER_SIZE(epctrl);
+	if (nt && (mode == 0 || mode == 2)) {
+		dev_err(dev->dev,
+			"%s mode: multiple trans./microframe not valid\n",
+			(mode == 2 ? "Bulk" : "Control"));
+		return -EINVAL;
+	} else if (nt == 0x3) {
+		dev_err(dev->dev,
+			"Invalid value 0x3 for additional trans./microframe\n");
+		return -EINVAL;
+	} else if ((nt + 1) * max > buffer_size) {
+		dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n",
+			buffer_size, (nt + 1), max);
+		return -EINVAL;
+	} else if (max == 0) {
+		dev_err(dev->dev, "Max payload cannot be set to 0\n");
+		return -EINVAL;
+	} else if (max > ep->ep.maxpacket_limit) {
+		dev_err(dev->dev, "Requested max payload %d > limit %d\n",
+			max, ep->ep.maxpacket_limit);
+		return -EINVAL;
+	}
+
+	spin_lock(&ep->dev->lock);
+
+	if (!ep->stopped) {
+		spin_unlock(&ep->dev->lock);
+		return -EBUSY;
+	}
+
+	ep->stopped = 0;
+	ep->wedged = 0;
+	ep->ep.desc = desc;
+	ep->ep.maxpacket = max;
+	ep->dma_start = 0;
+
+
+	if (nt) {
+		/*
+		 * Maximum possible size of all payloads in one microframe
+		 * regardless of direction when using high-bandwidth mode.
+		 */
+		ep->bytes_per_buffer = (nt + 1) * max;
+	} else if (ep->is_in) {
+		/*
+		 * The biggest multiple of maximum packet size that fits into
+		 * the buffer. The hardware will split up into many packets in
+		 * the IN direction.
+		 */
+		ep->bytes_per_buffer = (buffer_size / max) * max;
+	} else {
+		/*
+		 * Only single packets will be placed the buffers in the OUT
+		 * direction.
+		 */
+		ep->bytes_per_buffer = max;
+	}
+
+	epctrl = (max << GR_EPCTRL_MAXPL_POS)
+		| (nt << GR_EPCTRL_NT_POS)
+		| (mode << GR_EPCTRL_TT_POS)
+		| GR_EPCTRL_EV;
+	if (ep->is_in)
+		epctrl |= GR_EPCTRL_PI;
+	gr_write32(&ep->regs->epctrl, epctrl);
+
+	gr_write32(&ep->regs->dmactrl, GR_DMACTRL_IE | GR_DMACTRL_AI);
+
+	spin_unlock(&ep->dev->lock);
+
+	dev_dbg(ep->dev->dev, "EP: %s enabled - %s with %d bytes/buffer\n",
+		ep->ep.name, gr_modestring[mode], ep->bytes_per_buffer);
+	return 0;
+}
+
+/* Disable endpoint. Not for ep0in and ep0out that are handled separately. */
+static int gr_ep_disable(struct usb_ep *_ep)
+{
+	struct gr_ep *ep;
+	struct gr_udc *dev;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct gr_ep, ep);
+	if (!_ep || !ep->ep.desc)
+		return -ENODEV;
+
+	dev = ep->dev;
+
+	/* 'ep0' IN and OUT are reserved */
+	if (ep == &dev->epo[0] || ep == &dev->epi[0])
+		return -EINVAL;
+
+	if (dev->ep0state == GR_EP0_SUSPEND)
+		return -EBUSY;
+
+	dev_dbg(ep->dev->dev, "EP: disable %s\n", ep->ep.name);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	gr_ep_nuke(ep);
+	gr_ep_reset(ep);
+	ep->ep.desc = NULL;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/*
+ * Frees a request, but not any DMA buffers associated with it
+ * (gr_finish_request should already have taken care of that).
+ */
+static void gr_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct gr_request *req;
+
+	if (!_ep || !_req)
+		return;
+	req = container_of(_req, struct gr_request, req);
+
+	/* Leads to memory leak */
+	WARN(!list_empty(&req->queue),
+	     "request not dequeued properly before freeing\n");
+
+	kfree(req);
+}
+
+/* Queue a request from the gadget */
+static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct gr_ep *ep;
+	struct gr_request *req;
+	struct gr_udc *dev;
+	int ret;
+
+	if (unlikely(!_ep || !_req))
+		return -EINVAL;
+
+	ep = container_of(_ep, struct gr_ep, ep);
+	req = container_of(_req, struct gr_request, req);
+	dev = ep->dev;
+
+	spin_lock(&ep->dev->lock);
+
+	/*
+	 * The ep0 pointer in the gadget struct is used both for ep0in and
+	 * ep0out. In a data stage in the out direction ep0out needs to be used
+	 * instead of the default ep0in. Completion functions might use
+	 * driver_data, so that needs to be copied as well.
+	 */
+	if ((ep == &dev->epi[0]) && (dev->ep0state == GR_EP0_ODATA)) {
+		ep = &dev->epo[0];
+		ep->ep.driver_data = dev->epi[0].ep.driver_data;
+	}
+
+	if (ep->is_in)
+		gr_dbgprint_request("EXTERN", ep, req);
+
+	ret = gr_queue(ep, req, GFP_ATOMIC);
+
+	spin_unlock(&ep->dev->lock);
+
+	return ret;
+}
+
+/* Dequeue JUST ONE request */
+static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct gr_request *req;
+	struct gr_ep *ep;
+	struct gr_udc *dev;
+	int ret = 0;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct gr_ep, ep);
+	if (!_ep || !_req || (!ep->ep.desc && ep->num != 0))
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver)
+		return -ESHUTDOWN;
+
+	/* We can't touch (DMA) registers when suspended */
+	if (dev->ep0state == GR_EP0_SUSPEND)
+		return -EBUSY;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* Make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (list_first_entry(&ep->queue, struct gr_request, queue) == req) {
+		/* This request is currently being processed */
+		gr_abort_dma(ep);
+		if (ep->stopped)
+			gr_finish_request(ep, req, -ECONNRESET);
+		else
+			gr_dma_advance(ep, -ECONNRESET);
+	} else if (!list_empty(&req->queue)) {
+		/* Not being processed - gr_finish_request dequeues it */
+		gr_finish_request(ep, req, -ECONNRESET);
+	} else {
+		ret = -EOPNOTSUPP;
+	}
+
+out:
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return ret;
+}
+
+/* Helper for gr_set_halt and gr_set_wedge */
+static int gr_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+	int ret;
+	struct gr_ep *ep;
+
+	if (!_ep)
+		return -ENODEV;
+	ep = container_of(_ep, struct gr_ep, ep);
+
+	spin_lock(&ep->dev->lock);
+
+	/* Halting an IN endpoint should fail if queue is not empty */
+	if (halt && ep->is_in && !list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = gr_ep_halt_wedge(ep, halt, wedge, 0);
+
+out:
+	spin_unlock(&ep->dev->lock);
+
+	return ret;
+}
+
+/* Halt endpoint */
+static int gr_set_halt(struct usb_ep *_ep, int halt)
+{
+	return gr_set_halt_wedge(_ep, halt, 0);
+}
+
+/* Halt and wedge endpoint */
+static int gr_set_wedge(struct usb_ep *_ep)
+{
+	return gr_set_halt_wedge(_ep, 1, 1);
+}
+
+/*
+ * Return the total number of bytes currently stored in the internal buffers of
+ * the endpoint.
+ */
+static int gr_fifo_status(struct usb_ep *_ep)
+{
+	struct gr_ep *ep;
+	u32 epstat;
+	u32 bytes = 0;
+
+	if (!_ep)
+		return -ENODEV;
+	ep = container_of(_ep, struct gr_ep, ep);
+
+	epstat = gr_read32(&ep->regs->epstat);
+
+	if (epstat & GR_EPSTAT_B0)
+		bytes += (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS;
+	if (epstat & GR_EPSTAT_B1)
+		bytes += (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS;
+
+	return bytes;
+}
+
+
+/* Empty data from internal buffers of an endpoint. */
+static void gr_fifo_flush(struct usb_ep *_ep)
+{
+	struct gr_ep *ep;
+	u32 epctrl;
+
+	if (!_ep)
+		return;
+	ep = container_of(_ep, struct gr_ep, ep);
+	dev_vdbg(ep->dev->dev, "EP: flush fifo %s\n", ep->ep.name);
+
+	spin_lock(&ep->dev->lock);
+
+	epctrl = gr_read32(&ep->regs->epctrl);
+	epctrl |= GR_EPCTRL_CB;
+	gr_write32(&ep->regs->epctrl, epctrl);
+
+	spin_unlock(&ep->dev->lock);
+}
+
+static const struct usb_ep_ops gr_ep_ops = {
+	.enable		= gr_ep_enable,
+	.disable	= gr_ep_disable,
+
+	.alloc_request	= gr_alloc_request,
+	.free_request	= gr_free_request,
+
+	.queue		= gr_queue_ext,
+	.dequeue	= gr_dequeue,
+
+	.set_halt	= gr_set_halt,
+	.set_wedge	= gr_set_wedge,
+	.fifo_status	= gr_fifo_status,
+	.fifo_flush	= gr_fifo_flush,
+};
+
+/* ---------------------------------------------------------------------- */
+/* USB Gadget ops */
+
+static int gr_get_frame(struct usb_gadget *_gadget)
+{
+	struct gr_udc *dev;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct gr_udc, gadget);
+	return gr_read32(&dev->regs->status) & GR_STATUS_FN_MASK;
+}
+
+static int gr_wakeup(struct usb_gadget *_gadget)
+{
+	struct gr_udc *dev;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct gr_udc, gadget);
+
+	/* Remote wakeup feature not enabled by host*/
+	if (!dev->remote_wakeup)
+		return -EINVAL;
+
+	spin_lock(&dev->lock);
+
+	gr_write32(&dev->regs->control,
+		   gr_read32(&dev->regs->control) | GR_CONTROL_RW);
+
+	spin_unlock(&dev->lock);
+
+	return 0;
+}
+
+static int gr_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	struct gr_udc *dev;
+	u32 control;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct gr_udc, gadget);
+
+	spin_lock(&dev->lock);
+
+	control = gr_read32(&dev->regs->control);
+	if (is_on)
+		control |= GR_CONTROL_EP;
+	else
+		control &= ~GR_CONTROL_EP;
+	gr_write32(&dev->regs->control, control);
+
+	spin_unlock(&dev->lock);
+
+	return 0;
+}
+
+static int gr_udc_start(struct usb_gadget *gadget,
+			struct usb_gadget_driver *driver)
+{
+	struct gr_udc *dev = to_gr_udc(gadget);
+
+	spin_lock(&dev->lock);
+
+	/* Hook up the driver */
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+
+	/* Get ready for host detection */
+	gr_enable_vbus_detect(dev);
+
+	spin_unlock(&dev->lock);
+
+	return 0;
+}
+
+static int gr_udc_stop(struct usb_gadget *gadget)
+{
+	struct gr_udc *dev = to_gr_udc(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->driver = NULL;
+	gr_stop_activity(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops gr_ops = {
+	.get_frame	= gr_get_frame,
+	.wakeup         = gr_wakeup,
+	.pullup         = gr_pullup,
+	.udc_start	= gr_udc_start,
+	.udc_stop	= gr_udc_stop,
+	/* Other operations not supported */
+};
+
+/* ---------------------------------------------------------------------- */
+/* Module probe, removal and of-matching */
+
+static const char * const onames[] = {
+	"ep0out", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out",
+	"ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out",
+	"ep12out", "ep13out", "ep14out", "ep15out"
+};
+
+static const char * const inames[] = {
+	"ep0in", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in",
+	"ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in",
+	"ep12in", "ep13in", "ep14in", "ep15in"
+};
+
+/* Must be called with dev->lock held */
+static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
+{
+	struct gr_ep *ep;
+	struct gr_request *req;
+	struct usb_request *_req;
+	void *buf;
+
+	if (is_in) {
+		ep = &dev->epi[num];
+		ep->ep.name = inames[num];
+		ep->regs = &dev->regs->epi[num];
+	} else {
+		ep = &dev->epo[num];
+		ep->ep.name = onames[num];
+		ep->regs = &dev->regs->epo[num];
+	}
+
+	gr_ep_reset(ep);
+	ep->num = num;
+	ep->is_in = is_in;
+	ep->dev = dev;
+	ep->ep.ops = &gr_ep_ops;
+	INIT_LIST_HEAD(&ep->queue);
+
+	if (num == 0) {
+		_req = gr_alloc_request(&ep->ep, GFP_ATOMIC);
+		buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_ATOMIC);
+		if (!_req || !buf) {
+			/* possible _req freed by gr_probe via gr_remove */
+			return -ENOMEM;
+		}
+
+		req = container_of(_req, struct gr_request, req);
+		req->req.buf = buf;
+		req->req.length = MAX_CTRL_PL_SIZE;
+
+		if (is_in)
+			dev->ep0reqi = req; /* Complete gets set as used */
+		else
+			dev->ep0reqo = req; /* Completion treated separately */
+
+		usb_ep_set_maxpacket_limit(&ep->ep, MAX_CTRL_PL_SIZE);
+		ep->bytes_per_buffer = MAX_CTRL_PL_SIZE;
+
+		ep->ep.caps.type_control = true;
+	} else {
+		usb_ep_set_maxpacket_limit(&ep->ep, (u16)maxplimit);
+		list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->ep.caps.type_iso = true;
+		ep->ep.caps.type_bulk = true;
+		ep->ep.caps.type_int = true;
+	}
+	list_add_tail(&ep->ep_list, &dev->ep_list);
+
+	if (is_in)
+		ep->ep.caps.dir_in = true;
+	else
+		ep->ep.caps.dir_out = true;
+
+	ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit,
+					 &ep->tailbuf_paddr, GFP_ATOMIC);
+	if (!ep->tailbuf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/* Must be called with dev->lock held */
+static int gr_udc_init(struct gr_udc *dev)
+{
+	struct device_node *np = dev->dev->of_node;
+	u32 epctrl_val;
+	u32 dmactrl_val;
+	int i;
+	int ret = 0;
+	u32 bufsize;
+
+	gr_set_address(dev, 0);
+
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->gadget.ep0 = &dev->epi[0].ep;
+
+	INIT_LIST_HEAD(&dev->ep_list);
+	gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+
+	for (i = 0; i < dev->nepo; i++) {
+		if (of_property_read_u32_index(np, "epobufsizes", i, &bufsize))
+			bufsize = 1024;
+		ret = gr_ep_init(dev, i, 0, bufsize);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < dev->nepi; i++) {
+		if (of_property_read_u32_index(np, "epibufsizes", i, &bufsize))
+			bufsize = 1024;
+		ret = gr_ep_init(dev, i, 1, bufsize);
+		if (ret)
+			return ret;
+	}
+
+	/* Must be disabled by default */
+	dev->remote_wakeup = 0;
+
+	/* Enable ep0out and ep0in */
+	epctrl_val = (MAX_CTRL_PL_SIZE << GR_EPCTRL_MAXPL_POS) | GR_EPCTRL_EV;
+	dmactrl_val = GR_DMACTRL_IE | GR_DMACTRL_AI;
+	gr_write32(&dev->epo[0].regs->epctrl, epctrl_val);
+	gr_write32(&dev->epi[0].regs->epctrl, epctrl_val | GR_EPCTRL_PI);
+	gr_write32(&dev->epo[0].regs->dmactrl, dmactrl_val);
+	gr_write32(&dev->epi[0].regs->dmactrl, dmactrl_val);
+
+	return 0;
+}
+
+static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
+{
+	struct gr_ep *ep;
+
+	if (is_in)
+		ep = &dev->epi[num];
+	else
+		ep = &dev->epo[num];
+
+	if (ep->tailbuf)
+		dma_free_coherent(dev->dev, ep->ep.maxpacket_limit,
+				  ep->tailbuf, ep->tailbuf_paddr);
+}
+
+static int gr_remove(struct platform_device *pdev)
+{
+	struct gr_udc *dev = platform_get_drvdata(pdev);
+	int i;
+
+	if (dev->added)
+		usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
+	if (dev->driver)
+		return -EBUSY;
+
+	gr_dfs_delete(dev);
+	dma_pool_destroy(dev->desc_pool);
+	platform_set_drvdata(pdev, NULL);
+
+	gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
+	gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
+
+	for (i = 0; i < dev->nepo; i++)
+		gr_ep_remove(dev, i, 0);
+	for (i = 0; i < dev->nepi; i++)
+		gr_ep_remove(dev, i, 1);
+
+	return 0;
+}
+static int gr_request_irq(struct gr_udc *dev, int irq)
+{
+	return devm_request_threaded_irq(dev->dev, irq, gr_irq, gr_irq_handler,
+					 IRQF_SHARED, driver_name, dev);
+}
+
+static int gr_probe(struct platform_device *pdev)
+{
+	struct gr_udc *dev;
+	struct resource *res;
+	struct gr_regs __iomem *regs;
+	int retval;
+	u32 status;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev->dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	dev->irq = platform_get_irq(pdev, 0);
+	if (dev->irq <= 0) {
+		dev_err(dev->dev, "No irq found\n");
+		return -ENODEV;
+	}
+
+	/* Some core configurations has separate irqs for IN and OUT events */
+	dev->irqi = platform_get_irq(pdev, 1);
+	if (dev->irqi > 0) {
+		dev->irqo = platform_get_irq(pdev, 2);
+		if (dev->irqo <= 0) {
+			dev_err(dev->dev, "Found irqi but not irqo\n");
+			return -ENODEV;
+		}
+	} else {
+		dev->irqi = 0;
+	}
+
+	dev->gadget.name = driver_name;
+	dev->gadget.max_speed = USB_SPEED_HIGH;
+	dev->gadget.ops = &gr_ops;
+
+	spin_lock_init(&dev->lock);
+	dev->regs = regs;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* Determine number of endpoints and data interface mode */
+	status = gr_read32(&dev->regs->status);
+	dev->nepi = ((status & GR_STATUS_NEPI_MASK) >> GR_STATUS_NEPI_POS) + 1;
+	dev->nepo = ((status & GR_STATUS_NEPO_MASK) >> GR_STATUS_NEPO_POS) + 1;
+
+	if (!(status & GR_STATUS_DM)) {
+		dev_err(dev->dev, "Slave mode cores are not supported\n");
+		return -ENODEV;
+	}
+
+	/* --- Effects of the following calls might need explicit cleanup --- */
+
+	/* Create DMA pool for descriptors */
+	dev->desc_pool = dma_pool_create("desc_pool", dev->dev,
+					 sizeof(struct gr_dma_desc), 4, 0);
+	if (!dev->desc_pool) {
+		dev_err(dev->dev, "Could not allocate DMA pool");
+		return -ENOMEM;
+	}
+
+	spin_lock(&dev->lock);
+
+	/* Inside lock so that no gadget can use this udc until probe is done */
+	retval = usb_add_gadget_udc(dev->dev, &dev->gadget);
+	if (retval) {
+		dev_err(dev->dev, "Could not add gadget udc");
+		goto out;
+	}
+	dev->added = 1;
+
+	retval = gr_udc_init(dev);
+	if (retval)
+		goto out;
+
+	gr_dfs_create(dev);
+
+	/* Clear all interrupt enables that might be left on since last boot */
+	gr_disable_interrupts_and_pullup(dev);
+
+	retval = gr_request_irq(dev, dev->irq);
+	if (retval) {
+		dev_err(dev->dev, "Failed to request irq %d\n", dev->irq);
+		goto out;
+	}
+
+	if (dev->irqi) {
+		retval = gr_request_irq(dev, dev->irqi);
+		if (retval) {
+			dev_err(dev->dev, "Failed to request irqi %d\n",
+				dev->irqi);
+			goto out;
+		}
+		retval = gr_request_irq(dev, dev->irqo);
+		if (retval) {
+			dev_err(dev->dev, "Failed to request irqo %d\n",
+				dev->irqo);
+			goto out;
+		}
+	}
+
+	if (dev->irqi)
+		dev_info(dev->dev, "regs: %p, irqs %d, %d, %d\n", dev->regs,
+			 dev->irq, dev->irqi, dev->irqo);
+	else
+		dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq);
+
+out:
+	spin_unlock(&dev->lock);
+
+	if (retval)
+		gr_remove(pdev);
+
+	return retval;
+}
+
+static const struct of_device_id gr_match[] = {
+	{.name = "GAISLER_USBDC"},
+	{.name = "01_021"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, gr_match);
+
+static struct platform_driver gr_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = gr_match,
+	},
+	.probe = gr_probe,
+	.remove = gr_remove,
+};
+module_platform_driver(gr_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
new file mode 100644
index 0000000..417ad2a
--- /dev/null
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports GRUSBDC USB Device Controller cores available in the
+ * GRLIB VHDL IP core library.
+ *
+ * Full documentation of the GRUSBDC core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * Contributors:
+ * - Andreas Larsson <andreas@gaisler.com>
+ * - Marko Isomaki
+ */
+
+/* Control registers on the AMBA bus */
+
+#define GR_MAXEP	16	/* Max # endpoints for *each* direction */
+
+struct gr_epregs {
+	u32 epctrl;
+	union {
+		struct { /* Slave mode*/
+			u32 slvctrl;
+			u32 slvdata;
+		};
+		struct { /* DMA mode*/
+			u32 dmactrl;
+			u32 dmaaddr;
+		};
+	};
+	u32 epstat;
+};
+
+struct gr_regs {
+	struct gr_epregs	epo[GR_MAXEP];	/* 0x000 - 0x0fc */
+	struct gr_epregs	epi[GR_MAXEP];	/* 0x100 - 0x1fc */
+	u32			control;	/* 0x200 */
+	u32			status;		/* 0x204 */
+};
+
+#define GR_EPCTRL_BUFSZ_SCALER	8
+#define GR_EPCTRL_BUFSZ_MASK	0xffe00000
+#define GR_EPCTRL_BUFSZ_POS	21
+#define GR_EPCTRL_PI		BIT(20)
+#define GR_EPCTRL_CB		BIT(19)
+#define GR_EPCTRL_CS		BIT(18)
+#define GR_EPCTRL_MAXPL_MASK	0x0003ff80
+#define GR_EPCTRL_MAXPL_POS	7
+#define GR_EPCTRL_NT_MASK	0x00000060
+#define GR_EPCTRL_NT_POS	5
+#define GR_EPCTRL_TT_MASK	0x00000018
+#define GR_EPCTRL_TT_POS	3
+#define GR_EPCTRL_EH		BIT(2)
+#define GR_EPCTRL_ED		BIT(1)
+#define GR_EPCTRL_EV		BIT(0)
+
+#define GR_DMACTRL_AE		BIT(10)
+#define GR_DMACTRL_AD		BIT(3)
+#define GR_DMACTRL_AI		BIT(2)
+#define GR_DMACTRL_IE		BIT(1)
+#define GR_DMACTRL_DA		BIT(0)
+
+#define GR_EPSTAT_PT		BIT(29)
+#define GR_EPSTAT_PR		BIT(29)
+#define GR_EPSTAT_B1CNT_MASK	0x1fff0000
+#define GR_EPSTAT_B1CNT_POS	16
+#define GR_EPSTAT_B0CNT_MASK	0x0000fff8
+#define GR_EPSTAT_B0CNT_POS	3
+#define GR_EPSTAT_B1		BIT(2)
+#define GR_EPSTAT_B0		BIT(1)
+#define GR_EPSTAT_BS		BIT(0)
+
+#define GR_CONTROL_SI		BIT(31)
+#define GR_CONTROL_UI		BIT(30)
+#define GR_CONTROL_VI		BIT(29)
+#define GR_CONTROL_SP		BIT(28)
+#define GR_CONTROL_FI		BIT(27)
+#define GR_CONTROL_EP		BIT(14)
+#define GR_CONTROL_DH		BIT(13)
+#define GR_CONTROL_RW		BIT(12)
+#define GR_CONTROL_TS_MASK	0x00000e00
+#define GR_CONTROL_TS_POS	9
+#define GR_CONTROL_TM		BIT(8)
+#define GR_CONTROL_UA_MASK	0x000000fe
+#define GR_CONTROL_UA_POS	1
+#define GR_CONTROL_SU		BIT(0)
+
+#define GR_STATUS_NEPI_MASK	0xf0000000
+#define GR_STATUS_NEPI_POS	28
+#define GR_STATUS_NEPO_MASK	0x0f000000
+#define GR_STATUS_NEPO_POS	24
+#define GR_STATUS_DM		BIT(23)
+#define GR_STATUS_SU		BIT(17)
+#define GR_STATUS_UR		BIT(16)
+#define GR_STATUS_VB		BIT(15)
+#define GR_STATUS_SP		BIT(14)
+#define GR_STATUS_AF_MASK	0x00003800
+#define GR_STATUS_AF_POS	11
+#define GR_STATUS_FN_MASK	0x000007ff
+#define GR_STATUS_FN_POS	0
+
+
+#define MAX_CTRL_PL_SIZE 64 /* As per USB standard for full and high speed */
+
+/*-------------------------------------------------------------------------*/
+
+/* Driver data structures and utilities */
+
+struct gr_dma_desc {
+	u32 ctrl;
+	u32 data;
+	u32 next;
+
+	/* These must be last because hw uses the previous three */
+	u32 paddr;
+	struct gr_dma_desc *next_desc;
+};
+
+#define GR_DESC_OUT_CTRL_SE		BIT(17)
+#define GR_DESC_OUT_CTRL_IE		BIT(15)
+#define GR_DESC_OUT_CTRL_NX		BIT(14)
+#define GR_DESC_OUT_CTRL_EN		BIT(13)
+#define GR_DESC_OUT_CTRL_LEN_MASK	0x00001fff
+
+#define GR_DESC_IN_CTRL_MO		BIT(18)
+#define GR_DESC_IN_CTRL_PI		BIT(17)
+#define GR_DESC_IN_CTRL_ML		BIT(16)
+#define GR_DESC_IN_CTRL_IE		BIT(15)
+#define GR_DESC_IN_CTRL_NX		BIT(14)
+#define GR_DESC_IN_CTRL_EN		BIT(13)
+#define GR_DESC_IN_CTRL_LEN_MASK	0x00001fff
+
+#define GR_DESC_DMAADDR_MASK		0xfffffffc
+
+struct gr_ep {
+	struct usb_ep ep;
+	struct gr_udc *dev;
+	u16 bytes_per_buffer;
+	unsigned int dma_start;
+	struct gr_epregs __iomem *regs;
+
+	unsigned num:8;
+	unsigned is_in:1;
+	unsigned stopped:1;
+	unsigned wedged:1;
+	unsigned callback:1;
+
+	/* analogous to a host-side qh */
+	struct list_head queue;
+
+	struct list_head ep_list;
+
+	/* Bounce buffer for end of "odd" sized OUT requests */
+	void *tailbuf;
+	dma_addr_t tailbuf_paddr;
+};
+
+struct gr_request {
+	struct usb_request req;
+	struct list_head queue;
+
+	/* Chain of dma descriptors */
+	struct gr_dma_desc *first_desc; /* First in the chain */
+	struct gr_dma_desc *curr_desc; /* Current descriptor */
+	struct gr_dma_desc *last_desc; /* Last in the chain */
+
+	u16 evenlen; /* Size of even length head (if oddlen != 0) */
+	u16 oddlen; /* Size of odd length tail if buffer length is "odd" */
+
+	u8 setup; /* Setup packet */
+};
+
+enum gr_ep0state {
+	GR_EP0_DISCONNECT = 0,	/* No host */
+	GR_EP0_SETUP,		/* Between STATUS ack and SETUP report */
+	GR_EP0_IDATA,		/* IN data stage */
+	GR_EP0_ODATA,		/* OUT data stage */
+	GR_EP0_ISTATUS,		/* Status stage after IN data stage */
+	GR_EP0_OSTATUS,		/* Status stage after OUT data stage */
+	GR_EP0_STALL,		/* Data or status stages */
+	GR_EP0_SUSPEND,		/* USB suspend */
+};
+
+struct gr_udc {
+	struct usb_gadget gadget;
+	struct gr_ep epi[GR_MAXEP];
+	struct gr_ep epo[GR_MAXEP];
+	struct usb_gadget_driver *driver;
+	struct dma_pool *desc_pool;
+	struct device *dev;
+
+	enum gr_ep0state ep0state;
+	struct gr_request *ep0reqo;
+	struct gr_request *ep0reqi;
+
+	struct gr_regs __iomem *regs;
+	int irq;
+	int irqi;
+	int irqo;
+
+	unsigned added:1;
+	unsigned irq_enabled:1;
+	unsigned remote_wakeup:1;
+
+	u8 test_mode;
+
+	enum usb_device_state suspended_from;
+
+	unsigned int nepi;
+	unsigned int nepo;
+
+	struct list_head ep_list;
+
+	spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */
+
+	struct dentry *dfs_root;
+};
+
+#define to_gr_udc(gadget)	(container_of((gadget), struct gr_udc, gadget))
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
new file mode 100644
index 0000000..b078177
--- /dev/null
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -0,0 +1,3328 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB Gadget driver for LPC32xx
+ *
+ * Authors:
+ *    Kevin Wells <kevin.wells@nxp.com>
+ *    Mike James
+ *    Roland Stigge <stigge@antcom.de>
+ *
+ * Copyright (C) 2006 Philips Semiconductors
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2012 Roland Stigge
+ *
+ * Note: This driver is based on original work done by Mike James for
+ *       the LPC3180.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/isp1301.h>
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#endif
+
+#include <mach/hardware.h>
+
+/*
+ * USB device configuration structure
+ */
+typedef void (*usc_chg_event)(int);
+struct lpc32xx_usbd_cfg {
+	int vbus_drv_pol;   /* 0=active low drive for VBUS via ISP1301 */
+	usc_chg_event conn_chgb; /* Connection change event (optional) */
+	usc_chg_event susp_chgb; /* Suspend/resume event (optional) */
+	usc_chg_event rmwk_chgb; /* Enable/disable remote wakeup */
+};
+
+/*
+ * controller driver data structures
+ */
+
+/* 16 endpoints (not to be confused with 32 hardware endpoints) */
+#define	NUM_ENDPOINTS	16
+
+/*
+ * IRQ indices make reading the code a little easier
+ */
+#define IRQ_USB_LP	0
+#define IRQ_USB_HP	1
+#define IRQ_USB_DEVDMA	2
+#define IRQ_USB_ATX	3
+
+#define EP_OUT 0 /* RX (from host) */
+#define EP_IN 1 /* TX (to host) */
+
+/* Returns the interrupt mask for the selected hardware endpoint */
+#define EP_MASK_SEL(ep, dir) (1 << (((ep) * 2) + dir))
+
+#define EP_INT_TYPE 0
+#define EP_ISO_TYPE 1
+#define EP_BLK_TYPE 2
+#define EP_CTL_TYPE 3
+
+/* EP0 states */
+#define WAIT_FOR_SETUP 0 /* Wait for setup packet */
+#define DATA_IN        1 /* Expect dev->host transfer */
+#define DATA_OUT       2 /* Expect host->dev transfer */
+
+/* DD (DMA Descriptor) structure, requires word alignment, this is already
+ * defined in the LPC32XX USB device header file, but this version is slightly
+ * modified to tag some work data with each DMA descriptor. */
+struct lpc32xx_usbd_dd_gad {
+	u32 dd_next_phy;
+	u32 dd_setup;
+	u32 dd_buffer_addr;
+	u32 dd_status;
+	u32 dd_iso_ps_mem_addr;
+	u32 this_dma;
+	u32 iso_status[6]; /* 5 spare */
+	u32 dd_next_v;
+};
+
+/*
+ * Logical endpoint structure
+ */
+struct lpc32xx_ep {
+	struct usb_ep		ep;
+	struct list_head	queue;
+	struct lpc32xx_udc	*udc;
+
+	u32			hwep_num_base; /* Physical hardware EP */
+	u32			hwep_num; /* Maps to hardware endpoint */
+	u32			maxpacket;
+	u32			lep;
+
+	bool			is_in;
+	bool			req_pending;
+	u32			eptype;
+
+	u32                     totalints;
+
+	bool			wedge;
+};
+
+/*
+ * Common UDC structure
+ */
+struct lpc32xx_udc {
+	struct usb_gadget	gadget;
+	struct usb_gadget_driver *driver;
+	struct platform_device	*pdev;
+	struct device		*dev;
+	struct dentry		*pde;
+	spinlock_t		lock;
+	struct i2c_client	*isp1301_i2c_client;
+
+	/* Board and device specific */
+	struct lpc32xx_usbd_cfg	*board;
+	u32			io_p_start;
+	u32			io_p_size;
+	void __iomem		*udp_baseaddr;
+	int			udp_irq[4];
+	struct clk		*usb_slv_clk;
+
+	/* DMA support */
+	u32			*udca_v_base;
+	u32			udca_p_base;
+	struct dma_pool		*dd_cache;
+
+	/* Common EP and control data */
+	u32			enabled_devints;
+	u32			enabled_hwepints;
+	u32			dev_status;
+	u32			realized_eps;
+
+	/* VBUS detection, pullup, and power flags */
+	u8			vbus;
+	u8			last_vbus;
+	int			pullup;
+	int			poweron;
+
+	/* Work queues related to I2C support */
+	struct work_struct	pullup_job;
+	struct work_struct	vbus_job;
+	struct work_struct	power_job;
+
+	/* USB device peripheral - various */
+	struct lpc32xx_ep	ep[NUM_ENDPOINTS];
+	bool			enabled;
+	bool			clocked;
+	bool			suspended;
+	int                     ep0state;
+	atomic_t                enabled_ep_cnt;
+	wait_queue_head_t       ep_disable_wait_queue;
+};
+
+/*
+ * Endpoint request
+ */
+struct lpc32xx_request {
+	struct usb_request	req;
+	struct list_head	queue;
+	struct lpc32xx_usbd_dd_gad *dd_desc_ptr;
+	bool			mapped;
+	bool			send_zlp;
+};
+
+static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g)
+{
+	return container_of(g, struct lpc32xx_udc, gadget);
+}
+
+#define ep_dbg(epp, fmt, arg...) \
+	dev_dbg(epp->udc->dev, "%s: " fmt, __func__, ## arg)
+#define ep_err(epp, fmt, arg...) \
+	dev_err(epp->udc->dev, "%s: " fmt, __func__, ## arg)
+#define ep_info(epp, fmt, arg...) \
+	dev_info(epp->udc->dev, "%s: " fmt, __func__, ## arg)
+#define ep_warn(epp, fmt, arg...) \
+	dev_warn(epp->udc->dev, "%s:" fmt, __func__, ## arg)
+
+#define UDCA_BUFF_SIZE (128)
+
+/**********************************************************************
+ * USB device controller register offsets
+ **********************************************************************/
+
+#define USBD_DEVINTST(x)	((x) + 0x200)
+#define USBD_DEVINTEN(x)	((x) + 0x204)
+#define USBD_DEVINTCLR(x)	((x) + 0x208)
+#define USBD_DEVINTSET(x)	((x) + 0x20C)
+#define USBD_CMDCODE(x)		((x) + 0x210)
+#define USBD_CMDDATA(x)		((x) + 0x214)
+#define USBD_RXDATA(x)		((x) + 0x218)
+#define USBD_TXDATA(x)		((x) + 0x21C)
+#define USBD_RXPLEN(x)		((x) + 0x220)
+#define USBD_TXPLEN(x)		((x) + 0x224)
+#define USBD_CTRL(x)		((x) + 0x228)
+#define USBD_DEVINTPRI(x)	((x) + 0x22C)
+#define USBD_EPINTST(x)		((x) + 0x230)
+#define USBD_EPINTEN(x)		((x) + 0x234)
+#define USBD_EPINTCLR(x)	((x) + 0x238)
+#define USBD_EPINTSET(x)	((x) + 0x23C)
+#define USBD_EPINTPRI(x)	((x) + 0x240)
+#define USBD_REEP(x)		((x) + 0x244)
+#define USBD_EPIND(x)		((x) + 0x248)
+#define USBD_EPMAXPSIZE(x)	((x) + 0x24C)
+/* DMA support registers only below */
+/* Set, clear, or get enabled state of the DMA request status. If
+ * enabled, an IN or OUT token will start a DMA transfer for the EP */
+#define USBD_DMARST(x)		((x) + 0x250)
+#define USBD_DMARCLR(x)		((x) + 0x254)
+#define USBD_DMARSET(x)		((x) + 0x258)
+/* DMA UDCA head pointer */
+#define USBD_UDCAH(x)		((x) + 0x280)
+/* EP DMA status, enable, and disable. This is used to specifically
+ * enabled or disable DMA for a specific EP */
+#define USBD_EPDMAST(x)		((x) + 0x284)
+#define USBD_EPDMAEN(x)		((x) + 0x288)
+#define USBD_EPDMADIS(x)	((x) + 0x28C)
+/* DMA master interrupts enable and pending interrupts */
+#define USBD_DMAINTST(x)	((x) + 0x290)
+#define USBD_DMAINTEN(x)	((x) + 0x294)
+/* DMA end of transfer interrupt enable, disable, status */
+#define USBD_EOTINTST(x)	((x) + 0x2A0)
+#define USBD_EOTINTCLR(x)	((x) + 0x2A4)
+#define USBD_EOTINTSET(x)	((x) + 0x2A8)
+/* New DD request interrupt enable, disable, status */
+#define USBD_NDDRTINTST(x)	((x) + 0x2AC)
+#define USBD_NDDRTINTCLR(x)	((x) + 0x2B0)
+#define USBD_NDDRTINTSET(x)	((x) + 0x2B4)
+/* DMA error interrupt enable, disable, status */
+#define USBD_SYSERRTINTST(x)	((x) + 0x2B8)
+#define USBD_SYSERRTINTCLR(x)	((x) + 0x2BC)
+#define USBD_SYSERRTINTSET(x)	((x) + 0x2C0)
+
+/**********************************************************************
+ * USBD_DEVINTST/USBD_DEVINTEN/USBD_DEVINTCLR/USBD_DEVINTSET/
+ * USBD_DEVINTPRI register definitions
+ **********************************************************************/
+#define USBD_ERR_INT		(1 << 9)
+#define USBD_EP_RLZED		(1 << 8)
+#define USBD_TXENDPKT		(1 << 7)
+#define USBD_RXENDPKT		(1 << 6)
+#define USBD_CDFULL		(1 << 5)
+#define USBD_CCEMPTY		(1 << 4)
+#define USBD_DEV_STAT		(1 << 3)
+#define USBD_EP_SLOW		(1 << 2)
+#define USBD_EP_FAST		(1 << 1)
+#define USBD_FRAME		(1 << 0)
+
+/**********************************************************************
+ * USBD_EPINTST/USBD_EPINTEN/USBD_EPINTCLR/USBD_EPINTSET/
+ * USBD_EPINTPRI register definitions
+ **********************************************************************/
+/* End point selection macro (RX) */
+#define USBD_RX_EP_SEL(e)	(1 << ((e) << 1))
+
+/* End point selection macro (TX) */
+#define USBD_TX_EP_SEL(e)	(1 << (((e) << 1) + 1))
+
+/**********************************************************************
+ * USBD_REEP/USBD_DMARST/USBD_DMARCLR/USBD_DMARSET/USBD_EPDMAST/
+ * USBD_EPDMAEN/USBD_EPDMADIS/
+ * USBD_NDDRTINTST/USBD_NDDRTINTCLR/USBD_NDDRTINTSET/
+ * USBD_EOTINTST/USBD_EOTINTCLR/USBD_EOTINTSET/
+ * USBD_SYSERRTINTST/USBD_SYSERRTINTCLR/USBD_SYSERRTINTSET
+ * register definitions
+ **********************************************************************/
+/* Endpoint selection macro */
+#define USBD_EP_SEL(e)		(1 << (e))
+
+/**********************************************************************
+ * SBD_DMAINTST/USBD_DMAINTEN
+ **********************************************************************/
+#define USBD_SYS_ERR_INT	(1 << 2)
+#define USBD_NEW_DD_INT		(1 << 1)
+#define USBD_EOT_INT		(1 << 0)
+
+/**********************************************************************
+ * USBD_RXPLEN register definitions
+ **********************************************************************/
+#define USBD_PKT_RDY		(1 << 11)
+#define USBD_DV			(1 << 10)
+#define USBD_PK_LEN_MASK	0x3FF
+
+/**********************************************************************
+ * USBD_CTRL register definitions
+ **********************************************************************/
+#define USBD_LOG_ENDPOINT(e)	((e) << 2)
+#define USBD_WR_EN		(1 << 1)
+#define USBD_RD_EN		(1 << 0)
+
+/**********************************************************************
+ * USBD_CMDCODE register definitions
+ **********************************************************************/
+#define USBD_CMD_CODE(c)	((c) << 16)
+#define USBD_CMD_PHASE(p)	((p) << 8)
+
+/**********************************************************************
+ * USBD_DMARST/USBD_DMARCLR/USBD_DMARSET register definitions
+ **********************************************************************/
+#define USBD_DMAEP(e)		(1 << (e))
+
+/* DD (DMA Descriptor) structure, requires word alignment */
+struct lpc32xx_usbd_dd {
+	u32 *dd_next;
+	u32 dd_setup;
+	u32 dd_buffer_addr;
+	u32 dd_status;
+	u32 dd_iso_ps_mem_addr;
+};
+
+/* dd_setup bit defines */
+#define DD_SETUP_ATLE_DMA_MODE	0x01
+#define DD_SETUP_NEXT_DD_VALID	0x04
+#define DD_SETUP_ISO_EP		0x10
+#define DD_SETUP_PACKETLEN(n)	(((n) & 0x7FF) << 5)
+#define DD_SETUP_DMALENBYTES(n)	(((n) & 0xFFFF) << 16)
+
+/* dd_status bit defines */
+#define DD_STATUS_DD_RETIRED	0x01
+#define DD_STATUS_STS_MASK	0x1E
+#define DD_STATUS_STS_NS	0x00 /* Not serviced */
+#define DD_STATUS_STS_BS	0x02 /* Being serviced */
+#define DD_STATUS_STS_NC	0x04 /* Normal completion */
+#define DD_STATUS_STS_DUR	0x06 /* Data underrun (short packet) */
+#define DD_STATUS_STS_DOR	0x08 /* Data overrun */
+#define DD_STATUS_STS_SE	0x12 /* System error */
+#define DD_STATUS_PKT_VAL	0x20 /* Packet valid */
+#define DD_STATUS_LSB_EX	0x40 /* LS byte extracted (ATLE) */
+#define DD_STATUS_MSB_EX	0x80 /* MS byte extracted (ATLE) */
+#define DD_STATUS_MLEN(n)	(((n) >> 8) & 0x3F)
+#define DD_STATUS_CURDMACNT(n)	(((n) >> 16) & 0xFFFF)
+
+/*
+ *
+ * Protocol engine bits below
+ *
+ */
+/* Device Interrupt Bit Definitions */
+#define FRAME_INT		0x00000001
+#define EP_FAST_INT		0x00000002
+#define EP_SLOW_INT		0x00000004
+#define DEV_STAT_INT		0x00000008
+#define CCEMTY_INT		0x00000010
+#define CDFULL_INT		0x00000020
+#define RxENDPKT_INT		0x00000040
+#define TxENDPKT_INT		0x00000080
+#define EP_RLZED_INT		0x00000100
+#define ERR_INT			0x00000200
+
+/* Rx & Tx Packet Length Definitions */
+#define PKT_LNGTH_MASK		0x000003FF
+#define PKT_DV			0x00000400
+#define PKT_RDY			0x00000800
+
+/* USB Control Definitions */
+#define CTRL_RD_EN		0x00000001
+#define CTRL_WR_EN		0x00000002
+
+/* Command Codes */
+#define CMD_SET_ADDR		0x00D00500
+#define CMD_CFG_DEV		0x00D80500
+#define CMD_SET_MODE		0x00F30500
+#define CMD_RD_FRAME		0x00F50500
+#define DAT_RD_FRAME		0x00F50200
+#define CMD_RD_TEST		0x00FD0500
+#define DAT_RD_TEST		0x00FD0200
+#define CMD_SET_DEV_STAT	0x00FE0500
+#define CMD_GET_DEV_STAT	0x00FE0500
+#define DAT_GET_DEV_STAT	0x00FE0200
+#define CMD_GET_ERR_CODE	0x00FF0500
+#define DAT_GET_ERR_CODE	0x00FF0200
+#define CMD_RD_ERR_STAT		0x00FB0500
+#define DAT_RD_ERR_STAT		0x00FB0200
+#define DAT_WR_BYTE(x)		(0x00000100 | ((x) << 16))
+#define CMD_SEL_EP(x)		(0x00000500 | ((x) << 16))
+#define DAT_SEL_EP(x)		(0x00000200 | ((x) << 16))
+#define CMD_SEL_EP_CLRI(x)	(0x00400500 | ((x) << 16))
+#define DAT_SEL_EP_CLRI(x)	(0x00400200 | ((x) << 16))
+#define CMD_SET_EP_STAT(x)	(0x00400500 | ((x) << 16))
+#define CMD_CLR_BUF		0x00F20500
+#define DAT_CLR_BUF		0x00F20200
+#define CMD_VALID_BUF		0x00FA0500
+
+/* Device Address Register Definitions */
+#define DEV_ADDR_MASK		0x7F
+#define DEV_EN			0x80
+
+/* Device Configure Register Definitions */
+#define CONF_DVICE		0x01
+
+/* Device Mode Register Definitions */
+#define AP_CLK			0x01
+#define INAK_CI			0x02
+#define INAK_CO			0x04
+#define INAK_II			0x08
+#define INAK_IO			0x10
+#define INAK_BI			0x20
+#define INAK_BO			0x40
+
+/* Device Status Register Definitions */
+#define DEV_CON			0x01
+#define DEV_CON_CH		0x02
+#define DEV_SUS			0x04
+#define DEV_SUS_CH		0x08
+#define DEV_RST			0x10
+
+/* Error Code Register Definitions */
+#define ERR_EC_MASK		0x0F
+#define ERR_EA			0x10
+
+/* Error Status Register Definitions */
+#define ERR_PID			0x01
+#define ERR_UEPKT		0x02
+#define ERR_DCRC		0x04
+#define ERR_TIMOUT		0x08
+#define ERR_EOP			0x10
+#define ERR_B_OVRN		0x20
+#define ERR_BTSTF		0x40
+#define ERR_TGL			0x80
+
+/* Endpoint Select Register Definitions */
+#define EP_SEL_F		0x01
+#define EP_SEL_ST		0x02
+#define EP_SEL_STP		0x04
+#define EP_SEL_PO		0x08
+#define EP_SEL_EPN		0x10
+#define EP_SEL_B_1_FULL		0x20
+#define EP_SEL_B_2_FULL		0x40
+
+/* Endpoint Status Register Definitions */
+#define EP_STAT_ST		0x01
+#define EP_STAT_DA		0x20
+#define EP_STAT_RF_MO		0x40
+#define EP_STAT_CND_ST		0x80
+
+/* Clear Buffer Register Definitions */
+#define CLR_BUF_PO		0x01
+
+/* DMA Interrupt Bit Definitions */
+#define EOT_INT			0x01
+#define NDD_REQ_INT		0x02
+#define SYS_ERR_INT		0x04
+
+#define	DRIVER_VERSION	"1.03"
+static const char driver_name[] = "lpc32xx_udc";
+
+/*
+ *
+ * proc interface support
+ *
+ */
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+static char *epnames[] = {"INT", "ISO", "BULK", "CTRL"};
+static const char debug_filename[] = "driver/udc";
+
+static void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep)
+{
+	struct lpc32xx_request *req;
+
+	seq_printf(s, "\n");
+	seq_printf(s, "%12s, maxpacket %4d %3s",
+			ep->ep.name, ep->ep.maxpacket,
+			ep->is_in ? "in" : "out");
+	seq_printf(s, " type %4s", epnames[ep->eptype]);
+	seq_printf(s, " ints: %12d", ep->totalints);
+
+	if (list_empty(&ep->queue))
+		seq_printf(s, "\t(queue empty)\n");
+	else {
+		list_for_each_entry(req, &ep->queue, queue) {
+			u32 length = req->req.actual;
+
+			seq_printf(s, "\treq %p len %d/%d buf %p\n",
+				   &req->req, length,
+				   req->req.length, req->req.buf);
+		}
+	}
+}
+
+static int proc_udc_show(struct seq_file *s, void *unused)
+{
+	struct lpc32xx_udc *udc = s->private;
+	struct lpc32xx_ep *ep;
+	unsigned long flags;
+
+	seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n",
+		   udc->vbus ? "present" : "off",
+		   udc->enabled ? (udc->vbus ? "active" : "enabled") :
+		   "disabled",
+		   udc->gadget.is_selfpowered ? "self" : "VBUS",
+		   udc->suspended ? ", suspended" : "",
+		   udc->driver ? udc->driver->driver.name : "(none)");
+
+	if (udc->enabled && udc->vbus) {
+		proc_ep_show(s, &udc->ep[0]);
+		list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
+			proc_ep_show(s, ep);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int proc_udc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_udc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations proc_ops = {
+	.owner		= THIS_MODULE,
+	.open		= proc_udc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void create_debug_file(struct lpc32xx_udc *udc)
+{
+	udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops);
+}
+
+static void remove_debug_file(struct lpc32xx_udc *udc)
+{
+	debugfs_remove(udc->pde);
+}
+
+#else
+static inline void create_debug_file(struct lpc32xx_udc *udc) {}
+static inline void remove_debug_file(struct lpc32xx_udc *udc) {}
+#endif
+
+/* Primary initialization sequence for the ISP1301 transceiver */
+static void isp1301_udc_configure(struct lpc32xx_udc *udc)
+{
+	/* LPC32XX only supports DAT_SE0 USB mode */
+	/* This sequence is important */
+
+	/* Disable transparent UART mode first */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		(ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+		MC1_UART_EN);
+
+	/* Set full speed and SE0 mode */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		(ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_MODE_CONTROL_1, (MC1_SPEED_REG | MC1_DAT_SE0));
+
+	/*
+	 * The PSW_OE enable bit state is reversed in the ISP1301 User's Guide
+	 */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		(ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_SPD_SUSP_CTRL));
+
+	/* Driver VBUS_DRV high or low depending on board setup */
+	if (udc->board->vbus_drv_pol != 0)
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
+	else
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR,
+			OTG1_VBUS_DRV);
+
+	/* Bi-directional mode with suspend control
+	 * Enable both pulldowns for now - the pullup will be enable when VBUS
+	 * is detected */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		(ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_OTG_CONTROL_1,
+		(0 | OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+
+	/* Discharge VBUS (just in case) */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG);
+	msleep(1);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		(ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
+		OTG1_VBUS_DISCHRG);
+
+	/* Clear and enable VBUS high edge interrupt */
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_INTERRUPT_FALLING, INT_VBUS_VLD);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+	i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+		ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
+
+	dev_info(udc->dev, "ISP1301 Vendor ID  : 0x%04x\n",
+		 i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
+	dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n",
+		 i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02));
+	dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n",
+		 i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14));
+}
+
+/* Enables or disables the USB device pullup via the ISP1301 transceiver */
+static void isp1301_pullup_set(struct lpc32xx_udc *udc)
+{
+	if (udc->pullup)
+		/* Enable pullup for bus signalling */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1, OTG1_DP_PULLUP);
+	else
+		/* Enable pullup for bus signalling */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR,
+			OTG1_DP_PULLUP);
+}
+
+static void pullup_work(struct work_struct *work)
+{
+	struct lpc32xx_udc *udc =
+		container_of(work, struct lpc32xx_udc, pullup_job);
+
+	isp1301_pullup_set(udc);
+}
+
+static void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup,
+				  int block)
+{
+	if (en_pullup == udc->pullup)
+		return;
+
+	udc->pullup = en_pullup;
+	if (block)
+		isp1301_pullup_set(udc);
+	else
+		/* defer slow i2c pull up setting */
+		schedule_work(&udc->pullup_job);
+}
+
+#ifdef CONFIG_PM
+/* Powers up or down the ISP1301 transceiver */
+static void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable)
+{
+	if (enable != 0)
+		/* Power up ISP1301 - this ISP1301 will automatically wakeup
+		   when VBUS is detected */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR,
+			MC2_GLOBAL_PWR_DN);
+	else
+		/* Power down ISP1301 */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+}
+
+static void power_work(struct work_struct *work)
+{
+	struct lpc32xx_udc *udc =
+		container_of(work, struct lpc32xx_udc, power_job);
+
+	isp1301_set_powerstate(udc, udc->poweron);
+}
+#endif
+
+/*
+ *
+ * USB protocol engine command/data read/write helper functions
+ *
+ */
+/* Issues a single command to the USB device state machine */
+static void udc_protocol_cmd_w(struct lpc32xx_udc *udc, u32 cmd)
+{
+	u32 pass = 0;
+	int to;
+
+	/* EP may lock on CLRI if this read isn't done */
+	u32 tmp = readl(USBD_DEVINTST(udc->udp_baseaddr));
+	(void) tmp;
+
+	while (pass == 0) {
+		writel(USBD_CCEMPTY, USBD_DEVINTCLR(udc->udp_baseaddr));
+
+		/* Write command code */
+		writel(cmd, USBD_CMDCODE(udc->udp_baseaddr));
+		to = 10000;
+		while (((readl(USBD_DEVINTST(udc->udp_baseaddr)) &
+			 USBD_CCEMPTY) == 0) && (to > 0)) {
+			to--;
+		}
+
+		if (to > 0)
+			pass = 1;
+
+		cpu_relax();
+	}
+}
+
+/* Issues 2 commands (or command and data) to the USB device state machine */
+static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd,
+					   u32 data)
+{
+	udc_protocol_cmd_w(udc, cmd);
+	udc_protocol_cmd_w(udc, data);
+}
+
+/* Issues a single command to the USB device state machine and reads
+ * response data */
+static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
+{
+	u32 tmp;
+	int to = 1000;
+
+	/* Write a command and read data from the protocol engine */
+	writel((USBD_CDFULL | USBD_CCEMPTY),
+		     USBD_DEVINTCLR(udc->udp_baseaddr));
+
+	/* Write command code */
+	udc_protocol_cmd_w(udc, cmd);
+
+	tmp = readl(USBD_DEVINTST(udc->udp_baseaddr));
+	while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL))
+	       && (to > 0))
+		to--;
+	if (!to)
+		dev_dbg(udc->dev,
+			"Protocol engine didn't receive response (CDFULL)\n");
+
+	return readl(USBD_CMDDATA(udc->udp_baseaddr));
+}
+
+/*
+ *
+ * USB device interrupt mask support functions
+ *
+ */
+/* Enable one or more USB device interrupts */
+static inline void uda_enable_devint(struct lpc32xx_udc *udc, u32 devmask)
+{
+	udc->enabled_devints |= devmask;
+	writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr));
+}
+
+/* Disable one or more USB device interrupts */
+static inline void uda_disable_devint(struct lpc32xx_udc *udc, u32 mask)
+{
+	udc->enabled_devints &= ~mask;
+	writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr));
+}
+
+/* Clear one or more USB device interrupts */
+static inline void uda_clear_devint(struct lpc32xx_udc *udc, u32 mask)
+{
+	writel(mask, USBD_DEVINTCLR(udc->udp_baseaddr));
+}
+
+/*
+ *
+ * Endpoint interrupt disable/enable functions
+ *
+ */
+/* Enable one or more USB endpoint interrupts */
+static void uda_enable_hwepint(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc->enabled_hwepints |= (1 << hwep);
+	writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr));
+}
+
+/* Disable one or more USB endpoint interrupts */
+static void uda_disable_hwepint(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc->enabled_hwepints &= ~(1 << hwep);
+	writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr));
+}
+
+/* Clear one or more USB endpoint interrupts */
+static inline void uda_clear_hwepint(struct lpc32xx_udc *udc, u32 hwep)
+{
+	writel((1 << hwep), USBD_EPINTCLR(udc->udp_baseaddr));
+}
+
+/* Enable DMA for the HW channel */
+static inline void udc_ep_dma_enable(struct lpc32xx_udc *udc, u32 hwep)
+{
+	writel((1 << hwep), USBD_EPDMAEN(udc->udp_baseaddr));
+}
+
+/* Disable DMA for the HW channel */
+static inline void udc_ep_dma_disable(struct lpc32xx_udc *udc, u32 hwep)
+{
+	writel((1 << hwep), USBD_EPDMADIS(udc->udp_baseaddr));
+}
+
+/*
+ *
+ * Endpoint realize/unrealize functions
+ *
+ */
+/* Before an endpoint can be used, it needs to be realized
+ * in the USB protocol engine - this realizes the endpoint.
+ * The interrupt (FIFO or DMA) is not enabled with this function */
+static void udc_realize_hwep(struct lpc32xx_udc *udc, u32 hwep,
+			     u32 maxpacket)
+{
+	int to = 1000;
+
+	writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr));
+	writel(hwep, USBD_EPIND(udc->udp_baseaddr));
+	udc->realized_eps |= (1 << hwep);
+	writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr));
+	writel(maxpacket, USBD_EPMAXPSIZE(udc->udp_baseaddr));
+
+	/* Wait until endpoint is realized in hardware */
+	while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) &
+		  USBD_EP_RLZED)) && (to > 0))
+		to--;
+	if (!to)
+		dev_dbg(udc->dev, "EP not correctly realized in hardware\n");
+
+	writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr));
+}
+
+/* Unrealize an EP */
+static void udc_unrealize_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc->realized_eps &= ~(1 << hwep);
+	writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr));
+}
+
+/*
+ *
+ * Endpoint support functions
+ *
+ */
+/* Select and clear endpoint interrupt */
+static u32 udc_selep_clrint(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_protocol_cmd_w(udc, CMD_SEL_EP_CLRI(hwep));
+	return udc_protocol_cmd_r(udc, DAT_SEL_EP_CLRI(hwep));
+}
+
+/* Disables the endpoint in the USB protocol engine */
+static void udc_disable_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep),
+				DAT_WR_BYTE(EP_STAT_DA));
+}
+
+/* Stalls the endpoint - endpoint will return STALL */
+static void udc_stall_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep),
+				DAT_WR_BYTE(EP_STAT_ST));
+}
+
+/* Clear stall or reset endpoint */
+static void udc_clrstall_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep),
+				DAT_WR_BYTE(0));
+}
+
+/* Select an endpoint for endpoint status, clear, validate */
+static void udc_select_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_protocol_cmd_w(udc, CMD_SEL_EP(hwep));
+}
+
+/*
+ *
+ * Endpoint buffer management functions
+ *
+ */
+/* Clear the current endpoint's buffer */
+static void udc_clr_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_select_hwep(udc, hwep);
+	udc_protocol_cmd_w(udc, CMD_CLR_BUF);
+}
+
+/* Validate the current endpoint's buffer */
+static void udc_val_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep)
+{
+	udc_select_hwep(udc, hwep);
+	udc_protocol_cmd_w(udc, CMD_VALID_BUF);
+}
+
+static inline u32 udc_clearep_getsts(struct lpc32xx_udc *udc, u32 hwep)
+{
+	/* Clear EP interrupt */
+	uda_clear_hwepint(udc, hwep);
+	return udc_selep_clrint(udc, hwep);
+}
+
+/*
+ *
+ * USB EP DMA support
+ *
+ */
+/* Allocate a DMA Descriptor */
+static struct lpc32xx_usbd_dd_gad *udc_dd_alloc(struct lpc32xx_udc *udc)
+{
+	dma_addr_t			dma;
+	struct lpc32xx_usbd_dd_gad	*dd;
+
+	dd = (struct lpc32xx_usbd_dd_gad *) dma_pool_alloc(
+			udc->dd_cache, (GFP_KERNEL | GFP_DMA), &dma);
+	if (dd)
+		dd->this_dma = dma;
+
+	return dd;
+}
+
+/* Free a DMA Descriptor */
+static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd)
+{
+	dma_pool_free(udc->dd_cache, dd, dd->this_dma);
+}
+
+/*
+ *
+ * USB setup and shutdown functions
+ *
+ */
+/* Enables or disables most of the USB system clocks when low power mode is
+ * needed. Clocks are typically started on a connection event, and disabled
+ * when a cable is disconnected */
+static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
+{
+	if (enable != 0) {
+		if (udc->clocked)
+			return;
+
+		udc->clocked = 1;
+		clk_prepare_enable(udc->usb_slv_clk);
+	} else {
+		if (!udc->clocked)
+			return;
+
+		udc->clocked = 0;
+		clk_disable_unprepare(udc->usb_slv_clk);
+	}
+}
+
+/* Set/reset USB device address */
+static void udc_set_address(struct lpc32xx_udc *udc, u32 addr)
+{
+	/* Address will be latched at the end of the status phase, or
+	   latched immediately if function is called twice */
+	udc_protocol_cmd_data_w(udc, CMD_SET_ADDR,
+				DAT_WR_BYTE(DEV_EN | addr));
+}
+
+/* Setup up a IN request for DMA transfer - this consists of determining the
+ * list of DMA addresses for the transfer, allocating DMA Descriptors,
+ * installing the DD into the UDCA, and then enabling the DMA for that EP */
+static int udc_ep_in_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+{
+	struct lpc32xx_request *req;
+	u32 hwep = ep->hwep_num;
+
+	ep->req_pending = 1;
+
+	/* There will always be a request waiting here */
+	req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
+
+	/* Place the DD Descriptor into the UDCA */
+	udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma;
+
+	/* Enable DMA and interrupt for the HW EP */
+	udc_ep_dma_enable(udc, hwep);
+
+	/* Clear ZLP if last packet is not of MAXP size */
+	if (req->req.length % ep->ep.maxpacket)
+		req->send_zlp = 0;
+
+	return 0;
+}
+
+/* Setup up a OUT request for DMA transfer - this consists of determining the
+ * list of DMA addresses for the transfer, allocating DMA Descriptors,
+ * installing the DD into the UDCA, and then enabling the DMA for that EP */
+static int udc_ep_out_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+{
+	struct lpc32xx_request *req;
+	u32 hwep = ep->hwep_num;
+
+	ep->req_pending = 1;
+
+	/* There will always be a request waiting here */
+	req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
+
+	/* Place the DD Descriptor into the UDCA */
+	udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma;
+
+	/* Enable DMA and interrupt for the HW EP */
+	udc_ep_dma_enable(udc, hwep);
+	return 0;
+}
+
+static void udc_disable(struct lpc32xx_udc *udc)
+{
+	u32 i;
+
+	/* Disable device */
+	udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0));
+	udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(0));
+
+	/* Disable all device interrupts (including EP0) */
+	uda_disable_devint(udc, 0x3FF);
+
+	/* Disable and reset all endpoint interrupts */
+	for (i = 0; i < 32; i++) {
+		uda_disable_hwepint(udc, i);
+		uda_clear_hwepint(udc, i);
+		udc_disable_hwep(udc, i);
+		udc_unrealize_hwep(udc, i);
+		udc->udca_v_base[i] = 0;
+
+		/* Disable and clear all interrupts and DMA */
+		udc_ep_dma_disable(udc, i);
+		writel((1 << i), USBD_EOTINTCLR(udc->udp_baseaddr));
+		writel((1 << i), USBD_NDDRTINTCLR(udc->udp_baseaddr));
+		writel((1 << i), USBD_SYSERRTINTCLR(udc->udp_baseaddr));
+		writel((1 << i), USBD_DMARCLR(udc->udp_baseaddr));
+	}
+
+	/* Disable DMA interrupts */
+	writel(0, USBD_DMAINTEN(udc->udp_baseaddr));
+
+	writel(0, USBD_UDCAH(udc->udp_baseaddr));
+}
+
+static void udc_enable(struct lpc32xx_udc *udc)
+{
+	u32 i;
+	struct lpc32xx_ep *ep = &udc->ep[0];
+
+	/* Start with known state */
+	udc_disable(udc);
+
+	/* Enable device */
+	udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON));
+
+	/* EP interrupts on high priority, FRAME interrupt on low priority */
+	writel(USBD_EP_FAST, USBD_DEVINTPRI(udc->udp_baseaddr));
+	writel(0xFFFF, USBD_EPINTPRI(udc->udp_baseaddr));
+
+	/* Clear any pending device interrupts */
+	writel(0x3FF, USBD_DEVINTCLR(udc->udp_baseaddr));
+
+	/* Setup UDCA - not yet used (DMA) */
+	writel(udc->udca_p_base, USBD_UDCAH(udc->udp_baseaddr));
+
+	/* Only enable EP0 in and out for now, EP0 only works in FIFO mode */
+	for (i = 0; i <= 1; i++) {
+		udc_realize_hwep(udc, i, ep->ep.maxpacket);
+		uda_enable_hwepint(udc, i);
+		udc_select_hwep(udc, i);
+		udc_clrstall_hwep(udc, i);
+		udc_clr_buffer_hwep(udc, i);
+	}
+
+	/* Device interrupt setup */
+	uda_clear_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW |
+			       USBD_EP_FAST));
+	uda_enable_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW |
+				USBD_EP_FAST));
+
+	/* Set device address to 0 - called twice to force a latch in the USB
+	   engine without the need of a setup packet status closure */
+	udc_set_address(udc, 0);
+	udc_set_address(udc, 0);
+
+	/* Enable master DMA interrupts */
+	writel((USBD_SYS_ERR_INT | USBD_EOT_INT),
+		     USBD_DMAINTEN(udc->udp_baseaddr));
+
+	udc->dev_status = 0;
+}
+
+/*
+ *
+ * USB device board specific events handled via callbacks
+ *
+ */
+/* Connection change event - notify board function of change */
+static void uda_power_event(struct lpc32xx_udc *udc, u32 conn)
+{
+	/* Just notify of a connection change event (optional) */
+	if (udc->board->conn_chgb != NULL)
+		udc->board->conn_chgb(conn);
+}
+
+/* Suspend/resume event - notify board function of change */
+static void uda_resm_susp_event(struct lpc32xx_udc *udc, u32 conn)
+{
+	/* Just notify of a Suspend/resume change event (optional) */
+	if (udc->board->susp_chgb != NULL)
+		udc->board->susp_chgb(conn);
+
+	if (conn)
+		udc->suspended = 0;
+	else
+		udc->suspended = 1;
+}
+
+/* Remote wakeup enable/disable - notify board function of change */
+static void uda_remwkp_cgh(struct lpc32xx_udc *udc)
+{
+	if (udc->board->rmwk_chgb != NULL)
+		udc->board->rmwk_chgb(udc->dev_status &
+				      (1 << USB_DEVICE_REMOTE_WAKEUP));
+}
+
+/* Reads data from FIFO, adjusts for alignment and data size */
+static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes)
+{
+	int n, i, bl;
+	u16 *p16;
+	u32 *p32, tmp, cbytes;
+
+	/* Use optimal data transfer method based on source address and size */
+	switch (((u32) data) & 0x3) {
+	case 0: /* 32-bit aligned */
+		p32 = (u32 *) data;
+		cbytes = (bytes & ~0x3);
+
+		/* Copy 32-bit aligned data first */
+		for (n = 0; n < cbytes; n += 4)
+			*p32++ = readl(USBD_RXDATA(udc->udp_baseaddr));
+
+		/* Handle any remaining bytes */
+		bl = bytes - cbytes;
+		if (bl) {
+			tmp = readl(USBD_RXDATA(udc->udp_baseaddr));
+			for (n = 0; n < bl; n++)
+				data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF);
+
+		}
+		break;
+
+	case 1: /* 8-bit aligned */
+	case 3:
+		/* Each byte has to be handled independently */
+		for (n = 0; n < bytes; n += 4) {
+			tmp = readl(USBD_RXDATA(udc->udp_baseaddr));
+
+			bl = bytes - n;
+			if (bl > 3)
+				bl = 3;
+
+			for (i = 0; i < bl; i++)
+				data[n + i] = (u8) ((tmp >> (n * 8)) & 0xFF);
+		}
+		break;
+
+	case 2: /* 16-bit aligned */
+		p16 = (u16 *) data;
+		cbytes = (bytes & ~0x3);
+
+		/* Copy 32-bit sized objects first with 16-bit alignment */
+		for (n = 0; n < cbytes; n += 4) {
+			tmp = readl(USBD_RXDATA(udc->udp_baseaddr));
+			*p16++ = (u16)(tmp & 0xFFFF);
+			*p16++ = (u16)((tmp >> 16) & 0xFFFF);
+		}
+
+		/* Handle any remaining bytes */
+		bl = bytes - cbytes;
+		if (bl) {
+			tmp = readl(USBD_RXDATA(udc->udp_baseaddr));
+			for (n = 0; n < bl; n++)
+				data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF);
+		}
+		break;
+	}
+}
+
+/* Read data from the FIFO for an endpoint. This function is for endpoints (such
+ * as EP0) that don't use DMA. This function should only be called if a packet
+ * is known to be ready to read for the endpoint. Note that the endpoint must
+ * be selected in the protocol engine prior to this call. */
+static u32 udc_read_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data,
+			 u32 bytes)
+{
+	u32 tmpv;
+	int to = 1000;
+	u32 tmp, hwrep = ((hwep & 0x1E) << 1) | CTRL_RD_EN;
+
+	/* Setup read of endpoint */
+	writel(hwrep, USBD_CTRL(udc->udp_baseaddr));
+
+	/* Wait until packet is ready */
+	while ((((tmpv = readl(USBD_RXPLEN(udc->udp_baseaddr))) &
+		 PKT_RDY) == 0)	&& (to > 0))
+		to--;
+	if (!to)
+		dev_dbg(udc->dev, "No packet ready on FIFO EP read\n");
+
+	/* Mask out count */
+	tmp = tmpv & PKT_LNGTH_MASK;
+	if (bytes < tmp)
+		tmp = bytes;
+
+	if ((tmp > 0) && (data != NULL))
+		udc_pop_fifo(udc, (u8 *) data, tmp);
+
+	writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr));
+
+	/* Clear the buffer */
+	udc_clr_buffer_hwep(udc, hwep);
+
+	return tmp;
+}
+
+/* Stuffs data into the FIFO, adjusts for alignment and data size */
+static void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes)
+{
+	int n, i, bl;
+	u16 *p16;
+	u32 *p32, tmp, cbytes;
+
+	/* Use optimal data transfer method based on source address and size */
+	switch (((u32) data) & 0x3) {
+	case 0: /* 32-bit aligned */
+		p32 = (u32 *) data;
+		cbytes = (bytes & ~0x3);
+
+		/* Copy 32-bit aligned data first */
+		for (n = 0; n < cbytes; n += 4)
+			writel(*p32++, USBD_TXDATA(udc->udp_baseaddr));
+
+		/* Handle any remaining bytes */
+		bl = bytes - cbytes;
+		if (bl) {
+			tmp = 0;
+			for (n = 0; n < bl; n++)
+				tmp |= data[cbytes + n] << (n * 8);
+
+			writel(tmp, USBD_TXDATA(udc->udp_baseaddr));
+		}
+		break;
+
+	case 1: /* 8-bit aligned */
+	case 3:
+		/* Each byte has to be handled independently */
+		for (n = 0; n < bytes; n += 4) {
+			bl = bytes - n;
+			if (bl > 4)
+				bl = 4;
+
+			tmp = 0;
+			for (i = 0; i < bl; i++)
+				tmp |= data[n + i] << (i * 8);
+
+			writel(tmp, USBD_TXDATA(udc->udp_baseaddr));
+		}
+		break;
+
+	case 2: /* 16-bit aligned */
+		p16 = (u16 *) data;
+		cbytes = (bytes & ~0x3);
+
+		/* Copy 32-bit aligned data first */
+		for (n = 0; n < cbytes; n += 4) {
+			tmp = *p16++ & 0xFFFF;
+			tmp |= (*p16++ & 0xFFFF) << 16;
+			writel(tmp, USBD_TXDATA(udc->udp_baseaddr));
+		}
+
+		/* Handle any remaining bytes */
+		bl = bytes - cbytes;
+		if (bl) {
+			tmp = 0;
+			for (n = 0; n < bl; n++)
+				tmp |= data[cbytes + n] << (n * 8);
+
+			writel(tmp, USBD_TXDATA(udc->udp_baseaddr));
+		}
+		break;
+	}
+}
+
+/* Write data to the FIFO for an endpoint. This function is for endpoints (such
+ * as EP0) that don't use DMA. Note that the endpoint must be selected in the
+ * protocol engine prior to this call. */
+static void udc_write_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data,
+			   u32 bytes)
+{
+	u32 hwwep = ((hwep & 0x1E) << 1) | CTRL_WR_EN;
+
+	if ((bytes > 0) && (data == NULL))
+		return;
+
+	/* Setup write of endpoint */
+	writel(hwwep, USBD_CTRL(udc->udp_baseaddr));
+
+	writel(bytes, USBD_TXPLEN(udc->udp_baseaddr));
+
+	/* Need at least 1 byte to trigger TX */
+	if (bytes == 0)
+		writel(0, USBD_TXDATA(udc->udp_baseaddr));
+	else
+		udc_stuff_fifo(udc, (u8 *) data, bytes);
+
+	writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr));
+
+	udc_val_buffer_hwep(udc, hwep);
+}
+
+/* USB device reset - resets USB to a default state with just EP0
+   enabled */
+static void uda_usb_reset(struct lpc32xx_udc *udc)
+{
+	u32 i = 0;
+	/* Re-init device controller and EP0 */
+	udc_enable(udc);
+	udc->gadget.speed = USB_SPEED_FULL;
+
+	for (i = 1; i < NUM_ENDPOINTS; i++) {
+		struct lpc32xx_ep *ep = &udc->ep[i];
+		ep->req_pending = 0;
+	}
+}
+
+/* Send a ZLP on EP0 */
+static void udc_ep0_send_zlp(struct lpc32xx_udc *udc)
+{
+	udc_write_hwep(udc, EP_IN, NULL, 0);
+}
+
+/* Get current frame number */
+static u16 udc_get_current_frame(struct lpc32xx_udc *udc)
+{
+	u16 flo, fhi;
+
+	udc_protocol_cmd_w(udc, CMD_RD_FRAME);
+	flo = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME);
+	fhi = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME);
+
+	return (fhi << 8) | flo;
+}
+
+/* Set the device as configured - enables all endpoints */
+static inline void udc_set_device_configured(struct lpc32xx_udc *udc)
+{
+	udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(CONF_DVICE));
+}
+
+/* Set the device as unconfigured - disables all endpoints */
+static inline void udc_set_device_unconfigured(struct lpc32xx_udc *udc)
+{
+	udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0));
+}
+
+/* reinit == restore initial software state */
+static void udc_reinit(struct lpc32xx_udc *udc)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct lpc32xx_ep *ep = &udc->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
+		INIT_LIST_HEAD(&ep->queue);
+		ep->req_pending = 0;
+	}
+
+	udc->ep0state = WAIT_FOR_SETUP;
+}
+
+/* Must be called with lock */
+static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status)
+{
+	struct lpc32xx_udc *udc = ep->udc;
+
+	list_del_init(&req->queue);
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (ep->lep) {
+		usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in);
+
+		/* Free DDs */
+		udc_dd_free(udc, req->dd_desc_ptr);
+	}
+
+	if (status && status != -ESHUTDOWN)
+		ep_dbg(ep, "%s done %p, status %d\n", ep->ep.name, req, status);
+
+	ep->req_pending = 0;
+	spin_unlock(&udc->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&udc->lock);
+}
+
+/* Must be called with lock */
+static void nuke(struct lpc32xx_ep *ep, int status)
+{
+	struct lpc32xx_request *req;
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
+		done(ep, req, status);
+	}
+
+	if (status == -ESHUTDOWN) {
+		uda_disable_hwepint(ep->udc, ep->hwep_num);
+		udc_disable_hwep(ep->udc, ep->hwep_num);
+	}
+}
+
+/* IN endpoint 0 transfer */
+static int udc_ep0_in_req(struct lpc32xx_udc *udc)
+{
+	struct lpc32xx_request *req;
+	struct lpc32xx_ep *ep0 = &udc->ep[0];
+	u32 tsend, ts = 0;
+
+	if (list_empty(&ep0->queue))
+		/* Nothing to send */
+		return 0;
+	else
+		req = list_entry(ep0->queue.next, struct lpc32xx_request,
+				 queue);
+
+	tsend = ts = req->req.length - req->req.actual;
+	if (ts == 0) {
+		/* Send a ZLP */
+		udc_ep0_send_zlp(udc);
+		done(ep0, req, 0);
+		return 1;
+	} else if (ts > ep0->ep.maxpacket)
+		ts = ep0->ep.maxpacket; /* Just send what we can */
+
+	/* Write data to the EP0 FIFO and start transfer */
+	udc_write_hwep(udc, EP_IN, (req->req.buf + req->req.actual), ts);
+
+	/* Increment data pointer */
+	req->req.actual += ts;
+
+	if (tsend >= ep0->ep.maxpacket)
+		return 0; /* Stay in data transfer state */
+
+	/* Transfer request is complete */
+	udc->ep0state = WAIT_FOR_SETUP;
+	done(ep0, req, 0);
+	return 1;
+}
+
+/* OUT endpoint 0 transfer */
+static int udc_ep0_out_req(struct lpc32xx_udc *udc)
+{
+	struct lpc32xx_request *req;
+	struct lpc32xx_ep *ep0 = &udc->ep[0];
+	u32 tr, bufferspace;
+
+	if (list_empty(&ep0->queue))
+		return 0;
+	else
+		req = list_entry(ep0->queue.next, struct lpc32xx_request,
+				 queue);
+
+	if (req) {
+		if (req->req.length == 0) {
+			/* Just dequeue request */
+			done(ep0, req, 0);
+			udc->ep0state = WAIT_FOR_SETUP;
+			return 1;
+		}
+
+		/* Get data from FIFO */
+		bufferspace = req->req.length - req->req.actual;
+		if (bufferspace > ep0->ep.maxpacket)
+			bufferspace = ep0->ep.maxpacket;
+
+		/* Copy data to buffer */
+		prefetchw(req->req.buf + req->req.actual);
+		tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
+				   bufferspace);
+		req->req.actual += bufferspace;
+
+		if (tr < ep0->ep.maxpacket) {
+			/* This is the last packet */
+			done(ep0, req, 0);
+			udc->ep0state = WAIT_FOR_SETUP;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/* Must be called with lock */
+static void stop_activity(struct lpc32xx_udc *udc)
+{
+	struct usb_gadget_driver *driver = udc->driver;
+	int i;
+
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->suspended = 0;
+
+	for (i = 0; i < NUM_ENDPOINTS; i++) {
+		struct lpc32xx_ep *ep = &udc->ep[i];
+		nuke(ep, -ESHUTDOWN);
+	}
+	if (driver) {
+		spin_unlock(&udc->lock);
+		driver->disconnect(&udc->gadget);
+		spin_lock(&udc->lock);
+	}
+
+	isp1301_pullup_enable(udc, 0, 0);
+	udc_disable(udc);
+	udc_reinit(udc);
+}
+
+/*
+ * Activate or kill host pullup
+ * Can be called with or without lock
+ */
+static void pullup(struct lpc32xx_udc *udc, int is_on)
+{
+	if (!udc->clocked)
+		return;
+
+	if (!udc->enabled || !udc->vbus)
+		is_on = 0;
+
+	if (is_on != udc->pullup)
+		isp1301_pullup_enable(udc, is_on, 0);
+}
+
+/* Must be called without lock */
+static int lpc32xx_ep_disable(struct usb_ep *_ep)
+{
+	struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
+	struct lpc32xx_udc *udc = ep->udc;
+	unsigned long	flags;
+
+	if ((ep->hwep_num_base == 0) || (ep->hwep_num == 0))
+		return -EINVAL;
+	spin_lock_irqsave(&udc->lock, flags);
+
+	nuke(ep, -ESHUTDOWN);
+
+	/* Clear all DMA statuses for this EP */
+	udc_ep_dma_disable(udc, ep->hwep_num);
+	writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr));
+
+	/* Remove the DD pointer in the UDCA */
+	udc->udca_v_base[ep->hwep_num] = 0;
+
+	/* Disable and reset endpoint and interrupt */
+	uda_clear_hwepint(udc, ep->hwep_num);
+	udc_unrealize_hwep(udc, ep->hwep_num);
+
+	ep->hwep_num = 0;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	atomic_dec(&udc->enabled_ep_cnt);
+	wake_up(&udc->ep_disable_wait_queue);
+
+	return 0;
+}
+
+/* Must be called without lock */
+static int lpc32xx_ep_enable(struct usb_ep *_ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
+	struct lpc32xx_udc *udc = ep->udc;
+	u16 maxpacket;
+	u32 tmp;
+	unsigned long flags;
+
+	/* Verify EP data */
+	if ((!_ep) || (!ep) || (!desc) ||
+	    (desc->bDescriptorType != USB_DT_ENDPOINT)) {
+		dev_dbg(udc->dev, "bad ep or descriptor\n");
+		return -EINVAL;
+	}
+	maxpacket = usb_endpoint_maxp(desc);
+	if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) {
+		dev_dbg(udc->dev, "bad ep descriptor's packet size\n");
+		return -EINVAL;
+	}
+
+	/* Don't touch EP0 */
+	if (ep->hwep_num_base == 0) {
+		dev_dbg(udc->dev, "Can't re-enable EP0!!!\n");
+		return -EINVAL;
+	}
+
+	/* Is driver ready? */
+	if ((!udc->driver) || (udc->gadget.speed == USB_SPEED_UNKNOWN)) {
+		dev_dbg(udc->dev, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	switch (tmp) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		return -EINVAL;
+
+	case USB_ENDPOINT_XFER_INT:
+		if (maxpacket > ep->maxpacket) {
+			dev_dbg(udc->dev,
+				"Bad INT endpoint maxpacket %d\n", maxpacket);
+			return -EINVAL;
+		}
+		break;
+
+	case USB_ENDPOINT_XFER_BULK:
+		switch (maxpacket) {
+		case 8:
+		case 16:
+		case 32:
+		case 64:
+			break;
+
+		default:
+			dev_dbg(udc->dev,
+				"Bad BULK endpoint maxpacket %d\n", maxpacket);
+			return -EINVAL;
+		}
+		break;
+
+	case USB_ENDPOINT_XFER_ISOC:
+		break;
+	}
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* Initialize endpoint to match the selected descriptor */
+	ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+	ep->ep.maxpacket = maxpacket;
+
+	/* Map hardware endpoint from base and direction */
+	if (ep->is_in)
+		/* IN endpoints are offset 1 from the OUT endpoint */
+		ep->hwep_num = ep->hwep_num_base + EP_IN;
+	else
+		ep->hwep_num = ep->hwep_num_base;
+
+	ep_dbg(ep, "EP enabled: %s, HW:%d, MP:%d IN:%d\n", ep->ep.name,
+	       ep->hwep_num, maxpacket, (ep->is_in == 1));
+
+	/* Realize the endpoint, interrupt is enabled later when
+	 * buffers are queued, IN EPs will NAK until buffers are ready */
+	udc_realize_hwep(udc, ep->hwep_num, ep->ep.maxpacket);
+	udc_clr_buffer_hwep(udc, ep->hwep_num);
+	uda_disable_hwepint(udc, ep->hwep_num);
+	udc_clrstall_hwep(udc, ep->hwep_num);
+
+	/* Clear all DMA statuses for this EP */
+	udc_ep_dma_disable(udc, ep->hwep_num);
+	writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr));
+	writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr));
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	atomic_inc(&udc->enabled_ep_cnt);
+	return 0;
+}
+
+/*
+ * Allocate a USB request list
+ * Can be called with or without lock
+ */
+static struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep,
+						    gfp_t gfp_flags)
+{
+	struct lpc32xx_request *req;
+
+	req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+/*
+ * De-allocate a USB request list
+ * Can be called with or without lock
+ */
+static void lpc32xx_ep_free_request(struct usb_ep *_ep,
+				    struct usb_request *_req)
+{
+	struct lpc32xx_request *req;
+
+	req = container_of(_req, struct lpc32xx_request, req);
+	BUG_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+/* Must be called without lock */
+static int lpc32xx_ep_queue(struct usb_ep *_ep,
+			    struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct lpc32xx_request *req;
+	struct lpc32xx_ep *ep;
+	struct lpc32xx_udc *udc;
+	unsigned long flags;
+	int status = 0;
+
+	req = container_of(_req, struct lpc32xx_request, req);
+	ep = container_of(_ep, struct lpc32xx_ep, ep);
+
+	if (!_ep || !_req || !_req->complete || !_req->buf ||
+	    !list_empty(&req->queue))
+		return -EINVAL;
+
+	udc = ep->udc;
+
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -EPIPE;
+
+	if (ep->lep) {
+		struct lpc32xx_usbd_dd_gad *dd;
+
+		status = usb_gadget_map_request(&udc->gadget, _req, ep->is_in);
+		if (status)
+			return status;
+
+		/* For the request, build a list of DDs */
+		dd = udc_dd_alloc(udc);
+		if (!dd) {
+			/* Error allocating DD */
+			return -ENOMEM;
+		}
+		req->dd_desc_ptr = dd;
+
+		/* Setup the DMA descriptor */
+		dd->dd_next_phy = dd->dd_next_v = 0;
+		dd->dd_buffer_addr = req->req.dma;
+		dd->dd_status = 0;
+
+		/* Special handling for ISO EPs */
+		if (ep->eptype == EP_ISO_TYPE) {
+			dd->dd_setup = DD_SETUP_ISO_EP |
+				DD_SETUP_PACKETLEN(0) |
+				DD_SETUP_DMALENBYTES(1);
+			dd->dd_iso_ps_mem_addr = dd->this_dma + 24;
+			if (ep->is_in)
+				dd->iso_status[0] = req->req.length;
+			else
+				dd->iso_status[0] = 0;
+		} else
+			dd->dd_setup = DD_SETUP_PACKETLEN(ep->ep.maxpacket) |
+				DD_SETUP_DMALENBYTES(req->req.length);
+	}
+
+	ep_dbg(ep, "%s queue req %p len %d buf %p (in=%d) z=%d\n", _ep->name,
+	       _req, _req->length, _req->buf, ep->is_in, _req->zero);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	req->send_zlp = _req->zero;
+
+	/* Kickstart empty queues */
+	if (list_empty(&ep->queue)) {
+		list_add_tail(&req->queue, &ep->queue);
+
+		if (ep->hwep_num_base == 0) {
+			/* Handle expected data direction */
+			if (ep->is_in) {
+				/* IN packet to host */
+				udc->ep0state = DATA_IN;
+				status = udc_ep0_in_req(udc);
+			} else {
+				/* OUT packet from host */
+				udc->ep0state = DATA_OUT;
+				status = udc_ep0_out_req(udc);
+			}
+		} else if (ep->is_in) {
+			/* IN packet to host and kick off transfer */
+			if (!ep->req_pending)
+				udc_ep_in_req_dma(udc, ep);
+		} else
+			/* OUT packet from host and kick off list */
+			if (!ep->req_pending)
+				udc_ep_out_req_dma(udc, ep);
+	} else
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return (status < 0) ? status : 0;
+}
+
+/* Must be called without lock */
+static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct lpc32xx_ep *ep;
+	struct lpc32xx_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct lpc32xx_ep, ep);
+	if (!_ep || ep->hwep_num_base == 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually 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->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	return 0;
+}
+
+/* Must be called without lock */
+static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
+	struct lpc32xx_udc *udc = ep->udc;
+	unsigned long flags;
+
+	if ((!ep) || (ep->hwep_num <= 1))
+		return -EINVAL;
+
+	/* Don't halt an IN EP */
+	if (ep->is_in)
+		return -EAGAIN;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (value == 1) {
+		/* stall */
+		udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num),
+					DAT_WR_BYTE(EP_STAT_ST));
+	} else {
+		/* End stall */
+		ep->wedge = 0;
+		udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num),
+					DAT_WR_BYTE(0));
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* set the halt feature and ignores clear requests */
+static int lpc32xx_ep_set_wedge(struct usb_ep *_ep)
+{
+	struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
+
+	if (!_ep || !ep->udc)
+		return -EINVAL;
+
+	ep->wedge = 1;
+
+	return usb_ep_set_halt(_ep);
+}
+
+static const struct usb_ep_ops lpc32xx_ep_ops = {
+	.enable		= lpc32xx_ep_enable,
+	.disable	= lpc32xx_ep_disable,
+	.alloc_request	= lpc32xx_ep_alloc_request,
+	.free_request	= lpc32xx_ep_free_request,
+	.queue		= lpc32xx_ep_queue,
+	.dequeue	= lpc32xx_ep_dequeue,
+	.set_halt	= lpc32xx_ep_set_halt,
+	.set_wedge	= lpc32xx_ep_set_wedge,
+};
+
+/* Send a ZLP on a non-0 IN EP */
+void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+{
+	/* Clear EP status */
+	udc_clearep_getsts(udc, ep->hwep_num);
+
+	/* Send ZLP via FIFO mechanism */
+	udc_write_hwep(udc, ep->hwep_num, NULL, 0);
+}
+
+/*
+ * Handle EP completion for ZLP
+ * This function will only be called when a delayed ZLP needs to be sent out
+ * after a DMA transfer has filled both buffers.
+ */
+void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+{
+	u32 epstatus;
+	struct lpc32xx_request *req;
+
+	if (ep->hwep_num <= 0)
+		return;
+
+	uda_clear_hwepint(udc, ep->hwep_num);
+
+	/* If this interrupt isn't enabled, return now */
+	if (!(udc->enabled_hwepints & (1 << ep->hwep_num)))
+		return;
+
+	/* Get endpoint status */
+	epstatus = udc_clearep_getsts(udc, ep->hwep_num);
+
+	/*
+	 * This should never happen, but protect against writing to the
+	 * buffer when full.
+	 */
+	if (epstatus & EP_SEL_F)
+		return;
+
+	if (ep->is_in) {
+		udc_send_in_zlp(udc, ep);
+		uda_disable_hwepint(udc, ep->hwep_num);
+	} else
+		return;
+
+	/* If there isn't a request waiting, something went wrong */
+	req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
+	if (req) {
+		done(ep, req, 0);
+
+		/* Start another request if ready */
+		if (!list_empty(&ep->queue)) {
+			if (ep->is_in)
+				udc_ep_in_req_dma(udc, ep);
+			else
+				udc_ep_out_req_dma(udc, ep);
+		} else
+			ep->req_pending = 0;
+	}
+}
+
+
+/* DMA end of transfer completion */
+static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+{
+	u32 status, epstatus;
+	struct lpc32xx_request *req;
+	struct lpc32xx_usbd_dd_gad *dd;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	ep->totalints++;
+#endif
+
+	req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
+	if (!req) {
+		ep_err(ep, "DMA interrupt on no req!\n");
+		return;
+	}
+	dd = req->dd_desc_ptr;
+
+	/* DMA descriptor should always be retired for this call */
+	if (!(dd->dd_status & DD_STATUS_DD_RETIRED))
+		ep_warn(ep, "DMA descriptor did not retire\n");
+
+	/* Disable DMA */
+	udc_ep_dma_disable(udc, ep->hwep_num);
+	writel((1 << ep->hwep_num), USBD_EOTINTCLR(udc->udp_baseaddr));
+	writel((1 << ep->hwep_num), USBD_NDDRTINTCLR(udc->udp_baseaddr));
+
+	/* System error? */
+	if (readl(USBD_SYSERRTINTST(udc->udp_baseaddr)) &
+	    (1 << ep->hwep_num)) {
+		writel((1 << ep->hwep_num),
+			     USBD_SYSERRTINTCLR(udc->udp_baseaddr));
+		ep_err(ep, "AHB critical error!\n");
+		ep->req_pending = 0;
+
+		/* The error could have occurred on a packet of a multipacket
+		 * transfer, so recovering the transfer is not possible. Close
+		 * the request with an error */
+		done(ep, req, -ECONNABORTED);
+		return;
+	}
+
+	/* Handle the current DD's status */
+	status = dd->dd_status;
+	switch (status & DD_STATUS_STS_MASK) {
+	case DD_STATUS_STS_NS:
+		/* DD not serviced? This shouldn't happen! */
+		ep->req_pending = 0;
+		ep_err(ep, "DMA critical EP error: DD not serviced (0x%x)!\n",
+		       status);
+
+		done(ep, req, -ECONNABORTED);
+		return;
+
+	case DD_STATUS_STS_BS:
+		/* Interrupt only fires on EOT - This shouldn't happen! */
+		ep->req_pending = 0;
+		ep_err(ep, "DMA critical EP error: EOT prior to service completion (0x%x)!\n",
+		       status);
+		done(ep, req, -ECONNABORTED);
+		return;
+
+	case DD_STATUS_STS_NC:
+	case DD_STATUS_STS_DUR:
+		/* Really just a short packet, not an underrun */
+		/* This is a good status and what we expect */
+		break;
+
+	default:
+		/* Data overrun, system error, or unknown */
+		ep->req_pending = 0;
+		ep_err(ep, "DMA critical EP error: System error (0x%x)!\n",
+		       status);
+		done(ep, req, -ECONNABORTED);
+		return;
+	}
+
+	/* ISO endpoints are handled differently */
+	if (ep->eptype == EP_ISO_TYPE) {
+		if (ep->is_in)
+			req->req.actual = req->req.length;
+		else
+			req->req.actual = dd->iso_status[0] & 0xFFFF;
+	} else
+		req->req.actual += DD_STATUS_CURDMACNT(status);
+
+	/* Send a ZLP if necessary. This will be done for non-int
+	 * packets which have a size that is a divisor of MAXP */
+	if (req->send_zlp) {
+		/*
+		 * If at least 1 buffer is available, send the ZLP now.
+		 * Otherwise, the ZLP send needs to be deferred until a
+		 * buffer is available.
+		 */
+		if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) {
+			udc_clearep_getsts(udc, ep->hwep_num);
+			uda_enable_hwepint(udc, ep->hwep_num);
+			epstatus = udc_clearep_getsts(udc, ep->hwep_num);
+
+			/* Let the EP interrupt handle the ZLP */
+			return;
+		} else
+			udc_send_in_zlp(udc, ep);
+	}
+
+	/* Transfer request is complete */
+	done(ep, req, 0);
+
+	/* Start another request if ready */
+	udc_clearep_getsts(udc, ep->hwep_num);
+	if (!list_empty((&ep->queue))) {
+		if (ep->is_in)
+			udc_ep_in_req_dma(udc, ep);
+		else
+			udc_ep_out_req_dma(udc, ep);
+	} else
+		ep->req_pending = 0;
+
+}
+
+/*
+ *
+ * Endpoint 0 functions
+ *
+ */
+static void udc_handle_dev(struct lpc32xx_udc *udc)
+{
+	u32 tmp;
+
+	udc_protocol_cmd_w(udc, CMD_GET_DEV_STAT);
+	tmp = udc_protocol_cmd_r(udc, DAT_GET_DEV_STAT);
+
+	if (tmp & DEV_RST)
+		uda_usb_reset(udc);
+	else if (tmp & DEV_CON_CH)
+		uda_power_event(udc, (tmp & DEV_CON));
+	else if (tmp & DEV_SUS_CH) {
+		if (tmp & DEV_SUS) {
+			if (udc->vbus == 0)
+				stop_activity(udc);
+			else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) &&
+				 udc->driver) {
+				/* Power down transceiver */
+				udc->poweron = 0;
+				schedule_work(&udc->pullup_job);
+				uda_resm_susp_event(udc, 1);
+			}
+		} else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) &&
+			   udc->driver && udc->vbus) {
+			uda_resm_susp_event(udc, 0);
+			/* Power up transceiver */
+			udc->poweron = 1;
+			schedule_work(&udc->pullup_job);
+		}
+	}
+}
+
+static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex)
+{
+	struct lpc32xx_ep *ep;
+	u32 ep0buff = 0, tmp;
+
+	switch (reqtype & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		break; /* Not supported */
+
+	case USB_RECIP_DEVICE:
+		ep0buff = udc->gadget.is_selfpowered;
+		if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP))
+			ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		tmp = wIndex & USB_ENDPOINT_NUMBER_MASK;
+		ep = &udc->ep[tmp];
+		if ((tmp == 0) || (tmp >= NUM_ENDPOINTS))
+			return -EOPNOTSUPP;
+
+		if (wIndex & USB_DIR_IN) {
+			if (!ep->is_in)
+				return -EOPNOTSUPP; /* Something's wrong */
+		} else if (ep->is_in)
+			return -EOPNOTSUPP; /* Not an IN endpoint */
+
+		/* Get status of the endpoint */
+		udc_protocol_cmd_w(udc, CMD_SEL_EP(ep->hwep_num));
+		tmp = udc_protocol_cmd_r(udc, DAT_SEL_EP(ep->hwep_num));
+
+		if (tmp & EP_SEL_ST)
+			ep0buff = (1 << USB_ENDPOINT_HALT);
+		else
+			ep0buff = 0;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Return data */
+	udc_write_hwep(udc, EP_IN, &ep0buff, 2);
+
+	return 0;
+}
+
+static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
+{
+	struct lpc32xx_ep *ep, *ep0 = &udc->ep[0];
+	struct usb_ctrlrequest ctrlpkt;
+	int i, bytes;
+	u16 wIndex, wValue, wLength, reqtype, req, tmp;
+
+	/* Nuke previous transfers */
+	nuke(ep0, -EPROTO);
+
+	/* Get setup packet */
+	bytes = udc_read_hwep(udc, EP_OUT, (u32 *) &ctrlpkt, 8);
+	if (bytes != 8) {
+		ep_warn(ep0, "Incorrectly sized setup packet (s/b 8, is %d)!\n",
+			bytes);
+		return;
+	}
+
+	/* Native endianness */
+	wIndex = le16_to_cpu(ctrlpkt.wIndex);
+	wValue = le16_to_cpu(ctrlpkt.wValue);
+	wLength = le16_to_cpu(ctrlpkt.wLength);
+	reqtype = le16_to_cpu(ctrlpkt.bRequestType);
+
+	/* Set direction of EP0 */
+	if (likely(reqtype & USB_DIR_IN))
+		ep0->is_in = 1;
+	else
+		ep0->is_in = 0;
+
+	/* Handle SETUP packet */
+	req = le16_to_cpu(ctrlpkt.bRequest);
+	switch (req) {
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		switch (reqtype) {
+		case (USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+			if (wValue != USB_DEVICE_REMOTE_WAKEUP)
+				goto stall; /* Nothing else handled */
+
+			/* Tell board about event */
+			if (req == USB_REQ_CLEAR_FEATURE)
+				udc->dev_status &=
+					~(1 << USB_DEVICE_REMOTE_WAKEUP);
+			else
+				udc->dev_status |=
+					(1 << USB_DEVICE_REMOTE_WAKEUP);
+			uda_remwkp_cgh(udc);
+			goto zlp_send;
+
+		case (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+			tmp = wIndex & USB_ENDPOINT_NUMBER_MASK;
+			if ((wValue != USB_ENDPOINT_HALT) ||
+			    (tmp >= NUM_ENDPOINTS))
+				break;
+
+			/* Find hardware endpoint from logical endpoint */
+			ep = &udc->ep[tmp];
+			tmp = ep->hwep_num;
+			if (tmp == 0)
+				break;
+
+			if (req == USB_REQ_SET_FEATURE)
+				udc_stall_hwep(udc, tmp);
+			else if (!ep->wedge)
+				udc_clrstall_hwep(udc, tmp);
+
+			goto zlp_send;
+
+		default:
+			break;
+		}
+
+
+	case USB_REQ_SET_ADDRESS:
+		if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
+			udc_set_address(udc, wValue);
+			goto zlp_send;
+		}
+		break;
+
+	case USB_REQ_GET_STATUS:
+		udc_get_status(udc, reqtype, wIndex);
+		return;
+
+	default:
+		break; /* Let GadgetFS handle the descriptor instead */
+	}
+
+	if (likely(udc->driver)) {
+		/* device-2-host (IN) or no data setup command, process
+		 * immediately */
+		spin_unlock(&udc->lock);
+		i = udc->driver->setup(&udc->gadget, &ctrlpkt);
+
+		spin_lock(&udc->lock);
+		if (req == USB_REQ_SET_CONFIGURATION) {
+			/* Configuration is set after endpoints are realized */
+			if (wValue) {
+				/* Set configuration */
+				udc_set_device_configured(udc);
+
+				udc_protocol_cmd_data_w(udc, CMD_SET_MODE,
+							DAT_WR_BYTE(AP_CLK |
+							INAK_BI | INAK_II));
+			} else {
+				/* Clear configuration */
+				udc_set_device_unconfigured(udc);
+
+				/* Disable NAK interrupts */
+				udc_protocol_cmd_data_w(udc, CMD_SET_MODE,
+							DAT_WR_BYTE(AP_CLK));
+			}
+		}
+
+		if (i < 0) {
+			/* setup processing failed, force stall */
+			dev_dbg(udc->dev,
+				"req %02x.%02x protocol STALL; stat %d\n",
+				reqtype, req, i);
+			udc->ep0state = WAIT_FOR_SETUP;
+			goto stall;
+		}
+	}
+
+	if (!ep0->is_in)
+		udc_ep0_send_zlp(udc); /* ZLP IN packet on data phase */
+
+	return;
+
+stall:
+	udc_stall_hwep(udc, EP_IN);
+	return;
+
+zlp_send:
+	udc_ep0_send_zlp(udc);
+	return;
+}
+
+/* IN endpoint 0 transfer */
+static void udc_handle_ep0_in(struct lpc32xx_udc *udc)
+{
+	struct lpc32xx_ep *ep0 = &udc->ep[0];
+	u32 epstatus;
+
+	/* Clear EP interrupt */
+	epstatus = udc_clearep_getsts(udc, EP_IN);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	ep0->totalints++;
+#endif
+
+	/* Stalled? Clear stall and reset buffers */
+	if (epstatus & EP_SEL_ST) {
+		udc_clrstall_hwep(udc, EP_IN);
+		nuke(ep0, -ECONNABORTED);
+		udc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	/* Is a buffer available? */
+	if (!(epstatus & EP_SEL_F)) {
+		/* Handle based on current state */
+		if (udc->ep0state == DATA_IN)
+			udc_ep0_in_req(udc);
+		else {
+			/* Unknown state for EP0 oe end of DATA IN phase */
+			nuke(ep0, -ECONNABORTED);
+			udc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+}
+
+/* OUT endpoint 0 transfer */
+static void udc_handle_ep0_out(struct lpc32xx_udc *udc)
+{
+	struct lpc32xx_ep *ep0 = &udc->ep[0];
+	u32 epstatus;
+
+	/* Clear EP interrupt */
+	epstatus = udc_clearep_getsts(udc, EP_OUT);
+
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+	ep0->totalints++;
+#endif
+
+	/* Stalled? */
+	if (epstatus & EP_SEL_ST) {
+		udc_clrstall_hwep(udc, EP_OUT);
+		nuke(ep0, -ECONNABORTED);
+		udc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	/* A NAK may occur if a packet couldn't be received yet */
+	if (epstatus & EP_SEL_EPN)
+		return;
+	/* Setup packet incoming? */
+	if (epstatus & EP_SEL_STP) {
+		nuke(ep0, 0);
+		udc->ep0state = WAIT_FOR_SETUP;
+	}
+
+	/* Data available? */
+	if (epstatus & EP_SEL_F)
+		/* Handle based on current state */
+		switch (udc->ep0state) {
+		case WAIT_FOR_SETUP:
+			udc_handle_ep0_setup(udc);
+			break;
+
+		case DATA_OUT:
+			udc_ep0_out_req(udc);
+			break;
+
+		default:
+			/* Unknown state for EP0 */
+			nuke(ep0, -ECONNABORTED);
+			udc->ep0state = WAIT_FOR_SETUP;
+		}
+}
+
+/* Must be called without lock */
+static int lpc32xx_get_frame(struct usb_gadget *gadget)
+{
+	int frame;
+	unsigned long flags;
+	struct lpc32xx_udc *udc = to_udc(gadget);
+
+	if (!udc->clocked)
+		return -EINVAL;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	frame = (int) udc_get_current_frame(udc);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return frame;
+}
+
+static int lpc32xx_wakeup(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on)
+{
+	gadget->is_selfpowered = (is_on != 0);
+
+	return 0;
+}
+
+/*
+ * vbus is here!  turn everything on that's ready
+ * Must be called without lock
+ */
+static int lpc32xx_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	unsigned long flags;
+	struct lpc32xx_udc *udc = to_udc(gadget);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* Doesn't need lock */
+	if (udc->driver) {
+		udc_clk_set(udc, 1);
+		udc_enable(udc);
+		pullup(udc, is_active);
+	} else {
+		stop_activity(udc);
+		pullup(udc, 0);
+
+		spin_unlock_irqrestore(&udc->lock, flags);
+		/*
+		 *  Wait for all the endpoints to disable,
+		 *  before disabling clocks. Don't wait if
+		 *  endpoints are not enabled.
+		 */
+		if (atomic_read(&udc->enabled_ep_cnt))
+			wait_event_interruptible(udc->ep_disable_wait_queue,
+				 (atomic_read(&udc->enabled_ep_cnt) == 0));
+
+		spin_lock_irqsave(&udc->lock, flags);
+
+		udc_clk_set(udc, 0);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* Can be called with or without lock */
+static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct lpc32xx_udc *udc = to_udc(gadget);
+
+	/* Doesn't need lock */
+	pullup(udc, is_on);
+
+	return 0;
+}
+
+static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *);
+static int lpc32xx_stop(struct usb_gadget *);
+
+static const struct usb_gadget_ops lpc32xx_udc_ops = {
+	.get_frame		= lpc32xx_get_frame,
+	.wakeup			= lpc32xx_wakeup,
+	.set_selfpowered	= lpc32xx_set_selfpowered,
+	.vbus_session		= lpc32xx_vbus_session,
+	.pullup			= lpc32xx_pullup,
+	.udc_start		= lpc32xx_start,
+	.udc_stop		= lpc32xx_stop,
+};
+
+static void nop_release(struct device *dev)
+{
+	/* nothing to free */
+}
+
+static const struct lpc32xx_udc controller_template = {
+	.gadget = {
+		.ops	= &lpc32xx_udc_ops,
+		.name	= driver_name,
+		.dev	= {
+			.init_name = "gadget",
+			.release = nop_release,
+		}
+	},
+	.ep[0] = {
+		.ep = {
+			.name	= "ep0",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 0,
+		.hwep_num	= 0, /* Can be 0 or 1, has special handling */
+		.lep		= 0,
+		.eptype		= EP_CTL_TYPE,
+	},
+	.ep[1] = {
+		.ep = {
+			.name	= "ep1-int",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_INT,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 2,
+		.hwep_num	= 0, /* 2 or 3, will be set later */
+		.lep		= 1,
+		.eptype		= EP_INT_TYPE,
+	},
+	.ep[2] = {
+		.ep = {
+			.name	= "ep2-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 4,
+		.hwep_num	= 0, /* 4 or 5, will be set later */
+		.lep		= 2,
+		.eptype		= EP_BLK_TYPE,
+	},
+	.ep[3] = {
+		.ep = {
+			.name	= "ep3-iso",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 1023,
+		.hwep_num_base	= 6,
+		.hwep_num	= 0, /* 6 or 7, will be set later */
+		.lep		= 3,
+		.eptype		= EP_ISO_TYPE,
+	},
+	.ep[4] = {
+		.ep = {
+			.name	= "ep4-int",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_INT,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 8,
+		.hwep_num	= 0, /* 8 or 9, will be set later */
+		.lep		= 4,
+		.eptype		= EP_INT_TYPE,
+	},
+	.ep[5] = {
+		.ep = {
+			.name	= "ep5-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 10,
+		.hwep_num	= 0, /* 10 or 11, will be set later */
+		.lep		= 5,
+		.eptype		= EP_BLK_TYPE,
+	},
+	.ep[6] = {
+		.ep = {
+			.name	= "ep6-iso",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 1023,
+		.hwep_num_base	= 12,
+		.hwep_num	= 0, /* 12 or 13, will be set later */
+		.lep		= 6,
+		.eptype		= EP_ISO_TYPE,
+	},
+	.ep[7] = {
+		.ep = {
+			.name	= "ep7-int",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_INT,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 14,
+		.hwep_num	= 0,
+		.lep		= 7,
+		.eptype		= EP_INT_TYPE,
+	},
+	.ep[8] = {
+		.ep = {
+			.name	= "ep8-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 16,
+		.hwep_num	= 0,
+		.lep		= 8,
+		.eptype		= EP_BLK_TYPE,
+	},
+	.ep[9] = {
+		.ep = {
+			.name	= "ep9-iso",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 1023,
+		.hwep_num_base	= 18,
+		.hwep_num	= 0,
+		.lep		= 9,
+		.eptype		= EP_ISO_TYPE,
+	},
+	.ep[10] = {
+		.ep = {
+			.name	= "ep10-int",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_INT,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 20,
+		.hwep_num	= 0,
+		.lep		= 10,
+		.eptype		= EP_INT_TYPE,
+	},
+	.ep[11] = {
+		.ep = {
+			.name	= "ep11-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 22,
+		.hwep_num	= 0,
+		.lep		= 11,
+		.eptype		= EP_BLK_TYPE,
+	},
+	.ep[12] = {
+		.ep = {
+			.name	= "ep12-iso",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 1023,
+		.hwep_num_base	= 24,
+		.hwep_num	= 0,
+		.lep		= 12,
+		.eptype		= EP_ISO_TYPE,
+	},
+	.ep[13] = {
+		.ep = {
+			.name	= "ep13-int",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_INT,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 26,
+		.hwep_num	= 0,
+		.lep		= 13,
+		.eptype		= EP_INT_TYPE,
+	},
+	.ep[14] = {
+		.ep = {
+			.name	= "ep14-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 64,
+		.hwep_num_base	= 28,
+		.hwep_num	= 0,
+		.lep		= 14,
+		.eptype		= EP_BLK_TYPE,
+	},
+	.ep[15] = {
+		.ep = {
+			.name	= "ep15-bulk",
+			.ops	= &lpc32xx_ep_ops,
+			.caps	= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+					USB_EP_CAPS_DIR_ALL),
+		},
+		.maxpacket	= 1023,
+		.hwep_num_base	= 30,
+		.hwep_num	= 0,
+		.lep		= 15,
+		.eptype		= EP_BLK_TYPE,
+	},
+};
+
+/* ISO and status interrupts */
+static irqreturn_t lpc32xx_usb_lp_irq(int irq, void *_udc)
+{
+	u32 tmp, devstat;
+	struct lpc32xx_udc *udc = _udc;
+
+	spin_lock(&udc->lock);
+
+	/* Read the device status register */
+	devstat = readl(USBD_DEVINTST(udc->udp_baseaddr));
+
+	devstat &= ~USBD_EP_FAST;
+	writel(devstat, USBD_DEVINTCLR(udc->udp_baseaddr));
+	devstat = devstat & udc->enabled_devints;
+
+	/* Device specific handling needed? */
+	if (devstat & USBD_DEV_STAT)
+		udc_handle_dev(udc);
+
+	/* Start of frame? (devstat & FRAME_INT):
+	 * The frame interrupt isn't really needed for ISO support,
+	 * as the driver will queue the necessary packets */
+
+	/* Error? */
+	if (devstat & ERR_INT) {
+		/* All types of errors, from cable removal during transfer to
+		 * misc protocol and bit errors. These are mostly for just info,
+		 * as the USB hardware will work around these. If these errors
+		 * happen alot, something is wrong. */
+		udc_protocol_cmd_w(udc, CMD_RD_ERR_STAT);
+		tmp = udc_protocol_cmd_r(udc, DAT_RD_ERR_STAT);
+		dev_dbg(udc->dev, "Device error (0x%x)!\n", tmp);
+	}
+
+	spin_unlock(&udc->lock);
+
+	return IRQ_HANDLED;
+}
+
+/* EP interrupts */
+static irqreturn_t lpc32xx_usb_hp_irq(int irq, void *_udc)
+{
+	u32 tmp;
+	struct lpc32xx_udc *udc = _udc;
+
+	spin_lock(&udc->lock);
+
+	/* Read the device status register */
+	writel(USBD_EP_FAST, USBD_DEVINTCLR(udc->udp_baseaddr));
+
+	/* Endpoints */
+	tmp = readl(USBD_EPINTST(udc->udp_baseaddr));
+
+	/* Special handling for EP0 */
+	if (tmp & (EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) {
+		/* Handle EP0 IN */
+		if (tmp & (EP_MASK_SEL(0, EP_IN)))
+			udc_handle_ep0_in(udc);
+
+		/* Handle EP0 OUT */
+		if (tmp & (EP_MASK_SEL(0, EP_OUT)))
+			udc_handle_ep0_out(udc);
+	}
+
+	/* All other EPs */
+	if (tmp & ~(EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) {
+		int i;
+
+		/* Handle other EP interrupts */
+		for (i = 1; i < NUM_ENDPOINTS; i++) {
+			if (tmp & (1 << udc->ep[i].hwep_num))
+				udc_handle_eps(udc, &udc->ep[i]);
+		}
+	}
+
+	spin_unlock(&udc->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc)
+{
+	struct lpc32xx_udc *udc = _udc;
+
+	int i;
+	u32 tmp;
+
+	spin_lock(&udc->lock);
+
+	/* Handle EP DMA EOT interrupts */
+	tmp = readl(USBD_EOTINTST(udc->udp_baseaddr)) |
+		(readl(USBD_EPDMAST(udc->udp_baseaddr)) &
+		 readl(USBD_NDDRTINTST(udc->udp_baseaddr))) |
+		readl(USBD_SYSERRTINTST(udc->udp_baseaddr));
+	for (i = 1; i < NUM_ENDPOINTS; i++) {
+		if (tmp & (1 << udc->ep[i].hwep_num))
+			udc_handle_dma_ep(udc, &udc->ep[i]);
+	}
+
+	spin_unlock(&udc->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ *
+ * VBUS detection, pullup handler, and Gadget cable state notification
+ *
+ */
+static void vbus_work(struct work_struct *work)
+{
+	u8 value;
+	struct lpc32xx_udc *udc = container_of(work, struct lpc32xx_udc,
+					       vbus_job);
+
+	if (udc->enabled != 0) {
+		/* Discharge VBUS real quick */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG);
+
+		/* Give VBUS some time (100mS) to discharge */
+		msleep(100);
+
+		/* Disable VBUS discharge resistor */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR,
+			OTG1_VBUS_DISCHRG);
+
+		/* Clear interrupt */
+		i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
+			ISP1301_I2C_INTERRUPT_LATCH |
+			ISP1301_I2C_REG_CLEAR_ADDR, ~0);
+
+		/* Get the VBUS status from the transceiver */
+		value = i2c_smbus_read_byte_data(udc->isp1301_i2c_client,
+						 ISP1301_I2C_INTERRUPT_SOURCE);
+
+		/* VBUS on or off? */
+		if (value & INT_SESS_VLD)
+			udc->vbus = 1;
+		else
+			udc->vbus = 0;
+
+		/* VBUS changed? */
+		if (udc->last_vbus != udc->vbus) {
+			udc->last_vbus = udc->vbus;
+			lpc32xx_vbus_session(&udc->gadget, udc->vbus);
+		}
+	}
+
+	/* Re-enable after completion */
+	enable_irq(udc->udp_irq[IRQ_USB_ATX]);
+}
+
+static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc)
+{
+	struct lpc32xx_udc *udc = _udc;
+
+	/* Defer handling of VBUS IRQ to work queue */
+	disable_irq_nosync(udc->udp_irq[IRQ_USB_ATX]);
+	schedule_work(&udc->vbus_job);
+
+	return IRQ_HANDLED;
+}
+
+static int lpc32xx_start(struct usb_gadget *gadget,
+			 struct usb_gadget_driver *driver)
+{
+	struct lpc32xx_udc *udc = to_udc(gadget);
+	int i;
+
+	if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) {
+		dev_err(udc->dev, "bad parameter.\n");
+		return -EINVAL;
+	}
+
+	if (udc->driver) {
+		dev_err(udc->dev, "UDC already has a gadget driver\n");
+		return -EBUSY;
+	}
+
+	udc->driver = driver;
+	udc->gadget.dev.of_node = udc->dev->of_node;
+	udc->enabled = 1;
+	udc->gadget.is_selfpowered = 1;
+	udc->vbus = 0;
+
+	/* Force VBUS process once to check for cable insertion */
+	udc->last_vbus = udc->vbus = 0;
+	schedule_work(&udc->vbus_job);
+
+	/* Do not re-enable ATX IRQ (3) */
+	for (i = IRQ_USB_LP; i < IRQ_USB_ATX; i++)
+		enable_irq(udc->udp_irq[i]);
+
+	return 0;
+}
+
+static int lpc32xx_stop(struct usb_gadget *gadget)
+{
+	int i;
+	struct lpc32xx_udc *udc = to_udc(gadget);
+
+	for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
+		disable_irq(udc->udp_irq[i]);
+
+	if (udc->clocked) {
+		spin_lock(&udc->lock);
+		stop_activity(udc);
+		spin_unlock(&udc->lock);
+
+		/*
+		 *  Wait for all the endpoints to disable,
+		 *  before disabling clocks. Don't wait if
+		 *  endpoints are not enabled.
+		 */
+		if (atomic_read(&udc->enabled_ep_cnt))
+			wait_event_interruptible(udc->ep_disable_wait_queue,
+				(atomic_read(&udc->enabled_ep_cnt) == 0));
+
+		spin_lock(&udc->lock);
+		udc_clk_set(udc, 0);
+		spin_unlock(&udc->lock);
+	}
+
+	udc->enabled = 0;
+	udc->driver = NULL;
+
+	return 0;
+}
+
+static void lpc32xx_udc_shutdown(struct platform_device *dev)
+{
+	/* Force disconnect on reboot */
+	struct lpc32xx_udc *udc = platform_get_drvdata(dev);
+
+	pullup(udc, 0);
+}
+
+/*
+ * Callbacks to be overridden by options passed via OF (TODO)
+ */
+
+static void lpc32xx_usbd_conn_chg(int conn)
+{
+	/* Do nothing, it might be nice to enable an LED
+	 * based on conn state being !0 */
+}
+
+static void lpc32xx_usbd_susp_chg(int susp)
+{
+	/* Device suspend if susp != 0 */
+}
+
+static void lpc32xx_rmwkup_chg(int remote_wakup_enable)
+{
+	/* Enable or disable USB remote wakeup */
+}
+
+struct lpc32xx_usbd_cfg lpc32xx_usbddata = {
+	.vbus_drv_pol = 0,
+	.conn_chgb = &lpc32xx_usbd_conn_chg,
+	.susp_chgb = &lpc32xx_usbd_susp_chg,
+	.rmwk_chgb = &lpc32xx_rmwkup_chg,
+};
+
+
+static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F;
+
+static int lpc32xx_udc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct lpc32xx_udc *udc;
+	int retval, i;
+	struct resource *res;
+	dma_addr_t dma_handle;
+	struct device_node *isp1301_node;
+
+	udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	for (i = 0; i <= 15; i++)
+		udc->ep[i].udc = udc;
+	udc->gadget.ep0 = &udc->ep[0].ep;
+
+	/* init software state */
+	udc->gadget.dev.parent = dev;
+	udc->pdev = pdev;
+	udc->dev = &pdev->dev;
+	udc->enabled = 0;
+
+	if (pdev->dev.of_node) {
+		isp1301_node = of_parse_phandle(pdev->dev.of_node,
+						"transceiver", 0);
+	} else {
+		isp1301_node = NULL;
+	}
+
+	udc->isp1301_i2c_client = isp1301_get_client(isp1301_node);
+	if (!udc->isp1301_i2c_client) {
+		retval = -EPROBE_DEFER;
+		goto phy_fail;
+	}
+
+	dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n",
+		 udc->isp1301_i2c_client->addr);
+
+	pdev->dev.dma_mask = &lpc32xx_usbd_dmamask;
+	retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (retval)
+		goto resource_fail;
+
+	udc->board = &lpc32xx_usbddata;
+
+	/*
+	 * Resources are mapped as follows:
+	 *  IORESOURCE_MEM, base address and size of USB space
+	 *  IORESOURCE_IRQ, USB device low priority interrupt number
+	 *  IORESOURCE_IRQ, USB device high priority interrupt number
+	 *  IORESOURCE_IRQ, USB device interrupt number
+	 *  IORESOURCE_IRQ, USB transceiver interrupt number
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		retval = -ENXIO;
+		goto resource_fail;
+	}
+
+	spin_lock_init(&udc->lock);
+
+	/* Get IRQs */
+	for (i = 0; i < 4; i++) {
+		udc->udp_irq[i] = platform_get_irq(pdev, i);
+		if (udc->udp_irq[i] < 0) {
+			dev_err(udc->dev,
+				"irq resource %d not available!\n", i);
+			retval = udc->udp_irq[i];
+			goto irq_fail;
+		}
+	}
+
+	udc->io_p_start = res->start;
+	udc->io_p_size = resource_size(res);
+	if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) {
+		dev_err(udc->dev, "someone's using UDC memory\n");
+		retval = -EBUSY;
+		goto request_mem_region_fail;
+	}
+
+	udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size);
+	if (!udc->udp_baseaddr) {
+		retval = -ENOMEM;
+		dev_err(udc->dev, "IO map failure\n");
+		goto io_map_fail;
+	}
+
+	/* Get USB device clock */
+	udc->usb_slv_clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(udc->usb_slv_clk)) {
+		dev_err(udc->dev, "failed to acquire USB device clock\n");
+		retval = PTR_ERR(udc->usb_slv_clk);
+		goto usb_clk_get_fail;
+	}
+
+	/* Enable USB device clock */
+	retval = clk_prepare_enable(udc->usb_slv_clk);
+	if (retval < 0) {
+		dev_err(udc->dev, "failed to start USB device clock\n");
+		goto usb_clk_enable_fail;
+	}
+
+	/* Setup deferred workqueue data */
+	udc->poweron = udc->pullup = 0;
+	INIT_WORK(&udc->pullup_job, pullup_work);
+	INIT_WORK(&udc->vbus_job, vbus_work);
+#ifdef CONFIG_PM
+	INIT_WORK(&udc->power_job, power_work);
+#endif
+
+	/* All clocks are now on */
+	udc->clocked = 1;
+
+	isp1301_udc_configure(udc);
+	/* Allocate memory for the UDCA */
+	udc->udca_v_base = dma_alloc_coherent(&pdev->dev, UDCA_BUFF_SIZE,
+					      &dma_handle,
+					      (GFP_KERNEL | GFP_DMA));
+	if (!udc->udca_v_base) {
+		dev_err(udc->dev, "error getting UDCA region\n");
+		retval = -ENOMEM;
+		goto i2c_fail;
+	}
+	udc->udca_p_base = dma_handle;
+	dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n",
+		UDCA_BUFF_SIZE, udc->udca_p_base, udc->udca_v_base);
+
+	/* Setup the DD DMA memory pool */
+	udc->dd_cache = dma_pool_create("udc_dd", udc->dev,
+					sizeof(struct lpc32xx_usbd_dd_gad),
+					sizeof(u32), 0);
+	if (!udc->dd_cache) {
+		dev_err(udc->dev, "error getting DD DMA region\n");
+		retval = -ENOMEM;
+		goto dma_alloc_fail;
+	}
+
+	/* Clear USB peripheral and initialize gadget endpoints */
+	udc_disable(udc);
+	udc_reinit(udc);
+
+	/* Request IRQs - low and high priority USB device IRQs are routed to
+	 * the same handler, while the DMA interrupt is routed elsewhere */
+	retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq,
+			     0, "udc_lp", udc);
+	if (retval < 0) {
+		dev_err(udc->dev, "LP request irq %d failed\n",
+			udc->udp_irq[IRQ_USB_LP]);
+		goto irq_lp_fail;
+	}
+	retval = request_irq(udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq,
+			     0, "udc_hp", udc);
+	if (retval < 0) {
+		dev_err(udc->dev, "HP request irq %d failed\n",
+			udc->udp_irq[IRQ_USB_HP]);
+		goto irq_hp_fail;
+	}
+
+	retval = request_irq(udc->udp_irq[IRQ_USB_DEVDMA],
+			     lpc32xx_usb_devdma_irq, 0, "udc_dma", udc);
+	if (retval < 0) {
+		dev_err(udc->dev, "DEV request irq %d failed\n",
+			udc->udp_irq[IRQ_USB_DEVDMA]);
+		goto irq_dev_fail;
+	}
+
+	/* The transceiver interrupt is used for VBUS detection and will
+	   kick off the VBUS handler function */
+	retval = request_irq(udc->udp_irq[IRQ_USB_ATX], lpc32xx_usb_vbus_irq,
+			     0, "udc_otg", udc);
+	if (retval < 0) {
+		dev_err(udc->dev, "VBUS request irq %d failed\n",
+			udc->udp_irq[IRQ_USB_ATX]);
+		goto irq_xcvr_fail;
+	}
+
+	/* Initialize wait queue */
+	init_waitqueue_head(&udc->ep_disable_wait_queue);
+	atomic_set(&udc->enabled_ep_cnt, 0);
+
+	/* Keep all IRQs disabled until GadgetFS starts up */
+	for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
+		disable_irq(udc->udp_irq[i]);
+
+	retval = usb_add_gadget_udc(dev, &udc->gadget);
+	if (retval < 0)
+		goto add_gadget_fail;
+
+	dev_set_drvdata(dev, udc);
+	device_init_wakeup(dev, 1);
+	create_debug_file(udc);
+
+	/* Disable clocks for now */
+	udc_clk_set(udc, 0);
+
+	dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION);
+	return 0;
+
+add_gadget_fail:
+	free_irq(udc->udp_irq[IRQ_USB_ATX], udc);
+irq_xcvr_fail:
+	free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc);
+irq_dev_fail:
+	free_irq(udc->udp_irq[IRQ_USB_HP], udc);
+irq_hp_fail:
+	free_irq(udc->udp_irq[IRQ_USB_LP], udc);
+irq_lp_fail:
+	dma_pool_destroy(udc->dd_cache);
+dma_alloc_fail:
+	dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
+			  udc->udca_v_base, udc->udca_p_base);
+i2c_fail:
+	clk_disable_unprepare(udc->usb_slv_clk);
+usb_clk_enable_fail:
+	clk_put(udc->usb_slv_clk);
+usb_clk_get_fail:
+	iounmap(udc->udp_baseaddr);
+io_map_fail:
+	release_mem_region(udc->io_p_start, udc->io_p_size);
+	dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval);
+request_mem_region_fail:
+irq_fail:
+resource_fail:
+phy_fail:
+	kfree(udc);
+	return retval;
+}
+
+static int lpc32xx_udc_remove(struct platform_device *pdev)
+{
+	struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&udc->gadget);
+	if (udc->driver)
+		return -EBUSY;
+
+	udc_clk_set(udc, 1);
+	udc_disable(udc);
+	pullup(udc, 0);
+
+	free_irq(udc->udp_irq[IRQ_USB_ATX], udc);
+
+	device_init_wakeup(&pdev->dev, 0);
+	remove_debug_file(udc);
+
+	dma_pool_destroy(udc->dd_cache);
+	dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
+			  udc->udca_v_base, udc->udca_p_base);
+	free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc);
+	free_irq(udc->udp_irq[IRQ_USB_HP], udc);
+	free_irq(udc->udp_irq[IRQ_USB_LP], udc);
+
+	clk_disable_unprepare(udc->usb_slv_clk);
+	clk_put(udc->usb_slv_clk);
+
+	iounmap(udc->udp_baseaddr);
+	release_mem_region(udc->io_p_start, udc->io_p_size);
+	kfree(udc);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+	struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
+
+	if (udc->clocked) {
+		/* Power down ISP */
+		udc->poweron = 0;
+		isp1301_set_powerstate(udc, 0);
+
+		/* Disable clocking */
+		udc_clk_set(udc, 0);
+
+		/* Keep clock flag on, so we know to re-enable clocks
+		   on resume */
+		udc->clocked = 1;
+
+		/* Kill global USB clock */
+		clk_disable_unprepare(udc->usb_slv_clk);
+	}
+
+	return 0;
+}
+
+static int lpc32xx_udc_resume(struct platform_device *pdev)
+{
+	struct lpc32xx_udc *udc = platform_get_drvdata(pdev);
+
+	if (udc->clocked) {
+		/* Enable global USB clock */
+		clk_prepare_enable(udc->usb_slv_clk);
+
+		/* Enable clocking */
+		udc_clk_set(udc, 1);
+
+		/* ISP back to normal power mode */
+		udc->poweron = 1;
+		isp1301_set_powerstate(udc, 1);
+	}
+
+	return 0;
+}
+#else
+#define	lpc32xx_udc_suspend	NULL
+#define	lpc32xx_udc_resume	NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_udc_of_match[] = {
+	{ .compatible = "nxp,lpc3220-udc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match);
+#endif
+
+static struct platform_driver lpc32xx_udc_driver = {
+	.remove		= lpc32xx_udc_remove,
+	.shutdown	= lpc32xx_udc_shutdown,
+	.suspend	= lpc32xx_udc_suspend,
+	.resume		= lpc32xx_udc_resume,
+	.driver		= {
+		.name	= (char *) driver_name,
+		.of_match_table = of_match_ptr(lpc32xx_udc_of_match),
+	},
+};
+
+module_platform_driver_probe(lpc32xx_udc_driver, lpc32xx_udc_probe);
+
+MODULE_DESCRIPTION("LPC32XX udc driver");
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lpc32xx_udc");
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
new file mode 100644
index 0000000..a8288df
--- /dev/null
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -0,0 +1,1698 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "m66592-udc.h"
+
+MODULE_DESCRIPTION("M66592 USB gadget driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_ALIAS("platform:m66592_udc");
+
+#define DRIVER_VERSION	"21 July 2009"
+
+static const char udc_name[] = "m66592_udc";
+static const char *m66592_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
+};
+
+static void disable_controller(struct m66592 *m66592);
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags);
+
+static void transfer_complete(struct m66592_ep *ep,
+		struct m66592_request *req, int status);
+
+/*-------------------------------------------------------------------------*/
+static inline u16 get_usb_speed(struct m66592 *m66592)
+{
+	return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);
+}
+
+static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+		unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+			M66592_INTENB0);
+	m66592_bset(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+		unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+			M66592_INTENB0);
+	m66592_bclr(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void m66592_usb_connect(struct m66592 *m66592)
+{
+	m66592_bset(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+			M66592_INTENB0);
+	m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+
+	m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
+}
+
+static void m66592_usb_disconnect(struct m66592 *m66592)
+__releases(m66592->lock)
+__acquires(m66592->lock)
+{
+	m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+			M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+
+	m66592->gadget.speed = USB_SPEED_UNKNOWN;
+	spin_unlock(&m66592->lock);
+	m66592->driver->disconnect(&m66592->gadget);
+	spin_lock(&m66592->lock);
+
+	disable_controller(m66592);
+	INIT_LIST_HEAD(&m66592->ep[0].queue);
+}
+
+static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum)
+{
+	u16 pid = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID;
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		pid = m66592_read(m66592, offset) & M66592_PID;
+	} else
+		pr_err("unexpect pipe num (%d)\n", pipenum);
+
+	return pid;
+}
+
+static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum,
+		u16 pid)
+{
+	unsigned long offset;
+
+	if (pipenum == 0)
+		m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_mdfy(m66592, pid, M66592_PID, offset);
+	} else
+		pr_err("unexpect pipe num (%d)\n", pipenum);
+}
+
+static inline void pipe_start(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);
+}
+
+static inline void pipe_stop(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);
+}
+
+static inline void pipe_stall(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);
+}
+
+static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum)
+{
+	u16 ret = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		ret = m66592_read(m66592, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		ret = m66592_read(m66592, offset);
+	} else
+		pr_err("unexpect pipe num (%d)\n", pipenum);
+
+	return ret;
+}
+
+static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum)
+{
+	unsigned long offset;
+
+	pipe_stop(m66592, pipenum);
+
+	if (pipenum == 0)
+		m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_bset(m66592, M66592_SQCLR, offset);
+	} else
+		pr_err("unexpect pipe num(%d)\n", pipenum);
+}
+
+static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
+{
+	u16 tmp;
+	int size;
+
+	if (pipenum == 0) {
+		tmp = m66592_read(m66592, M66592_DCPCFG);
+		if ((tmp & M66592_CNTMD) != 0)
+			size = 256;
+		else {
+			tmp = m66592_read(m66592, M66592_DCPMAXP);
+			size = tmp & M66592_MAXP;
+		}
+	} else {
+		m66592_write(m66592, pipenum, M66592_PIPESEL);
+		tmp = m66592_read(m66592, M66592_PIPECFG);
+		if ((tmp & M66592_CNTMD) != 0) {
+			tmp = m66592_read(m66592, M66592_PIPEBUF);
+			size = ((tmp >> 10) + 1) * 64;
+		} else {
+			tmp = m66592_read(m66592, M66592_PIPEMAXP);
+			size = tmp & M66592_MXPS;
+		}
+	}
+
+	return size;
+}
+
+static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
+{
+	struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
+	unsigned short mbw;
+
+	if (ep->use_dma)
+		return;
+
+	m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel);
+
+	ndelay(450);
+
+	if (m66592->pdata->on_chip)
+		mbw = M66592_MBW_32;
+	else
+		mbw = M66592_MBW_16;
+
+	m66592_bset(m66592, mbw, ep->fifosel);
+}
+
+static int pipe_buffer_setting(struct m66592 *m66592,
+		struct m66592_pipe_info *info)
+{
+	u16 bufnum = 0, buf_bsize = 0;
+	u16 pipecfg = 0;
+
+	if (info->pipe == 0)
+		return -EINVAL;
+
+	m66592_write(m66592, info->pipe, M66592_PIPESEL);
+
+	if (info->dir_in)
+		pipecfg |= M66592_DIR;
+	pipecfg |= info->type;
+	pipecfg |= info->epnum;
+	switch (info->type) {
+	case M66592_INT:
+		bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT);
+		buf_bsize = 0;
+		break;
+	case M66592_BULK:
+		/* isochronous pipes may be used as bulk pipes */
+		if (info->pipe >= M66592_BASE_PIPENUM_BULK)
+			bufnum = info->pipe - M66592_BASE_PIPENUM_BULK;
+		else
+			bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC;
+
+		bufnum = M66592_BASE_BUFNUM + (bufnum * 16);
+		buf_bsize = 7;
+		pipecfg |= M66592_DBLB;
+		if (!info->dir_in)
+			pipecfg |= M66592_SHTNAK;
+		break;
+	case M66592_ISO:
+		bufnum = M66592_BASE_BUFNUM +
+			 (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
+		buf_bsize = 7;
+		break;
+	}
+
+	if (buf_bsize && ((bufnum + 16) >= M66592_MAX_BUFNUM)) {
+		pr_err("m66592 pipe memory is insufficient\n");
+		return -ENOMEM;
+	}
+
+	m66592_write(m66592, pipecfg, M66592_PIPECFG);
+	m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF);
+	m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP);
+	if (info->interval)
+		info->interval--;
+	m66592_write(m66592, info->interval, M66592_PIPEPERI);
+
+	return 0;
+}
+
+static void pipe_buffer_release(struct m66592 *m66592,
+				struct m66592_pipe_info *info)
+{
+	if (info->pipe == 0)
+		return;
+
+	if (is_bulk_pipe(info->pipe)) {
+		m66592->bulk--;
+	} else if (is_interrupt_pipe(info->pipe))
+		m66592->interrupt--;
+	else if (is_isoc_pipe(info->pipe)) {
+		m66592->isochronous--;
+		if (info->type == M66592_BULK)
+			m66592->bulk--;
+	} else
+		pr_err("ep_release: unexpect pipenum (%d)\n",
+				info->pipe);
+}
+
+static void pipe_initialize(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	unsigned short mbw;
+
+	m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
+
+	m66592_write(m66592, M66592_ACLRM, ep->pipectr);
+	m66592_write(m66592, 0, ep->pipectr);
+	m66592_write(m66592, M66592_SQCLR, ep->pipectr);
+	if (ep->use_dma) {
+		m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel);
+
+		ndelay(450);
+
+		if (m66592->pdata->on_chip)
+			mbw = M66592_MBW_32;
+		else
+			mbw = M66592_MBW_16;
+
+		m66592_bset(m66592, mbw, ep->fifosel);
+	}
+}
+
+static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
+		const struct usb_endpoint_descriptor *desc,
+		u16 pipenum, int dma)
+{
+	if ((pipenum != 0) && dma) {
+		if (m66592->num_dma == 0) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D0FIFO;
+			ep->fifosel = M66592_D0FIFOSEL;
+			ep->fifoctr = M66592_D0FIFOCTR;
+			ep->fifotrn = M66592_D0FIFOTRN;
+		} else if (!m66592->pdata->on_chip && m66592->num_dma == 1) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D1FIFO;
+			ep->fifosel = M66592_D1FIFOSEL;
+			ep->fifoctr = M66592_D1FIFOCTR;
+			ep->fifotrn = M66592_D1FIFOTRN;
+		} else {
+			ep->use_dma = 0;
+			ep->fifoaddr = M66592_CFIFO;
+			ep->fifosel = M66592_CFIFOSEL;
+			ep->fifoctr = M66592_CFIFOCTR;
+			ep->fifotrn = 0;
+		}
+	} else {
+		ep->use_dma = 0;
+		ep->fifoaddr = M66592_CFIFO;
+		ep->fifosel = M66592_CFIFOSEL;
+		ep->fifoctr = M66592_CFIFOCTR;
+		ep->fifotrn = 0;
+	}
+
+	ep->pipectr = get_pipectr_addr(pipenum);
+	ep->pipenum = pipenum;
+	ep->ep.maxpacket = usb_endpoint_maxp(desc);
+	m66592->pipenum2ep[pipenum] = ep;
+	m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
+	INIT_LIST_HEAD(&ep->queue);
+}
+
+static void m66592_ep_release(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (pipenum == 0)
+		return;
+
+	if (ep->use_dma)
+		m66592->num_dma--;
+	ep->pipenum = 0;
+	ep->busy = 0;
+	ep->use_dma = 0;
+}
+
+static int alloc_pipe_config(struct m66592_ep *ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+	int dma = 0;
+	int *counter;
+	int ret;
+
+	ep->ep.desc = desc;
+
+	BUG_ON(ep->pipenum);
+
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (m66592->bulk >= M66592_MAX_NUM_BULK) {
+			if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+				pr_err("bulk pipe is insufficient\n");
+				return -ENODEV;
+			} else {
+				info.pipe = M66592_BASE_PIPENUM_ISOC
+						+ m66592->isochronous;
+				counter = &m66592->isochronous;
+			}
+		} else {
+			info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk;
+			counter = &m66592->bulk;
+		}
+		info.type = M66592_BULK;
+		dma = 1;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (m66592->interrupt >= M66592_MAX_NUM_INT) {
+			pr_err("interrupt pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt;
+		info.type = M66592_INT;
+		counter = &m66592->interrupt;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+			pr_err("isochronous pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous;
+		info.type = M66592_ISO;
+		counter = &m66592->isochronous;
+		break;
+	default:
+		pr_err("unexpect xfer type\n");
+		return -EINVAL;
+	}
+	ep->type = info.type;
+
+	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	info.maxpacket = usb_endpoint_maxp(desc);
+	info.interval = desc->bInterval;
+	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		info.dir_in = 1;
+	else
+		info.dir_in = 0;
+
+	ret = pipe_buffer_setting(m66592, &info);
+	if (ret < 0) {
+		pr_err("pipe_buffer_setting fail\n");
+		return ret;
+	}
+
+	(*counter)++;
+	if ((counter == &m66592->isochronous) && info.type == M66592_BULK)
+		m66592->bulk++;
+
+	m66592_ep_setting(m66592, ep, desc, info.pipe, dma);
+	pipe_initialize(ep);
+
+	return 0;
+}
+
+static int free_pipe_config(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+
+	info.pipe = ep->pipenum;
+	info.type = ep->type;
+	pipe_buffer_release(m66592, &info);
+	m66592_ep_release(ep);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum)
+{
+	enable_irq_ready(m66592, pipenum);
+	enable_irq_nrdy(m66592, pipenum);
+}
+
+static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum)
+{
+	disable_irq_ready(m66592, pipenum);
+	disable_irq_nrdy(m66592, pipenum);
+}
+
+/* if complete is true, gadget driver complete function is not call */
+static void control_end(struct m66592 *m66592, unsigned ccpl)
+{
+	m66592->ep[0].internal_ccpl = ccpl;
+	pipe_start(m66592, 0);
+	m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);
+}
+
+static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, ep->pipenum);
+	m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0,
+			(M66592_ISEL | M66592_CURPIPE),
+			M66592_CFIFOSEL);
+	m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+	if (req->req.length == 0) {
+		m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+		pipe_start(m66592, 0);
+		transfer_complete(ep, req, 0);
+	} else {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+		irq_ep0_write(ep, req);
+	}
+}
+
+static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 tmp;
+
+	pipe_change(m66592, ep->pipenum);
+	disable_irq_empty(m66592, ep->pipenum);
+	pipe_start(m66592, ep->pipenum);
+
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0))
+		pipe_irq_enable(m66592, ep->pipenum);
+	else
+		irq_packet_write(ep, req);
+}
+
+static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (ep->pipenum == 0) {
+		m66592_mdfy(m66592, M66592_PIPE0,
+				(M66592_ISEL | M66592_CURPIPE),
+				M66592_CFIFOSEL);
+		m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		pipe_start(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	} else {
+		if (ep->use_dma) {
+			m66592_bset(m66592, M66592_TRCLR, ep->fifosel);
+			pipe_change(m66592, pipenum);
+			m66592_bset(m66592, M66592_TRENB, ep->fifosel);
+			m66592_write(m66592,
+				(req->req.length + ep->ep.maxpacket - 1)
+					/ ep->ep.maxpacket,
+				ep->fifotrn);
+		}
+		pipe_start(m66592, pipenum);	/* trigger once */
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void start_packet(struct m66592_ep *ep, struct m66592_request *req)
+{
+	if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
+		start_packet_write(ep, req);
+	else
+		start_packet_read(ep, req);
+}
+
+static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 ctsq;
+
+	ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ;
+
+	switch (ctsq) {
+	case M66592_CS_RDDS:
+		start_ep0_write(ep, req);
+		break;
+	case M66592_CS_WRDS:
+		start_packet_read(ep, req);
+		break;
+
+	case M66592_CS_WRND:
+		control_end(ep->m66592, 0);
+		break;
+	default:
+		pr_err("start_ep0: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static void init_controller(struct m66592 *m66592)
+{
+	unsigned int endian;
+
+	if (m66592->pdata->on_chip) {
+		if (m66592->pdata->endian)
+			endian = 0; /* big endian */
+		else
+			endian = M66592_LITTLE; /* little endian */
+
+		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
+		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+
+		/* This is a workaound for SH7722 2nd cut */
+		m66592_bset(m66592, 0x8000, M66592_DVSTCTR);
+		m66592_bset(m66592, 0x1000, M66592_TESTMODE);
+		m66592_bclr(m66592, 0x8000, M66592_DVSTCTR);
+
+		m66592_bset(m66592, M66592_INTL, M66592_INTENB1);
+
+		m66592_write(m66592, 0, M66592_CFBCFG);
+		m66592_write(m66592, 0, M66592_D0FBCFG);
+		m66592_bset(m66592, endian, M66592_CFBCFG);
+		m66592_bset(m66592, endian, M66592_D0FBCFG);
+	} else {
+		unsigned int clock, vif, irq_sense;
+
+		if (m66592->pdata->endian)
+			endian = M66592_BIGEND; /* big endian */
+		else
+			endian = 0; /* little endian */
+
+		if (m66592->pdata->vif)
+			vif = M66592_LDRV; /* 3.3v */
+		else
+			vif = 0; /* 1.5v */
+
+		switch (m66592->pdata->xtal) {
+		case M66592_PLATDATA_XTAL_12MHZ:
+			clock = M66592_XTAL12;
+			break;
+		case M66592_PLATDATA_XTAL_24MHZ:
+			clock = M66592_XTAL24;
+			break;
+		case M66592_PLATDATA_XTAL_48MHZ:
+			clock = M66592_XTAL48;
+			break;
+		default:
+			pr_warn("m66592-udc: xtal configuration error\n");
+			clock = 0;
+		}
+
+		switch (m66592->irq_trigger) {
+		case IRQF_TRIGGER_LOW:
+			irq_sense = M66592_INTL;
+			break;
+		case IRQF_TRIGGER_FALLING:
+			irq_sense = 0;
+			break;
+		default:
+			pr_warn("m66592-udc: irq trigger config error\n");
+			irq_sense = 0;
+		}
+
+		m66592_bset(m66592,
+			    (vif & M66592_LDRV) | (endian & M66592_BIGEND),
+			    M66592_PINCFG);
+		m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);	/* High spd */
+		m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL,
+			    M66592_SYSCFG);
+		m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+		m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+		m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+
+		m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+
+		msleep(3);
+
+		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+
+		msleep(1);
+
+		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+
+		m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
+		m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
+			     M66592_DMA0CFG);
+	}
+}
+
+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);
+		m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
+		udelay(1);
+		m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
+		udelay(1);
+		m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
+	}
+}
+
+static void m66592_start_xclock(struct m66592 *m66592)
+{
+	u16 tmp;
+
+	if (!m66592->pdata->on_chip) {
+		tmp = m66592_read(m66592, M66592_SYSCFG);
+		if (!(tmp & M66592_XCKE))
+			m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+static void transfer_complete(struct m66592_ep *ep,
+		struct m66592_request *req, int status)
+__releases(m66592->lock)
+__acquires(m66592->lock)
+{
+	int restart = 0;
+
+	if (unlikely(ep->pipenum == 0)) {
+		if (ep->internal_ccpl) {
+			ep->internal_ccpl = 0;
+			return;
+		}
+	}
+
+	list_del_init(&req->queue);
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	if (!list_empty(&ep->queue))
+		restart = 1;
+
+	spin_unlock(&ep->m66592->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&ep->m66592->lock);
+
+	if (restart) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		if (ep->ep.desc)
+			start_packet(ep, req);
+	}
+}
+
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	int i;
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	m66592_bset(m66592, M66592_ISEL, ep->fifosel);
+
+	i = 0;
+	do {
+		tmp = m66592_read(m66592, ep->fifoctr);
+		if (i++ > 100000) {
+			pr_err("pipe0 is busy. maybe cpu i/o bus "
+				"conflict. please power off this controller.");
+			return;
+		}
+		ndelay(1);
+	} while ((tmp & M66592_FRDY) == 0);
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		if (size > 0)
+			m66592_write_fifo(m66592, ep, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		disable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	}
+	pipe_start(m66592, pipenum);
+}
+
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		pr_err("write fifo not ready. pipnum=%d\n", pipenum);
+		return;
+	}
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		m66592_write_fifo(m66592, ep, buf, size);
+		if ((size == 0)
+				|| ((size % ep->ep.maxpacket) != 0)
+				|| ((bufsize != ep->ep.maxpacket)
+					&& (bufsize > size)))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_empty(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	int rcv_len, bufsize, req_len;
+	int size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+	int finish = 0;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		req->req.status = -EPIPE;
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		pr_err("read fifo not ready");
+		return;
+	}
+
+	/* prepare parameters */
+	rcv_len = tmp & M66592_DTLN;
+	bufsize = get_buffer_size(m66592, pipenum);
+
+	buf = req->req.buf + req->req.actual;
+	req_len = req->req.length - req->req.actual;
+	if (rcv_len < bufsize)
+		size = min(rcv_len, req_len);
+	else
+		size = min(bufsize, req_len);
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		finish = 1;
+	}
+
+	/* read fifo */
+	if (req->req.buf) {
+		if (size == 0)
+			m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		else
+			m66592_read_fifo(m66592, ep->fifoaddr, buf, size);
+	}
+
+	if ((ep->pipenum != 0) && finish)
+		transfer_complete(ep, req, 0);
+}
+
+static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) {
+		m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS);
+		m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE,
+				M66592_CFIFOSEL);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_packet_read(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BRDYSTS);
+				ep = m66592->pipenum2ep[pipenum];
+				req = list_entry(ep->queue.next,
+						 struct m66592_request, queue);
+				if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
+					irq_packet_write(ep, req);
+				else
+					irq_packet_read(ep, req);
+			}
+		}
+	}
+}
+
+static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 tmp;
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_ep0_write(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BEMPSTS);
+				tmp = control_reg_get(m66592, pipenum);
+				if ((tmp & M66592_INBUFM) == 0) {
+					disable_irq_empty(m66592, pipenum);
+					pipe_irq_disable(m66592, pipenum);
+					pipe_stop(m66592, pipenum);
+					ep = m66592->pipenum2ep[pipenum];
+					req = list_entry(ep->queue.next,
+							 struct m66592_request,
+							 queue);
+					if (!list_empty(&ep->queue))
+						transfer_complete(ep, req, 0);
+				}
+			}
+		}
+	}
+}
+
+static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+__releases(m66592->lock)
+__acquires(m66592->lock)
+{
+	struct m66592_ep *ep;
+	u16 pid;
+	u16 status = 0;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		status = 1 << USB_DEVICE_SELF_POWERED;
+		break;
+	case USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		pid = control_reg_get_pid(m66592, ep->pipenum);
+		if (pid == M66592_PID_STALL)
+			status = 1 << USB_ENDPOINT_HALT;
+		else
+			status = 0;
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		return;		/* exit */
+	}
+
+	m66592->ep0_data = cpu_to_le16(status);
+	m66592->ep0_req->buf = &m66592->ep0_data;
+	m66592->ep0_req->length = 2;
+	/* AV: what happens if we get called again before that gets through? */
+	spin_unlock(&m66592->lock);
+	m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL);
+	spin_lock(&m66592->lock);
+}
+
+static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+		u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		pipe_stop(m66592, ep->pipenum);
+		control_reg_sqclr(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+
+		req = list_entry(ep->queue.next,
+		struct m66592_request, queue);
+		if (ep->busy) {
+			ep->busy = 0;
+			if (list_empty(&ep->queue))
+				break;
+			start_packet(ep, req);
+		} else if (!list_empty(&ep->queue))
+			pipe_start(m66592, ep->pipenum);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+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:
+		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);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+		u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+		ep = m66592->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		pipe_stall(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+/* if return value is true, call class driver's setup() */
+static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	u16 *p = (u16 *)ctrl;
+	unsigned long offset = M66592_USBREQ;
+	int i, ret = 0;
+
+	/* read fifo */
+	m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0);
+
+	for (i = 0; i < 4; i++)
+		p[i] = m66592_read(m66592, offset + i*2);
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			get_status(m66592, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			clear_feature(m66592, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			set_feature(m66592, ctrl);
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else
+		ret = 1;
+	return ret;
+}
+
+static void m66592_update_usb_speed(struct m66592 *m66592)
+{
+	u16 speed = get_usb_speed(m66592);
+
+	switch (speed) {
+	case M66592_HSMODE:
+		m66592->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case M66592_FSMODE:
+		m66592->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		m66592->gadget.speed = USB_SPEED_UNKNOWN;
+		pr_err("USB speed unknown\n");
+	}
+}
+
+static void irq_device_state(struct m66592 *m66592)
+{
+	u16 dvsq;
+
+	dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ;
+	m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
+
+	if (dvsq == M66592_DS_DFLT) {	/* bus reset */
+		usb_gadget_udc_reset(&m66592->gadget, m66592->driver);
+		m66592_update_usb_speed(m66592);
+	}
+	if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
+		m66592_update_usb_speed(m66592);
+	if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS)
+			&& m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		m66592_update_usb_speed(m66592);
+
+	m66592->old_dvsq = dvsq;
+}
+
+static void irq_control_stage(struct m66592 *m66592)
+__releases(m66592->lock)
+__acquires(m66592->lock)
+{
+	struct usb_ctrlrequest ctrl;
+	u16 ctsq;
+
+	ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ;
+	m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0);
+
+	switch (ctsq) {
+	case M66592_CS_IDST: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		transfer_complete(ep, req, 0);
+		}
+		break;
+
+	case M66592_CS_RDDS:
+	case M66592_CS_WRDS:
+	case M66592_CS_WRND:
+		if (setup_packet(m66592, &ctrl)) {
+			spin_unlock(&m66592->lock);
+			if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0)
+				pipe_stall(m66592, 0);
+			spin_lock(&m66592->lock);
+		}
+		break;
+	case M66592_CS_RDSS:
+	case M66592_CS_WRSS:
+		control_end(m66592, 0);
+		break;
+	default:
+		pr_err("ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static irqreturn_t m66592_irq(int irq, void *_m66592)
+{
+	struct m66592 *m66592 = _m66592;
+	u16 intsts0;
+	u16 intenb0;
+	u16 savepipe;
+	u16 mask0;
+
+	spin_lock(&m66592->lock);
+
+	intsts0 = m66592_read(m66592, M66592_INTSTS0);
+	intenb0 = m66592_read(m66592, M66592_INTENB0);
+
+	if (m66592->pdata->on_chip && !intsts0 && !intenb0) {
+		/*
+		 * When USB clock stops, it cannot read register. Even if a
+		 * clock stops, the interrupt occurs. So this driver turn on
+		 * a clock by this timing and do re-reading of register.
+		 */
+		m66592_start_xclock(m66592);
+		intsts0 = m66592_read(m66592, M66592_INTSTS0);
+		intenb0 = m66592_read(m66592, M66592_INTENB0);
+	}
+
+	savepipe = m66592_read(m66592, M66592_CFIFOSEL);
+
+	mask0 = intsts0 & intenb0;
+	if (mask0) {
+		u16 brdysts = m66592_read(m66592, M66592_BRDYSTS);
+		u16 bempsts = m66592_read(m66592, M66592_BEMPSTS);
+		u16 brdyenb = m66592_read(m66592, M66592_BRDYENB);
+		u16 bempenb = m66592_read(m66592, M66592_BEMPENB);
+
+		if (mask0 & M66592_VBINT) {
+			m66592_write(m66592,  0xffff & ~M66592_VBINT,
+					M66592_INTSTS0);
+			m66592_start_xclock(m66592);
+
+			/* start vbus sampling */
+			m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0)
+					& M66592_VBSTS;
+			m66592->scount = M66592_MAX_SAMPLING;
+
+			mod_timer(&m66592->timer,
+					jiffies + msecs_to_jiffies(50));
+		}
+		if (intsts0 & M66592_DVSQ)
+			irq_device_state(m66592);
+
+		if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE)
+				&& (brdysts & brdyenb)) {
+			irq_pipe_ready(m66592, brdysts, brdyenb);
+		}
+		if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE)
+				&& (bempsts & bempenb)) {
+			irq_pipe_empty(m66592, bempsts, bempenb);
+		}
+
+		if (intsts0 & M66592_CTRT)
+			irq_control_stage(m66592);
+	}
+
+	m66592_write(m66592, savepipe, M66592_CFIFOSEL);
+
+	spin_unlock(&m66592->lock);
+	return IRQ_HANDLED;
+}
+
+static void m66592_timer(struct timer_list *t)
+{
+	struct m66592 *m66592 = from_timer(m66592, t, timer);
+	unsigned long flags;
+	u16 tmp;
+
+	spin_lock_irqsave(&m66592->lock, flags);
+	tmp = m66592_read(m66592, M66592_SYSCFG);
+	if (!(tmp & M66592_RCKE)) {
+		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+		udelay(10);
+		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+	}
+	if (m66592->scount > 0) {
+		tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS;
+		if (tmp == m66592->old_vbus) {
+			m66592->scount--;
+			if (m66592->scount == 0) {
+				if (tmp == M66592_VBSTS)
+					m66592_usb_connect(m66592);
+				else
+					m66592_usb_disconnect(m66592);
+			} else {
+				mod_timer(&m66592->timer,
+					jiffies + msecs_to_jiffies(50));
+			}
+		} else {
+			m66592->scount = M66592_MAX_SAMPLING;
+			m66592->old_vbus = tmp;
+			mod_timer(&m66592->timer,
+					jiffies + msecs_to_jiffies(50));
+		}
+	}
+	spin_unlock_irqrestore(&m66592->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static int m66592_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592_ep *ep;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	return alloc_pipe_config(ep, desc);
+}
+
+static int m66592_disable(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	BUG_ON(!ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		spin_lock_irqsave(&ep->m66592->lock, flags);
+		transfer_complete(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	}
+
+	pipe_irq_disable(ep->m66592, ep->pipenum);
+	return free_pipe_config(ep);
+}
+
+static struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct m66592_request *req;
+
+	req = kzalloc(sizeof(struct m66592_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_request *req;
+
+	req = container_of(_req, struct m66592_request, req);
+	kfree(req);
+}
+
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+	int request = 0;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (ep->ep.desc == NULL)	/* control */
+		start_ep0(ep, req);
+	else {
+		if (request && !ep->busy)
+			start_packet(ep, req);
+	}
+
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue))
+		transfer_complete(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_set_halt(struct usb_ep *_ep, int value)
+{
+	struct m66592_ep *ep = container_of(_ep, struct m66592_ep, ep);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+	} else if (value) {
+		ep->busy = 1;
+		pipe_stall(ep->m66592, ep->pipenum);
+	} else {
+		ep->busy = 0;
+		pipe_stop(ep->m66592, ep->pipenum);
+	}
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	return ret;
+}
+
+static void m66592_fifo_flush(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (list_empty(&ep->queue) && !ep->busy) {
+		pipe_stop(ep->m66592, ep->pipenum);
+		m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr);
+	}
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+}
+
+static const struct usb_ep_ops m66592_ep_ops = {
+	.enable		= m66592_enable,
+	.disable	= m66592_disable,
+
+	.alloc_request	= m66592_alloc_request,
+	.free_request	= m66592_free_request,
+
+	.queue		= m66592_queue,
+	.dequeue	= m66592_dequeue,
+
+	.set_halt	= m66592_set_halt,
+	.fifo_flush	= m66592_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+static int m66592_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct m66592 *m66592 = to_m66592(g);
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	m66592->driver = driver;
+
+	m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+	if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
+		m66592_start_xclock(m66592);
+		/* start vbus sampling */
+		m66592->old_vbus = m66592_read(m66592,
+					 M66592_INTSTS0) & M66592_VBSTS;
+		m66592->scount = M66592_MAX_SAMPLING;
+		mod_timer(&m66592->timer, jiffies + msecs_to_jiffies(50));
+	}
+
+	return 0;
+}
+
+static int m66592_udc_stop(struct usb_gadget *g)
+{
+	struct m66592 *m66592 = to_m66592(g);
+
+	m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+
+	init_controller(m66592);
+	disable_controller(m66592);
+
+	m66592->driver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int m66592_get_frame(struct usb_gadget *_gadget)
+{
+	struct m66592 *m66592 = gadget_to_m66592(_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 const struct usb_gadget_ops m66592_gadget_ops = {
+	.get_frame		= m66592_get_frame,
+	.udc_start		= m66592_udc_start,
+	.udc_stop		= m66592_udc_stop,
+	.pullup			= m66592_pullup,
+};
+
+static int m66592_remove(struct platform_device *pdev)
+{
+	struct m66592		*m66592 = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&m66592->gadget);
+
+	del_timer_sync(&m66592->timer);
+	iounmap(m66592->reg);
+	free_irq(platform_get_irq(pdev, 0), m66592);
+	m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
+	if (m66592->pdata->on_chip) {
+		clk_disable(m66592->clk);
+		clk_put(m66592->clk);
+	}
+	kfree(m66592);
+	return 0;
+}
+
+static void nop_completion(struct usb_ep *ep, struct usb_request *r)
+{
+}
+
+static int m66592_probe(struct platform_device *pdev)
+{
+	struct resource *res, *ires;
+	void __iomem *reg = NULL;
+	struct m66592 *m66592 = NULL;
+	char clk_name[8];
+	int ret = 0;
+	int i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		pr_err("platform_get_resource error.\n");
+		goto clean_up;
+	}
+
+	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!ires) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev,
+			"platform_get_resource IORESOURCE_IRQ error.\n");
+		goto clean_up;
+	}
+
+	reg = ioremap(res->start, resource_size(res));
+	if (reg == NULL) {
+		ret = -ENOMEM;
+		pr_err("ioremap error.\n");
+		goto clean_up;
+	}
+
+	if (dev_get_platdata(&pdev->dev) == NULL) {
+		dev_err(&pdev->dev, "no platform data\n");
+		ret = -ENODEV;
+		goto clean_up;
+	}
+
+	/* initialize ucd */
+	m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
+	if (m66592 == NULL) {
+		ret = -ENOMEM;
+		goto clean_up;
+	}
+
+	m66592->pdata = dev_get_platdata(&pdev->dev);
+	m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
+
+	spin_lock_init(&m66592->lock);
+	platform_set_drvdata(pdev, m66592);
+
+	m66592->gadget.ops = &m66592_gadget_ops;
+	m66592->gadget.max_speed = USB_SPEED_HIGH;
+	m66592->gadget.name = udc_name;
+
+	timer_setup(&m66592->timer, m66592_timer, 0);
+	m66592->reg = reg;
+
+	ret = request_irq(ires->start, m66592_irq, IRQF_SHARED,
+			udc_name, m66592);
+	if (ret < 0) {
+		pr_err("request_irq error (%d)\n", ret);
+		goto clean_up;
+	}
+
+	if (m66592->pdata->on_chip) {
+		snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
+		m66592->clk = clk_get(&pdev->dev, clk_name);
+		if (IS_ERR(m66592->clk)) {
+			dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
+				clk_name);
+			ret = PTR_ERR(m66592->clk);
+			goto clean_up2;
+		}
+		clk_enable(m66592->clk);
+	}
+
+	INIT_LIST_HEAD(&m66592->gadget.ep_list);
+	m66592->gadget.ep0 = &m66592->ep[0].ep;
+	INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
+	for (i = 0; i < M66592_MAX_NUM_PIPE; i++) {
+		struct m66592_ep *ep = &m66592->ep[i];
+
+		if (i != 0) {
+			INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list);
+			list_add_tail(&m66592->ep[i].ep.ep_list,
+					&m66592->gadget.ep_list);
+		}
+		ep->m66592 = m66592;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = m66592_ep_name[i];
+		ep->ep.ops = &m66592_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, 512);
+
+		if (i == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+	usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64);
+	m66592->ep[0].pipenum = 0;
+	m66592->ep[0].fifoaddr = M66592_CFIFO;
+	m66592->ep[0].fifosel = M66592_CFIFOSEL;
+	m66592->ep[0].fifoctr = M66592_CFIFOCTR;
+	m66592->ep[0].fifotrn = 0;
+	m66592->ep[0].pipectr = get_pipectr_addr(0);
+	m66592->pipenum2ep[0] = &m66592->ep[0];
+	m66592->epaddr2ep[0] = &m66592->ep[0];
+
+	m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
+	if (m66592->ep0_req == NULL) {
+		ret = -ENOMEM;
+		goto clean_up3;
+	}
+	m66592->ep0_req->complete = nop_completion;
+
+	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:
+	if (m66592->pdata->on_chip) {
+		clk_disable(m66592->clk);
+		clk_put(m66592->clk);
+	}
+clean_up2:
+	free_irq(ires->start, m66592);
+clean_up:
+	if (m66592) {
+		if (m66592->ep0_req)
+			m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
+		kfree(m66592);
+	}
+	if (reg)
+		iounmap(reg);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver m66592_driver = {
+	.remove =	m66592_remove,
+	.driver		= {
+		.name =	(char *) udc_name,
+	},
+};
+
+module_platform_driver_probe(m66592_driver, m66592_probe);
diff --git a/drivers/usb/gadget/udc/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h
new file mode 100644
index 0000000..01a6468
--- /dev/null
+++ b/drivers/usb/gadget/udc/m66592-udc.h
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+ */
+
+#ifndef __M66592_UDC_H__
+#define __M66592_UDC_H__
+
+#include <linux/clk.h>
+#include <linux/usb/m66592.h>
+
+#define M66592_SYSCFG		0x00
+#define M66592_XTAL		0xC000	/* b15-14: Crystal selection */
+#define   M66592_XTAL48		 0x8000		/* 48MHz */
+#define   M66592_XTAL24		 0x4000		/* 24MHz */
+#define   M66592_XTAL12		 0x0000		/* 12MHz */
+#define M66592_XCKE		0x2000	/* b13: External clock enable */
+#define M66592_RCKE		0x1000	/* b12: Register clock enable */
+#define M66592_PLLC		0x0800	/* b11: PLL control */
+#define M66592_SCKE		0x0400	/* b10: USB clock enable */
+#define M66592_ATCKM		0x0100	/* b8: Automatic clock supply */
+#define M66592_HSE		0x0080	/* b7: Hi-speed enable */
+#define M66592_DCFM		0x0040	/* b6: Controller function select  */
+#define M66592_DMRPD		0x0020	/* b5: D- pull down control */
+#define M66592_DPRPU		0x0010	/* b4: D+ pull up control */
+#define M66592_FSRPC		0x0004	/* b2: Full-speed receiver enable */
+#define M66592_PCUT		0x0002	/* b1: Low power sleep enable */
+#define M66592_USBE		0x0001	/* b0: USB module operation enable */
+
+#define M66592_SYSSTS		0x02
+#define M66592_LNST		0x0003	/* b1-0: D+, D- line status */
+#define   M66592_SE1		 0x0003		/* SE1 */
+#define   M66592_KSTS		 0x0002		/* K State */
+#define   M66592_JSTS		 0x0001		/* J State */
+#define   M66592_SE0		 0x0000		/* SE0 */
+
+#define M66592_DVSTCTR		0x04
+#define M66592_WKUP		0x0100	/* b8: Remote wakeup */
+#define M66592_RWUPE		0x0080	/* b7: Remote wakeup sense */
+#define M66592_USBRST		0x0040	/* b6: USB reset enable */
+#define M66592_RESUME		0x0020	/* b5: Resume enable */
+#define M66592_UACT		0x0010	/* b4: USB bus enable */
+#define M66592_RHST		0x0003	/* b1-0: Reset handshake status */
+#define   M66592_HSMODE		 0x0003		/* Hi-Speed mode */
+#define   M66592_FSMODE		 0x0002		/* Full-Speed mode */
+#define   M66592_HSPROC		 0x0001		/* HS handshake is processing */
+
+#define M66592_TESTMODE		0x06
+#define M66592_UTST		0x000F	/* b4-0: Test select */
+#define   M66592_H_TST_PACKET	 0x000C		/* HOST TEST Packet */
+#define   M66592_H_TST_SE0_NAK	 0x000B		/* HOST TEST SE0 NAK */
+#define   M66592_H_TST_K	 0x000A		/* HOST TEST K */
+#define   M66592_H_TST_J	 0x0009		/* HOST TEST J */
+#define   M66592_H_TST_NORMAL	 0x0000		/* HOST Normal Mode */
+#define   M66592_P_TST_PACKET	 0x0004		/* PERI TEST Packet */
+#define   M66592_P_TST_SE0_NAK	 0x0003		/* PERI TEST SE0 NAK */
+#define   M66592_P_TST_K	 0x0002		/* PERI TEST K */
+#define   M66592_P_TST_J	 0x0001		/* PERI TEST J */
+#define   M66592_P_TST_NORMAL	 0x0000		/* PERI Normal Mode */
+
+/* built-in registers */
+#define M66592_CFBCFG		0x0A
+#define M66592_D0FBCFG		0x0C
+#define M66592_LITTLE		0x0100	/* b8: Little endian mode */
+/* external chip case */
+#define M66592_PINCFG		0x0A
+#define M66592_LDRV		0x8000	/* b15: Drive Current Adjust */
+#define M66592_BIGEND		0x0100	/* b8: Big endian mode */
+
+#define M66592_DMA0CFG		0x0C
+#define M66592_DMA1CFG		0x0E
+#define M66592_DREQA		0x4000	/* b14: Dreq active select */
+#define M66592_BURST		0x2000	/* b13: Burst mode */
+#define M66592_DACKA		0x0400	/* b10: Dack active select */
+#define M66592_DFORM		0x0380	/* b9-7: DMA mode select */
+#define   M66592_CPU_ADR_RD_WR	 0x0000   /* Address + RD/WR mode (CPU bus) */
+#define   M66592_CPU_DACK_RD_WR	 0x0100   /* DACK + RD/WR mode (CPU bus) */
+#define   M66592_CPU_DACK_ONLY	 0x0180   /* DACK only mode (CPU bus) */
+#define   M66592_SPLIT_DACK_ONLY 0x0200   /* DACK only mode (SPLIT bus) */
+#define   M66592_SPLIT_DACK_DSTB 0x0300   /* DACK + DSTB0 mode (SPLIT bus) */
+#define M66592_DENDA		0x0040	/* b6: Dend active select */
+#define M66592_PKTM		0x0020	/* b5: Packet mode */
+#define M66592_DENDE		0x0010	/* b4: Dend enable */
+#define M66592_OBUS		0x0004	/* b2: OUTbus mode */
+
+/* common case */
+#define M66592_CFIFO		0x10
+#define M66592_D0FIFO		0x14
+#define M66592_D1FIFO		0x18
+
+#define M66592_CFIFOSEL		0x1E
+#define M66592_D0FIFOSEL	0x24
+#define M66592_D1FIFOSEL	0x2A
+#define M66592_RCNT		0x8000	/* b15: Read count mode */
+#define M66592_REW		0x4000	/* b14: Buffer rewind */
+#define M66592_DCLRM		0x2000	/* b13: DMA buffer clear mode */
+#define M66592_DREQE		0x1000	/* b12: DREQ output enable */
+#define M66592_MBW_8		0x0000   /*  8bit */
+#define M66592_MBW_16		0x0400   /* 16bit */
+#define M66592_MBW_32		0x0800   /* 32bit */
+#define M66592_TRENB		0x0200	/* b9: Transaction counter enable */
+#define M66592_TRCLR		0x0100	/* b8: Transaction counter clear */
+#define M66592_DEZPM		0x0080	/* b7: Zero-length packet mode */
+#define M66592_ISEL		0x0020	/* b5: DCP FIFO port direction select */
+#define M66592_CURPIPE		0x0007	/* b2-0: PIPE select */
+
+#define M66592_CFIFOCTR		0x20
+#define M66592_D0FIFOCTR	0x26
+#define M66592_D1FIFOCTR	0x2c
+#define M66592_BVAL		0x8000	/* b15: Buffer valid flag */
+#define M66592_BCLR		0x4000	/* b14: Buffer clear */
+#define M66592_FRDY		0x2000	/* b13: FIFO ready */
+#define M66592_DTLN		0x0FFF	/* b11-0: FIFO received data length */
+
+#define M66592_CFIFOSIE		0x22
+#define M66592_TGL		0x8000	/* b15: Buffer toggle */
+#define M66592_SCLR		0x4000	/* b14: Buffer clear */
+#define M66592_SBUSY		0x2000	/* b13: SIE_FIFO busy */
+
+#define M66592_D0FIFOTRN	0x28
+#define M66592_D1FIFOTRN	0x2E
+#define M66592_TRNCNT		0xFFFF	/* b15-0: Transaction counter */
+
+#define M66592_INTENB0	0x30
+#define M66592_VBSE	0x8000	/* b15: VBUS interrupt */
+#define M66592_RSME	0x4000	/* b14: Resume interrupt */
+#define M66592_SOFE	0x2000	/* b13: Frame update interrupt */
+#define M66592_DVSE	0x1000	/* b12: Device state transition interrupt */
+#define M66592_CTRE	0x0800	/* b11: Control transfer stage transition irq */
+#define M66592_BEMPE	0x0400	/* b10: Buffer empty interrupt */
+#define M66592_NRDYE	0x0200	/* b9: Buffer not ready interrupt */
+#define M66592_BRDYE	0x0100	/* b8: Buffer ready interrupt */
+#define M66592_URST	0x0080	/* b7: USB reset detected interrupt */
+#define M66592_SADR	0x0040	/* b6: Set address executed interrupt */
+#define M66592_SCFG	0x0020	/* b5: Set configuration executed interrupt */
+#define M66592_SUSP	0x0010	/* b4: Suspend detected interrupt */
+#define M66592_WDST	0x0008	/* b3: Control write data stage completed irq */
+#define M66592_RDST	0x0004	/* b2: Control read data stage completed irq */
+#define M66592_CMPL	0x0002	/* b1: Control transfer complete interrupt */
+#define M66592_SERR	0x0001	/* b0: Sequence error interrupt */
+
+#define M66592_INTENB1	0x32
+#define M66592_BCHGE	0x4000	/* b14: USB us chenge interrupt */
+#define M66592_DTCHE	0x1000	/* b12: Detach sense interrupt */
+#define M66592_SIGNE	0x0020	/* b5: SETUP IGNORE interrupt */
+#define M66592_SACKE	0x0010	/* b4: SETUP ACK interrupt */
+#define M66592_BRDYM	0x0004	/* b2: BRDY clear timing */
+#define M66592_INTL	0x0002	/* b1: Interrupt sense select */
+#define M66592_PCSE	0x0001	/* b0: PCUT enable by CS assert */
+
+#define M66592_BRDYENB		0x36
+#define M66592_BRDYSTS		0x46
+#define M66592_BRDY7		0x0080	/* b7: PIPE7 */
+#define M66592_BRDY6		0x0040	/* b6: PIPE6 */
+#define M66592_BRDY5		0x0020	/* b5: PIPE5 */
+#define M66592_BRDY4		0x0010	/* b4: PIPE4 */
+#define M66592_BRDY3		0x0008	/* b3: PIPE3 */
+#define M66592_BRDY2		0x0004	/* b2: PIPE2 */
+#define M66592_BRDY1		0x0002	/* b1: PIPE1 */
+#define M66592_BRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_NRDYENB		0x38
+#define M66592_NRDYSTS		0x48
+#define M66592_NRDY7		0x0080	/* b7: PIPE7 */
+#define M66592_NRDY6		0x0040	/* b6: PIPE6 */
+#define M66592_NRDY5		0x0020	/* b5: PIPE5 */
+#define M66592_NRDY4		0x0010	/* b4: PIPE4 */
+#define M66592_NRDY3		0x0008	/* b3: PIPE3 */
+#define M66592_NRDY2		0x0004	/* b2: PIPE2 */
+#define M66592_NRDY1		0x0002	/* b1: PIPE1 */
+#define M66592_NRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_BEMPENB		0x3A
+#define M66592_BEMPSTS		0x4A
+#define M66592_BEMP7		0x0080	/* b7: PIPE7 */
+#define M66592_BEMP6		0x0040	/* b6: PIPE6 */
+#define M66592_BEMP5		0x0020	/* b5: PIPE5 */
+#define M66592_BEMP4		0x0010	/* b4: PIPE4 */
+#define M66592_BEMP3		0x0008	/* b3: PIPE3 */
+#define M66592_BEMP2		0x0004	/* b2: PIPE2 */
+#define M66592_BEMP1		0x0002	/* b1: PIPE1 */
+#define M66592_BEMP0		0x0001	/* b0: PIPE0 */
+
+#define M66592_SOFCFG		0x3C
+#define M66592_SOFM		0x000C	/* b3-2: SOF palse mode */
+#define   M66592_SOF_125US	 0x0008   /* SOF OUT 125us uFrame Signal */
+#define   M66592_SOF_1MS	 0x0004   /* SOF OUT 1ms Frame Signal */
+#define   M66592_SOF_DISABLE	 0x0000   /* SOF OUT Disable */
+
+#define M66592_INTSTS0		0x40
+#define M66592_VBINT		0x8000	/* b15: VBUS interrupt */
+#define M66592_RESM		0x4000	/* b14: Resume interrupt */
+#define M66592_SOFR		0x2000	/* b13: SOF frame update interrupt */
+#define M66592_DVST		0x1000	/* b12: Device state transition */
+#define M66592_CTRT		0x0800	/* b11: Control stage transition */
+#define M66592_BEMP		0x0400	/* b10: Buffer empty interrupt */
+#define M66592_NRDY		0x0200	/* b9: Buffer not ready interrupt */
+#define M66592_BRDY		0x0100	/* b8: Buffer ready interrupt */
+#define M66592_VBSTS		0x0080	/* b7: VBUS input port */
+#define M66592_DVSQ		0x0070	/* b6-4: Device state */
+#define   M66592_DS_SPD_CNFG	 0x0070	   /* Suspend Configured */
+#define   M66592_DS_SPD_ADDR	 0x0060	   /* Suspend Address */
+#define   M66592_DS_SPD_DFLT	 0x0050	   /* Suspend Default */
+#define   M66592_DS_SPD_POWR	 0x0040	   /* Suspend Powered */
+#define   M66592_DS_SUSP	 0x0040	   /* Suspend */
+#define   M66592_DS_CNFG	 0x0030	   /* Configured */
+#define   M66592_DS_ADDS	 0x0020	   /* Address */
+#define   M66592_DS_DFLT	 0x0010	   /* Default */
+#define   M66592_DS_POWR	 0x0000	   /* Powered */
+#define M66592_DVSQS		0x0030	/* b5-4: Device state */
+#define M66592_VALID		0x0008	/* b3: Setup packet detected flag */
+#define M66592_CTSQ		0x0007	/* b2-0: Control transfer stage */
+#define   M66592_CS_SQER	 0x0006	  /* Sequence error */
+#define   M66592_CS_WRND	 0x0005	  /* Control write nodata status */
+#define   M66592_CS_WRSS	 0x0004	  /* Control write status stage */
+#define   M66592_CS_WRDS	 0x0003	  /* Control write data stage */
+#define   M66592_CS_RDSS	 0x0002	  /* Control read status stage */
+#define   M66592_CS_RDDS	 0x0001	  /* Control read data stage */
+#define   M66592_CS_IDST	 0x0000	  /* Idle or setup stage */
+
+#define M66592_INTSTS1		0x42
+#define M66592_BCHG		0x4000	/* b14: USB bus chenge interrupt */
+#define M66592_DTCH		0x1000	/* b12: Detach sense interrupt */
+#define M66592_SIGN		0x0020	/* b5: SETUP IGNORE interrupt */
+#define M66592_SACK		0x0010	/* b4: SETUP ACK interrupt */
+
+#define M66592_FRMNUM		0x4C
+#define M66592_OVRN		0x8000	/* b15: Overrun error */
+#define M66592_CRCE		0x4000	/* b14: Received data error */
+#define M66592_SOFRM		0x0800	/* b11: SOF output mode */
+#define M66592_FRNM		0x07FF	/* b10-0: Frame number */
+
+#define M66592_UFRMNUM		0x4E
+#define M66592_UFRNM		0x0007	/* b2-0: Micro frame number */
+
+#define M66592_RECOVER		0x50
+#define M66592_STSRECOV		0x0700	/* Status recovery */
+#define   M66592_STSR_HI	 0x0400		  /* FULL(0) or HI(1) Speed */
+#define   M66592_STSR_DEFAULT	 0x0100		  /* Default state */
+#define   M66592_STSR_ADDRESS	 0x0200		  /* Address state */
+#define   M66592_STSR_CONFIG	 0x0300		  /* Configured state */
+#define M66592_USBADDR		0x007F	/* b6-0: USB address */
+
+#define M66592_USBREQ			0x54
+#define M66592_bRequest			0xFF00	/* b15-8: bRequest */
+#define   M66592_GET_STATUS		 0x0000
+#define   M66592_CLEAR_FEATURE		 0x0100
+#define   M66592_ReqRESERVED		 0x0200
+#define   M66592_SET_FEATURE		 0x0300
+#define   M66592_ReqRESERVED1		 0x0400
+#define   M66592_SET_ADDRESS		 0x0500
+#define   M66592_GET_DESCRIPTOR		 0x0600
+#define   M66592_SET_DESCRIPTOR		 0x0700
+#define   M66592_GET_CONFIGURATION	 0x0800
+#define   M66592_SET_CONFIGURATION	 0x0900
+#define   M66592_GET_INTERFACE		 0x0A00
+#define   M66592_SET_INTERFACE		 0x0B00
+#define   M66592_SYNCH_FRAME		 0x0C00
+#define M66592_bmRequestType		0x00FF	/* b7-0: bmRequestType */
+#define M66592_bmRequestTypeDir		0x0080	/* b7  : Data direction */
+#define   M66592_HOST_TO_DEVICE		 0x0000
+#define   M66592_DEVICE_TO_HOST		 0x0080
+#define M66592_bmRequestTypeType	0x0060	/* b6-5: Type */
+#define   M66592_STANDARD		 0x0000
+#define   M66592_CLASS			 0x0020
+#define   M66592_VENDOR			 0x0040
+#define M66592_bmRequestTypeRecip	0x001F	/* b4-0: Recipient */
+#define   M66592_DEVICE			 0x0000
+#define   M66592_INTERFACE		 0x0001
+#define   M66592_ENDPOINT		 0x0002
+
+#define M66592_USBVAL				0x56
+#define M66592_wValue				0xFFFF	/* b15-0: wValue */
+/* Standard Feature Selector */
+#define   M66592_ENDPOINT_HALT			0x0000
+#define   M66592_DEVICE_REMOTE_WAKEUP		0x0001
+#define   M66592_TEST_MODE			0x0002
+/* Descriptor Types */
+#define M66592_DT_TYPE				0xFF00
+#define M66592_GET_DT_TYPE(v)			(((v) & DT_TYPE) >> 8)
+#define   M66592_DT_DEVICE			0x01
+#define   M66592_DT_CONFIGURATION		0x02
+#define   M66592_DT_STRING			0x03
+#define   M66592_DT_INTERFACE			0x04
+#define   M66592_DT_ENDPOINT			0x05
+#define   M66592_DT_DEVICE_QUALIFIER		0x06
+#define   M66592_DT_OTHER_SPEED_CONFIGURATION	0x07
+#define   M66592_DT_INTERFACE_POWER		0x08
+#define M66592_DT_INDEX				0x00FF
+#define M66592_CONF_NUM				0x00FF
+#define M66592_ALT_SET				0x00FF
+
+#define M66592_USBINDEX			0x58
+#define M66592_wIndex			0xFFFF	/* b15-0: wIndex */
+#define M66592_TEST_SELECT		0xFF00	/* b15-b8: Test Mode */
+#define   M66592_TEST_J			 0x0100	  /* Test_J */
+#define   M66592_TEST_K			 0x0200	  /* Test_K */
+#define   M66592_TEST_SE0_NAK		 0x0300	  /* Test_SE0_NAK */
+#define   M66592_TEST_PACKET		 0x0400	  /* Test_Packet */
+#define   M66592_TEST_FORCE_ENABLE	 0x0500	  /* Test_Force_Enable */
+#define   M66592_TEST_STSelectors	 0x0600	  /* Standard test selectors */
+#define   M66592_TEST_Reserved		 0x4000	  /* Reserved */
+#define   M66592_TEST_VSTModes		 0xC000	  /* Vendor-specific tests */
+#define M66592_EP_DIR			0x0080	/* b7: Endpoint Direction */
+#define   M66592_EP_DIR_IN		 0x0080
+#define   M66592_EP_DIR_OUT		 0x0000
+
+#define M66592_USBLENG		0x5A
+#define M66592_wLength		0xFFFF	/* b15-0: wLength */
+
+#define M66592_DCPCFG		0x5C
+#define M66592_CNTMD		0x0100	/* b8: Continuous transfer mode */
+#define M66592_DIR		0x0010	/* b4: Control transfer DIR select */
+
+#define M66592_DCPMAXP		0x5E
+#define M66592_DEVSEL		0xC000	/* b15-14: Device address select */
+#define   M66592_DEVICE_0	 0x0000		  /* Device address 0 */
+#define   M66592_DEVICE_1	 0x4000		  /* Device address 1 */
+#define   M66592_DEVICE_2	 0x8000		  /* Device address 2 */
+#define   M66592_DEVICE_3	 0xC000		  /* Device address 3 */
+#define M66592_MAXP		0x007F	/* b6-0: Maxpacket size of ep0 */
+
+#define M66592_DCPCTR		0x60
+#define M66592_BSTS		0x8000	/* b15: Buffer status */
+#define M66592_SUREQ		0x4000	/* b14: Send USB request  */
+#define M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define M66592_CCPL		0x0004	/* b2: control transfer complete */
+#define M66592_PID		0x0003	/* b1-0: Response PID */
+#define   M66592_PID_STALL	 0x0002		  /* STALL */
+#define   M66592_PID_BUF	 0x0001		  /* BUF */
+#define   M66592_PID_NAK	 0x0000		  /* NAK */
+
+#define M66592_PIPESEL		0x64
+#define M66592_PIPENM		0x0007	/* b2-0: Pipe select */
+#define   M66592_PIPE0		 0x0000		  /* PIPE 0 */
+#define   M66592_PIPE1		 0x0001		  /* PIPE 1 */
+#define   M66592_PIPE2		 0x0002		  /* PIPE 2 */
+#define   M66592_PIPE3		 0x0003		  /* PIPE 3 */
+#define   M66592_PIPE4		 0x0004		  /* PIPE 4 */
+#define   M66592_PIPE5		 0x0005		  /* PIPE 5 */
+#define   M66592_PIPE6		 0x0006		  /* PIPE 6 */
+#define   M66592_PIPE7		 0x0007		  /* PIPE 7 */
+
+#define M66592_PIPECFG		0x66
+#define M66592_TYP		0xC000	/* b15-14: Transfer type */
+#define   M66592_ISO		 0xC000		  /* Isochronous */
+#define   M66592_INT		 0x8000		  /* Interrupt */
+#define   M66592_BULK		 0x4000		  /* Bulk */
+#define M66592_BFRE		0x0400	/* b10: Buffer ready interrupt mode */
+#define M66592_DBLB		0x0200	/* b9: Double buffer mode select */
+#define M66592_CNTMD		0x0100	/* b8: Continuous transfer mode */
+#define M66592_SHTNAK		0x0080	/* b7: Transfer end NAK */
+#define M66592_DIR		0x0010	/* b4: Transfer direction select */
+#define   M66592_DIR_H_OUT	 0x0010		  /* HOST OUT */
+#define   M66592_DIR_P_IN	 0x0010		  /* PERI IN */
+#define   M66592_DIR_H_IN	 0x0000		  /* HOST IN */
+#define   M66592_DIR_P_OUT	 0x0000		  /* PERI OUT */
+#define M66592_EPNUM		0x000F	/* b3-0: Eendpoint number select */
+#define   M66592_EP1		 0x0001
+#define   M66592_EP2		 0x0002
+#define   M66592_EP3		 0x0003
+#define   M66592_EP4		 0x0004
+#define   M66592_EP5		 0x0005
+#define   M66592_EP6		 0x0006
+#define   M66592_EP7		 0x0007
+#define   M66592_EP8		 0x0008
+#define   M66592_EP9		 0x0009
+#define   M66592_EP10		 0x000A
+#define   M66592_EP11		 0x000B
+#define   M66592_EP12		 0x000C
+#define   M66592_EP13		 0x000D
+#define   M66592_EP14		 0x000E
+#define   M66592_EP15		 0x000F
+
+#define M66592_PIPEBUF		0x68
+#define M66592_BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */
+#define M66592_BUF_SIZE(x)	((((x) / 64) - 1) << 10)
+#define M66592_BUFNMB		0x00FF	/* b7-0: Pipe buffer number */
+
+#define M66592_PIPEMAXP		0x6A
+#define M66592_MXPS		0x07FF	/* b10-0: Maxpacket size */
+
+#define M66592_PIPEPERI		0x6C
+#define M66592_IFIS		0x1000	/* b12: ISO in-buffer flush mode */
+#define M66592_IITV		0x0007	/* b2-0: ISO interval */
+
+#define M66592_PIPE1CTR		0x70
+#define M66592_PIPE2CTR		0x72
+#define M66592_PIPE3CTR		0x74
+#define M66592_PIPE4CTR		0x76
+#define M66592_PIPE5CTR		0x78
+#define M66592_PIPE6CTR		0x7A
+#define M66592_PIPE7CTR		0x7C
+#define M66592_BSTS		0x8000	/* b15: Buffer status */
+#define M66592_INBUFM		0x4000	/* b14: IN buffer monitor (PIPE 1-5) */
+#define M66592_ACLRM		0x0200	/* b9: Out buffer auto clear mode */
+#define M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define M66592_PID		0x0003	/* b1-0: Response PID */
+
+#define M66592_INVALID_REG	0x7E
+
+
+#define get_pipectr_addr(pipenum)	(M66592_PIPE1CTR + (pipenum - 1) * 2)
+
+#define M66592_MAX_SAMPLING	10
+
+#define M66592_MAX_NUM_PIPE	8
+#define M66592_MAX_NUM_BULK	3
+#define M66592_MAX_NUM_ISOC	2
+#define M66592_MAX_NUM_INT	2
+
+#define M66592_BASE_PIPENUM_BULK	3
+#define M66592_BASE_PIPENUM_ISOC	1
+#define M66592_BASE_PIPENUM_INT		6
+
+#define M66592_BASE_BUFNUM	6
+#define M66592_MAX_BUFNUM	0x4F
+
+struct m66592_pipe_info {
+	u16	pipe;
+	u16	epnum;
+	u16	maxpacket;
+	u16	type;
+	u16	interval;
+	u16	dir_in;
+};
+
+struct m66592_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+struct m66592_ep {
+	struct usb_ep		ep;
+	struct m66592		*m66592;
+
+	struct list_head	queue;
+	unsigned		busy:1;
+	unsigned		internal_ccpl:1;	/* use only control */
+
+	/* this member can able to after m66592_enable */
+	unsigned		use_dma:1;
+	u16			pipenum;
+	u16			type;
+
+	/* register address */
+	unsigned long		fifoaddr;
+	unsigned long		fifosel;
+	unsigned long		fifoctr;
+	unsigned long		fifotrn;
+	unsigned long		pipectr;
+};
+
+struct m66592 {
+	spinlock_t		lock;
+	void __iomem		*reg;
+	struct clk *clk;
+	struct m66592_platdata	*pdata;
+	unsigned long		irq_trigger;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct m66592_ep	ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*pipenum2ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*epaddr2ep[16];
+
+	struct usb_request	*ep0_req;	/* for internal request */
+	__le16			ep0_data;	/* for internal request */
+	u16			old_vbus;
+
+	struct timer_list	timer;
+
+	int			scount;
+
+	int			old_dvsq;
+
+	/* pipe config */
+	int bulk;
+	int interrupt;
+	int isochronous;
+	int num_dma;
+};
+#define to_m66592(g)	(container_of((g), struct m66592, gadget))
+
+#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
+#define m66592_to_gadget(m66592) (&m66592->gadget)
+
+#define is_bulk_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_BULK) && \
+	 (pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK)))
+#define is_interrupt_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_INT) && \
+	 (pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT)))
+#define is_isoc_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_ISOC) && \
+	 (pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC)))
+
+#define enable_irq_ready(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define disable_irq_ready(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define enable_irq_empty(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define disable_irq_empty(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define enable_irq_nrdy(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+#define disable_irq_nrdy(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+
+/*-------------------------------------------------------------------------*/
+static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset)
+{
+	return ioread16(m66592->reg + offset);
+}
+
+static inline void m66592_read_fifo(struct m66592 *m66592,
+		unsigned long offset,
+		void *buf, unsigned long len)
+{
+	void __iomem *fifoaddr = m66592->reg + offset;
+
+	if (m66592->pdata->on_chip) {
+		len = (len + 3) / 4;
+		ioread32_rep(fifoaddr, buf, len);
+	} else {
+		len = (len + 1) / 2;
+		ioread16_rep(fifoaddr, buf, len);
+	}
+}
+
+static inline void m66592_write(struct m66592 *m66592, u16 val,
+				unsigned long offset)
+{
+	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,
+		struct m66592_ep *ep,
+		void *buf, unsigned long len)
+{
+	void __iomem *fifoaddr = m66592->reg + ep->fifoaddr;
+
+	if (m66592->pdata->on_chip) {
+		unsigned long count;
+		unsigned char *pb;
+		int i;
+
+		count = len / 4;
+		iowrite32_rep(fifoaddr, buf, count);
+
+		if (len & 0x00000003) {
+			pb = buf + count * 4;
+			for (i = 0; i < (len & 0x00000003); i++) {
+				if (m66592_read(m66592, M66592_CFBCFG))	/* le */
+					iowrite8(pb[i], fifoaddr + (3 - i));
+				else
+					iowrite8(pb[i], fifoaddr + i);
+			}
+		}
+	} else {
+		unsigned long odd = len & 0x0001;
+
+		len = len / 2;
+		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);
+		}
+	}
+}
+
+#endif	/* ifndef __M66592_UDC_H__ */
+
+
diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h
new file mode 100644
index 0000000..982625b
--- /dev/null
+++ b/drivers/usb/gadget/udc/mv_u3d.h
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ */
+
+#ifndef __MV_U3D_H
+#define __MV_U3D_H
+
+#define MV_U3D_EP_CONTEXT_ALIGNMENT	32
+#define MV_U3D_TRB_ALIGNMENT	16
+#define MV_U3D_DMA_BOUNDARY	4096
+#define MV_U3D_EP0_MAX_PKT_SIZE	512
+
+/* ep0 transfer state */
+#define MV_U3D_WAIT_FOR_SETUP		0
+#define MV_U3D_DATA_STATE_XMIT		1
+#define MV_U3D_DATA_STATE_NEED_ZLP	2
+#define MV_U3D_WAIT_FOR_OUT_STATUS	3
+#define MV_U3D_DATA_STATE_RECV		4
+#define MV_U3D_STATUS_STAGE		5
+
+#define MV_U3D_EP_MAX_LENGTH_TRANSFER	0x10000
+
+/* USB3 Interrupt Status */
+#define MV_U3D_USBINT_SETUP		0x00000001
+#define MV_U3D_USBINT_RX_COMPLETE	0x00000002
+#define MV_U3D_USBINT_TX_COMPLETE	0x00000004
+#define MV_U3D_USBINT_UNDER_RUN	0x00000008
+#define MV_U3D_USBINT_RXDESC_ERR	0x00000010
+#define MV_U3D_USBINT_TXDESC_ERR	0x00000020
+#define MV_U3D_USBINT_RX_TRB_COMPLETE	0x00000040
+#define MV_U3D_USBINT_TX_TRB_COMPLETE	0x00000080
+#define MV_U3D_USBINT_VBUS_VALID	0x00010000
+#define MV_U3D_USBINT_STORAGE_CMD_FULL	0x00020000
+#define MV_U3D_USBINT_LINK_CHG		0x01000000
+
+/* USB3 Interrupt Enable */
+#define MV_U3D_INTR_ENABLE_SETUP		0x00000001
+#define MV_U3D_INTR_ENABLE_RX_COMPLETE		0x00000002
+#define MV_U3D_INTR_ENABLE_TX_COMPLETE		0x00000004
+#define MV_U3D_INTR_ENABLE_UNDER_RUN		0x00000008
+#define MV_U3D_INTR_ENABLE_RXDESC_ERR		0x00000010
+#define MV_U3D_INTR_ENABLE_TXDESC_ERR		0x00000020
+#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE	0x00000040
+#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE	0x00000080
+#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR	0x00000100
+#define MV_U3D_INTR_ENABLE_VBUS_VALID		0x00010000
+#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL	0x00020000
+#define MV_U3D_INTR_ENABLE_LINK_CHG		0x01000000
+#define MV_U3D_INTR_ENABLE_PRIME_STATUS	0x02000000
+
+/* USB3 Link Change */
+#define MV_U3D_LINK_CHANGE_LINK_UP		0x00000001
+#define MV_U3D_LINK_CHANGE_SUSPEND		0x00000002
+#define MV_U3D_LINK_CHANGE_RESUME		0x00000004
+#define MV_U3D_LINK_CHANGE_WRESET		0x00000008
+#define MV_U3D_LINK_CHANGE_HRESET		0x00000010
+#define MV_U3D_LINK_CHANGE_VBUS_INVALID	0x00000020
+#define MV_U3D_LINK_CHANGE_INACT		0x00000040
+#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0	0x00000080
+#define MV_U3D_LINK_CHANGE_U1			0x00000100
+#define MV_U3D_LINK_CHANGE_U2			0x00000200
+#define MV_U3D_LINK_CHANGE_U3			0x00000400
+
+/* bridge setting */
+#define MV_U3D_BRIDGE_SETTING_VBUS_VALID	(1 << 16)
+
+/* Command Register Bit Masks */
+#define MV_U3D_CMD_RUN_STOP		0x00000001
+#define MV_U3D_CMD_CTRL_RESET		0x00000002
+
+/* ep control register */
+#define MV_U3D_EPXCR_EP_TYPE_CONTROL		0
+#define MV_U3D_EPXCR_EP_TYPE_ISOC		1
+#define MV_U3D_EPXCR_EP_TYPE_BULK		2
+#define MV_U3D_EPXCR_EP_TYPE_INT		3
+#define MV_U3D_EPXCR_EP_ENABLE_SHIFT		4
+#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT	12
+#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT	16
+#define MV_U3D_USB_BULK_BURST_OUT		6
+#define MV_U3D_USB_BULK_BURST_IN		14
+
+#define MV_U3D_EPXCR_EP_FLUSH		(1 << 7)
+#define MV_U3D_EPXCR_EP_HALT		(1 << 1)
+#define MV_U3D_EPXCR_EP_INIT		(1)
+
+/* TX/RX Status Register */
+#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT	24
+#define MV_U3D_COMPLETE_INVALID	0
+#define MV_U3D_COMPLETE_SUCCESS	1
+#define MV_U3D_COMPLETE_BUFF_ERR	2
+#define MV_U3D_COMPLETE_SHORT_PACKET	3
+#define MV_U3D_COMPLETE_TRB_ERR	5
+#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK	(0xFFFFFF)
+
+#define MV_U3D_USB_LINK_BYPASS_VBUS	0x8
+
+#define MV_U3D_LTSSM_PHY_INIT_DONE		0x80000000
+#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE	0x40000000
+
+#define MV_U3D_USB3_OP_REGS_OFFSET	0x100
+#define MV_U3D_USB3_PHY_OFFSET		0xB800
+
+#define DCS_ENABLE	0x1
+
+/* timeout */
+#define MV_U3D_RESET_TIMEOUT		10000
+#define MV_U3D_FLUSH_TIMEOUT		100000
+#define MV_U3D_OWN_TIMEOUT		10000
+#define LOOPS_USEC_SHIFT	4
+#define LOOPS_USEC		(1 << LOOPS_USEC_SHIFT)
+#define LOOPS(timeout)		((timeout) >> LOOPS_USEC_SHIFT)
+
+/* ep direction */
+#define MV_U3D_EP_DIR_IN		1
+#define MV_U3D_EP_DIR_OUT		0
+#define mv_u3d_ep_dir(ep)	(((ep)->ep_num == 0) ? \
+				((ep)->u3d->ep0_dir) : ((ep)->direction))
+
+/* usb capability registers */
+struct mv_u3d_cap_regs {
+	u32	rsvd[5];
+	u32	dboff;	/* doorbell register offset */
+	u32	rtsoff;	/* runtime register offset */
+	u32	vuoff;	/* vendor unique register offset */
+};
+
+/* operation registers */
+struct mv_u3d_op_regs {
+	u32	usbcmd;		/* Command register */
+	u32	rsvd1[11];
+	u32	dcbaapl;	/* Device Context Base Address low register */
+	u32	dcbaaph;	/* Device Context Base Address high register */
+	u32	rsvd2[243];
+	u32	portsc;		/* port status and control register*/
+	u32	portlinkinfo;	/* port link info register*/
+	u32	rsvd3[9917];
+	u32	doorbell;	/* doorbell register */
+};
+
+/* control enpoint enable registers */
+struct epxcr {
+	u32	epxoutcr0;	/* ep out control 0 register */
+	u32	epxoutcr1;	/* ep out control 1 register */
+	u32	epxincr0;	/* ep in control 0 register */
+	u32	epxincr1;	/* ep in control 1 register */
+};
+
+/* transfer status registers */
+struct xferstatus {
+	u32	curdeqlo;	/* current TRB pointer low */
+	u32	curdeqhi;	/* current TRB pointer high */
+	u32	statuslo;	/* transfer status low */
+	u32	statushi;	/* transfer status high */
+};
+
+/* vendor unique control registers */
+struct mv_u3d_vuc_regs {
+	u32	ctrlepenable;	/* control endpoint enable register */
+	u32	setuplock;	/* setup lock register */
+	u32	endcomplete;	/* endpoint transfer complete register */
+	u32	intrcause;	/* interrupt cause register */
+	u32	intrenable;	/* interrupt enable register */
+	u32	trbcomplete;	/* TRB complete register */
+	u32	linkchange;	/* link change register */
+	u32	rsvd1[5];
+	u32	trbunderrun;	/* TRB underrun register */
+	u32	rsvd2[43];
+	u32	bridgesetting;	/* bridge setting register */
+	u32	rsvd3[7];
+	struct xferstatus	txst[16];	/* TX status register */
+	struct xferstatus	rxst[16];	/* RX status register */
+	u32	ltssm;		/* LTSSM control register */
+	u32	pipe;		/* PIPE control register */
+	u32	linkcr0;	/* link control 0 register */
+	u32	linkcr1;	/* link control 1 register */
+	u32	rsvd6[60];
+	u32	mib0;		/* MIB0 counter register */
+	u32	usblink;	/* usb link control register */
+	u32	ltssmstate;	/* LTSSM state register */
+	u32	linkerrorcause;	/* link error cause register */
+	u32	rsvd7[60];
+	u32	devaddrtiebrkr;	/* device address and tie breaker */
+	u32	itpinfo0;	/* ITP info 0 register */
+	u32	itpinfo1;	/* ITP info 1 register */
+	u32	rsvd8[61];
+	struct epxcr	epcr[16];	/* ep control register */
+	u32	rsvd9[64];
+	u32	phyaddr;	/* PHY address register */
+	u32	phydata;	/* PHY data register */
+};
+
+/* Endpoint context structure */
+struct mv_u3d_ep_context {
+	u32	rsvd0;
+	u32	rsvd1;
+	u32	trb_addr_lo;		/* TRB address low 32 bit */
+	u32	trb_addr_hi;		/* TRB address high 32 bit */
+	u32	rsvd2;
+	u32	rsvd3;
+	struct usb_ctrlrequest setup_buffer;	/* setup data buffer */
+};
+
+/* TRB control data structure */
+struct mv_u3d_trb_ctrl {
+	u32	own:1;		/* owner of TRB */
+	u32	rsvd1:3;
+	u32	chain:1;	/* associate this TRB with the
+				next TRB on the Ring */
+	u32	ioc:1;		/* interrupt on complete */
+	u32	rsvd2:4;
+	u32	type:6;		/* TRB type */
+#define TYPE_NORMAL	1
+#define TYPE_DATA	3
+#define TYPE_LINK	6
+	u32	dir:1;		/* Working at data stage of control endpoint
+				operation. 0 is OUT and 1 is IN. */
+	u32	rsvd3:15;
+};
+
+/* TRB data structure
+ * For multiple TRB, all the TRBs' physical address should be continuous.
+ */
+struct mv_u3d_trb_hw {
+	u32	buf_addr_lo;	/* data buffer address low 32 bit */
+	u32	buf_addr_hi;	/* data buffer address high 32 bit */
+	u32	trb_len;	/* transfer length */
+	struct mv_u3d_trb_ctrl	ctrl;	/* TRB control data */
+};
+
+/* TRB structure */
+struct mv_u3d_trb {
+	struct mv_u3d_trb_hw *trb_hw;	/* point to the trb_hw structure */
+	dma_addr_t trb_dma;		/* dma address for this trb_hw */
+	struct list_head trb_list;	/* trb list */
+};
+
+/* device data structure */
+struct mv_u3d {
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	spinlock_t			lock;	/* device lock */
+	struct completion		*done;
+	struct device			*dev;
+	int				irq;
+
+	/* usb controller registers */
+	struct mv_u3d_cap_regs __iomem	*cap_regs;
+	struct mv_u3d_op_regs __iomem	*op_regs;
+	struct mv_u3d_vuc_regs __iomem	*vuc_regs;
+	void __iomem			*phy_regs;
+
+	unsigned int			max_eps;
+	struct mv_u3d_ep_context	*ep_context;
+	size_t				ep_context_size;
+	dma_addr_t			ep_context_dma;
+
+	struct dma_pool			*trb_pool; /* for TRB data structure */
+	struct mv_u3d_ep		*eps;
+
+	struct mv_u3d_req		*status_req; /* ep0 status request */
+	struct usb_ctrlrequest		local_setup_buff; /* store setup data*/
+
+	unsigned int		resume_state;	/* USB state to resume */
+	unsigned int		usb_state;	/* USB current state */
+	unsigned int		ep0_state;	/* Endpoint zero state */
+	unsigned int		ep0_dir;
+
+	unsigned int		dev_addr;	/* device address */
+
+	unsigned int		errors;
+
+	unsigned		softconnect:1;
+	unsigned		vbus_active:1;	/* vbus is active or not */
+	unsigned		remote_wakeup:1; /* support remote wakeup */
+	unsigned		clock_gating:1;	/* clock gating or not */
+	unsigned		active:1;	/* udc is active or not */
+	unsigned		vbus_valid_detect:1; /* udc vbus detection */
+
+	struct mv_usb_addon_irq *vbus;
+	unsigned int		power;
+
+	struct clk		*clk;
+};
+
+/* endpoint data structure */
+struct mv_u3d_ep {
+	struct usb_ep		ep;
+	struct mv_u3d		*u3d;
+	struct list_head	queue;	/* ep request queued hardware */
+	struct list_head	req_list; /* list of ep request */
+	struct mv_u3d_ep_context	*ep_context; /* ep context */
+	u32			direction;
+	char			name[14];
+	u32			processing; /* there is ep request
+						queued on haredware */
+	spinlock_t		req_lock; /* ep lock */
+	unsigned		wedge:1;
+	unsigned		enabled:1;
+	unsigned		ep_type:2;
+	unsigned		ep_num:8;
+};
+
+/* request data structure */
+struct mv_u3d_req {
+	struct usb_request	req;
+	struct mv_u3d_ep	*ep;
+	struct list_head	queue;	/* ep requst queued on hardware */
+	struct list_head	list;	/* ep request list */
+	struct list_head	trb_list; /* trb list of a request */
+
+	struct mv_u3d_trb	*trb_head; /* point to first trb of a request */
+	unsigned		trb_count; /* TRB number in the chain */
+	unsigned		chain;	   /* TRB chain or not */
+};
+
+#endif
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
new file mode 100644
index 0000000..35e02a8
--- /dev/null
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -0,0 +1,2063 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/clk.h>
+
+#include "mv_u3d.h"
+
+#define DRIVER_DESC		"Marvell PXA USB3.0 Device Controller driver"
+
+static const char driver_name[] = "mv_u3d";
+static const char driver_desc[] = DRIVER_DESC;
+
+static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status);
+static void mv_u3d_stop_activity(struct mv_u3d *u3d,
+			struct usb_gadget_driver *driver);
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	MV_U3D_EP0_MAX_PKT_SIZE,
+};
+
+static void mv_u3d_ep0_reset(struct mv_u3d *u3d)
+{
+	struct mv_u3d_ep *ep;
+	u32 epxcr;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		ep = &u3d->eps[i];
+		ep->u3d = u3d;
+
+		/* ep0 ep context, ep0 in and out share the same ep context */
+		ep->ep_context = &u3d->ep_context[1];
+	}
+
+	/* reset ep state machine */
+	/* reset ep0 out */
+	epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+	epxcr |= MV_U3D_EPXCR_EP_INIT;
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
+	udelay(5);
+	epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
+
+	epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
+		<< MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+		| (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+		| (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		| MV_U3D_EPXCR_EP_TYPE_CONTROL);
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1);
+
+	/* reset ep0 in */
+	epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+	epxcr |= MV_U3D_EPXCR_EP_INIT;
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
+	udelay(5);
+	epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
+
+	epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
+		<< MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+		| (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+		| (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		| MV_U3D_EPXCR_EP_TYPE_CONTROL);
+	iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1);
+}
+
+static void mv_u3d_ep0_stall(struct mv_u3d *u3d)
+{
+	u32 tmp;
+	dev_dbg(u3d->dev, "%s\n", __func__);
+
+	/* set TX and RX to stall */
+	tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+	tmp |= MV_U3D_EPXCR_EP_HALT;
+	iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+
+	tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+	tmp |= MV_U3D_EPXCR_EP_HALT;
+	iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+
+	/* update ep0 state */
+	u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+	u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+}
+
+static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index,
+	struct mv_u3d_req *curr_req)
+{
+	struct mv_u3d_trb	*curr_trb;
+	int actual, remaining_length = 0;
+	int direction, ep_num;
+	int retval = 0;
+	u32 tmp, status, length;
+
+	direction = index % 2;
+	ep_num = index / 2;
+
+	actual = curr_req->req.length;
+
+	while (!list_empty(&curr_req->trb_list)) {
+		curr_trb = list_entry(curr_req->trb_list.next,
+					struct mv_u3d_trb, trb_list);
+		if (!curr_trb->trb_hw->ctrl.own) {
+			dev_err(u3d->dev, "%s, TRB own error!\n",
+				u3d->eps[index].name);
+			return 1;
+		}
+
+		curr_trb->trb_hw->ctrl.own = 0;
+		if (direction == MV_U3D_EP_DIR_OUT)
+			tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo);
+		else
+			tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo);
+
+		status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT;
+		length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK;
+
+		if (status == MV_U3D_COMPLETE_SUCCESS ||
+			(status == MV_U3D_COMPLETE_SHORT_PACKET &&
+			direction == MV_U3D_EP_DIR_OUT)) {
+			remaining_length += length;
+			actual -= remaining_length;
+		} else {
+			dev_err(u3d->dev,
+				"complete_tr error: ep=%d %s: error = 0x%x\n",
+				index >> 1, direction ? "SEND" : "RECV",
+				status);
+			retval = -EPROTO;
+		}
+
+		list_del_init(&curr_trb->trb_list);
+	}
+	if (retval)
+		return retval;
+
+	curr_req->req.actual = actual;
+	return 0;
+}
+
+/*
+ * mv_u3d_done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ */
+static
+void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status)
+	__releases(&ep->udc->lock)
+	__acquires(&ep->udc->lock)
+{
+	struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d;
+
+	dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n");
+	/* Removed the req from ep queue */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	/* Free trb for the request */
+	if (!req->chain)
+		dma_pool_free(u3d->trb_pool,
+			req->trb_head->trb_hw, req->trb_head->trb_dma);
+	else {
+		dma_unmap_single(ep->u3d->gadget.dev.parent,
+			(dma_addr_t)req->trb_head->trb_dma,
+			req->trb_count * sizeof(struct mv_u3d_trb_hw),
+			DMA_BIDIRECTIONAL);
+		kfree(req->trb_head->trb_hw);
+	}
+	kfree(req->trb_head);
+
+	usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep));
+
+	if (status && (status != -ESHUTDOWN)) {
+		dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+	}
+
+	spin_unlock(&ep->u3d->lock);
+
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+
+	spin_lock(&ep->u3d->lock);
+}
+
+static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req)
+{
+	u32 tmp, direction;
+	struct mv_u3d *u3d;
+	struct mv_u3d_ep_context *ep_context;
+	int retval = 0;
+
+	u3d = ep->u3d;
+	direction = mv_u3d_ep_dir(ep);
+
+	/* ep0 in and out share the same ep context slot 1*/
+	if (ep->ep_num == 0)
+		ep_context = &(u3d->ep_context[1]);
+	else
+		ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]);
+
+	/* check if the pipe is empty or not */
+	if (!list_empty(&ep->queue)) {
+		dev_err(u3d->dev, "add trb to non-empty queue!\n");
+		retval = -ENOMEM;
+		WARN_ON(1);
+	} else {
+		ep_context->rsvd0 = cpu_to_le32(1);
+		ep_context->rsvd1 = 0;
+
+		/* Configure the trb address and set the DCS bit.
+		 * Both DCS bit and own bit in trb should be set.
+		 */
+		ep_context->trb_addr_lo =
+			cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE);
+		ep_context->trb_addr_hi = 0;
+
+		/* Ensure that updates to the EP Context will
+		 * occure before Ring Bell.
+		 */
+		wmb();
+
+		/* ring bell the ep */
+		if (ep->ep_num == 0)
+			tmp = 0x1;
+		else
+			tmp = ep->ep_num * 2
+				+ ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1);
+
+		iowrite32(tmp, &u3d->op_regs->doorbell);
+	}
+	return retval;
+}
+
+static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
+				unsigned *length, dma_addr_t *dma)
+{
+	u32 temp;
+	unsigned int direction;
+	struct mv_u3d_trb *trb;
+	struct mv_u3d_trb_hw *trb_hw;
+	struct mv_u3d *u3d;
+
+	/* how big will this transfer be? */
+	*length = req->req.length - req->req.actual;
+	BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
+
+	u3d = req->ep->u3d;
+
+	trb = kzalloc(sizeof(*trb), GFP_ATOMIC);
+	if (!trb)
+		return NULL;
+
+	/*
+	 * Be careful that no _GFP_HIGHMEM is set,
+	 * or we can not use dma_to_virt
+	 * cannot use GFP_KERNEL in spin lock
+	 */
+	trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma);
+	if (!trb_hw) {
+		kfree(trb);
+		dev_err(u3d->dev,
+			"%s, dma_pool_alloc fail\n", __func__);
+		return NULL;
+	}
+	trb->trb_dma = *dma;
+	trb->trb_hw = trb_hw;
+
+	/* initialize buffer page pointers */
+	temp = (u32)(req->req.dma + req->req.actual);
+
+	trb_hw->buf_addr_lo = cpu_to_le32(temp);
+	trb_hw->buf_addr_hi = 0;
+	trb_hw->trb_len = cpu_to_le32(*length);
+	trb_hw->ctrl.own = 1;
+
+	if (req->ep->ep_num == 0)
+		trb_hw->ctrl.type = TYPE_DATA;
+	else
+		trb_hw->ctrl.type = TYPE_NORMAL;
+
+	req->req.actual += *length;
+
+	direction = mv_u3d_ep_dir(req->ep);
+	if (direction == MV_U3D_EP_DIR_IN)
+		trb_hw->ctrl.dir = 1;
+	else
+		trb_hw->ctrl.dir = 0;
+
+	/* Enable interrupt for the last trb of a request */
+	if (!req->req.no_interrupt)
+		trb_hw->ctrl.ioc = 1;
+
+	trb_hw->ctrl.chain = 0;
+
+	wmb();
+	return trb;
+}
+
+static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length,
+		struct mv_u3d_trb *trb, int *is_last)
+{
+	u32 temp;
+	unsigned int direction;
+	struct mv_u3d *u3d;
+
+	/* how big will this transfer be? */
+	*length = min(req->req.length - req->req.actual,
+			(unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
+
+	u3d = req->ep->u3d;
+
+	trb->trb_dma = 0;
+
+	/* initialize buffer page pointers */
+	temp = (u32)(req->req.dma + req->req.actual);
+
+	trb->trb_hw->buf_addr_lo = cpu_to_le32(temp);
+	trb->trb_hw->buf_addr_hi = 0;
+	trb->trb_hw->trb_len = cpu_to_le32(*length);
+	trb->trb_hw->ctrl.own = 1;
+
+	if (req->ep->ep_num == 0)
+		trb->trb_hw->ctrl.type = TYPE_DATA;
+	else
+		trb->trb_hw->ctrl.type = TYPE_NORMAL;
+
+	req->req.actual += *length;
+
+	direction = mv_u3d_ep_dir(req->ep);
+	if (direction == MV_U3D_EP_DIR_IN)
+		trb->trb_hw->ctrl.dir = 1;
+	else
+		trb->trb_hw->ctrl.dir = 0;
+
+	/* zlp is needed if req->req.zero is set */
+	if (req->req.zero) {
+		if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+			*is_last = 1;
+		else
+			*is_last = 0;
+	} else if (req->req.length == req->req.actual)
+		*is_last = 1;
+	else
+		*is_last = 0;
+
+	/* Enable interrupt for the last trb of a request */
+	if (*is_last && !req->req.no_interrupt)
+		trb->trb_hw->ctrl.ioc = 1;
+
+	if (*is_last)
+		trb->trb_hw->ctrl.chain = 0;
+	else {
+		trb->trb_hw->ctrl.chain = 1;
+		dev_dbg(u3d->dev, "chain trb\n");
+	}
+
+	wmb();
+
+	return 0;
+}
+
+/* generate TRB linked list for a request
+ * usb controller only supports continous trb chain,
+ * that trb structure physical address should be continous.
+ */
+static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
+{
+	unsigned count;
+	int is_last;
+	struct mv_u3d_trb *trb;
+	struct mv_u3d_trb_hw *trb_hw;
+	struct mv_u3d *u3d;
+	dma_addr_t dma;
+	unsigned length;
+	unsigned trb_num;
+
+	u3d = req->ep->u3d;
+
+	INIT_LIST_HEAD(&req->trb_list);
+
+	length = req->req.length - req->req.actual;
+	/* normally the request transfer length is less than 16KB.
+	 * we use buil_trb_one() to optimize it.
+	 */
+	if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) {
+		trb = mv_u3d_build_trb_one(req, &count, &dma);
+		list_add_tail(&trb->trb_list, &req->trb_list);
+		req->trb_head = trb;
+		req->trb_count = 1;
+		req->chain = 0;
+	} else {
+		trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER;
+		if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER)
+			trb_num++;
+
+		trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC);
+		if (!trb)
+			return -ENOMEM;
+
+		trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
+		if (!trb_hw) {
+			kfree(trb);
+			return -ENOMEM;
+		}
+
+		do {
+			trb->trb_hw = trb_hw;
+			if (mv_u3d_build_trb_chain(req, &count,
+						trb, &is_last)) {
+				dev_err(u3d->dev,
+					"%s, mv_u3d_build_trb_chain fail\n",
+					__func__);
+				return -EIO;
+			}
+
+			list_add_tail(&trb->trb_list, &req->trb_list);
+			req->trb_count++;
+			trb++;
+			trb_hw++;
+		} while (!is_last);
+
+		req->trb_head = list_entry(req->trb_list.next,
+					struct mv_u3d_trb, trb_list);
+		req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent,
+					req->trb_head->trb_hw,
+					trb_num * sizeof(*trb_hw),
+					DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(u3d->gadget.dev.parent,
+					req->trb_head->trb_dma)) {
+			kfree(req->trb_head->trb_hw);
+			kfree(req->trb_head);
+			return -EFAULT;
+		}
+
+		req->chain = 1;
+	}
+
+	return 0;
+}
+
+static int
+mv_u3d_start_queue(struct mv_u3d_ep *ep)
+{
+	struct mv_u3d *u3d = ep->u3d;
+	struct mv_u3d_req *req;
+	int ret;
+
+	if (!list_empty(&ep->req_list) && !ep->processing)
+		req = list_entry(ep->req_list.next, struct mv_u3d_req, list);
+	else
+		return 0;
+
+	ep->processing = 1;
+
+	/* set up dma mapping */
+	ret = usb_gadget_map_request(&u3d->gadget, &req->req,
+					mv_u3d_ep_dir(ep));
+	if (ret)
+		goto break_processing;
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->trb_count = 0;
+
+	/* build trbs */
+	ret = mv_u3d_req_to_trb(req);
+	if (ret) {
+		dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__);
+		goto break_processing;
+	}
+
+	/* and push them to device queue */
+	ret = mv_u3d_queue_trb(ep, req);
+	if (ret)
+		goto break_processing;
+
+	/* irq handler advances the queue */
+	list_add_tail(&req->queue, &ep->queue);
+
+	return 0;
+
+break_processing:
+	ep->processing = 0;
+	return ret;
+}
+
+static int mv_u3d_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct mv_u3d *u3d;
+	struct mv_u3d_ep *ep;
+	u16 max = 0;
+	unsigned maxburst = 0;
+	u32 epxcr, direction;
+
+	if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+
+	ep = container_of(_ep, struct mv_u3d_ep, ep);
+	u3d = ep->u3d;
+
+	if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	direction = mv_u3d_ep_dir(ep);
+	max = le16_to_cpu(desc->wMaxPacketSize);
+
+	if (!_ep->maxburst)
+		_ep->maxburst = 1;
+	maxburst = _ep->maxburst;
+
+	/* Set the max burst size */
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (maxburst > 16) {
+			dev_dbg(u3d->dev,
+				"max burst should not be greater "
+				"than 16 on bulk ep\n");
+			maxburst = 1;
+			_ep->maxburst = maxburst;
+		}
+		dev_dbg(u3d->dev,
+			"maxburst: %d on bulk %s\n", maxburst, ep->name);
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+		/* control transfer only supports maxburst as one */
+		maxburst = 1;
+		_ep->maxburst = maxburst;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (maxburst != 1) {
+			dev_dbg(u3d->dev,
+				"max burst should be 1 on int ep "
+				"if transfer size is not 1024\n");
+			maxburst = 1;
+			_ep->maxburst = maxburst;
+		}
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (maxburst != 1) {
+			dev_dbg(u3d->dev,
+				"max burst should be 1 on isoc ep "
+				"if transfer size is not 1024\n");
+			maxburst = 1;
+			_ep->maxburst = maxburst;
+		}
+		break;
+	default:
+		goto en_done;
+	}
+
+	ep->ep.maxpacket = max;
+	ep->ep.desc = desc;
+	ep->enabled = 1;
+
+	/* Enable the endpoint for Rx or Tx and set the endpoint type */
+	if (direction == MV_U3D_EP_DIR_OUT) {
+		epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+		epxcr |= MV_U3D_EPXCR_EP_INIT;
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+		udelay(5);
+		epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+
+		epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+		      | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+		      | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		      | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+	} else {
+		epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+		epxcr |= MV_U3D_EPXCR_EP_INIT;
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+		udelay(5);
+		epxcr &= ~MV_U3D_EPXCR_EP_INIT;
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+
+		epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
+		      | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
+		      | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		      | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+	}
+
+	return 0;
+en_done:
+	return -EINVAL;
+}
+
+static int  mv_u3d_ep_disable(struct usb_ep *_ep)
+{
+	struct mv_u3d *u3d;
+	struct mv_u3d_ep *ep;
+	u32 epxcr, direction;
+	unsigned long flags;
+
+	if (!_ep)
+		return -EINVAL;
+
+	ep = container_of(_ep, struct mv_u3d_ep, ep);
+	if (!ep->ep.desc)
+		return -EINVAL;
+
+	u3d = ep->u3d;
+
+	direction = mv_u3d_ep_dir(ep);
+
+	/* nuke all pending requests (does flush) */
+	spin_lock_irqsave(&u3d->lock, flags);
+	mv_u3d_nuke(ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&u3d->lock, flags);
+
+	/* Disable the endpoint for Rx or Tx and reset the endpoint type */
+	if (direction == MV_U3D_EP_DIR_OUT) {
+		epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+		epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		      | USB_ENDPOINT_XFERTYPE_MASK);
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
+	} else {
+		epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+		epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
+		      | USB_ENDPOINT_XFERTYPE_MASK);
+		iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
+	}
+
+	ep->enabled = 0;
+
+	ep->ep.desc = NULL;
+	return 0;
+}
+
+static struct usb_request *
+mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct mv_u3d_req *req = NULL;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req);
+
+	kfree(req);
+}
+
+static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct mv_u3d *u3d;
+	u32 direction;
+	struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep);
+	unsigned int loops;
+	u32 tmp;
+
+	/* if endpoint is not enabled, cannot flush endpoint */
+	if (!ep->enabled)
+		return;
+
+	u3d = ep->u3d;
+	direction = mv_u3d_ep_dir(ep);
+
+	/* ep0 need clear bit after flushing fifo. */
+	if (!ep->ep_num) {
+		if (direction == MV_U3D_EP_DIR_OUT) {
+			tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
+			tmp |= MV_U3D_EPXCR_EP_FLUSH;
+			iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+			udelay(10);
+			tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
+			iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
+		} else {
+			tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
+			tmp |= MV_U3D_EPXCR_EP_FLUSH;
+			iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+			udelay(10);
+			tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
+			iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
+		}
+		return;
+	}
+
+	if (direction == MV_U3D_EP_DIR_OUT) {
+		tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+		tmp |= MV_U3D_EPXCR_EP_FLUSH;
+		iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+
+		/* Wait until flushing completed */
+		loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
+		while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) &
+			MV_U3D_EPXCR_EP_FLUSH) {
+			/*
+			 * EP_FLUSH bit should be cleared to indicate this
+			 * operation is complete
+			 */
+			if (loops == 0) {
+				dev_dbg(u3d->dev,
+				    "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
+				    direction ? "in" : "out");
+				return;
+			}
+			loops--;
+			udelay(LOOPS_USEC);
+		}
+	} else {	/* EP_DIR_IN */
+		tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+		tmp |= MV_U3D_EPXCR_EP_FLUSH;
+		iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+
+		/* Wait until flushing completed */
+		loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
+		while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) &
+			MV_U3D_EPXCR_EP_FLUSH) {
+			/*
+			* EP_FLUSH bit should be cleared to indicate this
+			* operation is complete
+			*/
+			if (loops == 0) {
+				dev_dbg(u3d->dev,
+				    "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
+				    direction ? "in" : "out");
+				return;
+			}
+			loops--;
+			udelay(LOOPS_USEC);
+		}
+	}
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct mv_u3d_ep *ep;
+	struct mv_u3d_req *req;
+	struct mv_u3d *u3d;
+	unsigned long flags;
+	int is_first_req = 0;
+
+	if (unlikely(!_ep || !_req))
+		return -EINVAL;
+
+	ep = container_of(_ep, struct mv_u3d_ep, ep);
+	u3d = ep->u3d;
+
+	req = container_of(_req, struct mv_u3d_req, req);
+
+	if (!ep->ep_num
+		&& u3d->ep0_state == MV_U3D_STATUS_STAGE
+		&& !_req->length) {
+		dev_dbg(u3d->dev, "ep0 status stage\n");
+		u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+		return 0;
+	}
+
+	dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n",
+			__func__, _ep->name, req);
+
+	/* catch various bogus parameters */
+	if (!req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		dev_err(u3d->dev,
+			"%s, bad params, _req: 0x%p,"
+			"req->req.complete: 0x%p, req->req.buf: 0x%p,"
+			"list_empty: 0x%x\n",
+			__func__, _req,
+			req->req.complete, req->req.buf,
+			list_empty(&req->queue));
+		return -EINVAL;
+	}
+	if (unlikely(!ep->ep.desc)) {
+		dev_err(u3d->dev, "%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+	if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		if (req->req.length > ep->ep.maxpacket)
+			return -EMSGSIZE;
+	}
+
+	if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(u3d->dev,
+			"bad params of driver/speed\n");
+		return -ESHUTDOWN;
+	}
+
+	req->ep = ep;
+
+	/* Software list handles usb request. */
+	spin_lock_irqsave(&ep->req_lock, flags);
+	is_first_req = list_empty(&ep->req_list);
+	list_add_tail(&req->list, &ep->req_list);
+	spin_unlock_irqrestore(&ep->req_lock, flags);
+	if (!is_first_req) {
+		dev_dbg(u3d->dev, "list is not empty\n");
+		return 0;
+	}
+
+	dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n");
+	spin_lock_irqsave(&u3d->lock, flags);
+	mv_u3d_start_queue(ep);
+	spin_unlock_irqrestore(&u3d->lock, flags);
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct mv_u3d_ep *ep;
+	struct mv_u3d_req *req;
+	struct mv_u3d *u3d;
+	struct mv_u3d_ep_context *ep_context;
+	struct mv_u3d_req *next_req;
+
+	unsigned long flags;
+	int ret = 0;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	ep = container_of(_ep, struct mv_u3d_ep, ep);
+	u3d = ep->u3d;
+
+	spin_lock_irqsave(&ep->u3d->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* The request is in progress, or completed but not dequeued */
+	if (ep->queue.next == &req->queue) {
+		_req->status = -ECONNRESET;
+		mv_u3d_ep_fifo_flush(_ep);
+
+		/* The request isn't the last request in this ep queue */
+		if (req->queue.next != &ep->queue) {
+			dev_dbg(u3d->dev,
+				"it is the last request in this ep queue\n");
+			ep_context = ep->ep_context;
+			next_req = list_entry(req->queue.next,
+					struct mv_u3d_req, queue);
+
+			/* Point first TRB of next request to the EP context. */
+			iowrite32((unsigned long) next_req->trb_head,
+					&ep_context->trb_addr_lo);
+		} else {
+			struct mv_u3d_ep_context *ep_context;
+			ep_context = ep->ep_context;
+			ep_context->trb_addr_lo = 0;
+			ep_context->trb_addr_hi = 0;
+		}
+
+	} else
+		WARN_ON(1);
+
+	mv_u3d_done(ep, req, -ECONNRESET);
+
+	/* remove the req from the ep req list */
+	if (!list_empty(&ep->req_list)) {
+		struct mv_u3d_req *curr_req;
+		curr_req = list_entry(ep->req_list.next,
+					struct mv_u3d_req, list);
+		if (curr_req == req) {
+			list_del_init(&req->list);
+			ep->processing = 0;
+		}
+	}
+
+out:
+	spin_unlock_irqrestore(&ep->u3d->lock, flags);
+	return ret;
+}
+
+static void
+mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall)
+{
+	u32 tmp;
+	struct mv_u3d_ep *ep = u3d->eps;
+
+	dev_dbg(u3d->dev, "%s\n", __func__);
+	if (direction == MV_U3D_EP_DIR_OUT) {
+		tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+		if (stall)
+			tmp |= MV_U3D_EPXCR_EP_HALT;
+		else
+			tmp &= ~MV_U3D_EPXCR_EP_HALT;
+		iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
+	} else {
+		tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+		if (stall)
+			tmp |= MV_U3D_EPXCR_EP_HALT;
+		else
+			tmp &= ~MV_U3D_EPXCR_EP_HALT;
+		iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
+	}
+}
+
+static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+	struct mv_u3d_ep *ep;
+	unsigned long flags = 0;
+	int status = 0;
+	struct mv_u3d *u3d;
+
+	ep = container_of(_ep, struct mv_u3d_ep, ep);
+	u3d = ep->u3d;
+	if (!ep->ep.desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		status = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
+	 * Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue
+	 */
+	if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN)
+			&& !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	spin_lock_irqsave(&ep->u3d->lock, flags);
+	mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt);
+	if (halt && wedge)
+		ep->wedge = 1;
+	else if (!halt)
+		ep->wedge = 0;
+	spin_unlock_irqrestore(&ep->u3d->lock, flags);
+
+	if (ep->ep_num == 0)
+		u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+out:
+	return status;
+}
+
+static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt)
+{
+	return mv_u3d_ep_set_halt_wedge(_ep, halt, 0);
+}
+
+static int mv_u3d_ep_set_wedge(struct usb_ep *_ep)
+{
+	return mv_u3d_ep_set_halt_wedge(_ep, 1, 1);
+}
+
+static const struct usb_ep_ops mv_u3d_ep_ops = {
+	.enable		= mv_u3d_ep_enable,
+	.disable	= mv_u3d_ep_disable,
+
+	.alloc_request	= mv_u3d_alloc_request,
+	.free_request	= mv_u3d_free_request,
+
+	.queue		= mv_u3d_ep_queue,
+	.dequeue	= mv_u3d_ep_dequeue,
+
+	.set_wedge	= mv_u3d_ep_set_wedge,
+	.set_halt	= mv_u3d_ep_set_halt,
+	.fifo_flush	= mv_u3d_ep_fifo_flush,
+};
+
+static void mv_u3d_controller_stop(struct mv_u3d *u3d)
+{
+	u32 tmp;
+
+	if (!u3d->clock_gating && u3d->vbus_valid_detect)
+		iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID,
+				&u3d->vuc_regs->intrenable);
+	else
+		iowrite32(0, &u3d->vuc_regs->intrenable);
+	iowrite32(~0x0, &u3d->vuc_regs->endcomplete);
+	iowrite32(~0x0, &u3d->vuc_regs->trbunderrun);
+	iowrite32(~0x0, &u3d->vuc_regs->trbcomplete);
+	iowrite32(~0x0, &u3d->vuc_regs->linkchange);
+	iowrite32(0x1, &u3d->vuc_regs->setuplock);
+
+	/* Reset the RUN bit in the command register to stop USB */
+	tmp = ioread32(&u3d->op_regs->usbcmd);
+	tmp &= ~MV_U3D_CMD_RUN_STOP;
+	iowrite32(tmp, &u3d->op_regs->usbcmd);
+	dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n",
+		ioread32(&u3d->op_regs->usbcmd));
+}
+
+static void mv_u3d_controller_start(struct mv_u3d *u3d)
+{
+	u32 usbintr;
+	u32 temp;
+
+	/* enable link LTSSM state machine */
+	temp = ioread32(&u3d->vuc_regs->ltssm);
+	temp |= MV_U3D_LTSSM_PHY_INIT_DONE;
+	iowrite32(temp, &u3d->vuc_regs->ltssm);
+
+	/* Enable interrupts */
+	usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR |
+		MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE |
+		MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP |
+		(u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0);
+	iowrite32(usbintr, &u3d->vuc_regs->intrenable);
+
+	/* Enable ctrl ep */
+	iowrite32(0x1, &u3d->vuc_regs->ctrlepenable);
+
+	/* Set the Run bit in the command register */
+	iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd);
+	dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n",
+		ioread32(&u3d->op_regs->usbcmd));
+}
+
+static int mv_u3d_controller_reset(struct mv_u3d *u3d)
+{
+	unsigned int loops;
+	u32 tmp;
+
+	/* Stop the controller */
+	tmp = ioread32(&u3d->op_regs->usbcmd);
+	tmp &= ~MV_U3D_CMD_RUN_STOP;
+	iowrite32(tmp, &u3d->op_regs->usbcmd);
+
+	/* Reset the controller to get default values */
+	iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd);
+
+	/* wait for reset to complete */
+	loops = LOOPS(MV_U3D_RESET_TIMEOUT);
+	while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) {
+		if (loops == 0) {
+			dev_err(u3d->dev,
+				"Wait for RESET completed TIMEOUT\n");
+			return -ETIMEDOUT;
+		}
+		loops--;
+		udelay(LOOPS_USEC);
+	}
+
+	/* Configure the Endpoint Context Address */
+	iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl);
+	iowrite32(0, &u3d->op_regs->dcbaaph);
+
+	return 0;
+}
+
+static int mv_u3d_enable(struct mv_u3d *u3d)
+{
+	struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
+	int retval;
+
+	if (u3d->active)
+		return 0;
+
+	if (!u3d->clock_gating) {
+		u3d->active = 1;
+		return 0;
+	}
+
+	dev_dbg(u3d->dev, "enable u3d\n");
+	clk_enable(u3d->clk);
+	if (pdata->phy_init) {
+		retval = pdata->phy_init(u3d->phy_regs);
+		if (retval) {
+			dev_err(u3d->dev,
+				"init phy error %d\n", retval);
+			clk_disable(u3d->clk);
+			return retval;
+		}
+	}
+	u3d->active = 1;
+
+	return 0;
+}
+
+static void mv_u3d_disable(struct mv_u3d *u3d)
+{
+	struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
+	if (u3d->clock_gating && u3d->active) {
+		dev_dbg(u3d->dev, "disable u3d\n");
+		if (pdata->phy_deinit)
+			pdata->phy_deinit(u3d->phy_regs);
+		clk_disable(u3d->clk);
+		u3d->active = 0;
+	}
+}
+
+static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct mv_u3d *u3d;
+	unsigned long flags;
+	int retval = 0;
+
+	u3d = container_of(gadget, struct mv_u3d, gadget);
+
+	spin_lock_irqsave(&u3d->lock, flags);
+
+	u3d->vbus_active = (is_active != 0);
+	dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
+		__func__, u3d->softconnect, u3d->vbus_active);
+	/*
+	 * 1. external VBUS detect: we can disable/enable clock on demand.
+	 * 2. UDC VBUS detect: we have to enable clock all the time.
+	 * 3. No VBUS detect: we have to enable clock all the time.
+	 */
+	if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
+		retval = mv_u3d_enable(u3d);
+		if (retval == 0) {
+			/*
+			 * after clock is disabled, we lost all the register
+			 *  context. We have to re-init registers
+			 */
+			mv_u3d_controller_reset(u3d);
+			mv_u3d_ep0_reset(u3d);
+			mv_u3d_controller_start(u3d);
+		}
+	} else if (u3d->driver && u3d->softconnect) {
+		if (!u3d->active)
+			goto out;
+
+		/* stop all the transfer in queue*/
+		mv_u3d_stop_activity(u3d, u3d->driver);
+		mv_u3d_controller_stop(u3d);
+		mv_u3d_disable(u3d);
+	}
+
+out:
+	spin_unlock_irqrestore(&u3d->lock, flags);
+	return retval;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
+
+	u3d->power = mA;
+
+	return 0;
+}
+
+static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
+	unsigned long flags;
+	int retval = 0;
+
+	spin_lock_irqsave(&u3d->lock, flags);
+
+	dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
+		__func__, u3d->softconnect, u3d->vbus_active);
+	u3d->softconnect = (is_on != 0);
+	if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
+		retval = mv_u3d_enable(u3d);
+		if (retval == 0) {
+			/*
+			 * after clock is disabled, we lost all the register
+			 *  context. We have to re-init registers
+			 */
+			mv_u3d_controller_reset(u3d);
+			mv_u3d_ep0_reset(u3d);
+			mv_u3d_controller_start(u3d);
+		}
+	} else if (u3d->driver && u3d->vbus_active) {
+		/* stop all the transfer in queue*/
+		mv_u3d_stop_activity(u3d, u3d->driver);
+		mv_u3d_controller_stop(u3d);
+		mv_u3d_disable(u3d);
+	}
+
+	spin_unlock_irqrestore(&u3d->lock, flags);
+
+	return retval;
+}
+
+static int mv_u3d_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
+	struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
+	unsigned long flags;
+
+	if (u3d->driver)
+		return -EBUSY;
+
+	spin_lock_irqsave(&u3d->lock, flags);
+
+	if (!u3d->clock_gating) {
+		clk_enable(u3d->clk);
+		if (pdata->phy_init)
+			pdata->phy_init(u3d->phy_regs);
+	}
+
+	/* hook up the driver ... */
+	driver->driver.bus = NULL;
+	u3d->driver = driver;
+
+	u3d->ep0_dir = USB_DIR_OUT;
+
+	spin_unlock_irqrestore(&u3d->lock, flags);
+
+	u3d->vbus_valid_detect = 1;
+
+	return 0;
+}
+
+static int mv_u3d_stop(struct usb_gadget *g)
+{
+	struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
+	struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
+	unsigned long flags;
+
+	u3d->vbus_valid_detect = 0;
+	spin_lock_irqsave(&u3d->lock, flags);
+
+	/* enable clock to access controller register */
+	clk_enable(u3d->clk);
+	if (pdata->phy_init)
+		pdata->phy_init(u3d->phy_regs);
+
+	mv_u3d_controller_stop(u3d);
+	/* stop all usb activities */
+	u3d->gadget.speed = USB_SPEED_UNKNOWN;
+	mv_u3d_stop_activity(u3d, NULL);
+	mv_u3d_disable(u3d);
+
+	if (pdata->phy_deinit)
+		pdata->phy_deinit(u3d->phy_regs);
+	clk_disable(u3d->clk);
+
+	spin_unlock_irqrestore(&u3d->lock, flags);
+
+	u3d->driver = NULL;
+
+	return 0;
+}
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops mv_u3d_ops = {
+	/* notify controller that VBUS is powered or not */
+	.vbus_session	= mv_u3d_vbus_session,
+
+	/* constrain controller's VBUS power usage */
+	.vbus_draw	= mv_u3d_vbus_draw,
+
+	.pullup		= mv_u3d_pullup,
+	.udc_start	= mv_u3d_start,
+	.udc_stop	= mv_u3d_stop,
+};
+
+static int mv_u3d_eps_init(struct mv_u3d *u3d)
+{
+	struct mv_u3d_ep	*ep;
+	char name[14];
+	int i;
+
+	/* initialize ep0, ep0 in/out use eps[1] */
+	ep = &u3d->eps[1];
+	ep->u3d = u3d;
+	strncpy(ep->name, "ep0", sizeof(ep->name));
+	ep->ep.name = ep->name;
+	ep->ep.ops = &mv_u3d_ep_ops;
+	ep->wedge = 0;
+	usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE);
+	ep->ep.caps.type_control = true;
+	ep->ep.caps.dir_in = true;
+	ep->ep.caps.dir_out = true;
+	ep->ep_num = 0;
+	ep->ep.desc = &mv_u3d_ep0_desc;
+	INIT_LIST_HEAD(&ep->queue);
+	INIT_LIST_HEAD(&ep->req_list);
+	ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+	/* add ep0 ep_context */
+	ep->ep_context = &u3d->ep_context[1];
+
+	/* initialize other endpoints */
+	for (i = 2; i < u3d->max_eps * 2; i++) {
+		ep = &u3d->eps[i];
+		if (i & 1) {
+			snprintf(name, sizeof(name), "ep%din", i >> 1);
+			ep->direction = MV_U3D_EP_DIR_IN;
+			ep->ep.caps.dir_in = true;
+		} else {
+			snprintf(name, sizeof(name), "ep%dout", i >> 1);
+			ep->direction = MV_U3D_EP_DIR_OUT;
+			ep->ep.caps.dir_out = true;
+		}
+		ep->u3d = u3d;
+		strncpy(ep->name, name, sizeof(ep->name));
+		ep->ep.name = ep->name;
+
+		ep->ep.caps.type_iso = true;
+		ep->ep.caps.type_bulk = true;
+		ep->ep.caps.type_int = true;
+
+		ep->ep.ops = &mv_u3d_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
+		ep->ep_num = i / 2;
+
+		INIT_LIST_HEAD(&ep->queue);
+		list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list);
+
+		INIT_LIST_HEAD(&ep->req_list);
+		spin_lock_init(&ep->req_lock);
+		ep->ep_context = &u3d->ep_context[i];
+	}
+
+	return 0;
+}
+
+/* delete all endpoint requests, called with spinlock held */
+static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status)
+{
+	/* endpoint fifo flush */
+	mv_u3d_ep_fifo_flush(&ep->ep);
+
+	while (!list_empty(&ep->queue)) {
+		struct mv_u3d_req *req = NULL;
+		req = list_entry(ep->queue.next, struct mv_u3d_req, queue);
+		mv_u3d_done(ep, req, status);
+	}
+}
+
+/* stop all USB activities */
+static
+void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver)
+{
+	struct mv_u3d_ep	*ep;
+
+	mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN);
+
+	list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) {
+		mv_u3d_nuke(ep, -ESHUTDOWN);
+	}
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&u3d->lock);
+		driver->disconnect(&u3d->gadget);
+		spin_lock(&u3d->lock);
+	}
+}
+
+static void mv_u3d_irq_process_error(struct mv_u3d *u3d)
+{
+	/* Increment the error count */
+	u3d->errors++;
+	dev_err(u3d->dev, "%s\n", __func__);
+}
+
+static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d)
+{
+	u32 linkchange;
+
+	linkchange = ioread32(&u3d->vuc_regs->linkchange);
+	iowrite32(linkchange, &u3d->vuc_regs->linkchange);
+
+	dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange);
+
+	if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) {
+		dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n",
+			ioread32(&u3d->vuc_regs->ltssmstate));
+
+		u3d->usb_state = USB_STATE_DEFAULT;
+		u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+		u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
+
+		/* set speed */
+		u3d->gadget.speed = USB_SPEED_SUPER;
+	}
+
+	if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) {
+		dev_dbg(u3d->dev, "link suspend\n");
+		u3d->resume_state = u3d->usb_state;
+		u3d->usb_state = USB_STATE_SUSPENDED;
+	}
+
+	if (linkchange & MV_U3D_LINK_CHANGE_RESUME) {
+		dev_dbg(u3d->dev, "link resume\n");
+		u3d->usb_state = u3d->resume_state;
+		u3d->resume_state = 0;
+	}
+
+	if (linkchange & MV_U3D_LINK_CHANGE_WRESET) {
+		dev_dbg(u3d->dev, "warm reset\n");
+		u3d->usb_state = USB_STATE_POWERED;
+	}
+
+	if (linkchange & MV_U3D_LINK_CHANGE_HRESET) {
+		dev_dbg(u3d->dev, "hot reset\n");
+		u3d->usb_state = USB_STATE_DEFAULT;
+	}
+
+	if (linkchange & MV_U3D_LINK_CHANGE_INACT)
+		dev_dbg(u3d->dev, "inactive\n");
+
+	if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0)
+		dev_dbg(u3d->dev, "ss.disabled\n");
+
+	if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) {
+		dev_dbg(u3d->dev, "vbus invalid\n");
+		u3d->usb_state = USB_STATE_ATTACHED;
+		u3d->vbus_valid_detect = 1;
+		/* if external vbus detect is not supported,
+		 * we handle it here.
+		 */
+		if (!u3d->vbus) {
+			spin_unlock(&u3d->lock);
+			mv_u3d_vbus_session(&u3d->gadget, 0);
+			spin_lock(&u3d->lock);
+		}
+	}
+}
+
+static void mv_u3d_ch9setaddress(struct mv_u3d *u3d,
+				struct usb_ctrlrequest *setup)
+{
+	u32 tmp;
+
+	if (u3d->usb_state != USB_STATE_DEFAULT) {
+		dev_err(u3d->dev,
+			"%s, cannot setaddr in this state (%d)\n",
+			__func__, u3d->usb_state);
+		goto err;
+	}
+
+	u3d->dev_addr = (u8)setup->wValue;
+
+	dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr);
+
+	if (u3d->dev_addr > 127) {
+		dev_err(u3d->dev,
+			"%s, u3d address is wrong (out of range)\n", __func__);
+		u3d->dev_addr = 0;
+		goto err;
+	}
+
+	/* update usb state */
+	u3d->usb_state = USB_STATE_ADDRESS;
+
+	/* set the new address */
+	tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr);
+	tmp &= ~0x7F;
+	tmp |= (u32)u3d->dev_addr;
+	iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr);
+
+	return;
+err:
+	mv_u3d_ep0_stall(u3d);
+}
+
+static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup)
+{
+	if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		if (setup->bRequest == USB_REQ_SET_CONFIGURATION)
+			return 1;
+
+	return 0;
+}
+
+static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
+	struct usb_ctrlrequest *setup)
+	__releases(&u3c->lock)
+	__acquires(&u3c->lock)
+{
+	bool delegate = false;
+
+	mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN);
+
+	dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+			setup->bRequestType, setup->bRequest,
+			setup->wValue, setup->wIndex, setup->wLength);
+
+	/* We process some stardard setup requests here */
+	if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (setup->bRequest) {
+		case USB_REQ_GET_STATUS:
+			delegate = true;
+			break;
+
+		case USB_REQ_SET_ADDRESS:
+			mv_u3d_ch9setaddress(u3d, setup);
+			break;
+
+		case USB_REQ_CLEAR_FEATURE:
+			delegate = true;
+			break;
+
+		case USB_REQ_SET_FEATURE:
+			delegate = true;
+			break;
+
+		default:
+			delegate = true;
+		}
+	} else
+		delegate = true;
+
+	/* delegate USB standard requests to the gadget driver */
+	if (delegate == true) {
+		/* USB requests handled by gadget */
+		if (setup->wLength) {
+			/* DATA phase from gadget, STATUS phase from u3d */
+			u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+					? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT;
+			spin_unlock(&u3d->lock);
+			if (u3d->driver->setup(&u3d->gadget,
+				&u3d->local_setup_buff) < 0) {
+				dev_err(u3d->dev, "setup error!\n");
+				mv_u3d_ep0_stall(u3d);
+			}
+			spin_lock(&u3d->lock);
+		} else {
+			/* no DATA phase, STATUS phase from gadget */
+			u3d->ep0_dir = MV_U3D_EP_DIR_IN;
+			u3d->ep0_state = MV_U3D_STATUS_STAGE;
+			spin_unlock(&u3d->lock);
+			if (u3d->driver->setup(&u3d->gadget,
+				&u3d->local_setup_buff) < 0)
+				mv_u3d_ep0_stall(u3d);
+			spin_lock(&u3d->lock);
+		}
+
+		if (mv_u3d_is_set_configuration(setup)) {
+			dev_dbg(u3d->dev, "u3d configured\n");
+			u3d->usb_state = USB_STATE_CONFIGURED;
+		}
+	}
+}
+
+static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr)
+{
+	struct mv_u3d_ep_context *epcontext;
+
+	epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN];
+
+	/* Copy the setup packet to local buffer */
+	memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8);
+}
+
+static void mv_u3d_irq_process_setup(struct mv_u3d *u3d)
+{
+	u32 tmp, i;
+	/* Process all Setup packet received interrupts */
+	tmp = ioread32(&u3d->vuc_regs->setuplock);
+	if (tmp) {
+		for (i = 0; i < u3d->max_eps; i++) {
+			if (tmp & (1 << i)) {
+				mv_u3d_get_setup_data(u3d, i,
+					(u8 *)(&u3d->local_setup_buff));
+				mv_u3d_handle_setup_packet(u3d, i,
+					&u3d->local_setup_buff);
+			}
+		}
+	}
+
+	iowrite32(tmp, &u3d->vuc_regs->setuplock);
+}
+
+static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d)
+{
+	u32 tmp, bit_pos;
+	int i, ep_num = 0, direction = 0;
+	struct mv_u3d_ep	*curr_ep;
+	struct mv_u3d_req *curr_req, *temp_req;
+	int status;
+
+	tmp = ioread32(&u3d->vuc_regs->endcomplete);
+
+	dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp);
+	if (!tmp)
+		return;
+	iowrite32(tmp, &u3d->vuc_regs->endcomplete);
+
+	for (i = 0; i < u3d->max_eps * 2; i++) {
+		ep_num = i >> 1;
+		direction = i % 2;
+
+		bit_pos = 1 << (ep_num + 16 * direction);
+
+		if (!(bit_pos & tmp))
+			continue;
+
+		if (i == 0)
+			curr_ep = &u3d->eps[1];
+		else
+			curr_ep = &u3d->eps[i];
+
+		/* remove req out of ep request list after completion */
+		dev_dbg(u3d->dev, "tr comp: check req_list\n");
+		spin_lock(&curr_ep->req_lock);
+		if (!list_empty(&curr_ep->req_list)) {
+			struct mv_u3d_req *req;
+			req = list_entry(curr_ep->req_list.next,
+						struct mv_u3d_req, list);
+			list_del_init(&req->list);
+			curr_ep->processing = 0;
+		}
+		spin_unlock(&curr_ep->req_lock);
+
+		/* process the req queue until an uncomplete request */
+		list_for_each_entry_safe(curr_req, temp_req,
+			&curr_ep->queue, queue) {
+			status = mv_u3d_process_ep_req(u3d, i, curr_req);
+			if (status)
+				break;
+			/* write back status to req */
+			curr_req->req.status = status;
+
+			/* ep0 request completion */
+			if (ep_num == 0) {
+				mv_u3d_done(curr_ep, curr_req, 0);
+				break;
+			} else {
+				mv_u3d_done(curr_ep, curr_req, status);
+			}
+		}
+
+		dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n");
+		mv_u3d_start_queue(curr_ep);
+	}
+}
+
+static irqreturn_t mv_u3d_irq(int irq, void *dev)
+{
+	struct mv_u3d *u3d = (struct mv_u3d *)dev;
+	u32 status, intr;
+	u32 bridgesetting;
+	u32 trbunderrun;
+
+	spin_lock(&u3d->lock);
+
+	status = ioread32(&u3d->vuc_regs->intrcause);
+	intr = ioread32(&u3d->vuc_regs->intrenable);
+	status &= intr;
+
+	if (status == 0) {
+		spin_unlock(&u3d->lock);
+		dev_err(u3d->dev, "irq error!\n");
+		return IRQ_NONE;
+	}
+
+	if (status & MV_U3D_USBINT_VBUS_VALID) {
+		bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting);
+		if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) {
+			/* write vbus valid bit of bridge setting to clear */
+			bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID;
+			iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting);
+			dev_dbg(u3d->dev, "vbus valid\n");
+
+			u3d->usb_state = USB_STATE_POWERED;
+			u3d->vbus_valid_detect = 0;
+			/* if external vbus detect is not supported,
+			 * we handle it here.
+			 */
+			if (!u3d->vbus) {
+				spin_unlock(&u3d->lock);
+				mv_u3d_vbus_session(&u3d->gadget, 1);
+				spin_lock(&u3d->lock);
+			}
+		} else
+			dev_err(u3d->dev, "vbus bit is not set\n");
+	}
+
+	/* RX data is already in the 16KB FIFO.*/
+	if (status & MV_U3D_USBINT_UNDER_RUN) {
+		trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun);
+		dev_err(u3d->dev, "under run, ep%d\n", trbunderrun);
+		iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun);
+		mv_u3d_irq_process_error(u3d);
+	}
+
+	if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) {
+		/* write one to clear */
+		iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR
+			| MV_U3D_USBINT_TXDESC_ERR),
+			&u3d->vuc_regs->intrcause);
+		dev_err(u3d->dev, "desc err 0x%x\n", status);
+		mv_u3d_irq_process_error(u3d);
+	}
+
+	if (status & MV_U3D_USBINT_LINK_CHG)
+		mv_u3d_irq_process_link_change(u3d);
+
+	if (status & MV_U3D_USBINT_TX_COMPLETE)
+		mv_u3d_irq_process_tr_complete(u3d);
+
+	if (status & MV_U3D_USBINT_RX_COMPLETE)
+		mv_u3d_irq_process_tr_complete(u3d);
+
+	if (status & MV_U3D_USBINT_SETUP)
+		mv_u3d_irq_process_setup(u3d);
+
+	spin_unlock(&u3d->lock);
+	return IRQ_HANDLED;
+}
+
+static int mv_u3d_remove(struct platform_device *dev)
+{
+	struct mv_u3d *u3d = platform_get_drvdata(dev);
+
+	BUG_ON(u3d == NULL);
+
+	usb_del_gadget_udc(&u3d->gadget);
+
+	/* free memory allocated in probe */
+	dma_pool_destroy(u3d->trb_pool);
+
+	if (u3d->ep_context)
+		dma_free_coherent(&dev->dev, u3d->ep_context_size,
+			u3d->ep_context, u3d->ep_context_dma);
+
+	kfree(u3d->eps);
+
+	if (u3d->irq)
+		free_irq(u3d->irq, u3d);
+
+	if (u3d->cap_regs)
+		iounmap(u3d->cap_regs);
+	u3d->cap_regs = NULL;
+
+	kfree(u3d->status_req);
+
+	clk_put(u3d->clk);
+
+	kfree(u3d);
+
+	return 0;
+}
+
+static int mv_u3d_probe(struct platform_device *dev)
+{
+	struct mv_u3d *u3d = NULL;
+	struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev);
+	int retval = 0;
+	struct resource *r;
+	size_t size;
+
+	if (!dev_get_platdata(&dev->dev)) {
+		dev_err(&dev->dev, "missing platform_data\n");
+		retval = -ENODEV;
+		goto err_pdata;
+	}
+
+	u3d = kzalloc(sizeof(*u3d), GFP_KERNEL);
+	if (!u3d) {
+		retval = -ENOMEM;
+		goto err_alloc_private;
+	}
+
+	spin_lock_init(&u3d->lock);
+
+	platform_set_drvdata(dev, u3d);
+
+	u3d->dev = &dev->dev;
+	u3d->vbus = pdata->vbus;
+
+	u3d->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(u3d->clk)) {
+		retval = PTR_ERR(u3d->clk);
+		goto err_get_clk;
+	}
+
+	r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs");
+	if (!r) {
+		dev_err(&dev->dev, "no I/O memory resource defined\n");
+		retval = -ENODEV;
+		goto err_get_cap_regs;
+	}
+
+	u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *)
+		ioremap(r->start, resource_size(r));
+	if (!u3d->cap_regs) {
+		dev_err(&dev->dev, "failed to map I/O memory\n");
+		retval = -EBUSY;
+		goto err_map_cap_regs;
+	} else {
+		dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n",
+			(unsigned long) r->start,
+			(unsigned long) u3d->cap_regs);
+	}
+
+	/* we will access controller register, so enable the u3d controller */
+	retval = clk_enable(u3d->clk);
+	if (retval) {
+		dev_err(&dev->dev, "clk_enable error %d\n", retval);
+		goto err_u3d_enable;
+	}
+
+	if (pdata->phy_init) {
+		retval = pdata->phy_init(u3d->phy_regs);
+		if (retval) {
+			dev_err(&dev->dev, "init phy error %d\n", retval);
+			clk_disable(u3d->clk);
+			goto err_phy_init;
+		}
+	}
+
+	u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs
+		+ MV_U3D_USB3_OP_REGS_OFFSET);
+
+	u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs
+		+ ioread32(&u3d->cap_regs->vuoff));
+
+	u3d->max_eps = 16;
+
+	/*
+	 * some platform will use usb to download image, it may not disconnect
+	 * usb gadget before loading kernel. So first stop u3d here.
+	 */
+	mv_u3d_controller_stop(u3d);
+	iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause);
+
+	if (pdata->phy_deinit)
+		pdata->phy_deinit(u3d->phy_regs);
+	clk_disable(u3d->clk);
+
+	size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2;
+	size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1)
+		& ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1);
+	u3d->ep_context = dma_alloc_coherent(&dev->dev, size,
+					&u3d->ep_context_dma, GFP_KERNEL);
+	if (!u3d->ep_context) {
+		dev_err(&dev->dev, "allocate ep context memory failed\n");
+		retval = -ENOMEM;
+		goto err_alloc_ep_context;
+	}
+	u3d->ep_context_size = size;
+
+	/* create TRB dma_pool resource */
+	u3d->trb_pool = dma_pool_create("u3d_trb",
+			&dev->dev,
+			sizeof(struct mv_u3d_trb_hw),
+			MV_U3D_TRB_ALIGNMENT,
+			MV_U3D_DMA_BOUNDARY);
+
+	if (!u3d->trb_pool) {
+		retval = -ENOMEM;
+		goto err_alloc_trb_pool;
+	}
+
+	size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2;
+	u3d->eps = kzalloc(size, GFP_KERNEL);
+	if (!u3d->eps) {
+		retval = -ENOMEM;
+		goto err_alloc_eps;
+	}
+
+	/* initialize ep0 status request structure */
+	u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL);
+	if (!u3d->status_req) {
+		retval = -ENOMEM;
+		goto err_alloc_status_req;
+	}
+	INIT_LIST_HEAD(&u3d->status_req->queue);
+
+	/* allocate a small amount of memory to get valid address */
+	u3d->status_req->req.buf = (char *)u3d->status_req
+					+ sizeof(struct mv_u3d_req);
+	u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf);
+
+	u3d->resume_state = USB_STATE_NOTATTACHED;
+	u3d->usb_state = USB_STATE_ATTACHED;
+	u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
+	u3d->remote_wakeup = 0;
+
+	r = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!r) {
+		dev_err(&dev->dev, "no IRQ resource defined\n");
+		retval = -ENODEV;
+		goto err_get_irq;
+	}
+	u3d->irq = r->start;
+	if (request_irq(u3d->irq, mv_u3d_irq,
+		IRQF_SHARED, driver_name, u3d)) {
+		u3d->irq = 0;
+		dev_err(&dev->dev, "Request irq %d for u3d failed\n",
+			u3d->irq);
+		retval = -ENODEV;
+		goto err_request_irq;
+	}
+
+	/* initialize gadget structure */
+	u3d->gadget.ops = &mv_u3d_ops;	/* usb_gadget_ops */
+	u3d->gadget.ep0 = &u3d->eps[1].ep;	/* gadget ep0 */
+	INIT_LIST_HEAD(&u3d->gadget.ep_list);	/* ep_list */
+	u3d->gadget.speed = USB_SPEED_UNKNOWN;	/* speed */
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	u3d->gadget.name = driver_name;		/* gadget name */
+
+	mv_u3d_eps_init(u3d);
+
+	/* external vbus detection */
+	if (u3d->vbus) {
+		u3d->clock_gating = 1;
+		dev_err(&dev->dev, "external vbus detection\n");
+	}
+
+	if (!u3d->clock_gating)
+		u3d->vbus_active = 1;
+
+	/* enable usb3 controller vbus detection */
+	u3d->vbus_valid_detect = 1;
+
+	retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget);
+	if (retval)
+		goto err_unregister;
+
+	dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n",
+		u3d->clock_gating ? "with" : "without");
+
+	return 0;
+
+err_unregister:
+	free_irq(u3d->irq, u3d);
+err_request_irq:
+err_get_irq:
+	kfree(u3d->status_req);
+err_alloc_status_req:
+	kfree(u3d->eps);
+err_alloc_eps:
+	dma_pool_destroy(u3d->trb_pool);
+err_alloc_trb_pool:
+	dma_free_coherent(&dev->dev, u3d->ep_context_size,
+		u3d->ep_context, u3d->ep_context_dma);
+err_alloc_ep_context:
+err_phy_init:
+err_u3d_enable:
+	iounmap(u3d->cap_regs);
+err_map_cap_regs:
+err_get_cap_regs:
+	clk_put(u3d->clk);
+err_get_clk:
+	kfree(u3d);
+err_alloc_private:
+err_pdata:
+	return retval;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mv_u3d_suspend(struct device *dev)
+{
+	struct mv_u3d *u3d = dev_get_drvdata(dev);
+
+	/*
+	 * only cable is unplugged, usb can suspend.
+	 * So do not care about clock_gating == 1, it is handled by
+	 * vbus session.
+	 */
+	if (!u3d->clock_gating) {
+		mv_u3d_controller_stop(u3d);
+
+		spin_lock_irq(&u3d->lock);
+		/* stop all usb activities */
+		mv_u3d_stop_activity(u3d, u3d->driver);
+		spin_unlock_irq(&u3d->lock);
+
+		mv_u3d_disable(u3d);
+	}
+
+	return 0;
+}
+
+static int mv_u3d_resume(struct device *dev)
+{
+	struct mv_u3d *u3d = dev_get_drvdata(dev);
+	int retval;
+
+	if (!u3d->clock_gating) {
+		retval = mv_u3d_enable(u3d);
+		if (retval)
+			return retval;
+
+		if (u3d->driver && u3d->softconnect) {
+			mv_u3d_controller_reset(u3d);
+			mv_u3d_ep0_reset(u3d);
+			mv_u3d_controller_start(u3d);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume);
+
+static void mv_u3d_shutdown(struct platform_device *dev)
+{
+	struct mv_u3d *u3d = platform_get_drvdata(dev);
+	u32 tmp;
+
+	tmp = ioread32(&u3d->op_regs->usbcmd);
+	tmp &= ~MV_U3D_CMD_RUN_STOP;
+	iowrite32(tmp, &u3d->op_regs->usbcmd);
+}
+
+static struct platform_driver mv_u3d_driver = {
+	.probe		= mv_u3d_probe,
+	.remove		= mv_u3d_remove,
+	.shutdown	= mv_u3d_shutdown,
+	.driver		= {
+		.name	= "mv-u3d",
+		.pm	= &mv_u3d_pm_ops,
+	},
+};
+
+module_platform_driver(mv_u3d_driver);
+MODULE_ALIAS("platform:mv-u3d");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h
new file mode 100644
index 0000000..b3f759c
--- /dev/null
+++ b/drivers/usb/gadget/udc/mv_udc.h
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ */
+
+#ifndef __MV_UDC_H
+#define __MV_UDC_H
+
+#define VUSBHS_MAX_PORTS	8
+
+#define DQH_ALIGNMENT		2048
+#define DTD_ALIGNMENT		64
+#define DMA_BOUNDARY		4096
+
+#define EP_DIR_IN	1
+#define EP_DIR_OUT	0
+
+#define DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+#define EP0_MAX_PKT_SIZE	64
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP		0
+#define DATA_STATE_XMIT		1
+#define DATA_STATE_NEED_ZLP	2
+#define WAIT_FOR_OUT_STATUS	3
+#define DATA_STATE_RECV		4
+
+#define CAPLENGTH_MASK		(0xff)
+#define DCCPARAMS_DEN_MASK	(0x1f)
+
+#define HCSPARAMS_PPC		(0x10)
+
+/* Frame Index Register Bit Masks */
+#define USB_FRINDEX_MASKS	0x3fff
+
+/* Command Register Bit Masks */
+#define USBCMD_RUN_STOP				(0x00000001)
+#define USBCMD_CTRL_RESET			(0x00000002)
+#define USBCMD_SETUP_TRIPWIRE_SET		(0x00002000)
+#define USBCMD_SETUP_TRIPWIRE_CLEAR		(~USBCMD_SETUP_TRIPWIRE_SET)
+
+#define USBCMD_ATDTW_TRIPWIRE_SET		(0x00004000)
+#define USBCMD_ATDTW_TRIPWIRE_CLEAR		(~USBCMD_ATDTW_TRIPWIRE_SET)
+
+/* bit 15,3,2 are for frame list size */
+#define USBCMD_FRAME_SIZE_1024			(0x00000000) /* 000 */
+#define USBCMD_FRAME_SIZE_512			(0x00000004) /* 001 */
+#define USBCMD_FRAME_SIZE_256			(0x00000008) /* 010 */
+#define USBCMD_FRAME_SIZE_128			(0x0000000C) /* 011 */
+#define USBCMD_FRAME_SIZE_64			(0x00008000) /* 100 */
+#define USBCMD_FRAME_SIZE_32			(0x00008004) /* 101 */
+#define USBCMD_FRAME_SIZE_16			(0x00008008) /* 110 */
+#define USBCMD_FRAME_SIZE_8			(0x0000800C) /* 111 */
+
+#define EPCTRL_TX_ALL_MASK			(0xFFFF0000)
+#define EPCTRL_RX_ALL_MASK			(0x0000FFFF)
+
+#define EPCTRL_TX_DATA_TOGGLE_RST		(0x00400000)
+#define EPCTRL_TX_EP_STALL			(0x00010000)
+#define EPCTRL_RX_EP_STALL			(0x00000001)
+#define EPCTRL_RX_DATA_TOGGLE_RST		(0x00000040)
+#define EPCTRL_RX_ENABLE			(0x00000080)
+#define EPCTRL_TX_ENABLE			(0x00800000)
+#define EPCTRL_CONTROL				(0x00000000)
+#define EPCTRL_ISOCHRONOUS			(0x00040000)
+#define EPCTRL_BULK				(0x00080000)
+#define EPCTRL_INT				(0x000C0000)
+#define EPCTRL_TX_TYPE				(0x000C0000)
+#define EPCTRL_RX_TYPE				(0x0000000C)
+#define EPCTRL_DATA_TOGGLE_INHIBIT		(0x00000020)
+#define EPCTRL_TX_EP_TYPE_SHIFT			(18)
+#define EPCTRL_RX_EP_TYPE_SHIFT			(2)
+
+#define EPCOMPLETE_MAX_ENDPOINTS		(16)
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK              0xfffff800
+
+#define PORTSCX_W1C_BITS			0x2a
+#define PORTSCX_PORT_RESET			0x00000100
+#define PORTSCX_PORT_POWER			0x00001000
+#define PORTSCX_FORCE_FULL_SPEED_CONNECT	0x01000000
+#define PORTSCX_PAR_XCVR_SELECT			0xC0000000
+#define PORTSCX_PORT_FORCE_RESUME		0x00000040
+#define PORTSCX_PORT_SUSPEND			0x00000080
+#define PORTSCX_PORT_SPEED_FULL			0x00000000
+#define PORTSCX_PORT_SPEED_LOW			0x04000000
+#define PORTSCX_PORT_SPEED_HIGH			0x08000000
+#define PORTSCX_PORT_SPEED_MASK			0x0C000000
+
+/* USB MODE Register Bit Masks */
+#define USBMODE_CTRL_MODE_IDLE			0x00000000
+#define USBMODE_CTRL_MODE_DEVICE		0x00000002
+#define USBMODE_CTRL_MODE_HOST			0x00000003
+#define USBMODE_CTRL_MODE_RSV			0x00000001
+#define USBMODE_SETUP_LOCK_OFF			0x00000008
+#define USBMODE_STREAM_DISABLE			0x00000010
+
+/* USB STS Register Bit Masks */
+#define USBSTS_INT			0x00000001
+#define USBSTS_ERR			0x00000002
+#define USBSTS_PORT_CHANGE		0x00000004
+#define USBSTS_FRM_LST_ROLL		0x00000008
+#define USBSTS_SYS_ERR			0x00000010
+#define USBSTS_IAA			0x00000020
+#define USBSTS_RESET			0x00000040
+#define USBSTS_SOF			0x00000080
+#define USBSTS_SUSPEND			0x00000100
+#define USBSTS_HC_HALTED		0x00001000
+#define USBSTS_RCL			0x00002000
+#define USBSTS_PERIODIC_SCHEDULE	0x00004000
+#define USBSTS_ASYNC_SCHEDULE		0x00008000
+
+
+/* Interrupt Enable Register Bit Masks */
+#define USBINTR_INT_EN                          (0x00000001)
+#define USBINTR_ERR_INT_EN                      (0x00000002)
+#define USBINTR_PORT_CHANGE_DETECT_EN           (0x00000004)
+
+#define USBINTR_ASYNC_ADV_AAE                   (0x00000020)
+#define USBINTR_ASYNC_ADV_AAE_ENABLE            (0x00000020)
+#define USBINTR_ASYNC_ADV_AAE_DISABLE           (0xFFFFFFDF)
+
+#define USBINTR_RESET_EN                        (0x00000040)
+#define USBINTR_SOF_UFRAME_EN                   (0x00000080)
+#define USBINTR_DEVICE_SUSPEND                  (0x00000100)
+
+#define USB_DEVICE_ADDRESS_MASK			(0xfe000000)
+#define USB_DEVICE_ADDRESS_BIT_SHIFT		(25)
+
+struct mv_cap_regs {
+	u32	caplength_hciversion;
+	u32	hcsparams;	/* HC structural parameters */
+	u32	hccparams;	/* HC Capability Parameters*/
+	u32	reserved[5];
+	u32	dciversion;	/* DC version number and reserved 16 bits */
+	u32	dccparams;	/* DC Capability Parameters */
+};
+
+struct mv_op_regs {
+	u32	usbcmd;		/* Command register */
+	u32	usbsts;		/* Status register */
+	u32	usbintr;	/* Interrupt enable */
+	u32	frindex;	/* Frame index */
+	u32	reserved1[1];
+	u32	deviceaddr;	/* Device Address */
+	u32	eplistaddr;	/* Endpoint List Address */
+	u32	ttctrl;		/* HOST TT status and control */
+	u32	burstsize;	/* Programmable Burst Size */
+	u32	txfilltuning;	/* Host Transmit Pre-Buffer Packet Tuning */
+	u32	reserved[4];
+	u32	epnak;		/* Endpoint NAK */
+	u32	epnaken;	/* Endpoint NAK Enable */
+	u32	configflag;	/* Configured Flag register */
+	u32	portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
+	u32	otgsc;
+	u32	usbmode;	/* USB Host/Device mode */
+	u32	epsetupstat;	/* Endpoint Setup Status */
+	u32	epprime;	/* Endpoint Initialize */
+	u32	epflush;	/* Endpoint De-initialize */
+	u32	epstatus;	/* Endpoint Status */
+	u32	epcomplete;	/* Endpoint Interrupt On Complete */
+	u32	epctrlx[16];	/* Endpoint Control, where x = 0.. 15 */
+	u32	mcr;		/* Mux Control */
+	u32	isr;		/* Interrupt Status */
+	u32	ier;		/* Interrupt Enable */
+};
+
+struct mv_udc {
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	spinlock_t			lock;
+	struct completion		*done;
+	struct platform_device		*dev;
+	int				irq;
+
+	struct mv_cap_regs __iomem	*cap_regs;
+	struct mv_op_regs __iomem	*op_regs;
+	void __iomem                    *phy_regs;
+	unsigned int			max_eps;
+	struct mv_dqh			*ep_dqh;
+	size_t				ep_dqh_size;
+	dma_addr_t			ep_dqh_dma;
+
+	struct dma_pool			*dtd_pool;
+	struct mv_ep			*eps;
+
+	struct mv_dtd			*dtd_head;
+	struct mv_dtd			*dtd_tail;
+	unsigned int			dtd_entries;
+
+	struct mv_req			*status_req;
+	struct usb_ctrlrequest		local_setup_buff;
+
+	unsigned int		resume_state;	/* USB state to resume */
+	unsigned int		usb_state;	/* USB current state */
+	unsigned int		ep0_state;	/* Endpoint zero state */
+	unsigned int		ep0_dir;
+
+	unsigned int		dev_addr;
+	unsigned int		test_mode;
+
+	int			errors;
+	unsigned		softconnect:1,
+				vbus_active:1,
+				remote_wakeup:1,
+				softconnected:1,
+				force_fs:1,
+				clock_gating:1,
+				active:1,
+				stopped:1;      /* stop bit is setted */
+
+	struct work_struct	vbus_work;
+	struct workqueue_struct *qwork;
+
+	struct usb_phy		*transceiver;
+
+	struct mv_usb_platform_data     *pdata;
+
+	/* some SOC has mutiple clock sources for USB*/
+	struct clk      *clk;
+};
+
+/* endpoint data structure */
+struct mv_ep {
+	struct usb_ep		ep;
+	struct mv_udc		*udc;
+	struct list_head	queue;
+	struct mv_dqh		*dqh;
+	u32			direction;
+	char			name[14];
+	unsigned		stopped:1,
+				wedge:1,
+				ep_type:2,
+				ep_num:8;
+};
+
+/* request data structure */
+struct mv_req {
+	struct usb_request	req;
+	struct mv_dtd		*dtd, *head, *tail;
+	struct mv_ep		*ep;
+	struct list_head	queue;
+	unsigned int            test_mode;
+	unsigned		dtd_count;
+	unsigned		mapped:1;
+};
+
+#define EP_QUEUE_HEAD_MULT_POS			30
+#define EP_QUEUE_HEAD_ZLT_SEL			0x20000000
+#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS		16
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info)	(((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_IOS			0x00008000
+#define EP_QUEUE_HEAD_NEXT_TERMINATE		0x00000001
+#define EP_QUEUE_HEAD_IOC			0x00008000
+#define EP_QUEUE_HEAD_MULTO			0x00000C00
+#define EP_QUEUE_HEAD_STATUS_HALT		0x00000040
+#define EP_QUEUE_HEAD_STATUS_ACTIVE		0x00000080
+#define EP_QUEUE_CURRENT_OFFSET_MASK		0x00000FFF
+#define EP_QUEUE_HEAD_NEXT_POINTER_MASK		0xFFFFFFE0
+#define EP_QUEUE_FRINDEX_MASK			0x000007FF
+#define EP_MAX_LENGTH_TRANSFER			0x4000
+
+struct mv_dqh {
+	/* Bits 16..26 Bit 15 is Interrupt On Setup */
+	u32	max_packet_length;
+	u32	curr_dtd_ptr;		/* Current dTD Pointer */
+	u32	next_dtd_ptr;		/* Next dTD Pointer */
+	/* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
+	u32	size_ioc_int_sts;
+	u32	buff_ptr0;		/* Buffer pointer Page 0 (12-31) */
+	u32	buff_ptr1;		/* Buffer pointer Page 1 (12-31) */
+	u32	buff_ptr2;		/* Buffer pointer Page 2 (12-31) */
+	u32	buff_ptr3;		/* Buffer pointer Page 3 (12-31) */
+	u32	buff_ptr4;		/* Buffer pointer Page 4 (12-31) */
+	u32	reserved1;
+	/* 8 bytes of setup data that follows the Setup PID */
+	u8	setup_buffer[8];
+	u32	reserved2[4];
+};
+
+
+#define DTD_NEXT_TERMINATE		(0x00000001)
+#define DTD_IOC				(0x00008000)
+#define DTD_STATUS_ACTIVE		(0x00000080)
+#define DTD_STATUS_HALTED		(0x00000040)
+#define DTD_STATUS_DATA_BUFF_ERR	(0x00000020)
+#define DTD_STATUS_TRANSACTION_ERR	(0x00000008)
+#define DTD_RESERVED_FIELDS		(0x00007F00)
+#define DTD_ERROR_MASK			(0x68)
+#define DTD_ADDR_MASK			(0xFFFFFFE0)
+#define DTD_PACKET_SIZE			0x7FFF0000
+#define DTD_LENGTH_BIT_POS		(16)
+
+struct mv_dtd {
+	u32	dtd_next;
+	u32	size_ioc_sts;
+	u32	buff_ptr0;		/* Buffer pointer Page 0 */
+	u32	buff_ptr1;		/* Buffer pointer Page 1 */
+	u32	buff_ptr2;		/* Buffer pointer Page 2 */
+	u32	buff_ptr3;		/* Buffer pointer Page 3 */
+	u32	buff_ptr4;		/* Buffer pointer Page 4 */
+	u32	scratch_ptr;
+	/* 32 bytes */
+	dma_addr_t td_dma;		/* dma address for this td */
+	struct mv_dtd *next_dtd_virt;
+};
+
+#endif
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
new file mode 100644
index 0000000..95f5223
--- /dev/null
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -0,0 +1,2425 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ *	   Neil Zhang <zhangwm@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/platform_data/mv_usb.h>
+#include <asm/unaligned.h>
+
+#include "mv_udc.h"
+
+#define DRIVER_DESC		"Marvell PXA USB Device Controller driver"
+
+#define ep_dir(ep)	(((ep)->ep_num == 0) ? \
+				((ep)->udc->ep0_dir) : ((ep)->direction))
+
+/* timeout value -- usec */
+#define RESET_TIMEOUT		10000
+#define FLUSH_TIMEOUT		10000
+#define EPSTATUS_TIMEOUT	10000
+#define PRIME_TIMEOUT		10000
+#define READSAFE_TIMEOUT	1000
+
+#define LOOPS_USEC_SHIFT	1
+#define LOOPS_USEC		(1 << LOOPS_USEC_SHIFT)
+#define LOOPS(timeout)		((timeout) >> LOOPS_USEC_SHIFT)
+
+static DECLARE_COMPLETION(release_done);
+
+static const char driver_name[] = "mv_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+static void nuke(struct mv_ep *ep, int status);
+static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor mv_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	EP0_MAX_PKT_SIZE,
+};
+
+static void ep0_reset(struct mv_udc *udc)
+{
+	struct mv_ep *ep;
+	u32 epctrlx;
+	int i = 0;
+
+	/* ep0 in and out */
+	for (i = 0; i < 2; i++) {
+		ep = &udc->eps[i];
+		ep->udc = udc;
+
+		/* ep0 dQH */
+		ep->dqh = &udc->ep_dqh[i];
+
+		/* configure ep0 endpoint capabilities in dQH */
+		ep->dqh->max_packet_length =
+			(EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+			| EP_QUEUE_HEAD_IOS;
+
+		ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE;
+
+		epctrlx = readl(&udc->op_regs->epctrlx[0]);
+		if (i) {	/* TX */
+			epctrlx |= EPCTRL_TX_ENABLE
+				| (USB_ENDPOINT_XFER_CONTROL
+					<< EPCTRL_TX_EP_TYPE_SHIFT);
+
+		} else {	/* RX */
+			epctrlx |= EPCTRL_RX_ENABLE
+				| (USB_ENDPOINT_XFER_CONTROL
+					<< EPCTRL_RX_EP_TYPE_SHIFT);
+		}
+
+		writel(epctrlx, &udc->op_regs->epctrlx[0]);
+	}
+}
+
+/* protocol ep0 stall, will automatically be cleared on new transaction */
+static void ep0_stall(struct mv_udc *udc)
+{
+	u32	epctrlx;
+
+	/* set TX and RX to stall */
+	epctrlx = readl(&udc->op_regs->epctrlx[0]);
+	epctrlx |= EPCTRL_RX_EP_STALL | EPCTRL_TX_EP_STALL;
+	writel(epctrlx, &udc->op_regs->epctrlx[0]);
+
+	/* update ep0 state */
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = EP_DIR_OUT;
+}
+
+static int process_ep_req(struct mv_udc *udc, int index,
+	struct mv_req *curr_req)
+{
+	struct mv_dtd	*curr_dtd;
+	struct mv_dqh	*curr_dqh;
+	int actual, remaining_length;
+	int i, direction;
+	int retval = 0;
+	u32 errors;
+	u32 bit_pos;
+
+	curr_dqh = &udc->ep_dqh[index];
+	direction = index % 2;
+
+	curr_dtd = curr_req->head;
+	actual = curr_req->req.length;
+
+	for (i = 0; i < curr_req->dtd_count; i++) {
+		if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) {
+			dev_dbg(&udc->dev->dev, "%s, dTD not completed\n",
+				udc->eps[index].name);
+			return 1;
+		}
+
+		errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK;
+		if (!errors) {
+			remaining_length =
+				(curr_dtd->size_ioc_sts	& DTD_PACKET_SIZE)
+					>> DTD_LENGTH_BIT_POS;
+			actual -= remaining_length;
+
+			if (remaining_length) {
+				if (direction) {
+					dev_dbg(&udc->dev->dev,
+						"TX dTD remains data\n");
+					retval = -EPROTO;
+					break;
+				} else
+					break;
+			}
+		} else {
+			dev_info(&udc->dev->dev,
+				"complete_tr error: ep=%d %s: error = 0x%x\n",
+				index >> 1, direction ? "SEND" : "RECV",
+				errors);
+			if (errors & DTD_STATUS_HALTED) {
+				/* Clear the errors and Halt condition */
+				curr_dqh->size_ioc_int_sts &= ~errors;
+				retval = -EPIPE;
+			} else if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+				retval = -EPROTO;
+			} else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+				retval = -EILSEQ;
+			}
+		}
+		if (i != curr_req->dtd_count - 1)
+			curr_dtd = (struct mv_dtd *)curr_dtd->next_dtd_virt;
+	}
+	if (retval)
+		return retval;
+
+	if (direction == EP_DIR_OUT)
+		bit_pos = 1 << curr_req->ep->ep_num;
+	else
+		bit_pos = 1 << (16 + curr_req->ep->ep_num);
+
+	while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) {
+		if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) {
+			while (readl(&udc->op_regs->epstatus) & bit_pos)
+				udelay(1);
+			break;
+		}
+		udelay(1);
+	}
+
+	curr_req->req.actual = actual;
+
+	return 0;
+}
+
+/*
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ */
+static void done(struct mv_ep *ep, struct mv_req *req, int status)
+	__releases(&ep->udc->lock)
+	__acquires(&ep->udc->lock)
+{
+	struct mv_udc *udc = NULL;
+	unsigned char stopped = ep->stopped;
+	struct mv_dtd *curr_td, *next_td;
+	int j;
+
+	udc = (struct mv_udc *)ep->udc;
+	/* Removed the req from fsl_ep->queue */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	/* Free dtd for the request */
+	next_td = req->head;
+	for (j = 0; j < req->dtd_count; j++) {
+		curr_td = next_td;
+		if (j != req->dtd_count - 1)
+			next_td = curr_td->next_dtd_virt;
+		dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma);
+	}
+
+	usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
+
+	if (status && (status != -ESHUTDOWN))
+		dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	ep->stopped = 1;
+
+	spin_unlock(&ep->udc->lock);
+
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+
+	spin_lock(&ep->udc->lock);
+	ep->stopped = stopped;
+}
+
+static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
+{
+	struct mv_udc *udc;
+	struct mv_dqh *dqh;
+	u32 bit_pos, direction;
+	u32 usbcmd, epstatus;
+	unsigned int loops;
+	int retval = 0;
+
+	udc = ep->udc;
+	direction = ep_dir(ep);
+	dqh = &(udc->ep_dqh[ep->ep_num * 2 + direction]);
+	bit_pos = 1 << (((direction == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
+
+	/* check if the pipe is empty */
+	if (!(list_empty(&ep->queue))) {
+		struct mv_req *lastreq;
+		lastreq = list_entry(ep->queue.prev, struct mv_req, queue);
+		lastreq->tail->dtd_next =
+			req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+
+		wmb();
+
+		if (readl(&udc->op_regs->epprime) & bit_pos)
+			goto done;
+
+		loops = LOOPS(READSAFE_TIMEOUT);
+		while (1) {
+			/* start with setting the semaphores */
+			usbcmd = readl(&udc->op_regs->usbcmd);
+			usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET;
+			writel(usbcmd, &udc->op_regs->usbcmd);
+
+			/* read the endpoint status */
+			epstatus = readl(&udc->op_regs->epstatus) & bit_pos;
+
+			/*
+			 * Reread the ATDTW semaphore bit to check if it is
+			 * cleared. When hardware see a hazard, it will clear
+			 * the bit or else we remain set to 1 and we can
+			 * proceed with priming of endpoint if not already
+			 * primed.
+			 */
+			if (readl(&udc->op_regs->usbcmd)
+				& USBCMD_ATDTW_TRIPWIRE_SET)
+				break;
+
+			loops--;
+			if (loops == 0) {
+				dev_err(&udc->dev->dev,
+					"Timeout for ATDTW_TRIPWIRE...\n");
+				retval = -ETIME;
+				goto done;
+			}
+			udelay(LOOPS_USEC);
+		}
+
+		/* Clear the semaphore */
+		usbcmd = readl(&udc->op_regs->usbcmd);
+		usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
+		writel(usbcmd, &udc->op_regs->usbcmd);
+
+		if (epstatus)
+			goto done;
+	}
+
+	/* Write dQH next pointer and terminate bit to 0 */
+	dqh->next_dtd_ptr = req->head->td_dma
+				& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+
+	/* clear active and halt bit, in case set from a previous error */
+	dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
+
+	/* Ensure that updates to the QH will occur before priming. */
+	wmb();
+
+	/* Prime the Endpoint */
+	writel(bit_pos, &udc->op_regs->epprime);
+
+done:
+	return retval;
+}
+
+static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
+		dma_addr_t *dma, int *is_last)
+{
+	struct mv_dtd *dtd;
+	struct mv_udc *udc;
+	struct mv_dqh *dqh;
+	u32 temp, mult = 0;
+
+	/* how big will this transfer be? */
+	if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) {
+		dqh = req->ep->dqh;
+		mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS)
+				& 0x3;
+		*length = min(req->req.length - req->req.actual,
+				(unsigned)(mult * req->ep->ep.maxpacket));
+	} else
+		*length = min(req->req.length - req->req.actual,
+				(unsigned)EP_MAX_LENGTH_TRANSFER);
+
+	udc = req->ep->udc;
+
+	/*
+	 * Be careful that no _GFP_HIGHMEM is set,
+	 * or we can not use dma_to_virt
+	 */
+	dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
+	if (dtd == NULL)
+		return dtd;
+
+	dtd->td_dma = *dma;
+	/* initialize buffer page pointers */
+	temp = (u32)(req->req.dma + req->req.actual);
+	dtd->buff_ptr0 = cpu_to_le32(temp);
+	temp &= ~0xFFF;
+	dtd->buff_ptr1 = cpu_to_le32(temp + 0x1000);
+	dtd->buff_ptr2 = cpu_to_le32(temp + 0x2000);
+	dtd->buff_ptr3 = cpu_to_le32(temp + 0x3000);
+	dtd->buff_ptr4 = cpu_to_le32(temp + 0x4000);
+
+	req->req.actual += *length;
+
+	/* zlp is needed if req->req.zero is set */
+	if (req->req.zero) {
+		if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+			*is_last = 1;
+		else
+			*is_last = 0;
+	} else if (req->req.length == req->req.actual)
+		*is_last = 1;
+	else
+		*is_last = 0;
+
+	/* Fill in the transfer size; set active bit */
+	temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+	/* Enable interrupt for the last dtd of a request */
+	if (*is_last && !req->req.no_interrupt)
+		temp |= DTD_IOC;
+
+	temp |= mult << 10;
+
+	dtd->size_ioc_sts = temp;
+
+	mb();
+
+	return dtd;
+}
+
+/* generate dTD linked list for a request */
+static int req_to_dtd(struct mv_req *req)
+{
+	unsigned count;
+	int is_last, is_first = 1;
+	struct mv_dtd *dtd, *last_dtd = NULL;
+	dma_addr_t dma;
+
+	do {
+		dtd = build_dtd(req, &count, &dma, &is_last);
+		if (dtd == NULL)
+			return -ENOMEM;
+
+		if (is_first) {
+			is_first = 0;
+			req->head = dtd;
+		} else {
+			last_dtd->dtd_next = dma;
+			last_dtd->next_dtd_virt = dtd;
+		}
+		last_dtd = dtd;
+		req->dtd_count++;
+	} while (!is_last);
+
+	/* set terminate bit to 1 for the last dTD */
+	dtd->dtd_next = DTD_NEXT_TERMINATE;
+
+	req->tail = dtd;
+
+	return 0;
+}
+
+static int mv_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct mv_udc *udc;
+	struct mv_ep *ep;
+	struct mv_dqh *dqh;
+	u16 max = 0;
+	u32 bit_pos, epctrlx, direction;
+	const unsigned char zlt = 1;
+	unsigned char ios, mult;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct mv_ep, ep);
+	udc = ep->udc;
+
+	if (!_ep || !desc
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	direction = ep_dir(ep);
+	max = usb_endpoint_maxp(desc);
+
+	/*
+	 * disable HW zero length termination select
+	 * driver handles zero length packet through req->req.zero
+	 */
+	bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
+
+	/* Check if the Endpoint is Primed */
+	if ((readl(&udc->op_regs->epprime) & bit_pos)
+		|| (readl(&udc->op_regs->epstatus) & bit_pos)) {
+		dev_info(&udc->dev->dev,
+			"ep=%d %s: Init ERROR: ENDPTPRIME=0x%x,"
+			" ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
+			(unsigned)ep->ep_num, direction ? "SEND" : "RECV",
+			(unsigned)readl(&udc->op_regs->epprime),
+			(unsigned)readl(&udc->op_regs->epstatus),
+			(unsigned)bit_pos);
+		goto en_done;
+	}
+
+	/* Set the max packet length, interrupt on Setup and Mult fields */
+	ios = 0;
+	mult = 0;
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+		ios = 1;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* Calculate transactions needed for high bandwidth iso */
+		mult = usb_endpoint_maxp_mult(desc);
+		/* 3 transactions at most */
+		if (mult > 3)
+			goto en_done;
+		break;
+	default:
+		goto en_done;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Get the endpoint queue head address */
+	dqh = ep->dqh;
+	dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+		| (mult << EP_QUEUE_HEAD_MULT_POS)
+		| (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0)
+		| (ios ? EP_QUEUE_HEAD_IOS : 0);
+	dqh->next_dtd_ptr = 1;
+	dqh->size_ioc_int_sts = 0;
+
+	ep->ep.maxpacket = max;
+	ep->ep.desc = desc;
+	ep->stopped = 0;
+
+	/* Enable the endpoint for Rx or Tx and set the endpoint type */
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	if (direction == EP_DIR_IN) {
+		epctrlx &= ~EPCTRL_TX_ALL_MASK;
+		epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST
+			| ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				<< EPCTRL_TX_EP_TYPE_SHIFT);
+	} else {
+		epctrlx &= ~EPCTRL_RX_ALL_MASK;
+		epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST
+			| ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				<< EPCTRL_RX_EP_TYPE_SHIFT);
+	}
+	writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+	/*
+	 * Implement Guideline (GL# USB-7) The unused endpoint type must
+	 * be programmed to bulk.
+	 */
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	if ((epctrlx & EPCTRL_RX_ENABLE) == 0) {
+		epctrlx |= (USB_ENDPOINT_XFER_BULK
+				<< EPCTRL_RX_EP_TYPE_SHIFT);
+		writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+	}
+
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	if ((epctrlx & EPCTRL_TX_ENABLE) == 0) {
+		epctrlx |= (USB_ENDPOINT_XFER_BULK
+				<< EPCTRL_TX_EP_TYPE_SHIFT);
+		writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+en_done:
+	return -EINVAL;
+}
+
+static int  mv_ep_disable(struct usb_ep *_ep)
+{
+	struct mv_udc *udc;
+	struct mv_ep *ep;
+	struct mv_dqh *dqh;
+	u32 epctrlx, direction;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct mv_ep, ep);
+	if ((_ep == NULL) || !ep->ep.desc)
+		return -EINVAL;
+
+	udc = ep->udc;
+
+	/* Get the endpoint queue head address */
+	dqh = ep->dqh;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	direction = ep_dir(ep);
+
+	/* Reset the max packet length and the interrupt on Setup */
+	dqh->max_packet_length = 0;
+
+	/* Disable the endpoint for Rx or Tx and reset the endpoint type */
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	epctrlx &= ~((direction == EP_DIR_IN)
+			? (EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE)
+			: (EPCTRL_RX_ENABLE | EPCTRL_RX_TYPE));
+	writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+	/* nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static struct usb_request *
+mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct mv_req *req = NULL;
+
+	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 mv_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct mv_req *req = NULL;
+
+	req = container_of(_req, struct mv_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+static void mv_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct mv_udc *udc;
+	u32 bit_pos, direction;
+	struct mv_ep *ep;
+	unsigned int loops;
+
+	if (!_ep)
+		return;
+
+	ep = container_of(_ep, struct mv_ep, ep);
+	if (!ep->ep.desc)
+		return;
+
+	udc = ep->udc;
+	direction = ep_dir(ep);
+
+	if (ep->ep_num == 0)
+		bit_pos = (1 << 16) | 1;
+	else if (direction == EP_DIR_OUT)
+		bit_pos = 1 << ep->ep_num;
+	else
+		bit_pos = 1 << (16 + ep->ep_num);
+
+	loops = LOOPS(EPSTATUS_TIMEOUT);
+	do {
+		unsigned int inter_loops;
+
+		if (loops == 0) {
+			dev_err(&udc->dev->dev,
+				"TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
+				(unsigned)readl(&udc->op_regs->epstatus),
+				(unsigned)bit_pos);
+			return;
+		}
+		/* Write 1 to the Flush register */
+		writel(bit_pos, &udc->op_regs->epflush);
+
+		/* Wait until flushing completed */
+		inter_loops = LOOPS(FLUSH_TIMEOUT);
+		while (readl(&udc->op_regs->epflush)) {
+			/*
+			 * ENDPTFLUSH bit should be cleared to indicate this
+			 * operation is complete
+			 */
+			if (inter_loops == 0) {
+				dev_err(&udc->dev->dev,
+					"TIMEOUT for ENDPTFLUSH=0x%x,"
+					"bit_pos=0x%x\n",
+					(unsigned)readl(&udc->op_regs->epflush),
+					(unsigned)bit_pos);
+				return;
+			}
+			inter_loops--;
+			udelay(LOOPS_USEC);
+		}
+		loops--;
+	} while (readl(&udc->op_regs->epstatus) & bit_pos);
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+	struct mv_req *req = container_of(_req, struct mv_req, req);
+	struct mv_udc *udc = ep->udc;
+	unsigned long flags;
+	int retval;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		dev_err(&udc->dev->dev, "%s, bad params", __func__);
+		return -EINVAL;
+	}
+	if (unlikely(!_ep || !ep->ep.desc)) {
+		dev_err(&udc->dev->dev, "%s, bad ep", __func__);
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep));
+	if (retval)
+		return retval;
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->dtd_count = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* build dtds and push them to device queue */
+	if (!req_to_dtd(req)) {
+		retval = queue_dtd(ep, req);
+		if (retval) {
+			spin_unlock_irqrestore(&udc->lock, flags);
+			dev_err(&udc->dev->dev, "Failed to queue dtd\n");
+			goto err_unmap_dma;
+		}
+	} else {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
+		retval = -ENOMEM;
+		goto err_unmap_dma;
+	}
+
+	/* Update ep0 state */
+	if (ep->ep_num == 0)
+		udc->ep0_state = DATA_STATE_XMIT;
+
+	/* irq handler advances the queue */
+	list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+
+err_unmap_dma:
+	usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep));
+
+	return retval;
+}
+
+static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
+{
+	struct mv_dqh *dqh = ep->dqh;
+	u32 bit_pos;
+
+	/* Write dQH next pointer and terminate bit to 0 */
+	dqh->next_dtd_ptr = req->head->td_dma
+		& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+
+	/* clear active and halt bit, in case set from a previous error */
+	dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
+
+	/* Ensure that updates to the QH will occure before priming. */
+	wmb();
+
+	bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
+
+	/* Prime the Endpoint */
+	writel(bit_pos, &ep->udc->op_regs->epprime);
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+	struct mv_req *req;
+	struct mv_udc *udc = ep->udc;
+	unsigned long flags;
+	int stopped, ret = 0;
+	u32 epctrlx;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	stopped = ep->stopped;
+
+	/* Stop the ep before we deal with the queue */
+	ep->stopped = 1;
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	if (ep_dir(ep) == EP_DIR_IN)
+		epctrlx &= ~EPCTRL_TX_ENABLE;
+	else
+		epctrlx &= ~EPCTRL_RX_ENABLE;
+	writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* The request is in progress, or completed but not dequeued */
+	if (ep->queue.next == &req->queue) {
+		_req->status = -ECONNRESET;
+		mv_ep_fifo_flush(_ep);	/* flush current transfer */
+
+		/* The request isn't the last request in this ep queue */
+		if (req->queue.next != &ep->queue) {
+			struct mv_req *next_req;
+
+			next_req = list_entry(req->queue.next,
+				struct mv_req, queue);
+
+			/* Point the QH to the first TD of next request */
+			mv_prime_ep(ep, next_req);
+		} else {
+			struct mv_dqh *qh;
+
+			qh = ep->dqh;
+			qh->next_dtd_ptr = 1;
+			qh->size_ioc_int_sts = 0;
+		}
+
+		/* The request hasn't been processed, patch up the TD chain */
+	} else {
+		struct mv_req *prev_req;
+
+		prev_req = list_entry(req->queue.prev, struct mv_req, queue);
+		writel(readl(&req->tail->dtd_next),
+				&prev_req->tail->dtd_next);
+
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	/* Enable EP */
+out:
+	epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+	if (ep_dir(ep) == EP_DIR_IN)
+		epctrlx |= EPCTRL_TX_ENABLE;
+	else
+		epctrlx |= EPCTRL_RX_ENABLE;
+	writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+	ep->stopped = stopped;
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return ret;
+}
+
+static void ep_set_stall(struct mv_udc *udc, u8 ep_num, u8 direction, int stall)
+{
+	u32 epctrlx;
+
+	epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
+
+	if (stall) {
+		if (direction == EP_DIR_IN)
+			epctrlx |= EPCTRL_TX_EP_STALL;
+		else
+			epctrlx |= EPCTRL_RX_EP_STALL;
+	} else {
+		if (direction == EP_DIR_IN) {
+			epctrlx &= ~EPCTRL_TX_EP_STALL;
+			epctrlx |= EPCTRL_TX_DATA_TOGGLE_RST;
+		} else {
+			epctrlx &= ~EPCTRL_RX_EP_STALL;
+			epctrlx |= EPCTRL_RX_DATA_TOGGLE_RST;
+		}
+	}
+	writel(epctrlx, &udc->op_regs->epctrlx[ep_num]);
+}
+
+static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction)
+{
+	u32 epctrlx;
+
+	epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
+
+	if (direction == EP_DIR_OUT)
+		return (epctrlx & EPCTRL_RX_EP_STALL) ? 1 : 0;
+	else
+		return (epctrlx & EPCTRL_TX_EP_STALL) ? 1 : 0;
+}
+
+static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+	struct mv_ep *ep;
+	unsigned long flags = 0;
+	int status = 0;
+	struct mv_udc *udc;
+
+	ep = container_of(_ep, struct mv_ep, ep);
+	udc = ep->udc;
+	if (!_ep || !ep->ep.desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		status = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
+	 * Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue
+	 */
+	if (halt && (ep_dir(ep) == EP_DIR_IN) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	ep_set_stall(udc, ep->ep_num, ep_dir(ep), halt);
+	if (halt && wedge)
+		ep->wedge = 1;
+	else if (!halt)
+		ep->wedge = 0;
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep->ep_num == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = EP_DIR_OUT;
+	}
+out:
+	return status;
+}
+
+static int mv_ep_set_halt(struct usb_ep *_ep, int halt)
+{
+	return mv_ep_set_halt_wedge(_ep, halt, 0);
+}
+
+static int mv_ep_set_wedge(struct usb_ep *_ep)
+{
+	return mv_ep_set_halt_wedge(_ep, 1, 1);
+}
+
+static const struct usb_ep_ops mv_ep_ops = {
+	.enable		= mv_ep_enable,
+	.disable	= mv_ep_disable,
+
+	.alloc_request	= mv_alloc_request,
+	.free_request	= mv_free_request,
+
+	.queue		= mv_ep_queue,
+	.dequeue	= mv_ep_dequeue,
+
+	.set_wedge	= mv_ep_set_wedge,
+	.set_halt	= mv_ep_set_halt,
+	.fifo_flush	= mv_ep_fifo_flush,	/* flush fifo */
+};
+
+static int udc_clock_enable(struct mv_udc *udc)
+{
+	return clk_prepare_enable(udc->clk);
+}
+
+static void udc_clock_disable(struct mv_udc *udc)
+{
+	clk_disable_unprepare(udc->clk);
+}
+
+static void udc_stop(struct mv_udc *udc)
+{
+	u32 tmp;
+
+	/* Disable interrupts */
+	tmp = readl(&udc->op_regs->usbintr);
+	tmp &= ~(USBINTR_INT_EN | USBINTR_ERR_INT_EN |
+		USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN);
+	writel(tmp, &udc->op_regs->usbintr);
+
+	udc->stopped = 1;
+
+	/* Reset the Run the bit in the command register to stop VUSB */
+	tmp = readl(&udc->op_regs->usbcmd);
+	tmp &= ~USBCMD_RUN_STOP;
+	writel(tmp, &udc->op_regs->usbcmd);
+}
+
+static void udc_start(struct mv_udc *udc)
+{
+	u32 usbintr;
+
+	usbintr = USBINTR_INT_EN | USBINTR_ERR_INT_EN
+		| USBINTR_PORT_CHANGE_DETECT_EN
+		| USBINTR_RESET_EN | USBINTR_DEVICE_SUSPEND;
+	/* Enable interrupts */
+	writel(usbintr, &udc->op_regs->usbintr);
+
+	udc->stopped = 0;
+
+	/* Set the Run bit in the command register */
+	writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd);
+}
+
+static int udc_reset(struct mv_udc *udc)
+{
+	unsigned int loops;
+	u32 tmp, portsc;
+
+	/* Stop the controller */
+	tmp = readl(&udc->op_regs->usbcmd);
+	tmp &= ~USBCMD_RUN_STOP;
+	writel(tmp, &udc->op_regs->usbcmd);
+
+	/* Reset the controller to get default values */
+	writel(USBCMD_CTRL_RESET, &udc->op_regs->usbcmd);
+
+	/* wait for reset to complete */
+	loops = LOOPS(RESET_TIMEOUT);
+	while (readl(&udc->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
+		if (loops == 0) {
+			dev_err(&udc->dev->dev,
+				"Wait for RESET completed TIMEOUT\n");
+			return -ETIMEDOUT;
+		}
+		loops--;
+		udelay(LOOPS_USEC);
+	}
+
+	/* set controller to device mode */
+	tmp = readl(&udc->op_regs->usbmode);
+	tmp |= USBMODE_CTRL_MODE_DEVICE;
+
+	/* turn setup lockout off, require setup tripwire in usbcmd */
+	tmp |= USBMODE_SETUP_LOCK_OFF;
+
+	writel(tmp, &udc->op_regs->usbmode);
+
+	writel(0x0, &udc->op_regs->epsetupstat);
+
+	/* Configure the Endpoint List Address */
+	writel(udc->ep_dqh_dma & USB_EP_LIST_ADDRESS_MASK,
+		&udc->op_regs->eplistaddr);
+
+	portsc = readl(&udc->op_regs->portsc[0]);
+	if (readl(&udc->cap_regs->hcsparams) & HCSPARAMS_PPC)
+		portsc &= (~PORTSCX_W1C_BITS | ~PORTSCX_PORT_POWER);
+
+	if (udc->force_fs)
+		portsc |= PORTSCX_FORCE_FULL_SPEED_CONNECT;
+	else
+		portsc &= (~PORTSCX_FORCE_FULL_SPEED_CONNECT);
+
+	writel(portsc, &udc->op_regs->portsc[0]);
+
+	tmp = readl(&udc->op_regs->epctrlx[0]);
+	tmp &= ~(EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL);
+	writel(tmp, &udc->op_regs->epctrlx[0]);
+
+	return 0;
+}
+
+static int mv_udc_enable_internal(struct mv_udc *udc)
+{
+	int retval;
+
+	if (udc->active)
+		return 0;
+
+	dev_dbg(&udc->dev->dev, "enable udc\n");
+	retval = udc_clock_enable(udc);
+	if (retval)
+		return retval;
+
+	if (udc->pdata->phy_init) {
+		retval = udc->pdata->phy_init(udc->phy_regs);
+		if (retval) {
+			dev_err(&udc->dev->dev,
+				"init phy error %d\n", retval);
+			udc_clock_disable(udc);
+			return retval;
+		}
+	}
+	udc->active = 1;
+
+	return 0;
+}
+
+static int mv_udc_enable(struct mv_udc *udc)
+{
+	if (udc->clock_gating)
+		return mv_udc_enable_internal(udc);
+
+	return 0;
+}
+
+static void mv_udc_disable_internal(struct mv_udc *udc)
+{
+	if (udc->active) {
+		dev_dbg(&udc->dev->dev, "disable udc\n");
+		if (udc->pdata->phy_deinit)
+			udc->pdata->phy_deinit(udc->phy_regs);
+		udc_clock_disable(udc);
+		udc->active = 0;
+	}
+}
+
+static void mv_udc_disable(struct mv_udc *udc)
+{
+	if (udc->clock_gating)
+		mv_udc_disable_internal(udc);
+}
+
+static int mv_udc_get_frame(struct usb_gadget *gadget)
+{
+	struct mv_udc *udc;
+	u16	retval;
+
+	if (!gadget)
+		return -ENODEV;
+
+	udc = container_of(gadget, struct mv_udc, gadget);
+
+	retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS;
+
+	return retval;
+}
+
+/* Tries to wake up the host connected to this gadget */
+static int mv_udc_wakeup(struct usb_gadget *gadget)
+{
+	struct mv_udc *udc = container_of(gadget, struct mv_udc, gadget);
+	u32 portsc;
+
+	/* Remote wakeup feature not enabled by host */
+	if (!udc->remote_wakeup)
+		return -ENOTSUPP;
+
+	portsc = readl(&udc->op_regs->portsc);
+	/* not suspended? */
+	if (!(portsc & PORTSCX_PORT_SUSPEND))
+		return 0;
+	/* trigger force resume */
+	portsc |= PORTSCX_PORT_FORCE_RESUME;
+	writel(portsc, &udc->op_regs->portsc[0]);
+	return 0;
+}
+
+static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct mv_udc *udc;
+	unsigned long flags;
+	int retval = 0;
+
+	udc = container_of(gadget, struct mv_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+
+	udc->vbus_active = (is_active != 0);
+
+	dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
+		__func__, udc->softconnect, udc->vbus_active);
+
+	if (udc->driver && udc->softconnect && udc->vbus_active) {
+		retval = mv_udc_enable(udc);
+		if (retval == 0) {
+			/* Clock is disabled, need re-init registers */
+			udc_reset(udc);
+			ep0_reset(udc);
+			udc_start(udc);
+		}
+	} else if (udc->driver && udc->softconnect) {
+		if (!udc->active)
+			goto out;
+
+		/* stop all the transfer in queue*/
+		stop_activity(udc, udc->driver);
+		udc_stop(udc);
+		mv_udc_disable(udc);
+	}
+
+out:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return retval;
+}
+
+static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct mv_udc *udc;
+	unsigned long flags;
+	int retval = 0;
+
+	udc = container_of(gadget, struct mv_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+
+	udc->softconnect = (is_on != 0);
+
+	dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
+			__func__, udc->softconnect, udc->vbus_active);
+
+	if (udc->driver && udc->softconnect && udc->vbus_active) {
+		retval = mv_udc_enable(udc);
+		if (retval == 0) {
+			/* Clock is disabled, need re-init registers */
+			udc_reset(udc);
+			ep0_reset(udc);
+			udc_start(udc);
+		}
+	} else if (udc->driver && udc->vbus_active) {
+		/* stop all the transfer in queue*/
+		stop_activity(udc, udc->driver);
+		udc_stop(udc);
+		mv_udc_disable(udc);
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return retval;
+}
+
+static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
+static int mv_udc_stop(struct usb_gadget *);
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops mv_ops = {
+
+	/* returns the current frame number */
+	.get_frame	= mv_udc_get_frame,
+
+	/* tries to wake up the host connected to this gadget */
+	.wakeup		= mv_udc_wakeup,
+
+	/* notify controller that VBUS is powered or not */
+	.vbus_session	= mv_udc_vbus_session,
+
+	/* D+ pullup, software-controlled connect/disconnect to USB host */
+	.pullup		= mv_udc_pullup,
+	.udc_start	= mv_udc_start,
+	.udc_stop	= mv_udc_stop,
+};
+
+static int eps_init(struct mv_udc *udc)
+{
+	struct mv_ep	*ep;
+	char name[14];
+	int i;
+
+	/* initialize ep0 */
+	ep = &udc->eps[0];
+	ep->udc = udc;
+	strncpy(ep->name, "ep0", sizeof(ep->name));
+	ep->ep.name = ep->name;
+	ep->ep.ops = &mv_ep_ops;
+	ep->wedge = 0;
+	ep->stopped = 0;
+	usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE);
+	ep->ep.caps.type_control = true;
+	ep->ep.caps.dir_in = true;
+	ep->ep.caps.dir_out = true;
+	ep->ep_num = 0;
+	ep->ep.desc = &mv_ep0_desc;
+	INIT_LIST_HEAD(&ep->queue);
+
+	ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+	/* initialize other endpoints */
+	for (i = 2; i < udc->max_eps * 2; i++) {
+		ep = &udc->eps[i];
+		if (i % 2) {
+			snprintf(name, sizeof(name), "ep%din", i / 2);
+			ep->direction = EP_DIR_IN;
+			ep->ep.caps.dir_in = true;
+		} else {
+			snprintf(name, sizeof(name), "ep%dout", i / 2);
+			ep->direction = EP_DIR_OUT;
+			ep->ep.caps.dir_out = true;
+		}
+		ep->udc = udc;
+		strncpy(ep->name, name, sizeof(ep->name));
+		ep->ep.name = ep->name;
+
+		ep->ep.caps.type_iso = true;
+		ep->ep.caps.type_bulk = true;
+		ep->ep.caps.type_int = true;
+
+		ep->ep.ops = &mv_ep_ops;
+		ep->stopped = 0;
+		usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
+		ep->ep_num = i / 2;
+
+		INIT_LIST_HEAD(&ep->queue);
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+		ep->dqh = &udc->ep_dqh[i];
+	}
+
+	return 0;
+}
+
+/* delete all endpoint requests, called with spinlock held */
+static void nuke(struct mv_ep *ep, int status)
+{
+	/* called with spinlock held */
+	ep->stopped = 1;
+
+	/* endpoint fifo flush */
+	mv_ep_fifo_flush(&ep->ep);
+
+	while (!list_empty(&ep->queue)) {
+		struct mv_req *req = NULL;
+		req = list_entry(ep->queue.next, struct mv_req, queue);
+		done(ep, req, status);
+	}
+}
+
+static void gadget_reset(struct mv_udc *udc, struct usb_gadget_driver *driver)
+{
+	struct mv_ep	*ep;
+
+	nuke(&udc->eps[0], -ESHUTDOWN);
+
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		nuke(ep, -ESHUTDOWN);
+	}
+
+	/* report reset; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&udc->lock);
+		usb_gadget_udc_reset(&udc->gadget, driver);
+		spin_lock(&udc->lock);
+	}
+}
+/* stop all USB activities */
+static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
+{
+	struct mv_ep	*ep;
+
+	nuke(&udc->eps[0], -ESHUTDOWN);
+
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		nuke(ep, -ESHUTDOWN);
+	}
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&udc->lock);
+		driver->disconnect(&udc->gadget);
+		spin_lock(&udc->lock);
+	}
+}
+
+static int mv_udc_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct mv_udc *udc;
+	int retval = 0;
+	unsigned long flags;
+
+	udc = container_of(gadget, struct mv_udc, gadget);
+
+	if (udc->driver)
+		return -EBUSY;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* hook up the driver ... */
+	driver->driver.bus = NULL;
+	udc->driver = driver;
+
+	udc->usb_state = USB_STATE_ATTACHED;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = EP_DIR_OUT;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	if (udc->transceiver) {
+		retval = otg_set_peripheral(udc->transceiver->otg,
+					&udc->gadget);
+		if (retval) {
+			dev_err(&udc->dev->dev,
+				"unable to register peripheral to otg\n");
+			udc->driver = NULL;
+			return retval;
+		}
+	}
+
+	/* When boot with cable attached, there will be no vbus irq occurred */
+	if (udc->qwork)
+		queue_work(udc->qwork, &udc->vbus_work);
+
+	return 0;
+}
+
+static int mv_udc_stop(struct usb_gadget *gadget)
+{
+	struct mv_udc *udc;
+	unsigned long flags;
+
+	udc = container_of(gadget, struct mv_udc, gadget);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	mv_udc_enable(udc);
+	udc_stop(udc);
+
+	/* stop all usb activities */
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	stop_activity(udc, NULL);
+	mv_udc_disable(udc);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	/* unbind gadget driver */
+	udc->driver = NULL;
+
+	return 0;
+}
+
+static void mv_set_ptc(struct mv_udc *udc, u32 mode)
+{
+	u32 portsc;
+
+	portsc = readl(&udc->op_regs->portsc[0]);
+	portsc |= mode << 16;
+	writel(portsc, &udc->op_regs->portsc[0]);
+}
+
+static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct mv_ep *mvep = container_of(ep, struct mv_ep, ep);
+	struct mv_req *req = container_of(_req, struct mv_req, req);
+	struct mv_udc *udc;
+	unsigned long flags;
+
+	udc = mvep->udc;
+
+	dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode);
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (req->test_mode) {
+		mv_set_ptc(udc, req->test_mode);
+		req->test_mode = 0;
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static int
+udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
+{
+	int retval = 0;
+	struct mv_req *req;
+	struct mv_ep *ep;
+
+	ep = &udc->eps[0];
+	udc->ep0_dir = direction;
+	udc->ep0_state = WAIT_FOR_OUT_STATUS;
+
+	req = udc->status_req;
+
+	/* fill in the reqest structure */
+	if (empty == false) {
+		*((u16 *) req->req.buf) = cpu_to_le16(status);
+		req->req.length = 2;
+	} else
+		req->req.length = 0;
+
+	req->ep = ep;
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	if (udc->test_mode) {
+		req->req.complete = prime_status_complete;
+		req->test_mode = udc->test_mode;
+		udc->test_mode = 0;
+	} else
+		req->req.complete = NULL;
+	req->dtd_count = 0;
+
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+				req->req.buf, req->req.length,
+				ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+		req->mapped = 1;
+	}
+
+	/* prime the data phase */
+	if (!req_to_dtd(req)) {
+		retval = queue_dtd(ep, req);
+		if (retval) {
+			dev_err(&udc->dev->dev,
+				"Failed to queue dtd when prime status\n");
+			goto out;
+		}
+	} else{	/* no mem */
+		retval = -ENOMEM;
+		dev_err(&udc->dev->dev,
+			"Failed to dma_pool_alloc when prime status\n");
+		goto out;
+	}
+
+	list_add_tail(&req->queue, &ep->queue);
+
+	return 0;
+out:
+	usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
+
+	return retval;
+}
+
+static void mv_udc_testmode(struct mv_udc *udc, u16 index)
+{
+	if (index <= TEST_FORCE_EN) {
+		udc->test_mode = index;
+		if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+			ep0_stall(udc);
+	} else
+		dev_err(&udc->dev->dev,
+			"This test mode(%d) is not supported\n", index);
+}
+
+static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+	udc->dev_addr = (u8)setup->wValue;
+
+	/* update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+
+	if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+		ep0_stall(udc);
+}
+
+static void ch9getstatus(struct mv_udc *udc, u8 ep_num,
+	struct usb_ctrlrequest *setup)
+{
+	u16 status = 0;
+	int retval;
+
+	if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+		!= (USB_DIR_IN | USB_TYPE_STANDARD))
+		return;
+
+	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		status = 1 << USB_DEVICE_SELF_POWERED;
+		status |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+	} else if ((setup->bRequestType & USB_RECIP_MASK)
+			== USB_RECIP_INTERFACE) {
+		/* get interface status */
+		status = 0;
+	} else if ((setup->bRequestType & USB_RECIP_MASK)
+			== USB_RECIP_ENDPOINT) {
+		u8 ep_num, direction;
+
+		ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+		direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+				? EP_DIR_IN : EP_DIR_OUT;
+		status = ep_is_stall(udc, ep_num, direction)
+				<< USB_ENDPOINT_HALT;
+	}
+
+	retval = udc_prime_status(udc, EP_DIR_IN, status, false);
+	if (retval)
+		ep0_stall(udc);
+	else
+		udc->ep0_state = DATA_STATE_XMIT;
+}
+
+static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+	u8 ep_num;
+	u8 direction;
+	struct mv_ep *ep;
+
+	if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+		== ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
+		switch (setup->wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			udc->remote_wakeup = 0;
+			break;
+		default:
+			goto out;
+		}
+	} else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+		== ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
+		switch (setup->wValue) {
+		case USB_ENDPOINT_HALT:
+			ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+			direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+				? EP_DIR_IN : EP_DIR_OUT;
+			if (setup->wValue != 0 || setup->wLength != 0
+				|| ep_num > udc->max_eps)
+				goto out;
+			ep = &udc->eps[ep_num * 2 + direction];
+			if (ep->wedge == 1)
+				break;
+			spin_unlock(&udc->lock);
+			ep_set_stall(udc, ep_num, direction, 0);
+			spin_lock(&udc->lock);
+			break;
+		default:
+			goto out;
+		}
+	} else
+		goto out;
+
+	if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+		ep0_stall(udc);
+out:
+	return;
+}
+
+static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+	u8 ep_num;
+	u8 direction;
+
+	if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+		== ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
+		switch (setup->wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			udc->remote_wakeup = 1;
+			break;
+		case USB_DEVICE_TEST_MODE:
+			if (setup->wIndex & 0xFF
+				||  udc->gadget.speed != USB_SPEED_HIGH)
+				ep0_stall(udc);
+
+			if (udc->usb_state != USB_STATE_CONFIGURED
+				&& udc->usb_state != USB_STATE_ADDRESS
+				&& udc->usb_state != USB_STATE_DEFAULT)
+				ep0_stall(udc);
+
+			mv_udc_testmode(udc, (setup->wIndex >> 8));
+			goto out;
+		default:
+			goto out;
+		}
+	} else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+		== ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
+		switch (setup->wValue) {
+		case USB_ENDPOINT_HALT:
+			ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+			direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+				? EP_DIR_IN : EP_DIR_OUT;
+			if (setup->wValue != 0 || setup->wLength != 0
+				|| ep_num > udc->max_eps)
+				goto out;
+			spin_unlock(&udc->lock);
+			ep_set_stall(udc, ep_num, direction, 1);
+			spin_lock(&udc->lock);
+			break;
+		default:
+			goto out;
+		}
+	} else
+		goto out;
+
+	if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+		ep0_stall(udc);
+out:
+	return;
+}
+
+static void handle_setup_packet(struct mv_udc *udc, u8 ep_num,
+	struct usb_ctrlrequest *setup)
+	__releases(&ep->udc->lock)
+	__acquires(&ep->udc->lock)
+{
+	bool delegate = false;
+
+	nuke(&udc->eps[ep_num * 2 + EP_DIR_OUT], -ESHUTDOWN);
+
+	dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+			setup->bRequestType, setup->bRequest,
+			setup->wValue, setup->wIndex, setup->wLength);
+	/* We process some standard setup requests here */
+	if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (setup->bRequest) {
+		case USB_REQ_GET_STATUS:
+			ch9getstatus(udc, ep_num, setup);
+			break;
+
+		case USB_REQ_SET_ADDRESS:
+			ch9setaddress(udc, setup);
+			break;
+
+		case USB_REQ_CLEAR_FEATURE:
+			ch9clearfeature(udc, setup);
+			break;
+
+		case USB_REQ_SET_FEATURE:
+			ch9setfeature(udc, setup);
+			break;
+
+		default:
+			delegate = true;
+		}
+	} else
+		delegate = true;
+
+	/* delegate USB standard requests to the gadget driver */
+	if (delegate == true) {
+		/* USB requests handled by gadget */
+		if (setup->wLength) {
+			/* DATA phase from gadget, STATUS phase from udc */
+			udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+					?  EP_DIR_IN : EP_DIR_OUT;
+			spin_unlock(&udc->lock);
+			if (udc->driver->setup(&udc->gadget,
+				&udc->local_setup_buff) < 0)
+				ep0_stall(udc);
+			spin_lock(&udc->lock);
+			udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+					?  DATA_STATE_XMIT : DATA_STATE_RECV;
+		} else {
+			/* no DATA phase, IN STATUS phase from gadget */
+			udc->ep0_dir = EP_DIR_IN;
+			spin_unlock(&udc->lock);
+			if (udc->driver->setup(&udc->gadget,
+				&udc->local_setup_buff) < 0)
+				ep0_stall(udc);
+			spin_lock(&udc->lock);
+			udc->ep0_state = WAIT_FOR_OUT_STATUS;
+		}
+	}
+}
+
+/* complete DATA or STATUS phase of ep0 prime status phase if needed */
+static void ep0_req_complete(struct mv_udc *udc,
+	struct mv_ep *ep0, struct mv_req *req)
+{
+	u32 new_addr;
+
+	if (udc->usb_state == USB_STATE_ADDRESS) {
+		/* set the new address */
+		new_addr = (u32)udc->dev_addr;
+		writel(new_addr << USB_DEVICE_ADDRESS_BIT_SHIFT,
+			&udc->op_regs->deviceaddr);
+	}
+
+	done(ep0, req, 0);
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		/* receive status phase */
+		if (udc_prime_status(udc, EP_DIR_OUT, 0, true))
+			ep0_stall(udc);
+		break;
+	case DATA_STATE_RECV:
+		/* send status phase */
+		if (udc_prime_status(udc, EP_DIR_IN, 0 , true))
+			ep0_stall(udc);
+		break;
+	case WAIT_FOR_OUT_STATUS:
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+	case WAIT_FOR_SETUP:
+		dev_err(&udc->dev->dev, "unexpect ep0 packets\n");
+		break;
+	default:
+		ep0_stall(udc);
+		break;
+	}
+}
+
+static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+	u32 temp;
+	struct mv_dqh *dqh;
+
+	dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT];
+
+	/* Clear bit in ENDPTSETUPSTAT */
+	writel((1 << ep_num), &udc->op_regs->epsetupstat);
+
+	/* while a hazard exists when setup package arrives */
+	do {
+		/* Set Setup Tripwire */
+		temp = readl(&udc->op_regs->usbcmd);
+		writel(temp | USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
+
+		/* Copy the setup packet to local buffer */
+		memcpy(buffer_ptr, (u8 *) dqh->setup_buffer, 8);
+	} while (!(readl(&udc->op_regs->usbcmd) & USBCMD_SETUP_TRIPWIRE_SET));
+
+	/* Clear Setup Tripwire */
+	temp = readl(&udc->op_regs->usbcmd);
+	writel(temp & ~USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
+}
+
+static void irq_process_tr_complete(struct mv_udc *udc)
+{
+	u32 tmp, bit_pos;
+	int i, ep_num = 0, direction = 0;
+	struct mv_ep	*curr_ep;
+	struct mv_req *curr_req, *temp_req;
+	int status;
+
+	/*
+	 * We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE
+	 * because the setup packets are to be read ASAP
+	 */
+
+	/* Process all Setup packet received interrupts */
+	tmp = readl(&udc->op_regs->epsetupstat);
+
+	if (tmp) {
+		for (i = 0; i < udc->max_eps; i++) {
+			if (tmp & (1 << i)) {
+				get_setup_data(udc, i,
+					(u8 *)(&udc->local_setup_buff));
+				handle_setup_packet(udc, i,
+					&udc->local_setup_buff);
+			}
+		}
+	}
+
+	/* Don't clear the endpoint setup status register here.
+	 * It is cleared as a setup packet is read out of the buffer
+	 */
+
+	/* Process non-setup transaction complete interrupts */
+	tmp = readl(&udc->op_regs->epcomplete);
+
+	if (!tmp)
+		return;
+
+	writel(tmp, &udc->op_regs->epcomplete);
+
+	for (i = 0; i < udc->max_eps * 2; i++) {
+		ep_num = i >> 1;
+		direction = i % 2;
+
+		bit_pos = 1 << (ep_num + 16 * direction);
+
+		if (!(bit_pos & tmp))
+			continue;
+
+		if (i == 1)
+			curr_ep = &udc->eps[0];
+		else
+			curr_ep = &udc->eps[i];
+		/* process the req queue until an uncomplete request */
+		list_for_each_entry_safe(curr_req, temp_req,
+			&curr_ep->queue, queue) {
+			status = process_ep_req(udc, i, curr_req);
+			if (status)
+				break;
+
+			/* write back status to req */
+			curr_req->req.status = status;
+
+			/* ep0 request completion */
+			if (ep_num == 0) {
+				ep0_req_complete(udc, curr_ep, curr_req);
+				break;
+			} else {
+				done(curr_ep, curr_req, status);
+			}
+		}
+	}
+}
+
+static void irq_process_reset(struct mv_udc *udc)
+{
+	u32 tmp;
+	unsigned int loops;
+
+	udc->ep0_dir = EP_DIR_OUT;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->remote_wakeup = 0;		/* default to 0 on reset */
+
+	/* The address bits are past bit 25-31. Set the address */
+	tmp = readl(&udc->op_regs->deviceaddr);
+	tmp &= ~(USB_DEVICE_ADDRESS_MASK);
+	writel(tmp, &udc->op_regs->deviceaddr);
+
+	/* Clear all the setup token semaphores */
+	tmp = readl(&udc->op_regs->epsetupstat);
+	writel(tmp, &udc->op_regs->epsetupstat);
+
+	/* Clear all the endpoint complete status bits */
+	tmp = readl(&udc->op_regs->epcomplete);
+	writel(tmp, &udc->op_regs->epcomplete);
+
+	/* wait until all endptprime bits cleared */
+	loops = LOOPS(PRIME_TIMEOUT);
+	while (readl(&udc->op_regs->epprime) & 0xFFFFFFFF) {
+		if (loops == 0) {
+			dev_err(&udc->dev->dev,
+				"Timeout for ENDPTPRIME = 0x%x\n",
+				readl(&udc->op_regs->epprime));
+			break;
+		}
+		loops--;
+		udelay(LOOPS_USEC);
+	}
+
+	/* Write 1s to the Flush register */
+	writel((u32)~0, &udc->op_regs->epflush);
+
+	if (readl(&udc->op_regs->portsc[0]) & PORTSCX_PORT_RESET) {
+		dev_info(&udc->dev->dev, "usb bus reset\n");
+		udc->usb_state = USB_STATE_DEFAULT;
+		/* reset all the queues, stop all USB activities */
+		gadget_reset(udc, udc->driver);
+	} else {
+		dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n",
+			readl(&udc->op_regs->portsc));
+
+		/*
+		 * re-initialize
+		 * controller reset
+		 */
+		udc_reset(udc);
+
+		/* reset all the queues, stop all USB activities */
+		stop_activity(udc, udc->driver);
+
+		/* reset ep0 dQH and endptctrl */
+		ep0_reset(udc);
+
+		/* enable interrupt and set controller to run state */
+		udc_start(udc);
+
+		udc->usb_state = USB_STATE_ATTACHED;
+	}
+}
+
+static void handle_bus_resume(struct mv_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver */
+	if (udc->driver) {
+		if (udc->driver->resume) {
+			spin_unlock(&udc->lock);
+			udc->driver->resume(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+}
+
+static void irq_process_suspend(struct mv_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	if (udc->driver->suspend) {
+		spin_unlock(&udc->lock);
+		udc->driver->suspend(&udc->gadget);
+		spin_lock(&udc->lock);
+	}
+}
+
+static void irq_process_port_change(struct mv_udc *udc)
+{
+	u32 portsc;
+
+	portsc = readl(&udc->op_regs->portsc[0]);
+	if (!(portsc & PORTSCX_PORT_RESET)) {
+		/* Get the speed */
+		u32 speed = portsc & PORTSCX_PORT_SPEED_MASK;
+		switch (speed) {
+		case PORTSCX_PORT_SPEED_HIGH:
+			udc->gadget.speed = USB_SPEED_HIGH;
+			break;
+		case PORTSCX_PORT_SPEED_FULL:
+			udc->gadget.speed = USB_SPEED_FULL;
+			break;
+		case PORTSCX_PORT_SPEED_LOW:
+			udc->gadget.speed = USB_SPEED_LOW;
+			break;
+		default:
+			udc->gadget.speed = USB_SPEED_UNKNOWN;
+			break;
+		}
+	}
+
+	if (portsc & PORTSCX_PORT_SUSPEND) {
+		udc->resume_state = udc->usb_state;
+		udc->usb_state = USB_STATE_SUSPENDED;
+		if (udc->driver->suspend) {
+			spin_unlock(&udc->lock);
+			udc->driver->suspend(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+
+	if (!(portsc & PORTSCX_PORT_SUSPEND)
+		&& udc->usb_state == USB_STATE_SUSPENDED) {
+		handle_bus_resume(udc);
+	}
+
+	if (!udc->resume_state)
+		udc->usb_state = USB_STATE_DEFAULT;
+}
+
+static void irq_process_error(struct mv_udc *udc)
+{
+	/* Increment the error count */
+	udc->errors++;
+}
+
+static irqreturn_t mv_udc_irq(int irq, void *dev)
+{
+	struct mv_udc *udc = (struct mv_udc *)dev;
+	u32 status, intr;
+
+	/* Disable ISR when stopped bit is set */
+	if (udc->stopped)
+		return IRQ_NONE;
+
+	spin_lock(&udc->lock);
+
+	status = readl(&udc->op_regs->usbsts);
+	intr = readl(&udc->op_regs->usbintr);
+	status &= intr;
+
+	if (status == 0) {
+		spin_unlock(&udc->lock);
+		return IRQ_NONE;
+	}
+
+	/* Clear all the interrupts occurred */
+	writel(status, &udc->op_regs->usbsts);
+
+	if (status & USBSTS_ERR)
+		irq_process_error(udc);
+
+	if (status & USBSTS_RESET)
+		irq_process_reset(udc);
+
+	if (status & USBSTS_PORT_CHANGE)
+		irq_process_port_change(udc);
+
+	if (status & USBSTS_INT)
+		irq_process_tr_complete(udc);
+
+	if (status & USBSTS_SUSPEND)
+		irq_process_suspend(udc);
+
+	spin_unlock(&udc->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mv_udc_vbus_irq(int irq, void *dev)
+{
+	struct mv_udc *udc = (struct mv_udc *)dev;
+
+	/* polling VBUS and init phy may cause too much time*/
+	if (udc->qwork)
+		queue_work(udc->qwork, &udc->vbus_work);
+
+	return IRQ_HANDLED;
+}
+
+static void mv_udc_vbus_work(struct work_struct *work)
+{
+	struct mv_udc *udc;
+	unsigned int vbus;
+
+	udc = container_of(work, struct mv_udc, vbus_work);
+	if (!udc->pdata->vbus)
+		return;
+
+	vbus = udc->pdata->vbus->poll();
+	dev_info(&udc->dev->dev, "vbus is %d\n", vbus);
+
+	if (vbus == VBUS_HIGH)
+		mv_udc_vbus_session(&udc->gadget, 1);
+	else if (vbus == VBUS_LOW)
+		mv_udc_vbus_session(&udc->gadget, 0);
+}
+
+/* release device structure */
+static void gadget_release(struct device *_dev)
+{
+	struct mv_udc *udc;
+
+	udc = dev_get_drvdata(_dev);
+
+	complete(udc->done);
+}
+
+static int mv_udc_remove(struct platform_device *pdev)
+{
+	struct mv_udc *udc;
+
+	udc = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&udc->gadget);
+
+	if (udc->qwork) {
+		flush_workqueue(udc->qwork);
+		destroy_workqueue(udc->qwork);
+	}
+
+	/* free memory allocated in probe */
+	dma_pool_destroy(udc->dtd_pool);
+
+	if (udc->ep_dqh)
+		dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
+			udc->ep_dqh, udc->ep_dqh_dma);
+
+	mv_udc_disable(udc);
+
+	/* free dev, wait for the release() finished */
+	wait_for_completion(udc->done);
+
+	return 0;
+}
+
+static int mv_udc_probe(struct platform_device *pdev)
+{
+	struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct mv_udc *udc;
+	int retval = 0;
+	struct resource *r;
+	size_t size;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "missing platform_data\n");
+		return -ENODEV;
+	}
+
+	udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+	if (udc == NULL)
+		return -ENOMEM;
+
+	udc->done = &release_done;
+	udc->pdata = dev_get_platdata(&pdev->dev);
+	spin_lock_init(&udc->lock);
+
+	udc->dev = pdev;
+
+	if (pdata->mode == MV_USB_MODE_OTG) {
+		udc->transceiver = devm_usb_get_phy(&pdev->dev,
+					USB_PHY_TYPE_USB2);
+		if (IS_ERR(udc->transceiver)) {
+			retval = PTR_ERR(udc->transceiver);
+
+			if (retval == -ENXIO)
+				return retval;
+
+			udc->transceiver = NULL;
+			return -EPROBE_DEFER;
+		}
+	}
+
+	/* udc only have one sysclk. */
+	udc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(udc->clk))
+		return PTR_ERR(udc->clk);
+
+	r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs");
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		return -ENODEV;
+	}
+
+	udc->cap_regs = (struct mv_cap_regs __iomem *)
+		devm_ioremap(&pdev->dev, r->start, resource_size(r));
+	if (udc->cap_regs == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		return -EBUSY;
+	}
+
+	r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
+		return -ENODEV;
+	}
+
+	udc->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+	if (udc->phy_regs == NULL) {
+		dev_err(&pdev->dev, "failed to map phy I/O memory\n");
+		return -EBUSY;
+	}
+
+	/* we will acces controller register, so enable the clk */
+	retval = mv_udc_enable_internal(udc);
+	if (retval)
+		return retval;
+
+	udc->op_regs =
+		(struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
+		+ (readl(&udc->cap_regs->caplength_hciversion)
+			& CAPLENGTH_MASK));
+	udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
+
+	/*
+	 * some platform will use usb to download image, it may not disconnect
+	 * usb gadget before loading kernel. So first stop udc here.
+	 */
+	udc_stop(udc);
+	writel(0xFFFFFFFF, &udc->op_regs->usbsts);
+
+	size = udc->max_eps * sizeof(struct mv_dqh) *2;
+	size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
+	udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
+					&udc->ep_dqh_dma, GFP_KERNEL);
+
+	if (udc->ep_dqh == NULL) {
+		dev_err(&pdev->dev, "allocate dQH memory failed\n");
+		retval = -ENOMEM;
+		goto err_disable_clock;
+	}
+	udc->ep_dqh_size = size;
+
+	/* create dTD dma_pool resource */
+	udc->dtd_pool = dma_pool_create("mv_dtd",
+			&pdev->dev,
+			sizeof(struct mv_dtd),
+			DTD_ALIGNMENT,
+			DMA_BOUNDARY);
+
+	if (!udc->dtd_pool) {
+		retval = -ENOMEM;
+		goto err_free_dma;
+	}
+
+	size = udc->max_eps * sizeof(struct mv_ep) *2;
+	udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (udc->eps == NULL) {
+		retval = -ENOMEM;
+		goto err_destroy_dma;
+	}
+
+	/* initialize ep0 status request structure */
+	udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
+					GFP_KERNEL);
+	if (!udc->status_req) {
+		retval = -ENOMEM;
+		goto err_destroy_dma;
+	}
+	INIT_LIST_HEAD(&udc->status_req->queue);
+
+	/* allocate a small amount of memory to get valid address */
+	udc->status_req->req.buf = kzalloc(8, GFP_KERNEL);
+	udc->status_req->req.dma = DMA_ADDR_INVALID;
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = EP_DIR_OUT;
+	udc->remote_wakeup = 0;
+
+	r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no IRQ resource defined\n");
+		retval = -ENODEV;
+		goto err_destroy_dma;
+	}
+	udc->irq = r->start;
+	if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq,
+		IRQF_SHARED, driver_name, udc)) {
+		dev_err(&pdev->dev, "Request irq %d for UDC failed\n",
+			udc->irq);
+		retval = -ENODEV;
+		goto err_destroy_dma;
+	}
+
+	/* initialize gadget structure */
+	udc->gadget.ops = &mv_ops;	/* usb_gadget_ops */
+	udc->gadget.ep0 = &udc->eps[0].ep;	/* gadget ep0 */
+	INIT_LIST_HEAD(&udc->gadget.ep_list);	/* ep_list */
+	udc->gadget.speed = USB_SPEED_UNKNOWN;	/* speed */
+	udc->gadget.max_speed = USB_SPEED_HIGH;	/* support dual speed */
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	udc->gadget.name = driver_name;		/* gadget name */
+
+	eps_init(udc);
+
+	/* VBUS detect: we can disable/enable clock on demand.*/
+	if (udc->transceiver)
+		udc->clock_gating = 1;
+	else if (pdata->vbus) {
+		udc->clock_gating = 1;
+		retval = devm_request_threaded_irq(&pdev->dev,
+				pdata->vbus->irq, NULL,
+				mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
+		if (retval) {
+			dev_info(&pdev->dev,
+				"Can not request irq for VBUS, "
+				"disable clock gating\n");
+			udc->clock_gating = 0;
+		}
+
+		udc->qwork = create_singlethread_workqueue("mv_udc_queue");
+		if (!udc->qwork) {
+			dev_err(&pdev->dev, "cannot create workqueue\n");
+			retval = -ENOMEM;
+			goto err_destroy_dma;
+		}
+
+		INIT_WORK(&udc->vbus_work, mv_udc_vbus_work);
+	}
+
+	/*
+	 * When clock gating is supported, we can disable clk and phy.
+	 * If not, it means that VBUS detection is not supported, we
+	 * have to enable vbus active all the time to let controller work.
+	 */
+	if (udc->clock_gating)
+		mv_udc_disable_internal(udc);
+	else
+		udc->vbus_active = 1;
+
+	retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+			gadget_release);
+	if (retval)
+		goto err_create_workqueue;
+
+	platform_set_drvdata(pdev, udc);
+	dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n",
+		udc->clock_gating ? "with" : "without");
+
+	return 0;
+
+err_create_workqueue:
+	destroy_workqueue(udc->qwork);
+err_destroy_dma:
+	dma_pool_destroy(udc->dtd_pool);
+err_free_dma:
+	dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
+			udc->ep_dqh, udc->ep_dqh_dma);
+err_disable_clock:
+	mv_udc_disable_internal(udc);
+
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_udc_suspend(struct device *dev)
+{
+	struct mv_udc *udc;
+
+	udc = dev_get_drvdata(dev);
+
+	/* if OTG is enabled, the following will be done in OTG driver*/
+	if (udc->transceiver)
+		return 0;
+
+	if (udc->pdata->vbus && udc->pdata->vbus->poll)
+		if (udc->pdata->vbus->poll() == VBUS_HIGH) {
+			dev_info(&udc->dev->dev, "USB cable is connected!\n");
+			return -EAGAIN;
+		}
+
+	/*
+	 * only cable is unplugged, udc can suspend.
+	 * So do not care about clock_gating == 1.
+	 */
+	if (!udc->clock_gating) {
+		udc_stop(udc);
+
+		spin_lock_irq(&udc->lock);
+		/* stop all usb activities */
+		stop_activity(udc, udc->driver);
+		spin_unlock_irq(&udc->lock);
+
+		mv_udc_disable_internal(udc);
+	}
+
+	return 0;
+}
+
+static int mv_udc_resume(struct device *dev)
+{
+	struct mv_udc *udc;
+	int retval;
+
+	udc = dev_get_drvdata(dev);
+
+	/* if OTG is enabled, the following will be done in OTG driver*/
+	if (udc->transceiver)
+		return 0;
+
+	if (!udc->clock_gating) {
+		retval = mv_udc_enable_internal(udc);
+		if (retval)
+			return retval;
+
+		if (udc->driver && udc->softconnect) {
+			udc_reset(udc);
+			ep0_reset(udc);
+			udc_start(udc);
+		}
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops mv_udc_pm_ops = {
+	.suspend	= mv_udc_suspend,
+	.resume		= mv_udc_resume,
+};
+#endif
+
+static void mv_udc_shutdown(struct platform_device *pdev)
+{
+	struct mv_udc *udc;
+	u32 mode;
+
+	udc = platform_get_drvdata(pdev);
+	/* reset controller mode to IDLE */
+	mv_udc_enable(udc);
+	mode = readl(&udc->op_regs->usbmode);
+	mode &= ~3;
+	writel(mode, &udc->op_regs->usbmode);
+	mv_udc_disable(udc);
+}
+
+static struct platform_driver udc_driver = {
+	.probe		= mv_udc_probe,
+	.remove		= mv_udc_remove,
+	.shutdown	= mv_udc_shutdown,
+	.driver		= {
+		.name	= "mv-udc",
+#ifdef CONFIG_PM
+		.pm	= &mv_udc_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(udc_driver);
+MODULE_ALIAS("platform:mv-udc");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
new file mode 100644
index 0000000..660878a
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -0,0 +1,2710 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for PLX NET2272 USB device controller
+ *
+ * Copyright (C) 2005-2006 PLX Technology, Inc.
+ * Copyright (C) 2006-2011 Analog Devices, Inc.
+ */
+
+#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/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/prefetch.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/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",
+};
+
+#ifdef CONFIG_USB_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 bool 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 const 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 = usb_endpoint_maxp(desc);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	_ep->maxpacket = max;
+	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);
+
+	usb_ep_set_maxpacket_limit(&ep->ep, ~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_request *req;
+
+	if (!_ep)
+		return NULL;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void
+net2272_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2272_request *req;
+
+	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 && ep->dma)
+		usb_gadget_unmap_request(&dev->gadget, &req->req,
+				ep->is_in);
+
+	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);
+	usb_gadget_giveback_request(&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_USB_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_USB_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_USB_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) {
+		status = usb_gadget_map_request(&dev->gadget, _req,
+				ep->is_in);
+		if (status)
+			return status;
+	}
+
+	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))
+		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 const 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)
+{
+	if (!_gadget)
+		return -ENODEV;
+
+	_gadget->is_selfpowered = (value != 0);
+
+	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 *_gadget,
+		struct usb_gadget_driver *driver);
+static int net2272_stop(struct usb_gadget *_gadget);
+
+static const struct usb_gadget_ops net2272_ops = {
+	.get_frame	= net2272_get_frame,
+	.wakeup		= net2272_wakeup,
+	.set_selfpowered = net2272_set_selfpowered,
+	.pullup		= net2272_pullup,
+	.udc_start	= net2272_start,
+	.udc_stop	= net2272_stop,
+};
+
+/*---------------------------------------------------------------------------*/
+
+static ssize_t
+registers_show(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_RO(registers);
+
+/*---------------------------------------------------------------------------*/
+
+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 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);
+
+		if (i == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+	usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 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 *_gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct net2272 *dev;
+	unsigned i;
+
+	if (!driver || !driver->setup ||
+	    driver->max_speed != USB_SPEED_HIGH)
+		return -EINVAL;
+
+	dev = container_of(_gadget, struct net2272, gadget);
+
+	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;
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	net2272_ep0_start(dev);
+
+	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 *_gadget)
+{
+	struct net2272 *dev;
+	unsigned long flags;
+
+	dev = container_of(_gadget, struct net2272, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	stop_activity(dev, NULL);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	dev->driver = NULL;
+
+	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 << 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 : NULL);
+
+	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\n",
+				usb_speed_string(dev->gadget.speed));
+		}
+
+		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 = cpu_to_le16(1);
+				else
+					status = 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->gadget.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) {
+		bool	reset = false;
+		bool	disconnect = false;
+
+		/*
+		 * Ignore disconnects and resets if the speed hasn't been set.
+		 * VBUS can bounce and there's always an initial reset.
+		 */
+		net2272_write(dev, IRQSTAT1, tmp);
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
+			if ((stat & (1 << VBUS_INTERRUPT)) &&
+					(net2272_read(dev, USBCTL1) &
+						(1 << VBUS_PIN)) == 0) {
+				disconnect = true;
+				dev_dbg(dev->dev, "disconnect %s\n",
+					dev->driver->driver.name);
+			} else if ((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
+					(net2272_read(dev, USBCTL1) & mask)
+						== 0) {
+				reset = true;
+				dev_dbg(dev->dev, "reset %s\n",
+					dev->driver->driver.name);
+			}
+
+			if (disconnect || reset) {
+				stop_activity(dev, dev->driver);
+				net2272_ep0_start(dev);
+				spin_unlock(&dev->lock);
+				if (reset)
+					usb_gadget_udc_reset
+						(&dev->gadget, dev->driver);
+				else
+					(dev->driver->disconnect)
+						(&dev->gadget);
+				spin_lock(&dev->lock);
+				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)) {
+		spin_unlock(&dev->lock);
+		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
+net2272_remove(struct net2272 *dev)
+{
+	usb_del_gadget_udc(&dev->gadget);
+	free_irq(dev->irq, dev);
+	iounmap(dev->base_addr);
+	device_remove_file(dev->dev, &dev_attr_registers);
+
+	dev_info(dev->dev, "unbind\n");
+}
+
+static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq)
+{
+	struct net2272 *ret;
+
+	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.max_speed = USB_SPEED_HIGH;
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	ret->gadget.name = driver_name;
+
+	return ret;
+}
+
+static int
+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);
+
+	ret = device_create_file(dev->dev, &dev_attr_registers);
+	if (ret)
+		goto err_irq;
+
+	ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget,
+			net2272_gadget_release);
+	if (ret)
+		goto err_add_udc;
+
+	return 0;
+
+err_add_udc:
+	device_remove_file(dev->dev, &dev_attr_registers);
+ err_irq:
+	free_irq(dev->irq, dev);
+ err:
+	return ret;
+}
+
+#ifdef CONFIG_USB_PCI
+
+/*
+ * wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us
+ */
+
+static int
+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
+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
+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
+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
+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
+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 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   = 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
+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 ");
+
+	return 0;
+
+ err_io:
+	iounmap(dev->base_addr);
+ err_req:
+	release_mem_region(base, len);
+ err:
+	return ret;
+}
+
+static int
+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  = net2272_plat_remove,
+	.driver  = {
+		.name  = driver_name,
+	},
+	/* 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/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
new file mode 100644
index 0000000..8e64462
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2272.h
@@ -0,0 +1,587 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PLX NET2272 high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2006 PLX Technology, Inc.
+ * Copyright (C) 2006-2011 Analog Devices, Inc.
+ */
+
+#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,
+	         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_USB_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/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
new file mode 100644
index 0000000..b02ab2a
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -0,0 +1,3876 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the PLX NET2280 USB device controller.
+ * Specs and errata are available from <http://www.plxtech.com>.
+ *
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
+ * development of this driver.
+ *
+ *
+ * CODE STATUS HIGHLIGHTS
+ *
+ * This driver should work well with most "gadget" drivers, including
+ * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
+ * as well as Gadget Zero and Gadgetfs.
+ *
+ * DMA is enabled by default.
+ *
+ * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
+ * be enabled.
+ *
+ * Note that almost all the errata workarounds here are only needed for
+ * rev1 chips.  Rev1a silicon (0110) fixes almost all of them.
+ */
+
+/*
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2005 PLX Technology, Inc.
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ *	with 2282 chip
+ *
+ * Modified Ricardo Ribalda Qtechnology AS  to provide compatibility
+ *	with usb 338x chip. Based on PLX driver
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
+#include <linux/io.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#define	DRIVER_DESC		"PLX NET228x/USB338x USB Peripheral Controller"
+#define	DRIVER_VERSION		"2005 Sept 27/v3.0"
+
+#define	EP_DONTUSE		13	/* nonzero */
+
+#define USE_RDK_LEDS		/* GPIO pins control three LEDs */
+
+
+static const char driver_name[] = "net2280";
+static const char driver_desc[] = DRIVER_DESC;
+
+static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 };
+static const char ep0name[] = "ep0";
+
+#define EP_INFO(_name, _caps) \
+	{ \
+		.name = _name, \
+		.caps = _caps, \
+	}
+
+static const struct {
+	const char *name;
+	const struct usb_ep_caps caps;
+} ep_info_dft[] = { /* Default endpoint configuration */
+	EP_INFO(ep0name,
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-a",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-b",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-c",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-d",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-e",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-f",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-g",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep-h",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
+}, ep_info_adv[] = { /* Endpoints for usb3380 advance mode */
+	EP_INFO(ep0name,
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
+	EP_INFO("ep1in",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep2out",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep3in",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep4out",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep1out",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep2in",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep3out",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep4in",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
+};
+
+#undef EP_INFO
+
+/* mode 0 == ep-{a,b,c,d} 1K fifo each
+ * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
+ * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
+ */
+static ushort fifo_mode;
+
+/* "modprobe net2280 fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0644);
+
+/* enable_suspend -- When enabled, the driver will respond to
+ * USB suspend requests by powering down the NET2280.  Otherwise,
+ * USB suspend requests will be ignored.  This is acceptable for
+ * self-powered devices
+ */
+static bool enable_suspend;
+
+/* "modprobe net2280 enable_suspend=1" etc */
+module_param(enable_suspend, bool, 0444);
+
+#define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "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";
+	}
+	return "control";
+}
+
+#include "net2280.h"
+
+#define valid_bit	cpu_to_le32(BIT(VALID_BIT))
+#define dma_done_ie	cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE))
+
+static void ep_clear_seqnum(struct net2280_ep *ep);
+static void stop_activity(struct net2280 *dev,
+					struct usb_gadget_driver *driver);
+static void ep0_start(struct net2280 *dev);
+
+/*-------------------------------------------------------------------------*/
+static inline void enable_pciirqenb(struct net2280_ep *ep)
+{
+	u32 tmp = readl(&ep->dev->regs->pciirqenb0);
+
+	if (ep->dev->quirks & PLX_LEGACY)
+		tmp |= BIT(ep->num);
+	else
+		tmp |= BIT(ep_bit[ep->num]);
+	writel(tmp, &ep->dev->regs->pciirqenb0);
+
+	return;
+}
+
+static int
+net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct net2280		*dev;
+	struct net2280_ep	*ep;
+	u32			max;
+	u32 tmp = 0;
+	u32 type;
+	unsigned long		flags;
+	static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
+	int ret = 0;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
+			desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_err("%s: failed at line=%d\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		ret = -ESHUTDOWN;
+		goto print_err;
+	}
+
+	/* erratum 0119 workaround ties up an endpoint number */
+	if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
+		ret = -EDOM;
+		goto print_err;
+	}
+
+	if (dev->quirks & PLX_PCIE) {
+		if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
+			ret = -EDOM;
+			goto print_err;
+		}
+		ep->is_in = !!usb_endpoint_dir_in(desc);
+		if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
+			ret = -EINVAL;
+			goto print_err;
+		}
+	}
+
+	/* sanity check ep-e/ep-f since their fifos are small */
+	max = usb_endpoint_maxp(desc);
+	if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
+		ret = -ERANGE;
+		goto print_err;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	_ep->maxpacket = max;
+	ep->desc = desc;
+
+	/* ep_reset() has already been called */
+	ep->stopped = 0;
+	ep->wedged = 0;
+	ep->out_overflow = 0;
+
+	/* set speed-dependent max packet; may kick in high bandwidth */
+	set_max_speed(ep, max);
+
+	/* set type, direction, address; reset fifo counters */
+	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+
+	if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) {
+		tmp = readl(&ep->cfg->ep_cfg);
+		/* If USB ep number doesn't match hardware ep number */
+		if ((tmp & 0xf) != usb_endpoint_num(desc)) {
+			ret = -EINVAL;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			goto print_err;
+		}
+		if (ep->is_in)
+			tmp &= ~USB3380_EP_CFG_MASK_IN;
+		else
+			tmp &= ~USB3380_EP_CFG_MASK_OUT;
+	}
+	type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	if (type == USB_ENDPOINT_XFER_INT) {
+		/* erratum 0105 workaround prevents hs NYET */
+		if (dev->chiprev == 0100 &&
+				dev->gadget.speed == USB_SPEED_HIGH &&
+				!(desc->bEndpointAddress & USB_DIR_IN))
+			writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+				&ep->regs->ep_rsp);
+	} else if (type == USB_ENDPOINT_XFER_BULK) {
+		/* catch some particularly blatant driver bugs */
+		if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
+		    (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+		    (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			ret = -ERANGE;
+			goto print_err;
+		}
+	}
+	ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC);
+	/* Enable this endpoint */
+	if (dev->quirks & PLX_LEGACY) {
+		tmp |= type << ENDPOINT_TYPE;
+		tmp |= desc->bEndpointAddress;
+		/* default full fifo lines */
+		tmp |= (4 << ENDPOINT_BYTE_COUNT);
+		tmp |= BIT(ENDPOINT_ENABLE);
+		ep->is_in = (tmp & USB_DIR_IN) != 0;
+	} else {
+		/* In Legacy mode, only OUT endpoints are used */
+		if (dev->enhanced_mode && ep->is_in) {
+			tmp |= type << IN_ENDPOINT_TYPE;
+			tmp |= BIT(IN_ENDPOINT_ENABLE);
+		} else {
+			tmp |= type << OUT_ENDPOINT_TYPE;
+			tmp |= BIT(OUT_ENDPOINT_ENABLE);
+			tmp |= (ep->is_in << ENDPOINT_DIRECTION);
+		}
+
+		tmp |= (4 << ENDPOINT_BYTE_COUNT);
+		if (!dev->enhanced_mode)
+			tmp |= usb_endpoint_num(desc);
+		tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
+	}
+
+	/* Make sure all the registers are written before ep_rsp*/
+	wmb();
+
+	/* for OUT transfers, block the rx fifo until a read is posted */
+	if (!ep->is_in)
+		writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	else if (!(dev->quirks & PLX_2280)) {
+		/* Added for 2282, Don't use nak packets on an in endpoint,
+		 * this was ignored on 2280
+		 */
+		writel(BIT(CLEAR_NAK_OUT_PACKETS) |
+			BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
+	}
+
+	if (dev->quirks & PLX_PCIE)
+		ep_clear_seqnum(ep);
+	writel(tmp, &ep->cfg->ep_cfg);
+
+	/* enable irqs */
+	if (!ep->dma) {				/* pio, per-packet */
+		enable_pciirqenb(ep);
+
+		tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) |
+			BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
+		if (dev->quirks & PLX_2280)
+			tmp |= readl(&ep->regs->ep_irqenb);
+		writel(tmp, &ep->regs->ep_irqenb);
+	} else {				/* dma, per-request */
+		tmp = BIT((8 + ep->num));	/* completion */
+		tmp |= readl(&dev->regs->pciirqenb1);
+		writel(tmp, &dev->regs->pciirqenb1);
+
+		/* for short OUT transfers, dma completions can't
+		 * advance the queue; do it pio-style, by hand.
+		 * NOTE erratum 0112 workaround #2
+		 */
+		if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
+			tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+			writel(tmp, &ep->regs->ep_irqenb);
+
+			enable_pciirqenb(ep);
+		}
+	}
+
+	tmp = desc->bEndpointAddress;
+	ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+		_ep->name, tmp & 0x0f, DIR_STRING(tmp),
+		type_string(desc->bmAttributes),
+		ep->dma ? "dma" : "pio", max);
+
+	/* pci writes may still be posted */
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return ret;
+
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+	return ret;
+}
+
+static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl(ptr);
+		if (result == ~(u32)0)		/* "device unplugged" */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+static const struct usb_ep_ops net2280_ep_ops;
+
+static void ep_reset_228x(struct net2280_regs __iomem *regs,
+			  struct net2280_ep *ep)
+{
+	u32		tmp;
+
+	ep->desc = NULL;
+	INIT_LIST_HEAD(&ep->queue);
+
+	usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+	ep->ep.ops = &net2280_ep_ops;
+
+	/* disable the dma, irqs, endpoint... */
+	if (ep->dma) {
+		writel(0, &ep->dma->dmactl);
+		writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+			BIT(DMA_TRANSACTION_DONE_INTERRUPT) |
+			BIT(DMA_ABORT),
+			&ep->dma->dmastat);
+
+		tmp = readl(&regs->pciirqenb0);
+		tmp &= ~BIT(ep->num);
+		writel(tmp, &regs->pciirqenb0);
+	} else {
+		tmp = readl(&regs->pciirqenb1);
+		tmp &= ~BIT((8 + ep->num));	/* completion */
+		writel(tmp, &regs->pciirqenb1);
+	}
+	writel(0, &ep->regs->ep_irqenb);
+
+	/* init to our chosen defaults, notably so that we NAK OUT
+	 * packets until the driver queues a read (+note erratum 0112)
+	 */
+	if (!ep->is_in || (ep->dev->quirks & PLX_2280)) {
+		tmp = BIT(SET_NAK_OUT_PACKETS_MODE) |
+		BIT(SET_NAK_OUT_PACKETS) |
+		BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+		BIT(CLEAR_INTERRUPT_MODE);
+	} else {
+		/* added for 2282 */
+		tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+		BIT(CLEAR_NAK_OUT_PACKETS) |
+		BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+		BIT(CLEAR_INTERRUPT_MODE);
+	}
+
+	if (ep->num != 0) {
+		tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) |
+			BIT(CLEAR_ENDPOINT_HALT);
+	}
+	writel(tmp, &ep->regs->ep_rsp);
+
+	/* scrub most status bits, and flush any fifo state */
+	if (ep->dev->quirks & PLX_2280)
+		tmp = BIT(FIFO_OVERFLOW) |
+			BIT(FIFO_UNDERFLOW);
+	else
+		tmp = 0;
+
+	writel(tmp | BIT(TIMEOUT) |
+		BIT(USB_STALL_SENT) |
+		BIT(USB_IN_NAK_SENT) |
+		BIT(USB_IN_ACK_RCVD) |
+		BIT(USB_OUT_PING_NAK_SENT) |
+		BIT(USB_OUT_ACK_SENT) |
+		BIT(FIFO_FLUSH) |
+		BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+		BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+		BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+		BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+		BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+		BIT(DATA_IN_TOKEN_INTERRUPT),
+		&ep->regs->ep_stat);
+
+	/* fifo size is handled separately */
+}
+
+static void ep_reset_338x(struct net2280_regs __iomem *regs,
+					struct net2280_ep *ep)
+{
+	u32 tmp, dmastat;
+
+	ep->desc = NULL;
+	INIT_LIST_HEAD(&ep->queue);
+
+	usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+	ep->ep.ops = &net2280_ep_ops;
+
+	/* disable the dma, irqs, endpoint... */
+	if (ep->dma) {
+		writel(0, &ep->dma->dmactl);
+		writel(BIT(DMA_ABORT_DONE_INTERRUPT) |
+		       BIT(DMA_PAUSE_DONE_INTERRUPT) |
+		       BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+		       BIT(DMA_TRANSACTION_DONE_INTERRUPT),
+		       /* | BIT(DMA_ABORT), */
+		       &ep->dma->dmastat);
+
+		dmastat = readl(&ep->dma->dmastat);
+		if (dmastat == 0x5002) {
+			ep_warn(ep->dev, "The dmastat return = %x!!\n",
+			       dmastat);
+			writel(0x5a, &ep->dma->dmastat);
+		}
+
+		tmp = readl(&regs->pciirqenb0);
+		tmp &= ~BIT(ep_bit[ep->num]);
+		writel(tmp, &regs->pciirqenb0);
+	} else {
+		if (ep->num < 5) {
+			tmp = readl(&regs->pciirqenb1);
+			tmp &= ~BIT((8 + ep->num));	/* completion */
+			writel(tmp, &regs->pciirqenb1);
+		}
+	}
+	writel(0, &ep->regs->ep_irqenb);
+
+	writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+	       BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+	       BIT(FIFO_OVERFLOW) |
+	       BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+	       BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+	       BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+	       BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
+
+	tmp = readl(&ep->cfg->ep_cfg);
+	if (ep->is_in)
+		tmp &= ~USB3380_EP_CFG_MASK_IN;
+	else
+		tmp &= ~USB3380_EP_CFG_MASK_OUT;
+	writel(tmp, &ep->cfg->ep_cfg);
+}
+
+static void nuke(struct net2280_ep *);
+
+static int net2280_disable(struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || !ep->desc || _ep->name == ep0name) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	nuke(ep);
+
+	if (ep->dev->quirks & PLX_PCIE)
+		ep_reset_338x(ep->dev->regs, ep);
+	else
+		ep_reset_228x(ep->dev->regs, ep);
+
+	ep_vdbg(ep->dev, "disabled %s %s\n",
+			ep->dma ? "dma" : "pio", _ep->name);
+
+	/* synch memory views with the device */
+	(void)readl(&ep->cfg->ep_cfg);
+
+	if (!ep->dma && ep->num >= 1 && ep->num <= 4)
+		ep->dma = &ep->dev->dma[ep->num - 1];
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request
+*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	if (!_ep) {
+		pr_err("%s: Invalid ep\n", __func__);
+		return NULL;
+	}
+	ep = container_of(_ep, struct net2280_ep, ep);
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	/* this dma descriptor may be swapped with the previous dummy */
+	if (ep->dma) {
+		struct net2280_dma	*td;
+
+		td = dma_pool_alloc(ep->dev->requests, gfp_flags,
+				&req->td_dma);
+		if (!td) {
+			kfree(req);
+			return NULL;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmadesc = td->dmaaddr;
+		req->td = td;
+	}
+	return &req->req;
+}
+
+static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || !_req) {
+		dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n",
+							__func__, _ep, _req);
+		return;
+	}
+
+	req = container_of(_req, struct net2280_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	if (req->td)
+		dma_pool_free(ep->dev->requests, req->td, req->td_dma);
+	kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet.  ep-a..ep-d should use dma instead.
+ */
+static void write_fifo(struct net2280_ep *ep, struct usb_request *req)
+{
+	struct net2280_ep_regs	__iomem *regs = ep->regs;
+	u8			*buf;
+	u32			tmp;
+	unsigned		count, total;
+
+	/* INVARIANT:  fifo is currently empty. (testable) */
+
+	if (req) {
+		buf = req->buf + req->actual;
+		prefetch(buf);
+		total = req->length - req->actual;
+	} else {
+		total = 0;
+		buf = NULL;
+	}
+
+	/* write just one packet at a time */
+	count = ep->ep.maxpacket;
+	if (count > total)	/* min() cannot be used on a bitfield */
+		count = total;
+
+	ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+			ep->ep.name, count,
+			(count != ep->ep.maxpacket) ? " (short)" : "",
+			req);
+	while (count >= 4) {
+		/* NOTE be careful if you try to align these. fifo lines
+		 * should normally be full (4 bytes) and successive partial
+		 * lines are ok only in certain cases.
+		 */
+		tmp = get_unaligned((u32 *)buf);
+		cpu_to_le32s(&tmp);
+		writel(tmp, &regs->ep_data);
+		buf += 4;
+		count -= 4;
+	}
+
+	/* last fifo entry is "short" unless we wrote a full packet.
+	 * also explicitly validate last word in (periodic) transfers
+	 * when maxpacket is not a multiple of 4 bytes.
+	 */
+	if (count || total < ep->ep.maxpacket) {
+		tmp = count ? get_unaligned((u32 *)buf) : count;
+		cpu_to_le32s(&tmp);
+		set_fifo_bytecount(ep, count & 0x03);
+		writel(tmp, &regs->ep_data);
+	}
+
+	/* pci writes may still be posted */
+}
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ *
+ * NOTE:  also used in cases where that erratum doesn't apply:
+ * where the host wrote "too much" data to us.
+ */
+static void out_flush(struct net2280_ep *ep)
+{
+	u32	__iomem *statp;
+	u32	tmp;
+
+	statp = &ep->regs->ep_stat;
+
+	tmp = readl(statp);
+	if (tmp & BIT(NAK_OUT_PACKETS)) {
+		ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+			ep->ep.name, __func__, tmp);
+		writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	}
+
+	writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+		BIT(DATA_PACKET_RECEIVED_INTERRUPT),
+		statp);
+	writel(BIT(FIFO_FLUSH), statp);
+	/* Make sure that stap is written */
+	mb();
+	tmp = readl(statp);
+	if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) &&
+			/* high speed did bulk NYET; fifo isn't filling */
+			ep->dev->gadget.speed == USB_SPEED_FULL) {
+		unsigned	usec;
+
+		usec = 50;		/* 64 byte bulk/interrupt */
+		handshake(statp, BIT(USB_OUT_PING_NAK_SENT),
+				BIT(USB_OUT_PING_NAK_SENT), usec);
+		/* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+	}
+}
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep-a..ep-d this will read multiple packets out when they
+ * have been accepted.
+ */
+static int read_fifo(struct net2280_ep *ep, struct net2280_request *req)
+{
+	struct net2280_ep_regs	__iomem *regs = ep->regs;
+	u8			*buf = req->req.buf + req->req.actual;
+	unsigned		count, tmp, is_short;
+	unsigned		cleanup = 0, prevent = 0;
+
+	/* erratum 0106 ... packets coming in during fifo reads might
+	 * be incompletely rejected.  not all cases have workarounds.
+	 */
+	if (ep->dev->chiprev == 0x0100 &&
+			ep->dev->gadget.speed == USB_SPEED_FULL) {
+		udelay(1);
+		tmp = readl(&ep->regs->ep_stat);
+		if ((tmp & BIT(NAK_OUT_PACKETS)))
+			cleanup = 1;
+		else if ((tmp & BIT(FIFO_FULL))) {
+			start_out_naking(ep);
+			prevent = 1;
+		}
+		/* else: hope we don't see the problem */
+	}
+
+	/* never overflow the rx buffer. the fifo reads packets until
+	 * it sees a short one; we might not be ready for them all.
+	 */
+	prefetchw(buf);
+	count = readl(&regs->ep_avail);
+	if (unlikely(count == 0)) {
+		udelay(1);
+		tmp = readl(&ep->regs->ep_stat);
+		count = readl(&regs->ep_avail);
+		/* handled that data already? */
+		if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0)
+			return 0;
+	}
+
+	tmp = req->req.length - req->req.actual;
+	if (count > tmp) {
+		/* as with DMA, data overflow gets flushed */
+		if ((tmp % ep->ep.maxpacket) != 0) {
+			ep_err(ep->dev,
+				"%s out fifo %d bytes, expected %d\n",
+				ep->ep.name, count, tmp);
+			req->req.status = -EOVERFLOW;
+			cleanup = 1;
+			/* NAK_OUT_PACKETS will be set, so flushing is safe;
+			 * the next read will start with the next packet
+			 */
+		} /* else it's a ZLP, no worries */
+		count = tmp;
+	}
+	req->req.actual += count;
+
+	is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+	ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+			ep->ep.name, count, is_short ? " (short)" : "",
+			cleanup ? " flush" : "", prevent ? " nak" : "",
+			req, req->req.actual, req->req.length);
+
+	while (count >= 4) {
+		tmp = readl(&regs->ep_data);
+		cpu_to_le32s(&tmp);
+		put_unaligned(tmp, (u32 *)buf);
+		buf += 4;
+		count -= 4;
+	}
+	if (count) {
+		tmp = readl(&regs->ep_data);
+		/* LE conversion is implicit here: */
+		do {
+			*buf++ = (u8) tmp;
+			tmp >>= 8;
+		} while (--count);
+	}
+	if (cleanup)
+		out_flush(ep);
+	if (prevent) {
+		writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+		(void) readl(&ep->regs->ep_rsp);
+	}
+
+	return is_short || ((req->req.actual == req->req.length) &&
+			!req->req.zero);
+}
+
+/* fill out dma descriptor to match a given request */
+static void fill_dma_desc(struct net2280_ep *ep,
+					struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*td = req->td;
+	u32			dmacount = req->req.length;
+
+	/* don't let DMA continue after a short OUT packet,
+	 * so overruns can't affect the next transfer.
+	 * in case of overruns on max-size packets, we can't
+	 * stop the fifo from filling but we can flush it.
+	 */
+	if (ep->is_in)
+		dmacount |= BIT(DMA_DIRECTION);
+	if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) ||
+					!(ep->dev->quirks & PLX_2280))
+		dmacount |= BIT(END_OF_CHAIN);
+
+	req->valid = valid;
+	if (valid)
+		dmacount |= BIT(VALID_BIT);
+	dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+
+	/* td->dmadesc = previously set by caller */
+	td->dmaaddr = cpu_to_le32 (req->req.dma);
+
+	/* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
+	wmb();
+	td->dmacount = cpu_to_le32(dmacount);
+}
+
+static const u32 dmactl_default =
+		BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+		BIT(DMA_CLEAR_COUNT_ENABLE) |
+		/* erratum 0116 workaround part 1 (use POLLING) */
+		(POLL_100_USEC << DESCRIPTOR_POLLING_RATE) |
+		BIT(DMA_VALID_BIT_POLLING_ENABLE) |
+		BIT(DMA_VALID_BIT_ENABLE) |
+		BIT(DMA_SCATTER_GATHER_ENABLE) |
+		/* erratum 0116 workaround part 2 (no AUTOSTART) */
+		BIT(DMA_ENABLE);
+
+static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+	handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50);
+}
+
+static inline void stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+	writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl);
+	spin_stop_dma(dma);
+}
+
+static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma)
+{
+	struct net2280_dma_regs	__iomem *dma = ep->dma;
+	unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION);
+
+	if (!(ep->dev->quirks & PLX_2280))
+		tmp |= BIT(END_OF_CHAIN);
+
+	writel(tmp, &dma->dmacount);
+	writel(readl(&dma->dmastat), &dma->dmastat);
+
+	writel(td_dma, &dma->dmadesc);
+	if (ep->dev->quirks & PLX_PCIE)
+		dmactl |= BIT(DMA_REQUEST_OUTSTANDING);
+	writel(dmactl, &dma->dmactl);
+
+	/* erratum 0116 workaround part 3:  pci arbiter away from net2280 */
+	(void) readl(&ep->dev->pci->pcimstctl);
+
+	writel(BIT(DMA_START), &dma->dmastat);
+
+	if (!ep->is_in)
+		stop_out_naking(ep);
+}
+
+static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
+{
+	u32			tmp;
+	struct net2280_dma_regs	__iomem *dma = ep->dma;
+
+	/* FIXME can't use DMA for ZLPs */
+
+	/* on this path we "know" there's no dma active (yet) */
+	WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE));
+	writel(0, &ep->dma->dmactl);
+
+	/* previous OUT packet might have been short */
+	if (!ep->is_in && (readl(&ep->regs->ep_stat) &
+				BIT(NAK_OUT_PACKETS))) {
+		writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT),
+			&ep->regs->ep_stat);
+
+		tmp = readl(&ep->regs->ep_avail);
+		if (tmp) {
+			writel(readl(&dma->dmastat), &dma->dmastat);
+
+			/* transfer all/some fifo data */
+			writel(req->req.dma, &dma->dmaaddr);
+			tmp = min(tmp, req->req.length);
+
+			/* dma irq, faking scatterlist status */
+			req->td->dmacount = cpu_to_le32(req->req.length - tmp);
+			writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp,
+					&dma->dmacount);
+			req->td->dmadesc = 0;
+			req->valid = 1;
+
+			writel(BIT(DMA_ENABLE), &dma->dmactl);
+			writel(BIT(DMA_START), &dma->dmastat);
+			return;
+		}
+	}
+
+	tmp = dmactl_default;
+
+	/* force packet boundaries between dma requests, but prevent the
+	 * controller from automagically writing a last "short" packet
+	 * (zero length) unless the driver explicitly said to do that.
+	 */
+	if (ep->is_in) {
+		if (likely((req->req.length % ep->ep.maxpacket) ||
+							req->req.zero)){
+			tmp |= BIT(DMA_FIFO_VALIDATE);
+			ep->in_fifo_validate = 1;
+		} else
+			ep->in_fifo_validate = 0;
+	}
+
+	/* init req->td, pointing to the current dummy */
+	req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+	fill_dma_desc(ep, req, 1);
+
+	req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+
+	start_queue(ep, tmp, req->td_dma);
+}
+
+static inline void
+queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+	struct net2280_dma	*end;
+	dma_addr_t		tmp;
+
+	/* swap new dummy for old, link; fill and maybe activate */
+	end = ep->dummy;
+	ep->dummy = req->td;
+	req->td = end;
+
+	tmp = ep->td_dma;
+	ep->td_dma = req->td_dma;
+	req->td_dma = tmp;
+
+	end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+	fill_dma_desc(ep, req, valid);
+}
+
+static void
+done(struct net2280_ep *ep, struct net2280_request *req, int status)
+{
+	struct net2280		*dev;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (ep->dma)
+		usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+
+	if (status && status != -ESHUTDOWN)
+		ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&dev->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&dev->lock);
+	ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct net2280_request	*req;
+	struct net2280_ep	*ep;
+	struct net2280		*dev;
+	unsigned long		flags;
+	int ret = 0;
+
+	/* we always require a cpu-view buffer, so that we can
+	 * always use pio (as fallback or whatever).
+	 */
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
+		return -EINVAL;
+	}
+	req = container_of(_req, struct net2280_request, req);
+	if (!_req || !_req->complete || !_req->buf ||
+				!list_empty(&req->queue)) {
+		ret = -EINVAL;
+		goto print_err;
+	}
+	if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
+		ret = -EDOM;
+		goto print_err;
+	}
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		ret = -ESHUTDOWN;
+		goto print_err;
+	}
+
+	/* FIXME implement PIO fallback for ZLPs with DMA */
+	if (ep->dma && _req->length == 0) {
+		ret = -EOPNOTSUPP;
+		goto print_err;
+	}
+
+	/* set up dma mapping in case the caller didn't */
+	if (ep->dma) {
+		ret = usb_gadget_map_request(&dev->gadget, _req,
+				ep->is_in);
+		if (ret)
+			goto print_err;
+	}
+
+	ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
+			_ep->name, _req, _req->length, _req->buf);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	if  (list_empty(&ep->queue) && !ep->stopped &&
+		!((dev->quirks & PLX_PCIE) && ep->dma &&
+		  (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) {
+
+		/* use DMA if the endpoint supports it, else pio */
+		if (ep->dma)
+			start_dma(ep, req);
+		else {
+			/* maybe there's no control data, just status ack */
+			if (ep->num == 0 && _req->length == 0) {
+				allow_status(ep);
+				done(ep, req, 0);
+				ep_vdbg(dev, "%s status ack\n", ep->ep.name);
+				goto done;
+			}
+
+			/* PIO ... stuff the fifo, or unblock it.  */
+			if (ep->is_in)
+				write_fifo(ep, _req);
+			else if (list_empty(&ep->queue)) {
+				u32	s;
+
+				/* OUT FIFO might have packet(s) buffered */
+				s = readl(&ep->regs->ep_stat);
+				if ((s & BIT(FIFO_EMPTY)) == 0) {
+					/* note:  _req->short_not_ok is
+					 * ignored here since PIO _always_
+					 * stops queue advance here, and
+					 * _req->status doesn't change for
+					 * short reads (only _req->actual)
+					 */
+					if (read_fifo(ep, req) &&
+							ep->num == 0) {
+						done(ep, req, 0);
+						allow_status(ep);
+						/* don't queue it */
+						req = NULL;
+					} else if (read_fifo(ep, req) &&
+							ep->num != 0) {
+						done(ep, req, 0);
+						req = NULL;
+					} else
+						s = readl(&ep->regs->ep_stat);
+				}
+
+				/* don't NAK, let the fifo fill */
+				if (req && (s & BIT(NAK_OUT_PACKETS)))
+					writel(BIT(CLEAR_NAK_OUT_PACKETS),
+							&ep->regs->ep_rsp);
+			}
+		}
+
+	} else if (ep->dma) {
+		int	valid = 1;
+
+		if (ep->is_in) {
+			int	expect;
+
+			/* preventing magic zlps is per-engine state, not
+			 * per-transfer; irq logic must recover hiccups.
+			 */
+			expect = likely(req->req.zero ||
+				(req->req.length % ep->ep.maxpacket));
+			if (expect != ep->in_fifo_validate)
+				valid = 0;
+		}
+		queue_dma(ep, req, valid);
+
+	} /* else the irq handler advances the queue. */
+
+	ep->responded = 1;
+	if (req)
+		list_add_tail(&req->queue, &ep->queue);
+done:
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* pci writes may still be posted */
+	return ret;
+
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+	return ret;
+}
+
+static inline void
+dma_done(struct net2280_ep *ep,	struct net2280_request *req, u32 dmacount,
+		int status)
+{
+	req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
+	done(ep, req, status);
+}
+
+static int scan_dma_completions(struct net2280_ep *ep)
+{
+	int num_completed = 0;
+
+	/* only look at descriptors that were "naturally" retired,
+	 * so fifo and list head state won't matter
+	 */
+	while (!list_empty(&ep->queue)) {
+		struct net2280_request	*req;
+		u32 req_dma_count;
+
+		req = list_entry(ep->queue.next,
+				struct net2280_request, queue);
+		if (!req->valid)
+			break;
+		rmb();
+		req_dma_count = le32_to_cpup(&req->td->dmacount);
+		if ((req_dma_count & BIT(VALID_BIT)) != 0)
+			break;
+
+		/* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
+		 * cases where DMA must be aborted; this code handles
+		 * all non-abort DMA completions.
+		 */
+		if (unlikely(req->td->dmadesc == 0)) {
+			/* paranoia */
+			u32 const ep_dmacount = readl(&ep->dma->dmacount);
+
+			if (ep_dmacount & DMA_BYTE_COUNT_MASK)
+				break;
+			/* single transfer mode */
+			dma_done(ep, req, req_dma_count, 0);
+			num_completed++;
+			break;
+		} else if (!ep->is_in &&
+			   (req->req.length % ep->ep.maxpacket) &&
+			   !(ep->dev->quirks & PLX_PCIE)) {
+
+			u32 const ep_stat = readl(&ep->regs->ep_stat);
+			/* AVOID TROUBLE HERE by not issuing short reads from
+			 * your gadget driver.  That helps avoids errata 0121,
+			 * 0122, and 0124; not all cases trigger the warning.
+			 */
+			if ((ep_stat & BIT(NAK_OUT_PACKETS)) == 0) {
+				ep_warn(ep->dev, "%s lost packet sync!\n",
+						ep->ep.name);
+				req->req.status = -EOVERFLOW;
+			} else {
+				u32 const ep_avail = readl(&ep->regs->ep_avail);
+				if (ep_avail) {
+					/* fifo gets flushed later */
+					ep->out_overflow = 1;
+					ep_dbg(ep->dev,
+						"%s dma, discard %d len %d\n",
+						ep->ep.name, ep_avail,
+						req->req.length);
+					req->req.status = -EOVERFLOW;
+				}
+			}
+		}
+		dma_done(ep, req, req_dma_count, 0);
+		num_completed++;
+	}
+
+	return num_completed;
+}
+
+static void restart_dma(struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	if (ep->stopped)
+		return;
+	req = list_entry(ep->queue.next, struct net2280_request, queue);
+
+	start_dma(ep, req);
+}
+
+static void abort_dma(struct net2280_ep *ep)
+{
+	/* abort the current transfer */
+	if (likely(!list_empty(&ep->queue))) {
+		/* FIXME work around errata 0121, 0122, 0124 */
+		writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+		spin_stop_dma(ep->dma);
+	} else
+		stop_dma(ep->dma);
+	scan_dma_completions(ep);
+}
+
+/* dequeue ALL requests */
+static void nuke(struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+
+	/* called with spinlock held */
+	ep->stopped = 1;
+	if (ep->dma)
+		abort_dma(ep);
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+				struct net2280_request,
+				queue);
+		done(ep, req, -ESHUTDOWN);
+	}
+}
+
+/* dequeue JUST ONE request */
+static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct net2280_ep	*ep;
+	struct net2280_request	*req;
+	unsigned long		flags;
+	u32			dmactl;
+	int			stopped;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
+		pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
+						__func__, _ep, _req);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	stopped = ep->stopped;
+
+	/* quiesce dma while we patch the queue */
+	dmactl = 0;
+	ep->stopped = 1;
+	if (ep->dma) {
+		dmactl = readl(&ep->dma->dmactl);
+		/* WARNING erratum 0127 may kick in ... */
+		stop_dma(ep->dma);
+		scan_dma_completions(ep);
+	}
+
+	/* 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);
+		dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
+								__func__);
+		return -EINVAL;
+	}
+
+	/* queue head may be partially complete. */
+	if (ep->queue.next == &req->queue) {
+		if (ep->dma) {
+			ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name);
+			_req->status = -ECONNRESET;
+			abort_dma(ep);
+			if (likely(ep->queue.next == &req->queue)) {
+				/* NOTE: misreports single-transfer mode*/
+				req->td->dmacount = 0;	/* invalidate */
+				dma_done(ep, req,
+					readl(&ep->dma->dmacount),
+					-ECONNRESET);
+			}
+		} else {
+			ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name);
+			done(ep, req, -ECONNRESET);
+		}
+		req = NULL;
+	}
+
+	if (req)
+		done(ep, req, -ECONNRESET);
+	ep->stopped = stopped;
+
+	if (ep->dma) {
+		/* turn off dma on inactive queues */
+		if (list_empty(&ep->queue))
+			stop_dma(ep->dma);
+		else if (!ep->stopped) {
+			/* resume current request, or start new one */
+			if (req)
+				writel(dmactl, &ep->dma->dmactl);
+			else
+				start_dma(ep, list_entry(ep->queue.next,
+					struct net2280_request, queue));
+		}
+	}
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_fifo_status(struct usb_ep *_ep);
+
+static int
+net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+	struct net2280_ep	*ep;
+	unsigned long		flags;
+	int			retval = 0;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
+		return -EINVAL;
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		retval = -ESHUTDOWN;
+		goto print_err;
+	}
+	if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+						== USB_ENDPOINT_XFER_ISOC) {
+		retval = -EINVAL;
+		goto print_err;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	if (!list_empty(&ep->queue)) {
+		retval = -EAGAIN;
+		goto print_unlock;
+	} else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
+		retval = -EAGAIN;
+		goto print_unlock;
+	} else {
+		ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
+				value ? "set" : "clear",
+				wedged ? "wedge" : "halt");
+		/* set/clear, then synch memory views with the device */
+		if (value) {
+			if (ep->num == 0)
+				ep->dev->protocol_stall = 1;
+			else
+				set_halt(ep);
+			if (wedged)
+				ep->wedged = 1;
+		} else {
+			clear_halt(ep);
+			if (ep->dev->quirks & PLX_PCIE &&
+				!list_empty(&ep->queue) && ep->td_dma)
+					restart_dma(ep);
+			ep->wedged = 0;
+		}
+		(void) readl(&ep->regs->ep_rsp);
+	}
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	return retval;
+
+print_unlock:
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
+	return retval;
+}
+
+static int net2280_set_halt(struct usb_ep *_ep, int value)
+{
+	return net2280_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int net2280_set_wedge(struct usb_ep *_ep)
+{
+	if (!_ep || _ep->name == ep0name) {
+		pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
+		return -EINVAL;
+	}
+	return net2280_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static int net2280_fifo_status(struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+	u32			avail;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
+		return -ENODEV;
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(&ep->dev->pdev->dev,
+			"%s: Invalid driver=%p or speed=%d\n",
+			__func__, ep->dev->driver, ep->dev->gadget.speed);
+		return -ESHUTDOWN;
+	}
+
+	avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
+	if (avail > ep->fifo_size) {
+		dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
+		return -EOVERFLOW;
+	}
+	if (ep->is_in)
+		avail = ep->fifo_size - avail;
+	return avail;
+}
+
+static void net2280_fifo_flush(struct usb_ep *_ep)
+{
+	struct net2280_ep	*ep;
+
+	ep = container_of(_ep, struct net2280_ep, ep);
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
+		return;
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(&ep->dev->pdev->dev,
+			"%s: Invalid driver=%p or speed=%d\n",
+			__func__, ep->dev->driver, ep->dev->gadget.speed);
+		return;
+	}
+
+	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+	(void) readl(&ep->regs->ep_rsp);
+}
+
+static const struct usb_ep_ops net2280_ep_ops = {
+	.enable		= net2280_enable,
+	.disable	= net2280_disable,
+
+	.alloc_request	= net2280_alloc_request,
+	.free_request	= net2280_free_request,
+
+	.queue		= net2280_queue,
+	.dequeue	= net2280_dequeue,
+
+	.set_halt	= net2280_set_halt,
+	.set_wedge	= net2280_set_wedge,
+	.fifo_status	= net2280_fifo_status,
+	.fifo_flush	= net2280_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_get_frame(struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+	unsigned long		flags;
+	u16			retval;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct net2280, gadget);
+	spin_lock_irqsave(&dev->lock, flags);
+	retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff;
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return retval;
+}
+
+static int net2280_wakeup(struct usb_gadget *_gadget)
+{
+	struct net2280		*dev;
+	u32			tmp;
+	unsigned long		flags;
+
+	if (!_gadget)
+		return 0;
+	dev = container_of(_gadget, struct net2280, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	tmp = readl(&dev->usb->usbctl);
+	if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE))
+		writel(BIT(GENERATE_RESUME), &dev->usb->usbstat);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* pci writes may still be posted */
+	return 0;
+}
+
+static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+	struct net2280		*dev;
+	u32			tmp;
+	unsigned long		flags;
+
+	if (!_gadget)
+		return 0;
+	dev = container_of(_gadget, struct net2280, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	tmp = readl(&dev->usb->usbctl);
+	if (value) {
+		tmp |= BIT(SELF_POWERED_STATUS);
+		_gadget->is_selfpowered = 1;
+	} else {
+		tmp &= ~BIT(SELF_POWERED_STATUS);
+		_gadget->is_selfpowered = 0;
+	}
+	writel(tmp, &dev->usb->usbctl);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	struct net2280  *dev;
+	u32             tmp;
+	unsigned long   flags;
+
+	if (!_gadget)
+		return -ENODEV;
+	dev = container_of(_gadget, struct net2280, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	tmp = readl(&dev->usb->usbctl);
+	dev->softconnect = (is_on != 0);
+	if (is_on) {
+		ep0_start(dev);
+		writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
+	} else {
+		writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
+		stop_activity(dev, NULL);
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (!is_on && dev->driver)
+		dev->driver->disconnect(&dev->gadget);
+
+	return 0;
+}
+
+static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
+		struct usb_endpoint_descriptor *desc,
+		struct usb_ss_ep_comp_descriptor *ep_comp)
+{
+	char name[8];
+	struct usb_ep *ep;
+
+	if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT) {
+		/* ep-e, ep-f are PIO with only 64 byte fifos */
+		ep = gadget_find_ep_by_name(_gadget, "ep-e");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+		ep = gadget_find_ep_by_name(_gadget, "ep-f");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+	}
+
+	/* USB3380: Only first four endpoints have DMA channels. Allocate
+	 * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc
+	 * endpoints use DMA hw endpoints.
+	 */
+	if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+	    usb_endpoint_dir_in(desc)) {
+		ep = gadget_find_ep_by_name(_gadget, "ep2in");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+		ep = gadget_find_ep_by_name(_gadget, "ep4in");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+	} else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+		   !usb_endpoint_dir_in(desc)) {
+		ep = gadget_find_ep_by_name(_gadget, "ep1out");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+		ep = gadget_find_ep_by_name(_gadget, "ep3out");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+	} else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+		   usb_endpoint_dir_in(desc)) {
+		ep = gadget_find_ep_by_name(_gadget, "ep1in");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+		ep = gadget_find_ep_by_name(_gadget, "ep3in");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+	} else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+		   !usb_endpoint_dir_in(desc)) {
+		ep = gadget_find_ep_by_name(_gadget, "ep2out");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+		ep = gadget_find_ep_by_name(_gadget, "ep4out");
+		if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+			return ep;
+	}
+
+	/* USB3380: use same address for usb and hardware endpoints */
+	snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
+			usb_endpoint_dir_in(desc) ? "in" : "out");
+	ep = gadget_find_ep_by_name(_gadget, name);
+	if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+		return ep;
+
+	return NULL;
+}
+
+static int net2280_start(struct usb_gadget *_gadget,
+		struct usb_gadget_driver *driver);
+static int net2280_stop(struct usb_gadget *_gadget);
+
+static const struct usb_gadget_ops net2280_ops = {
+	.get_frame	= net2280_get_frame,
+	.wakeup		= net2280_wakeup,
+	.set_selfpowered = net2280_set_selfpowered,
+	.pullup		= net2280_pullup,
+	.udc_start	= net2280_start,
+	.udc_stop	= net2280_stop,
+	.match_ep	= net2280_match_ep,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
+
+/* "function" sysfs attribute */
+static ssize_t function_show(struct device *_dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct net2280	*dev = dev_get_drvdata(_dev);
+
+	if (!dev->driver || !dev->driver->function ||
+			strlen(dev->driver->function) > PAGE_SIZE)
+		return 0;
+	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR_RO(function);
+
+static ssize_t registers_show(struct device *_dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size, t;
+	unsigned long		flags;
+	int			i;
+	u32			t1, t2;
+	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 " DRIVER_VERSION
+			", chiprev %04x\n\n"
+			"devinit %03x fifoctl %08x gadget '%s'\n"
+			"pci irqenb0 %02x irqenb1 %08x "
+			"irqstat0 %04x irqstat1 %08x\n",
+			driver_name, dev->chiprev,
+			readl(&dev->regs->devinit),
+			readl(&dev->regs->fifoctl),
+			s,
+			readl(&dev->regs->pciirqenb0),
+			readl(&dev->regs->pciirqenb1),
+			readl(&dev->regs->irqstat0),
+			readl(&dev->regs->irqstat1));
+	size -= t;
+	next += t;
+
+	/* USB Control Registers */
+	t1 = readl(&dev->usb->usbctl);
+	t2 = readl(&dev->usb->usbstat);
+	if (t1 & BIT(VBUS_PIN)) {
+		if (t2 & BIT(HIGH_SPEED))
+			s = "high speed";
+		else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+			s = "powered";
+		else
+			s = "full speed";
+		/* full speed bit (6) not working?? */
+	} else
+			s = "not attached";
+	t = scnprintf(next, size,
+			"stdrsp %08x usbctl %08x usbstat %08x "
+				"addr 0x%02x (%s)\n",
+			readl(&dev->usb->stdrsp), t1, t2,
+			readl(&dev->usb->ouraddr), s);
+	size -= t;
+	next += t;
+
+	/* PCI Master Control Registers */
+
+	/* DMA Control Registers */
+
+	/* Configurable EP Control Registers */
+	for (i = 0; i < dev->n_ep; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep[i];
+		if (i && !ep->desc)
+			continue;
+
+		t1 = readl(&ep->cfg->ep_cfg);
+		t2 = readl(&ep->regs->ep_rsp) & 0xff;
+		t = scnprintf(next, size,
+				"\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
+					"irqenb %02x\n",
+				ep->ep.name, t1, t2,
+				(t2 & BIT(CLEAR_NAK_OUT_PACKETS))
+					? "NAK " : "",
+				(t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE))
+					? "hide " : "",
+				(t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR))
+					? "CRC " : "",
+				(t2 & BIT(CLEAR_INTERRUPT_MODE))
+					? "interrupt " : "",
+				(t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
+					? "status " : "",
+				(t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE))
+					? "NAKmode " : "",
+				(t2 & BIT(CLEAR_ENDPOINT_TOGGLE))
+					? "DATA1 " : "DATA0 ",
+				(t2 & BIT(CLEAR_ENDPOINT_HALT))
+					? "HALT " : "",
+				readl(&ep->regs->ep_irqenb));
+		size -= t;
+		next += t;
+
+		t = scnprintf(next, size,
+				"\tstat %08x avail %04x "
+				"(ep%d%s-%s)%s\n",
+				readl(&ep->regs->ep_stat),
+				readl(&ep->regs->ep_avail),
+				t1 & 0x0f, DIR_STRING(t1),
+				type_string(t1 >> 8),
+				ep->stopped ? "*" : "");
+		size -= t;
+		next += t;
+
+		if (!ep->dma)
+			continue;
+
+		t = scnprintf(next, size,
+				"  dma\tctl %08x stat %08x count %08x\n"
+				"\taddr %08x desc %08x\n",
+				readl(&ep->dma->dmactl),
+				readl(&ep->dma->dmastat),
+				readl(&ep->dma->dmacount),
+				readl(&ep->dma->dmaaddr),
+				readl(&ep->dma->dmadesc));
+		size -= t;
+		next += t;
+
+	}
+
+	/* Indexed Registers (none yet) */
+
+	/* Statistics */
+	t = scnprintf(next, size, "\nirqs:  ");
+	size -= t;
+	next += t;
+	for (i = 0; i < dev->n_ep; i++) {
+		struct net2280_ep	*ep;
+
+		ep = &dev->ep[i];
+		if (i && !ep->irqs)
+			continue;
+		t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs);
+		size -= t;
+		next += t;
+
+	}
+	t = scnprintf(next, size, "\n");
+	size -= t;
+	next += t;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(registers);
+
+static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct net2280		*dev;
+	char			*next;
+	unsigned		size;
+	unsigned long		flags;
+	int			i;
+
+	dev = dev_get_drvdata(_dev);
+	next = buf;
+	size = PAGE_SIZE;
+	spin_lock_irqsave(&dev->lock, flags);
+
+	for (i = 0; i < dev->n_ep; i++) {
+		struct net2280_ep		*ep = &dev->ep[i];
+		struct net2280_request		*req;
+		int				t;
+
+		if (i != 0) {
+			const struct usb_endpoint_descriptor	*d;
+
+			d = ep->desc;
+			if (!d)
+				continue;
+			t = d->bEndpointAddress;
+			t = scnprintf(next, size,
+				"\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
+				ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+				(t & USB_DIR_IN) ? "in" : "out",
+				type_string(d->bmAttributes),
+				usb_endpoint_maxp(d),
+				ep->dma ? "dma" : "pio", ep->fifo_size
+				);
+		} else /* ep0 should only have one transfer queued */
+			t = scnprintf(next, size, "ep0 max 64 pio %s\n",
+					ep->is_in ? "in" : "out");
+		if (t <= 0 || t > size)
+			goto done;
+		size -= t;
+		next += t;
+
+		if (list_empty(&ep->queue)) {
+			t = scnprintf(next, size, "\t(nothing queued)\n");
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+			continue;
+		}
+		list_for_each_entry(req, &ep->queue, queue) {
+			if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc))
+				t = scnprintf(next, size,
+					"\treq %p len %d/%d "
+					"buf %p (dmacount %08x)\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf,
+					readl(&ep->dma->dmacount));
+			else
+				t = scnprintf(next, size,
+					"\treq %p len %d/%d buf %p\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf);
+			if (t <= 0 || t > size)
+				goto done;
+			size -= t;
+			next += t;
+
+			if (ep->dma) {
+				struct net2280_dma	*td;
+
+				td = req->td;
+				t = scnprintf(next, size, "\t    td %08x "
+					" count %08x buf %08x desc %08x\n",
+					(u32) req->td_dma,
+					le32_to_cpu(td->dmacount),
+					le32_to_cpu(td->dmaaddr),
+					le32_to_cpu(td->dmadesc));
+				if (t <= 0 || t > size)
+					goto done;
+				size -= t;
+				next += t;
+			}
+		}
+	}
+
+done:
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(queues);
+
+
+#else
+
+#define device_create_file(a, b)	(0)
+#define device_remove_file(a, b)	do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* another driver-specific mode might be a request type doing dma
+ * to/from another device fifo instead of to/from memory.
+ */
+
+static void set_fifo_mode(struct net2280 *dev, int mode)
+{
+	/* keeping high bits preserves BAR2 */
+	writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
+
+	/* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+	switch (mode) {
+	case 0:
+		list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+		list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024;
+		break;
+	case 1:
+		dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048;
+		break;
+	case 2:
+		list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+		dev->ep[1].fifo_size = 2048;
+		dev->ep[2].fifo_size = 1024;
+		break;
+	}
+	/* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
+	list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list);
+	list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list);
+}
+
+static void defect7374_disable_data_eps(struct net2280 *dev)
+{
+	/*
+	 * For Defect 7374, disable data EPs (and more):
+	 *  - This phase undoes the earlier phase of the Defect 7374 workaround,
+	 *    returing ep regs back to normal.
+	 */
+	struct net2280_ep *ep;
+	int i;
+	unsigned char ep_sel;
+	u32 tmp_reg;
+
+	for (i = 1; i < 5; i++) {
+		ep = &dev->ep[i];
+		writel(i, &ep->cfg->ep_cfg);
+	}
+
+	/* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */
+	for (i = 0; i < 6; i++)
+		writel(0, &dev->dep[i].dep_cfg);
+
+	for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+		/* Select an endpoint for subsequent operations: */
+		tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+		writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+		if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) ||
+					ep_sel == 18 || ep_sel == 20)
+			continue;
+
+		/* Change settings on some selected endpoints */
+		tmp_reg = readl(&dev->plregs->pl_ep_cfg_4);
+		tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR);
+		writel(tmp_reg, &dev->plregs->pl_ep_cfg_4);
+		tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+		tmp_reg |= BIT(EP_INITIALIZED);
+		writel(tmp_reg, &dev->plregs->pl_ep_ctrl);
+	}
+}
+
+static void defect7374_enable_data_eps_zero(struct net2280 *dev)
+{
+	u32 tmp = 0, tmp_reg;
+	u32 scratch;
+	int i;
+	unsigned char ep_sel;
+
+	scratch = get_idx_reg(dev->regs, SCRATCH);
+
+	WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD))
+		== DEFECT7374_FSM_SS_CONTROL_READ);
+
+	scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+	ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+	ep_warn(dev, "It will operate on cold-reboot and SS connect");
+
+	/*GPEPs:*/
+	tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+			(2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+			((dev->enhanced_mode) ?
+			 BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) :
+			 BIT(ENDPOINT_ENABLE)));
+
+	for (i = 1; i < 5; i++)
+		writel(tmp, &dev->ep[i].cfg->ep_cfg);
+
+	/* CSRIN, PCIIN, STATIN, RCIN*/
+	tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+	writel(tmp, &dev->dep[1].dep_cfg);
+	writel(tmp, &dev->dep[3].dep_cfg);
+	writel(tmp, &dev->dep[4].dep_cfg);
+	writel(tmp, &dev->dep[5].dep_cfg);
+
+	/*Implemented for development and debug.
+	 * Can be refined/tuned later.*/
+	for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+		/* Select an endpoint for subsequent operations: */
+		tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+		writel(((tmp_reg & ~0x1f) | ep_sel),
+				&dev->plregs->pl_ep_ctrl);
+
+		if (ep_sel == 1) {
+			tmp =
+				(readl(&dev->plregs->pl_ep_ctrl) |
+				 BIT(CLEAR_ACK_ERROR_CODE) | 0);
+			writel(tmp, &dev->plregs->pl_ep_ctrl);
+			continue;
+		}
+
+		if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+				ep_sel == 18  || ep_sel == 20)
+			continue;
+
+		tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+				BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+		writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+		tmp = readl(&dev->plregs->pl_ep_ctrl) &
+			~BIT(EP_INITIALIZED);
+		writel(tmp, &dev->plregs->pl_ep_ctrl);
+
+	}
+
+	/* Set FSM to focus on the first Control Read:
+	 * - Tip: Connection speed is known upon the first
+	 * setup request.*/
+	scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+	set_idx_reg(dev->regs, SCRATCH, scratch);
+
+}
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple net2280 controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static void usb_reset_228x(struct net2280 *dev)
+{
+	u32	tmp;
+
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	(void) readl(&dev->usb->usbctl);
+
+	net2280_led_init(dev);
+
+	/* disable automatic responses, and irqs */
+	writel(0, &dev->usb->stdrsp);
+	writel(0, &dev->regs->pciirqenb0);
+	writel(0, &dev->regs->pciirqenb1);
+
+	/* clear old dma and irq state */
+	for (tmp = 0; tmp < 4; tmp++) {
+		struct net2280_ep       *ep = &dev->ep[tmp + 1];
+		if (ep->dma)
+			abort_dma(ep);
+	}
+
+	writel(~0, &dev->regs->irqstat0),
+	writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
+
+	/* reset, and enable pci */
+	tmp = readl(&dev->regs->devinit) |
+		BIT(PCI_ENABLE) |
+		BIT(FIFO_SOFT_RESET) |
+		BIT(USB_SOFT_RESET) |
+		BIT(M8051_RESET);
+	writel(tmp, &dev->regs->devinit);
+
+	/* standard fifo and endpoint allocations */
+	set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0);
+}
+
+static void usb_reset_338x(struct net2280 *dev)
+{
+	u32 tmp;
+
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	(void)readl(&dev->usb->usbctl);
+
+	net2280_led_init(dev);
+
+	if (dev->bug7734_patched) {
+		/* disable automatic responses, and irqs */
+		writel(0, &dev->usb->stdrsp);
+		writel(0, &dev->regs->pciirqenb0);
+		writel(0, &dev->regs->pciirqenb1);
+	}
+
+	/* clear old dma and irq state */
+	for (tmp = 0; tmp < 4; tmp++) {
+		struct net2280_ep *ep = &dev->ep[tmp + 1];
+		struct net2280_dma_regs __iomem *dma;
+
+		if (ep->dma) {
+			abort_dma(ep);
+		} else {
+			dma = &dev->dma[tmp];
+			writel(BIT(DMA_ABORT), &dma->dmastat);
+			writel(0, &dma->dmactl);
+		}
+	}
+
+	writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
+
+	if (dev->bug7734_patched) {
+		/* reset, and enable pci */
+		tmp = readl(&dev->regs->devinit) |
+		    BIT(PCI_ENABLE) |
+		    BIT(FIFO_SOFT_RESET) |
+		    BIT(USB_SOFT_RESET) |
+		    BIT(M8051_RESET);
+
+		writel(tmp, &dev->regs->devinit);
+	}
+
+	/* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+	for (tmp = 1; tmp < dev->n_ep; tmp++)
+		list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list);
+
+}
+
+static void usb_reset(struct net2280 *dev)
+{
+	if (dev->quirks & PLX_LEGACY)
+		return usb_reset_228x(dev);
+	return usb_reset_338x(dev);
+}
+
+static void usb_reinit_228x(struct net2280 *dev)
+{
+	u32	tmp;
+
+	/* basic endpoint init */
+	for (tmp = 0; tmp < 7; tmp++) {
+		struct net2280_ep	*ep = &dev->ep[tmp];
+
+		ep->ep.name = ep_info_dft[tmp].name;
+		ep->ep.caps = ep_info_dft[tmp].caps;
+		ep->dev = dev;
+		ep->num = tmp;
+
+		if (tmp > 0 && tmp <= 4) {
+			ep->fifo_size = 1024;
+			ep->dma = &dev->dma[tmp - 1];
+		} else
+			ep->fifo_size = 64;
+		ep->regs = &dev->epregs[tmp];
+		ep->cfg = &dev->epregs[tmp];
+		ep_reset_228x(dev->regs, ep);
+	}
+	usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
+	usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64);
+	usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64);
+
+	dev->gadget.ep0 = &dev->ep[0].ep;
+	dev->ep[0].stopped = 0;
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+	/* we want to prevent lowlevel/insecure access from the USB host,
+	 * but erratum 0119 means this enable bit is ignored
+	 */
+	for (tmp = 0; tmp < 5; tmp++)
+		writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg);
+}
+
+static void usb_reinit_338x(struct net2280 *dev)
+{
+	int i;
+	u32 tmp, val;
+	static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
+	static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
+						0x00, 0xC0, 0x00, 0xC0 };
+
+	/* basic endpoint init */
+	for (i = 0; i < dev->n_ep; i++) {
+		struct net2280_ep *ep = &dev->ep[i];
+
+		ep->ep.name = dev->enhanced_mode ? ep_info_adv[i].name :
+						   ep_info_dft[i].name;
+		ep->ep.caps = dev->enhanced_mode ? ep_info_adv[i].caps :
+						   ep_info_dft[i].caps;
+		ep->dev = dev;
+		ep->num = i;
+
+		if (i > 0 && i <= 4)
+			ep->dma = &dev->dma[i - 1];
+
+		if (dev->enhanced_mode) {
+			ep->cfg = &dev->epregs[ne[i]];
+			/*
+			 * Set USB endpoint number, hardware allows same number
+			 * in both directions.
+			 */
+			 if (i > 0 && i < 5)
+				writel(ne[i], &ep->cfg->ep_cfg);
+			ep->regs = (struct net2280_ep_regs __iomem *)
+				(((void __iomem *)&dev->epregs[ne[i]]) +
+				ep_reg_addr[i]);
+		} else {
+			ep->cfg = &dev->epregs[i];
+			ep->regs = &dev->epregs[i];
+		}
+
+		ep->fifo_size = (i != 0) ? 2048 : 512;
+
+		ep_reset_338x(dev->regs, ep);
+	}
+	usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512);
+
+	dev->gadget.ep0 = &dev->ep[0].ep;
+	dev->ep[0].stopped = 0;
+
+	/* Link layer set up */
+	if (dev->bug7734_patched) {
+		tmp = readl(&dev->usb_ext->usbctl2) &
+		    ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
+		writel(tmp, &dev->usb_ext->usbctl2);
+	}
+
+	/* Hardware Defect and Workaround */
+	val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+	val &= ~(0xf << TIMER_LFPS_6US);
+	val |= 0x5 << TIMER_LFPS_6US;
+	writel(val, &dev->ll_lfps_regs->ll_lfps_5);
+
+	val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+	val &= ~(0xffff << TIMER_LFPS_80US);
+	val |= 0x0100 << TIMER_LFPS_80US;
+	writel(val, &dev->ll_lfps_regs->ll_lfps_6);
+
+	/*
+	 * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
+	 * Hot Reset Exit Handshake may Fail in Specific Case using
+	 * Default Register Settings. Workaround for Enumeration test.
+	 */
+	val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
+	val &= ~(0x1f << HOT_TX_NORESET_TS2);
+	val |= 0x10 << HOT_TX_NORESET_TS2;
+	writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+
+	val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
+	val &= ~(0x1f << HOT_RX_RESET_TS2);
+	val |= 0x3 << HOT_RX_RESET_TS2;
+	writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
+
+	/*
+	 * Set Recovery Idle to Recover bit:
+	 * - On SS connections, setting Recovery Idle to Recover Fmw improves
+	 *   link robustness with various hosts and hubs.
+	 * - It is safe to set for all connection speeds; all chip revisions.
+	 * - R-M-W to leave other bits undisturbed.
+	 * - Reference PLX TT-7372
+	*/
+	val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
+	val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
+	writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+	/* disable dedicated endpoints */
+	writel(0x0D, &dev->dep[0].dep_cfg);
+	writel(0x0D, &dev->dep[1].dep_cfg);
+	writel(0x0E, &dev->dep[2].dep_cfg);
+	writel(0x0E, &dev->dep[3].dep_cfg);
+	writel(0x0F, &dev->dep[4].dep_cfg);
+	writel(0x0C, &dev->dep[5].dep_cfg);
+}
+
+static void usb_reinit(struct net2280 *dev)
+{
+	if (dev->quirks & PLX_LEGACY)
+		return usb_reinit_228x(dev);
+	return usb_reinit_338x(dev);
+}
+
+static void ep0_start_228x(struct net2280 *dev)
+{
+	writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+		BIT(CLEAR_NAK_OUT_PACKETS) |
+		BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+		&dev->epregs[0].ep_rsp);
+
+	/*
+	 * hardware optionally handles a bunch of standard requests
+	 * that the API hides from drivers anyway.  have it do so.
+	 * endpoint status/features are handled in software, to
+	 * help pass tests for some dubious behavior.
+	 */
+	writel(BIT(SET_TEST_MODE) |
+		BIT(SET_ADDRESS) |
+		BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) |
+		BIT(GET_DEVICE_STATUS) |
+		BIT(GET_INTERFACE_STATUS),
+		&dev->usb->stdrsp);
+	writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+		BIT(SELF_POWERED_USB_DEVICE) |
+		BIT(REMOTE_WAKEUP_SUPPORT) |
+		(dev->softconnect << USB_DETECT_ENABLE) |
+		BIT(SELF_POWERED_STATUS),
+		&dev->usb->usbctl);
+
+	/* enable irqs so we can see ep0 and general operation  */
+	writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+		BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+		&dev->regs->pciirqenb0);
+	writel(BIT(PCI_INTERRUPT_ENABLE) |
+		BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+		BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+		BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) |
+		BIT(VBUS_INTERRUPT_ENABLE) |
+		BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+		BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE),
+		&dev->regs->pciirqenb1);
+
+	/* don't leave any writes posted */
+	(void) readl(&dev->usb->usbctl);
+}
+
+static void ep0_start_338x(struct net2280 *dev)
+{
+
+	if (dev->bug7734_patched)
+		writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+		       BIT(SET_EP_HIDE_STATUS_PHASE),
+		       &dev->epregs[0].ep_rsp);
+
+	/*
+	 * hardware optionally handles a bunch of standard requests
+	 * that the API hides from drivers anyway.  have it do so.
+	 * endpoint status/features are handled in software, to
+	 * help pass tests for some dubious behavior.
+	 */
+	writel(BIT(SET_ISOCHRONOUS_DELAY) |
+	       BIT(SET_SEL) |
+	       BIT(SET_TEST_MODE) |
+	       BIT(SET_ADDRESS) |
+	       BIT(GET_INTERFACE_STATUS) |
+	       BIT(GET_DEVICE_STATUS),
+		&dev->usb->stdrsp);
+	dev->wakeup_enable = 1;
+	writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+	       (dev->softconnect << USB_DETECT_ENABLE) |
+	       BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+	       &dev->usb->usbctl);
+
+	/* enable irqs so we can see ep0 and general operation  */
+	writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+	       BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+	       &dev->regs->pciirqenb0);
+	writel(BIT(PCI_INTERRUPT_ENABLE) |
+	       BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+	       BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) |
+	       BIT(VBUS_INTERRUPT_ENABLE),
+	       &dev->regs->pciirqenb1);
+
+	/* don't leave any writes posted */
+	(void)readl(&dev->usb->usbctl);
+}
+
+static void ep0_start(struct net2280 *dev)
+{
+	if (dev->quirks & PLX_LEGACY)
+		return ep0_start_228x(dev);
+	return ep0_start_338x(dev);
+}
+
+/* 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 net2280_start(struct usb_gadget *_gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct net2280		*dev;
+	int			retval;
+	unsigned		i;
+
+	/* insist on high speed support from the driver, since
+	 * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+	 * "must not be used in normal operation"
+	 */
+	if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+			!driver->setup)
+		return -EINVAL;
+
+	dev = container_of(_gadget, struct net2280, gadget);
+
+	for (i = 0; i < dev->n_ep; i++)
+		dev->ep[i].irqs = 0;
+
+	/* hook up the driver ... */
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+
+	retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
+	if (retval)
+		goto err_unbind;
+	retval = device_create_file(&dev->pdev->dev, &dev_attr_queues);
+	if (retval)
+		goto err_func;
+
+	/* enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	net2280_led_active(dev, 1);
+
+	if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched)
+		defect7374_enable_data_eps_zero(dev);
+
+	ep0_start(dev);
+
+	/* pci writes may still be posted */
+	return 0;
+
+err_func:
+	device_remove_file(&dev->pdev->dev, &dev_attr_function);
+err_unbind:
+	dev->driver = NULL;
+	return retval;
+}
+
+static void stop_activity(struct net2280 *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.
+	 */
+	usb_reset(dev);
+	for (i = 0; i < dev->n_ep; i++)
+		nuke(&dev->ep[i]);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&dev->lock);
+		driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+	}
+
+	usb_reinit(dev);
+}
+
+static int net2280_stop(struct usb_gadget *_gadget)
+{
+	struct net2280	*dev;
+	unsigned long	flags;
+
+	dev = container_of(_gadget, struct net2280, gadget);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	stop_activity(dev, NULL);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	net2280_led_active(dev, 0);
+
+	device_remove_file(&dev->pdev->dev, &dev_attr_function);
+	device_remove_file(&dev->pdev->dev, &dev_attr_queues);
+
+	dev->driver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void handle_ep_small(struct net2280_ep *ep)
+{
+	struct net2280_request	*req;
+	u32			t;
+	/* 0 error, 1 mid-data, 2 done */
+	int			mode = 1;
+
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next,
+			struct net2280_request, queue);
+	else
+		req = NULL;
+
+	/* ack all, and handle what we care about */
+	t = readl(&ep->regs->ep_stat);
+	ep->irqs++;
+
+	ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
+			ep->ep.name, t, req ? &req->req : NULL);
+
+	if (!ep->is_in || (ep->dev->quirks & PLX_2280))
+		writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
+	else
+		/* Added for 2282 */
+		writel(t, &ep->regs->ep_stat);
+
+	/* for ep0, monitor token irqs to catch data stage length errors
+	 * and to synchronize on status.
+	 *
+	 * also, to defer reporting of protocol stalls ... here's where
+	 * data or status first appears, handling stalls here should never
+	 * cause trouble on the host side..
+	 *
+	 * control requests could be slightly faster without token synch for
+	 * status, but status can jam up that way.
+	 */
+	if (unlikely(ep->num == 0)) {
+		if (ep->is_in) {
+			/* status; stop NAKing */
+			if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt(ep);
+				}
+				if (!req)
+					allow_status(ep);
+				mode = 2;
+			/* reply to extra IN data tokens with a zlp */
+			} else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt(ep);
+					mode = 2;
+				} else if (ep->responded &&
+						!req && !ep->stopped)
+					write_fifo(ep, NULL);
+			}
+		} else {
+			/* status; stop NAKing */
+			if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+				if (ep->dev->protocol_stall) {
+					ep->stopped = 1;
+					set_halt(ep);
+				}
+				mode = 2;
+			/* an extra OUT token is an error */
+			} else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) &&
+					req &&
+					req->req.actual == req->req.length) ||
+					(ep->responded && !req)) {
+				ep->dev->protocol_stall = 1;
+				set_halt(ep);
+				ep->stopped = 1;
+				if (req)
+					done(ep, req, -EOVERFLOW);
+				req = NULL;
+			}
+		}
+	}
+
+	if (unlikely(!req))
+		return;
+
+	/* manual DMA queue advance after short OUT */
+	if (likely(ep->dma)) {
+		if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+			struct net2280_request *stuck_req = NULL;
+			int	stopped = ep->stopped;
+			int	num_completed;
+			int	stuck = 0;
+			u32	count;
+
+			/* TRANSFERRED works around OUT_DONE erratum 0112.
+			 * we expect (N <= maxpacket) bytes; host wrote M.
+			 * iff (M < N) we won't ever see a DMA interrupt.
+			 */
+			ep->stopped = 1;
+			for (count = 0; ; t = readl(&ep->regs->ep_stat)) {
+
+				/* any preceding dma transfers must finish.
+				 * dma handles (M >= N), may empty the queue
+				 */
+				num_completed = scan_dma_completions(ep);
+				if (unlikely(list_empty(&ep->queue) ||
+						ep->out_overflow)) {
+					req = NULL;
+					break;
+				}
+				req = list_entry(ep->queue.next,
+					struct net2280_request, queue);
+
+				/* here either (M < N), a "real" short rx;
+				 * or (M == N) and the queue didn't empty
+				 */
+				if (likely(t & BIT(FIFO_EMPTY))) {
+					count = readl(&ep->dma->dmacount);
+					count &= DMA_BYTE_COUNT_MASK;
+					if (readl(&ep->dma->dmadesc)
+							!= req->td_dma)
+						req = NULL;
+					break;
+				}
+
+				/* Escape loop if no dma transfers completed
+				 * after few retries.
+				 */
+				if (num_completed == 0) {
+					if (stuck_req == req &&
+					    readl(&ep->dma->dmadesc) !=
+						  req->td_dma && stuck++ > 5) {
+						count = readl(
+							&ep->dma->dmacount);
+						count &= DMA_BYTE_COUNT_MASK;
+						req = NULL;
+						ep_dbg(ep->dev, "%s escape stuck %d, count %u\n",
+							ep->ep.name, stuck,
+							count);
+						break;
+					} else if (stuck_req != req) {
+						stuck_req = req;
+						stuck = 0;
+					}
+				} else {
+					stuck_req = NULL;
+					stuck = 0;
+				}
+
+				udelay(1);
+			}
+
+			/* stop DMA, leave ep NAKing */
+			writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+			spin_stop_dma(ep->dma);
+
+			if (likely(req)) {
+				req->td->dmacount = 0;
+				t = readl(&ep->regs->ep_avail);
+				dma_done(ep, req, count,
+					(ep->out_overflow || t)
+						? -EOVERFLOW : 0);
+			}
+
+			/* also flush to prevent erratum 0106 trouble */
+			if (unlikely(ep->out_overflow ||
+					(ep->dev->chiprev == 0x0100 &&
+					ep->dev->gadget.speed
+					== USB_SPEED_FULL))) {
+				out_flush(ep);
+				ep->out_overflow = 0;
+			}
+
+			/* (re)start dma if needed, stop NAKing */
+			ep->stopped = stopped;
+			if (!list_empty(&ep->queue))
+				restart_dma(ep);
+		} else
+			ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n",
+					ep->ep.name, t);
+		return;
+
+	/* data packet(s) received (in the fifo, OUT) */
+	} else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) {
+		if (read_fifo(ep, req) && ep->num != 0)
+			mode = 2;
+
+	/* data packet(s) transmitted (IN) */
+	} else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) {
+		unsigned	len;
+
+		len = req->req.length - req->req.actual;
+		if (len > ep->ep.maxpacket)
+			len = ep->ep.maxpacket;
+		req->req.actual += len;
+
+		/* if we wrote it all, we're usually done */
+		/* send zlps until the status stage */
+		if ((req->req.actual == req->req.length) &&
+			(!req->req.zero || len != ep->ep.maxpacket) && ep->num)
+				mode = 2;
+
+	/* there was nothing to do ...  */
+	} else if (mode == 1)
+		return;
+
+	/* done */
+	if (mode == 2) {
+		/* stream endpoints often resubmit/unlink in completion */
+		done(ep, req, 0);
+
+		/* maybe advance queue to next request */
+		if (ep->num == 0) {
+			/* NOTE:  net2280 could let gadget driver start the
+			 * status stage later. since not all controllers let
+			 * them control that, the api doesn't (yet) allow it.
+			 */
+			if (!ep->stopped)
+				allow_status(ep);
+			req = NULL;
+		} else {
+			if (!list_empty(&ep->queue) && !ep->stopped)
+				req = list_entry(ep->queue.next,
+					struct net2280_request, queue);
+			else
+				req = NULL;
+			if (req && !ep->is_in)
+				stop_out_naking(ep);
+		}
+	}
+
+	/* is there a buffer for the next packet?
+	 * for best streaming performance, make sure there is one.
+	 */
+	if (req && !ep->stopped) {
+
+		/* load IN fifo with next packet (may be zlp) */
+		if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT))
+			write_fifo(ep, &req->req);
+	}
+}
+
+static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex)
+{
+	struct net2280_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;
+}
+
+static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
+{
+	u32 scratch, fsmvalue;
+	u32 ack_wait_timeout, state;
+
+	/* Workaround for Defect 7374 (U1/U2 erroneously rejected): */
+	scratch = get_idx_reg(dev->regs, SCRATCH);
+	fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+	scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+	if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) &&
+				(r.bRequestType & USB_DIR_IN)))
+		return;
+
+	/* This is the first Control Read for this connection: */
+	if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) {
+		/*
+		 * Connection is NOT SS:
+		 * - Connection must be FS or HS.
+		 * - This FSM state should allow workaround software to
+		 * run after the next USB connection.
+		 */
+		scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+		dev->bug7734_patched = 1;
+		goto restore_data_eps;
+	}
+
+	/* Connection is SS: */
+	for (ack_wait_timeout = 0;
+			ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS;
+			ack_wait_timeout++) {
+
+		state =	readl(&dev->plregs->pl_ep_status_1)
+			& (0xff << STATE);
+		if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
+			(state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
+			scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+			dev->bug7734_patched = 1;
+			break;
+		}
+
+		/*
+		 * We have not yet received host's Data Phase ACK
+		 * - Wait and try again.
+		 */
+		udelay(DEFECT_7374_PROCESSOR_WAIT_TIME);
+
+		continue;
+	}
+
+
+	if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) {
+		ep_err(dev, "FAIL: Defect 7374 workaround waited but failed "
+		"to detect SS host's data phase ACK.");
+		ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16"
+		"got 0x%2.2x.\n", state >> STATE);
+	} else {
+		ep_warn(dev, "INFO: Defect 7374 workaround waited about\n"
+		"%duSec for Control Read Data Phase ACK\n",
+			DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout);
+	}
+
+restore_data_eps:
+	/*
+	 * Restore data EPs to their pre-workaround settings (disabled,
+	 * initialized, and other details).
+	 */
+	defect7374_disable_data_eps(dev);
+
+	set_idx_reg(dev->regs, SCRATCH, scratch);
+
+	return;
+}
+
+static void ep_clear_seqnum(struct net2280_ep *ep)
+{
+	struct net2280 *dev = ep->dev;
+	u32 val;
+	static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
+
+	val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f;
+	val |= ep_pl[ep->num];
+	writel(val, &dev->plregs->pl_ep_ctrl);
+	val |= BIT(SEQUENCE_NUMBER_RESET);
+	writel(val, &dev->plregs->pl_ep_ctrl);
+
+	return;
+}
+
+static void handle_stat0_irqs_superspeed(struct net2280 *dev,
+		struct net2280_ep *ep, struct usb_ctrlrequest r)
+{
+	int tmp = 0;
+
+#define	w_value		le16_to_cpu(r.wValue)
+#define	w_index		le16_to_cpu(r.wIndex)
+#define	w_length	le16_to_cpu(r.wLength)
+
+	switch (r.bRequest) {
+		struct net2280_ep *e;
+		u16 status;
+
+	case USB_REQ_SET_CONFIGURATION:
+		dev->addressed_state = !w_value;
+		goto usb3_delegate;
+
+	case USB_REQ_GET_STATUS:
+		switch (r.bRequestType) {
+		case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+			status = dev->wakeup_enable ? 0x02 : 0x00;
+			if (dev->gadget.is_selfpowered)
+				status |= BIT(0);
+			status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
+							dev->ltm_enable << 4);
+			writel(0, &dev->epregs[0].ep_irqenb);
+			set_fifo_bytecount(ep, sizeof(status));
+			writel((__force u32) status, &dev->epregs[0].ep_data);
+			allow_status_338x(ep);
+			break;
+
+		case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+			e = get_ep_by_addr(dev, w_index);
+			if (!e)
+				goto do_stall3;
+			status = readl(&e->regs->ep_rsp) &
+						BIT(CLEAR_ENDPOINT_HALT);
+			writel(0, &dev->epregs[0].ep_irqenb);
+			set_fifo_bytecount(ep, sizeof(status));
+			writel((__force u32) status, &dev->epregs[0].ep_data);
+			allow_status_338x(ep);
+			break;
+
+		default:
+			goto usb3_delegate;
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		switch (r.bRequestType) {
+		case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+			if (!dev->addressed_state) {
+				switch (w_value) {
+				case USB_DEVICE_U1_ENABLE:
+					dev->u1_enable = 0;
+					writel(readl(&dev->usb_ext->usbctl2) &
+						~BIT(U1_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+
+				case USB_DEVICE_U2_ENABLE:
+					dev->u2_enable = 0;
+					writel(readl(&dev->usb_ext->usbctl2) &
+						~BIT(U2_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+
+				case USB_DEVICE_LTM_ENABLE:
+					dev->ltm_enable = 0;
+					writel(readl(&dev->usb_ext->usbctl2) &
+						~BIT(LTM_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+
+				default:
+					break;
+				}
+			}
+			if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+				dev->wakeup_enable = 0;
+				writel(readl(&dev->usb->usbctl) &
+					~BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+					&dev->usb->usbctl);
+				allow_status_338x(ep);
+				break;
+			}
+			goto usb3_delegate;
+
+		case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+			e = get_ep_by_addr(dev,	w_index);
+			if (!e)
+				goto do_stall3;
+			if (w_value != USB_ENDPOINT_HALT)
+				goto do_stall3;
+			ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+			/*
+			 * Workaround for SS SeqNum not cleared via
+			 * Endpoint Halt (Clear) bit. select endpoint
+			 */
+			ep_clear_seqnum(e);
+			clear_halt(e);
+			if (!list_empty(&e->queue) && e->td_dma)
+				restart_dma(e);
+			allow_status(ep);
+			ep->stopped = 1;
+			break;
+
+		default:
+			goto usb3_delegate;
+		}
+		break;
+	case USB_REQ_SET_FEATURE:
+		switch (r.bRequestType) {
+		case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+			if (!dev->addressed_state) {
+				switch (w_value) {
+				case USB_DEVICE_U1_ENABLE:
+					dev->u1_enable = 1;
+					writel(readl(&dev->usb_ext->usbctl2) |
+						BIT(U1_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+
+				case USB_DEVICE_U2_ENABLE:
+					dev->u2_enable = 1;
+					writel(readl(&dev->usb_ext->usbctl2) |
+						BIT(U2_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+
+				case USB_DEVICE_LTM_ENABLE:
+					dev->ltm_enable = 1;
+					writel(readl(&dev->usb_ext->usbctl2) |
+						BIT(LTM_ENABLE),
+						&dev->usb_ext->usbctl2);
+					allow_status_338x(ep);
+					goto next_endpoints3;
+				default:
+					break;
+				}
+			}
+
+			if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+				dev->wakeup_enable = 1;
+				writel(readl(&dev->usb->usbctl) |
+					BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+					&dev->usb->usbctl);
+				allow_status_338x(ep);
+				break;
+			}
+			goto usb3_delegate;
+
+		case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+			e = get_ep_by_addr(dev,	w_index);
+			if (!e || (w_value != USB_ENDPOINT_HALT))
+				goto do_stall3;
+			ep->stopped = 1;
+			if (ep->num == 0)
+				ep->dev->protocol_stall = 1;
+			else {
+				if (ep->dma)
+					abort_dma(ep);
+				set_halt(ep);
+			}
+			allow_status_338x(ep);
+			break;
+
+		default:
+			goto usb3_delegate;
+		}
+
+		break;
+	default:
+
+usb3_delegate:
+		ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n",
+				r.bRequestType, r.bRequest,
+				w_value, w_index, w_length,
+				readl(&ep->cfg->ep_cfg));
+
+		ep->responded = 0;
+		spin_unlock(&dev->lock);
+		tmp = dev->driver->setup(&dev->gadget, &r);
+		spin_lock(&dev->lock);
+	}
+do_stall3:
+	if (tmp < 0) {
+		ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+				r.bRequestType, r.bRequest, tmp);
+		dev->protocol_stall = 1;
+		/* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
+		set_halt(ep);
+	}
+
+next_endpoints3:
+
+#undef	w_value
+#undef	w_index
+#undef	w_length
+
+	return;
+}
+
+static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0)
+{
+	u32 index;
+	u32 bit;
+
+	for (index = 0; index < ARRAY_SIZE(ep_bit); index++) {
+		bit = BIT(ep_bit[index]);
+
+		if (!stat0)
+			break;
+
+		if (!(stat0 & bit))
+			continue;
+
+		stat0 &= ~bit;
+
+		handle_ep_small(&dev->ep[index]);
+	}
+}
+
+static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
+{
+	struct net2280_ep	*ep;
+	u32			num, scratch;
+
+	/* most of these don't need individual acks */
+	stat &= ~BIT(INTA_ASSERTED);
+	if (!stat)
+		return;
+	/* ep_dbg(dev, "irqstat0 %04x\n", stat); */
+
+	/* starting a control request? */
+	if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) {
+		union {
+			u32			raw[2];
+			struct usb_ctrlrequest	r;
+		} u;
+		int				tmp;
+		struct net2280_request		*req;
+
+		if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+			u32 val = readl(&dev->usb->usbstat);
+			if (val & BIT(SUPER_SPEED)) {
+				dev->gadget.speed = USB_SPEED_SUPER;
+				usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+						EP0_SS_MAX_PACKET_SIZE);
+			} else if (val & BIT(HIGH_SPEED)) {
+				dev->gadget.speed = USB_SPEED_HIGH;
+				usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+						EP0_HS_MAX_PACKET_SIZE);
+			} else {
+				dev->gadget.speed = USB_SPEED_FULL;
+				usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+						EP0_HS_MAX_PACKET_SIZE);
+			}
+			net2280_led_speed(dev, dev->gadget.speed);
+			ep_dbg(dev, "%s\n",
+					usb_speed_string(dev->gadget.speed));
+		}
+
+		ep = &dev->ep[0];
+		ep->irqs++;
+
+		/* make sure any leftover request state is cleared */
+		stat &= ~BIT(ENDPOINT_0_INTERRUPT);
+		while (!list_empty(&ep->queue)) {
+			req = list_entry(ep->queue.next,
+					struct net2280_request, queue);
+			done(ep, req, (req->req.actual == req->req.length)
+						? 0 : -EPROTO);
+		}
+		ep->stopped = 0;
+		dev->protocol_stall = 0;
+		if (!(dev->quirks & PLX_PCIE)) {
+			if (ep->dev->quirks & PLX_2280)
+				tmp = BIT(FIFO_OVERFLOW) |
+				    BIT(FIFO_UNDERFLOW);
+			else
+				tmp = 0;
+
+			writel(tmp | BIT(TIMEOUT) |
+				   BIT(USB_STALL_SENT) |
+				   BIT(USB_IN_NAK_SENT) |
+				   BIT(USB_IN_ACK_RCVD) |
+				   BIT(USB_OUT_PING_NAK_SENT) |
+				   BIT(USB_OUT_ACK_SENT) |
+				   BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+				   BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+				   BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+				   BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+				   BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+				   BIT(DATA_IN_TOKEN_INTERRUPT),
+				   &ep->regs->ep_stat);
+		}
+		u.raw[0] = readl(&dev->usb->setup0123);
+		u.raw[1] = readl(&dev->usb->setup4567);
+
+		cpu_to_le32s(&u.raw[0]);
+		cpu_to_le32s(&u.raw[1]);
+
+		if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched)
+			defect7374_workaround(dev, u.r);
+
+		tmp = 0;
+
+#define	w_value		le16_to_cpu(u.r.wValue)
+#define	w_index		le16_to_cpu(u.r.wIndex)
+#define	w_length	le16_to_cpu(u.r.wLength)
+
+		/* ack the irq */
+		writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0);
+		stat ^= BIT(SETUP_PACKET_INTERRUPT);
+
+		/* watch control traffic at the token level, and force
+		 * synchronization before letting the status stage happen.
+		 * FIXME ignore tokens we'll NAK, until driver responds.
+		 * that'll mean a lot less irqs for some drivers.
+		 */
+		ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+		if (ep->is_in) {
+			scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+				BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+				BIT(DATA_IN_TOKEN_INTERRUPT);
+			stop_out_naking(ep);
+		} else
+			scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+				BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+				BIT(DATA_IN_TOKEN_INTERRUPT);
+		writel(scratch, &dev->epregs[0].ep_irqenb);
+
+		/* we made the hardware handle most lowlevel requests;
+		 * everything else goes uplevel to the gadget code.
+		 */
+		ep->responded = 1;
+
+		if (dev->gadget.speed == USB_SPEED_SUPER) {
+			handle_stat0_irqs_superspeed(dev, ep, u.r);
+			goto next_endpoints;
+		}
+
+		switch (u.r.bRequest) {
+		case USB_REQ_GET_STATUS: {
+			struct net2280_ep	*e;
+			__le32			status;
+
+			/* hw handles device and interface status */
+			if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+				goto delegate;
+			e = get_ep_by_addr(dev, w_index);
+			if (!e || w_length > 2)
+				goto do_stall;
+
+			if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT))
+				status = cpu_to_le32(1);
+			else
+				status = cpu_to_le32(0);
+
+			/* don't bother with a request object! */
+			writel(0, &dev->epregs[0].ep_irqenb);
+			set_fifo_bytecount(ep, w_length);
+			writel((__force u32)status, &dev->epregs[0].ep_data);
+			allow_status(ep);
+			ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status);
+			goto next_endpoints;
+			}
+			break;
+		case USB_REQ_CLEAR_FEATURE: {
+			struct net2280_ep	*e;
+
+			/* hw handles device features */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+				goto do_stall;
+			e = get_ep_by_addr(dev, w_index);
+			if (!e)
+				goto do_stall;
+			if (e->wedged) {
+				ep_vdbg(dev, "%s wedged, halt not cleared\n",
+						ep->ep.name);
+			} else {
+				ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+				clear_halt(e);
+				if ((ep->dev->quirks & PLX_PCIE) &&
+					!list_empty(&e->queue) && e->td_dma)
+						restart_dma(e);
+			}
+			allow_status(ep);
+			goto next_endpoints;
+			}
+			break;
+		case USB_REQ_SET_FEATURE: {
+			struct net2280_ep	*e;
+
+			/* hw handles device features */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+				goto do_stall;
+			e = get_ep_by_addr(dev, w_index);
+			if (!e)
+				goto do_stall;
+			if (e->ep.name == ep0name)
+				goto do_stall;
+			set_halt(e);
+			if ((dev->quirks & PLX_PCIE) && e->dma)
+				abort_dma(e);
+			allow_status(ep);
+			ep_vdbg(dev, "%s set halt\n", ep->ep.name);
+			goto next_endpoints;
+			}
+			break;
+		default:
+delegate:
+			ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x "
+				"ep_cfg %08x\n",
+				u.r.bRequestType, u.r.bRequest,
+				w_value, w_index, w_length,
+				readl(&ep->cfg->ep_cfg));
+			ep->responded = 0;
+			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:
+			ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+					u.r.bRequestType, u.r.bRequest, tmp);
+			dev->protocol_stall = 1;
+		}
+
+		/* some in/out token irq should follow; maybe stall then.
+		 * driver must queue a request (even zlp) or halt ep0
+		 * before the host times out.
+		 */
+	}
+
+#undef	w_value
+#undef	w_index
+#undef	w_length
+
+next_endpoints:
+	if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) {
+		u32 mask = (BIT(ENDPOINT_0_INTERRUPT) |
+			USB3380_IRQSTAT0_EP_INTR_MASK_IN |
+			USB3380_IRQSTAT0_EP_INTR_MASK_OUT);
+
+		if (stat & mask) {
+			usb338x_handle_ep_intr(dev, stat & mask);
+			stat &= ~mask;
+		}
+	} else {
+		/* endpoint data irq ? */
+		scratch = stat & 0x7f;
+		stat &= ~0x7f;
+		for (num = 0; scratch; num++) {
+			u32		t;
+
+			/* do this endpoint's FIFO and queue need tending? */
+			t = BIT(num);
+			if ((scratch & t) == 0)
+				continue;
+			scratch ^= t;
+
+			ep = &dev->ep[num];
+			handle_ep_small(ep);
+		}
+	}
+
+	if (stat)
+		ep_dbg(dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \
+		BIT(DMA_C_INTERRUPT) | \
+		BIT(DMA_B_INTERRUPT) | \
+		BIT(DMA_A_INTERRUPT))
+#define	PCI_ERROR_INTERRUPTS ( \
+		BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \
+		BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \
+		BIT(PCI_RETRY_ABORT_INTERRUPT))
+
+static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	struct net2280_ep	*ep;
+	u32			tmp, num, mask, scratch;
+
+	/* after disconnect there's nothing else to do! */
+	tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT);
+	mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED);
+
+	/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
+	 * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
+	 * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
+	 * only indicates a change in the reset state).
+	 */
+	if (stat & tmp) {
+		bool	reset = false;
+		bool	disconnect = false;
+
+		/*
+		 * Ignore disconnects and resets if the speed hasn't been set.
+		 * VBUS can bounce and there's always an initial reset.
+		 */
+		writel(tmp, &dev->regs->irqstat1);
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
+			if ((stat & BIT(VBUS_INTERRUPT)) &&
+					(readl(&dev->usb->usbctl) &
+						BIT(VBUS_PIN)) == 0) {
+				disconnect = true;
+				ep_dbg(dev, "disconnect %s\n",
+						dev->driver->driver.name);
+			} else if ((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
+					(readl(&dev->usb->usbstat) & mask)
+						== 0) {
+				reset = true;
+				ep_dbg(dev, "reset %s\n",
+						dev->driver->driver.name);
+			}
+
+			if (disconnect || reset) {
+				stop_activity(dev, dev->driver);
+				ep0_start(dev);
+				spin_unlock(&dev->lock);
+				if (reset)
+					usb_gadget_udc_reset
+						(&dev->gadget, dev->driver);
+				else
+					(dev->driver->disconnect)
+						(&dev->gadget);
+				spin_lock(&dev->lock);
+				return;
+			}
+		}
+		stat &= ~tmp;
+
+		/* vBUS can bounce ... one of many reasons to ignore the
+		 * notion of hotplug events on bus connect/disconnect!
+		 */
+		if (!stat)
+			return;
+	}
+
+	/* NOTE: chip stays in PCI D0 state for now, but it could
+	 * enter D1 to save more power
+	 */
+	tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
+	if (stat & tmp) {
+		writel(tmp, &dev->regs->irqstat1);
+		spin_unlock(&dev->lock);
+		if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
+			if (dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+			if (!enable_suspend)
+				stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
+		} else {
+			if (dev->driver->resume)
+				dev->driver->resume(&dev->gadget);
+			/* at high speed, note erratum 0133 */
+		}
+		spin_lock(&dev->lock);
+		stat &= ~tmp;
+	}
+
+	/* clear any other status/irqs */
+	if (stat)
+		writel(stat, &dev->regs->irqstat1);
+
+	/* some status we can just ignore */
+	if (dev->quirks & PLX_2280)
+		stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+			  BIT(SUSPEND_REQUEST_INTERRUPT) |
+			  BIT(RESUME_INTERRUPT) |
+			  BIT(SOF_INTERRUPT));
+	else
+		stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+			  BIT(RESUME_INTERRUPT) |
+			  BIT(SOF_DOWN_INTERRUPT) |
+			  BIT(SOF_INTERRUPT));
+
+	if (!stat)
+		return;
+	/* ep_dbg(dev, "irqstat1 %08x\n", stat);*/
+
+	/* DMA status, for ep-{a,b,c,d} */
+	scratch = stat & DMA_INTERRUPTS;
+	stat &= ~DMA_INTERRUPTS;
+	scratch >>= 9;
+	for (num = 0; scratch; num++) {
+		struct net2280_dma_regs	__iomem *dma;
+
+		tmp = BIT(num);
+		if ((tmp & scratch) == 0)
+			continue;
+		scratch ^= tmp;
+
+		ep = &dev->ep[num + 1];
+		dma = ep->dma;
+
+		if (!dma)
+			continue;
+
+		/* clear ep's dma status */
+		tmp = readl(&dma->dmastat);
+		writel(tmp, &dma->dmastat);
+
+		/* dma sync*/
+		if (dev->quirks & PLX_PCIE) {
+			u32 r_dmacount = readl(&dma->dmacount);
+			if (!ep->is_in &&  (r_dmacount & 0x00FFFFFF) &&
+			    (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT)))
+				continue;
+		}
+
+		if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+			ep_dbg(ep->dev, "%s no xact done? %08x\n",
+				ep->ep.name, tmp);
+			continue;
+		}
+		stop_dma(ep->dma);
+
+		/* OUT transfers terminate when the data from the
+		 * host is in our memory.  Process whatever's done.
+		 * On this path, we know transfer's last packet wasn't
+		 * less than req->length. NAK_OUT_PACKETS may be set,
+		 * or the FIFO may already be holding new packets.
+		 *
+		 * IN transfers can linger in the FIFO for a very
+		 * long time ... we ignore that for now, accounting
+		 * precisely (like PIO does) needs per-packet irqs
+		 */
+		scan_dma_completions(ep);
+
+		/* disable dma on inactive queues; else maybe restart */
+		if (!list_empty(&ep->queue)) {
+			tmp = readl(&dma->dmactl);
+			restart_dma(ep);
+		}
+		ep->irqs++;
+	}
+
+	/* NOTE:  there are other PCI errors we might usefully notice.
+	 * if they appear very often, here's where to try recovering.
+	 */
+	if (stat & PCI_ERROR_INTERRUPTS) {
+		ep_err(dev, "pci dma error; stat %08x\n", stat);
+		stat &= ~PCI_ERROR_INTERRUPTS;
+		/* these are fatal errors, but "maybe" they won't
+		 * happen again ...
+		 */
+		stop_activity(dev, dev->driver);
+		ep0_start(dev);
+		stat = 0;
+	}
+
+	if (stat)
+		ep_dbg(dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+static irqreturn_t net2280_irq(int irq, void *_dev)
+{
+	struct net2280		*dev = _dev;
+
+	/* shared interrupt, not ours */
+	if ((dev->quirks & PLX_LEGACY) &&
+		(!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED))))
+		return IRQ_NONE;
+
+	spin_lock(&dev->lock);
+
+	/* handle disconnect, dma, and more */
+	handle_stat1_irqs(dev, readl(&dev->regs->irqstat1));
+
+	/* control requests and PIO */
+	handle_stat0_irqs(dev, readl(&dev->regs->irqstat0));
+
+	if (dev->quirks & PLX_PCIE) {
+		/* re-enable interrupt to trigger any possible new interrupt */
+		u32 pciirqenb1 = readl(&dev->regs->pciirqenb1);
+		writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1);
+		writel(pciirqenb1, &dev->regs->pciirqenb1);
+	}
+
+	spin_unlock(&dev->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void gadget_release(struct device *_dev)
+{
+	struct net2280	*dev = dev_get_drvdata(_dev);
+
+	kfree(dev);
+}
+
+/* tear down the binding between this driver and the pci device */
+
+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() */
+	if (dev->requests) {
+		int		i;
+		for (i = 1; i < 5; i++) {
+			if (!dev->ep[i].dummy)
+				continue;
+			dma_pool_free(dev->requests, dev->ep[i].dummy,
+					dev->ep[i].td_dma);
+		}
+		dma_pool_destroy(dev->requests);
+	}
+	if (dev->got_irq)
+		free_irq(pdev->irq, dev);
+	if (dev->quirks & PLX_PCIE)
+		pci_disable_msi(pdev);
+	if (dev->regs) {
+		net2280_led_shutdown(dev);
+		iounmap(dev->regs);
+	}
+	if (dev->region)
+		release_mem_region(pci_resource_start(pdev, 0),
+				pci_resource_len(pdev, 0));
+	if (dev->enabled)
+		pci_disable_device(pdev);
+	device_remove_file(&pdev->dev, &dev_attr_registers);
+
+	ep_info(dev, "unbind\n");
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net2280		*dev;
+	unsigned long		resource, len;
+	void			__iomem *base = NULL;
+	int			retval, i;
+
+	/* alloc, and start init */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		retval = -ENOMEM;
+		goto done;
+	}
+
+	pci_set_drvdata(pdev, dev);
+	spin_lock_init(&dev->lock);
+	dev->quirks = id->driver_data;
+	dev->pdev = pdev;
+	dev->gadget.ops = &net2280_ops;
+	dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ?
+				USB_SPEED_SUPER : USB_SPEED_HIGH;
+
+	/* the "gadget" abstracts/virtualizes the controller */
+	dev->gadget.name = driver_name;
+
+	/* now all the pci goodies ... */
+	if (pci_enable_device(pdev) < 0) {
+		retval = -ENODEV;
+		goto done;
+	}
+	dev->enabled = 1;
+
+	/* BAR 0 holds all the registers
+	 * BAR 1 is 8051 memory; unused here (note erratum 0103)
+	 * BAR 2 is fifo memory; unused here
+	 */
+	resource = pci_resource_start(pdev, 0);
+	len = pci_resource_len(pdev, 0);
+	if (!request_mem_region(resource, len, driver_name)) {
+		ep_dbg(dev, "controller already in use\n");
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->region = 1;
+
+	/* FIXME provide firmware download interface to put
+	 * 8051 code into the chip, e.g. to turn on PCI PM.
+	 */
+
+	base = ioremap_nocache(resource, len);
+	if (base == NULL) {
+		ep_dbg(dev, "can't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+	dev->regs = (struct net2280_regs __iomem *) base;
+	dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080);
+	dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100);
+	dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180);
+	dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
+	dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
+
+	if (dev->quirks & PLX_PCIE) {
+		u32 fsmvalue;
+		u32 usbstat;
+		dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
+							(base + 0x00b4);
+		dev->llregs = (struct usb338x_ll_regs __iomem *)
+							(base + 0x0700);
+		dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
+							(base + 0x0748);
+		dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
+							(base + 0x077c);
+		dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
+							(base + 0x079c);
+		dev->plregs = (struct usb338x_pl_regs __iomem *)
+							(base + 0x0800);
+		usbstat = readl(&dev->usb->usbstat);
+		dev->enhanced_mode = !!(usbstat & BIT(11));
+		dev->n_ep = (dev->enhanced_mode) ? 9 : 5;
+		/* put into initial config, link up all endpoints */
+		fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+					(0xf << DEFECT7374_FSM_FIELD);
+		/* See if firmware needs to set up for workaround: */
+		if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+			dev->bug7734_patched = 1;
+			writel(0, &dev->usb->usbctl);
+		} else
+			dev->bug7734_patched = 0;
+	} else {
+		dev->enhanced_mode = 0;
+		dev->n_ep = 7;
+		/* put into initial config, link up all endpoints */
+		writel(0, &dev->usb->usbctl);
+	}
+
+	usb_reset(dev);
+	usb_reinit(dev);
+
+	/* irq setup after old hardware is cleaned up */
+	if (!pdev->irq) {
+		ep_err(dev, "No IRQ.  Check PCI setup!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+
+	if (dev->quirks & PLX_PCIE)
+		if (pci_enable_msi(pdev))
+			ep_err(dev, "Failed to enable MSI mode\n");
+
+	if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED,
+							driver_name, dev)) {
+		ep_err(dev, "request interrupt %d failed\n", pdev->irq);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+	/* DMA setup */
+	/* NOTE:  we know only the 32 LSBs of dma addresses may be nonzero */
+	dev->requests = dma_pool_create("requests", &pdev->dev,
+		sizeof(struct net2280_dma),
+		0 /* no alignment requirements */,
+		0 /* or page-crossing issues */);
+	if (!dev->requests) {
+		ep_dbg(dev, "can't get request pool\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+	for (i = 1; i < 5; i++) {
+		struct net2280_dma	*td;
+
+		td = dma_pool_alloc(dev->requests, GFP_KERNEL,
+				&dev->ep[i].td_dma);
+		if (!td) {
+			ep_dbg(dev, "can't get dummy %d\n", i);
+			retval = -ENOMEM;
+			goto done;
+		}
+		td->dmacount = 0;	/* not VALID */
+		td->dmadesc = td->dmaaddr;
+		dev->ep[i].dummy = td;
+	}
+
+	/* enable lower-overhead pci memory bursts during DMA */
+	if (dev->quirks & PLX_LEGACY)
+		writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) |
+			/*
+			 * 256 write retries may not be enough...
+			   BIT(PCI_RETRY_ABORT_ENABLE) |
+			*/
+			BIT(DMA_READ_MULTIPLE_ENABLE) |
+			BIT(DMA_READ_LINE_ENABLE),
+			&dev->pci->pcimstctl);
+	/* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
+	pci_set_master(pdev);
+	pci_try_set_mwi(pdev);
+
+	/* ... also flushes any posted pci writes */
+	dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff;
+
+	/* done */
+	ep_info(dev, "%s\n", driver_desc);
+	ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
+			pdev->irq, base, dev->chiprev);
+	ep_info(dev, "version: " DRIVER_VERSION "; %s\n",
+		dev->enhanced_mode ? "enhanced mode" : "legacy mode");
+	retval = device_create_file(&pdev->dev, &dev_attr_registers);
+	if (retval)
+		goto done;
+
+	retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+			gadget_release);
+	if (retval)
+		goto done;
+	return 0;
+
+done:
+	if (dev)
+		net2280_remove(pdev);
+	return retval;
+}
+
+/* make sure the board is quiescent; otherwise it will continue
+ * generating IRQs across the upcoming reboot.
+ */
+
+static void net2280_shutdown(struct pci_dev *pdev)
+{
+	struct net2280		*dev = pci_get_drvdata(pdev);
+
+	/* disable IRQs */
+	writel(0, &dev->regs->pciirqenb0);
+	writel(0, &dev->regs->pciirqenb1);
+
+	/* disable the pullup so the host will think we're gone */
+	writel(0, &dev->usb->usbctl);
+
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+	.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+	.class_mask =	~0,
+	.vendor =	PCI_VENDOR_ID_PLX_LEGACY,
+	.device =	0x2280,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.driver_data =	PLX_LEGACY | PLX_2280,
+	}, {
+	.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+	.class_mask =	~0,
+	.vendor =	PCI_VENDOR_ID_PLX_LEGACY,
+	.device =	0x2282,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.driver_data =	PLX_LEGACY,
+	},
+	{
+	.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+	.class_mask =	~0,
+	.vendor =	PCI_VENDOR_ID_PLX,
+	.device =	0x2380,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.driver_data =	PLX_PCIE,
+	 },
+	{
+	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask =	~0,
+	.vendor =	PCI_VENDOR_ID_PLX,
+	.device =	0x3380,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.driver_data =	PLX_PCIE | PLX_SUPERSPEED,
+	 },
+	{
+	.class =	PCI_CLASS_SERIAL_USB_DEVICE,
+	.class_mask =	~0,
+	.vendor =	PCI_VENDOR_ID_PLX,
+	.device =	0x3382,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.driver_data =	PLX_PCIE | PLX_SUPERSPEED,
+	 },
+{ /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver net2280_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+
+	.probe =	net2280_probe,
+	.remove =	net2280_remove,
+	.shutdown =	net2280_shutdown,
+
+	/* FIXME add power management support */
+};
+
+module_pci_driver(net2280_pci_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
new file mode 100644
index 0000000..b65a797
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NetChip 2280 high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ */
+
+/*
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ */
+
+#include <linux/usb/net2280.h>
+#include <linux/usb/usb338x.h>
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	__KERNEL__
+
+/* indexed registers [11.10] are accessed indirectly
+ * caller must own the device lock.
+ */
+
+static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index)
+{
+	writel(index, &regs->idxaddr);
+	/* NOTE:  synchs device/cpu memory views */
+	return readl(&regs->idxdata);
+}
+
+static inline void
+set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value)
+{
+	writel(index, &regs->idxaddr);
+	writel(value, &regs->idxdata);
+	/* posted, may not be visible yet */
+}
+
+#endif	/* __KERNEL__ */
+
+#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc
+
+#define PLX_LEGACY		BIT(0)
+#define PLX_2280		BIT(1)
+#define PLX_SUPERSPEED		BIT(2)
+#define PLX_PCIE		BIT(3)
+
+#define REG_DIAG		0x0
+#define     RETRY_COUNTER                                       16
+#define     FORCE_PCI_SERR                                      11
+#define     FORCE_PCI_INTERRUPT                                 10
+#define     FORCE_USB_INTERRUPT                                 9
+#define     FORCE_CPU_INTERRUPT                                 8
+#define     ILLEGAL_BYTE_ENABLES                                5
+#define     FAST_TIMES                                          4
+#define     FORCE_RECEIVE_ERROR                                 2
+#define     FORCE_TRANSMIT_CRC_ERROR                            0
+#define REG_FRAME		0x02	/* from last sof */
+#define REG_CHIPREV		0x03	/* in bcd */
+#define	REG_HS_NAK_RATE		0x0a	/* NAK per N uframes */
+
+#define	CHIPREV_1	0x0100
+#define	CHIPREV_1A	0x0110
+
+/* DEFECT 7374 */
+#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS         200
+#define DEFECT_7374_PROCESSOR_WAIT_TIME             10
+
+/* ep0 max packet size */
+#define EP0_SS_MAX_PACKET_SIZE  0x200
+#define EP0_HS_MAX_PACKET_SIZE  0x40
+#ifdef	__KERNEL__
+
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct net2280_dma_regs bitfields
+ */
+struct net2280_dma {
+	__le32		dmacount;
+	__le32		dmaaddr;		/* the buffer */
+	__le32		dmadesc;		/* next dma descriptor */
+	__le32		_reserved;
+} __aligned(16);
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct net2280_ep {
+	struct usb_ep				ep;
+	struct net2280_ep_regs __iomem *cfg;
+	struct net2280_ep_regs			__iomem *regs;
+	struct net2280_dma_regs			__iomem *dma;
+	struct net2280_dma			*dummy;
+	dma_addr_t				td_dma;	/* of dummy */
+	struct net2280				*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,
+						in_fifo_validate : 1,
+						out_overflow : 1,
+						stopped : 1,
+						wedged : 1,
+						is_in : 1,
+						is_iso : 1,
+						responded : 1;
+};
+
+static inline void allow_status(struct net2280_ep *ep)
+{
+	/* ep0 only */
+	writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+		BIT(CLEAR_NAK_OUT_PACKETS) |
+		BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+		&ep->regs->ep_rsp);
+	ep->stopped = 1;
+}
+
+static inline void allow_status_338x(struct net2280_ep *ep)
+{
+	/*
+	 * Control Status Phase Handshake was set by the chip when the setup
+	 * packet arrived. While set, the chip automatically NAKs the host's
+	 * Status Phase tokens.
+	 */
+	writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp);
+
+	ep->stopped = 1;
+
+	/* TD 9.9 Halt Endpoint test.  TD 9.22 set feature test. */
+	ep->responded = 0;
+}
+
+struct net2280_request {
+	struct usb_request		req;
+	struct net2280_dma		*td;
+	dma_addr_t			td_dma;
+	struct list_head		queue;
+	unsigned			mapped : 1,
+					valid : 1;
+};
+
+struct net2280 {
+	/* each pci device provides one gadget, several endpoints */
+	struct usb_gadget		gadget;
+	spinlock_t			lock;
+	struct net2280_ep		ep[9];
+	struct usb_gadget_driver	*driver;
+	unsigned			enabled : 1,
+					protocol_stall : 1,
+					softconnect : 1,
+					got_irq : 1,
+					region:1,
+					u1_enable:1,
+					u2_enable:1,
+					ltm_enable:1,
+					wakeup_enable:1,
+					addressed_state:1,
+					bug7734_patched:1;
+	u16				chiprev;
+	int enhanced_mode;
+	int n_ep;
+	kernel_ulong_t			quirks;
+
+
+	/* pci state used to access those endpoints */
+	struct pci_dev			*pdev;
+	struct net2280_regs		__iomem *regs;
+	struct net2280_usb_regs		__iomem *usb;
+	struct usb338x_usb_ext_regs	__iomem *usb_ext;
+	struct net2280_pci_regs		__iomem *pci;
+	struct net2280_dma_regs		__iomem *dma;
+	struct net2280_dep_regs		__iomem *dep;
+	struct net2280_ep_regs		__iomem *epregs;
+	struct usb338x_ll_regs		__iomem *llregs;
+	struct usb338x_ll_lfps_regs	__iomem *ll_lfps_regs;
+	struct usb338x_ll_tsn_regs	__iomem *ll_tsn_regs;
+	struct usb338x_ll_chi_regs	__iomem *ll_chicken_reg;
+	struct usb338x_pl_regs		__iomem *plregs;
+
+	struct dma_pool			*requests;
+	/* statistics...*/
+};
+
+static inline void set_halt(struct net2280_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+		/* set NAK_OUT for erratum 0114 */
+		((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+		BIT(SET_ENDPOINT_HALT),
+		&ep->regs->ep_rsp);
+}
+
+static inline void clear_halt(struct net2280_ep *ep)
+{
+	/* ep0 and bulk/intr endpoints */
+	writel(BIT(CLEAR_ENDPOINT_HALT) |
+		BIT(CLEAR_ENDPOINT_TOGGLE) |
+		    /*
+		     * unless the gadget driver left a short packet in the
+		     * fifo, this reverses the erratum 0114 workaround.
+		     */
+		((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
+		&ep->regs->ep_rsp);
+}
+
+/*
+ * FSM value for Defect 7374 (U1U2 Test) is managed in
+ * chip's SCRATCH register:
+ */
+#define DEFECT7374_FSM_FIELD    28
+
+/* Waiting for Control Read:
+ *  - A transition to this state indicates a fresh USB connection,
+ *    before the first Setup Packet. The connection speed is not
+ *    known. Firmware is waiting for the first Control Read.
+ *  - Starting state: This state can be thought of as the FSM's typical
+ *    starting state.
+ *  - Tip: Upon the first SS Control Read the FSM never
+ *    returns to this state.
+ */
+#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD)
+
+/* Non-SS Control Read:
+ *  - A transition to this state indicates detection of the first HS
+ *    or FS Control Read.
+ *  - Tip: Upon the first SS Control Read the FSM never
+ *    returns to this state.
+ */
+#define	DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD)
+
+/* SS Control Read:
+ *  - A transition to this state indicates detection of the
+ *    first SS Control Read.
+ *  - This state indicates workaround completion. Workarounds no longer
+ *    need to be applied (as long as the chip remains powered up).
+ *  - Tip: Once in this state the FSM state does not change (until
+ *    the chip's power is lost and restored).
+ *  - This can be thought of as the final state of the FSM;
+ *    the FSM 'locks-up' in this state until the chip loses power.
+ */
+#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD)
+
+#ifdef USE_RDK_LEDS
+
+static inline void net2280_led_init(struct net2280 *dev)
+{
+	/* LED3 (green) is on during USB activity. note erratum 0113. */
+	writel(BIT(GPIO3_LED_SELECT) |
+		BIT(GPIO3_OUTPUT_ENABLE) |
+		BIT(GPIO2_OUTPUT_ENABLE) |
+		BIT(GPIO1_OUTPUT_ENABLE) |
+		BIT(GPIO0_OUTPUT_ENABLE),
+		&dev->regs->gpioctl);
+}
+
+/* indicate speed with bi-color LED 0/1 */
+static inline
+void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed)
+{
+	u32	val = readl(&dev->regs->gpioctl);
+	switch (speed) {
+	case USB_SPEED_SUPER:		/* green + red */
+		val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA);
+		break;
+	case USB_SPEED_HIGH:		/* green */
+		val &= ~BIT(GPIO0_DATA);
+		val |= BIT(GPIO1_DATA);
+		break;
+	case USB_SPEED_FULL:		/* red */
+		val &= ~BIT(GPIO1_DATA);
+		val |= BIT(GPIO0_DATA);
+		break;
+	default:			/* (off/black) */
+		val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA));
+		break;
+	}
+	writel(val, &dev->regs->gpioctl);
+}
+
+/* indicate power with LED 2 */
+static inline void net2280_led_active(struct net2280 *dev, int is_active)
+{
+	u32	val = readl(&dev->regs->gpioctl);
+
+	/* FIXME this LED never seems to turn on.*/
+	if (is_active)
+		val |= GPIO2_DATA;
+	else
+		val &= ~GPIO2_DATA;
+	writel(val, &dev->regs->gpioctl);
+}
+
+static inline void net2280_led_shutdown(struct net2280 *dev)
+{
+	/* turn off all four GPIO*_DATA bits */
+	writel(readl(&dev->regs->gpioctl) & ~0x0f,
+			&dev->regs->gpioctl);
+}
+
+#else
+
+#define net2280_led_init(dev)		do { } while (0)
+#define net2280_led_speed(dev, speed)	do { } while (0)
+#define net2280_led_shutdown(dev)	do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define ep_dbg(ndev, fmt, args...) \
+	dev_dbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_vdbg(ndev, fmt, args...) \
+	dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_info(ndev, fmt, args...) \
+	dev_info((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_warn(ndev, fmt, args...) \
+	dev_warn((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_err(ndev, fmt, args...) \
+	dev_err((&((ndev)->pdev->dev)), fmt, ##args)
+
+/*-------------------------------------------------------------------------*/
+
+static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count)
+{
+	if (ep->dev->pdev->vendor == 0x17cc)
+		writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
+	else{
+		u32 tmp = readl(&ep->cfg->ep_cfg) &
+					(~(0x07 << EP_FIFO_BYTE_COUNT));
+		writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+	}
+}
+
+static inline void start_out_naking(struct net2280_ep *ep)
+{
+	/* NOTE:  hardware races lurk here, and PING protocol issues */
+	writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	/* synch with device */
+	readl(&ep->regs->ep_rsp);
+}
+
+static inline void stop_out_naking(struct net2280_ep *ep)
+{
+	u32	tmp;
+
+	tmp = readl(&ep->regs->ep_stat);
+	if ((tmp & BIT(NAK_OUT_PACKETS)) != 0)
+		writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+
+static inline void set_max_speed(struct net2280_ep *ep, u32 max)
+{
+	u32 reg;
+	static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80,
+					  0x50, 0x20, 0x70, 0x40, 0x90 };
+
+	if (ep->dev->enhanced_mode) {
+		reg = ep_enhanced[ep->num];
+		switch (ep->dev->gadget.speed) {
+		case USB_SPEED_SUPER:
+			reg += 2;
+			break;
+		case USB_SPEED_FULL:
+			reg += 1;
+			break;
+		case USB_SPEED_HIGH:
+		default:
+			break;
+		}
+	} else {
+		reg = (ep->num + 1) * 0x10;
+		if (ep->dev->gadget.speed != USB_SPEED_HIGH)
+			reg += 1;
+	}
+
+	set_idx_reg(ep->dev->regs, reg, max);
+}
+
+#endif	/* __KERNEL__ */
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
new file mode 100644
index 0000000..fcf13ef
--- /dev/null
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -0,0 +1,3013 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * omap_udc.c -- for OMAP full speed udc; most chips support OTG.
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * OMAP2 & DMA support by Kyungmin Park <kyungmin.park@samsung.com>
+ */
+
+#undef	DEBUG
+#undef	VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/prefetch.h>
+#include <linux/io.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+#include <asm/mach-types.h>
+
+#include <linux/omap-dma.h>
+
+#include <mach/usb.h>
+
+#include "omap_udc.h"
+
+#undef	USB_TRACE
+
+/* bulk DMA seems to be behaving for both IN and OUT */
+#define	USE_DMA
+
+/* ISO too */
+#define	USE_ISO
+
+#define	DRIVER_DESC	"OMAP UDC driver"
+#define	DRIVER_VERSION	"4 October 2004"
+
+#define OMAP_DMA_USB_W2FC_TX0		29
+#define OMAP_DMA_USB_W2FC_RX0		26
+
+/*
+ * The OMAP UDC needs _very_ early endpoint setup:  before enabling the
+ * D+ pullup to allow enumeration.  That's too early for the gadget
+ * framework to use from usb_endpoint_enable(), which happens after
+ * enumeration as part of activating an interface.  (But if we add an
+ * optional new "UDC not yet running" state to the gadget driver model,
+ * even just during driver binding, the endpoint autoconfig logic is the
+ * natural spot to manufacture new endpoints.)
+ *
+ * So instead of using endpoint enable calls to control the hardware setup,
+ * this driver defines a "fifo mode" parameter.  It's used during driver
+ * initialization to choose among a set of pre-defined endpoint configs.
+ * See omap_udc_setup() for available modes, or to add others.  That code
+ * lives in an init section, so use this driver as a module if you need
+ * to change the fifo mode after the kernel boots.
+ *
+ * Gadget drivers normally ignore endpoints they don't care about, and
+ * won't include them in configuration descriptors.  That means only
+ * misbehaving hosts would even notice they exist.
+ */
+#ifdef	USE_ISO
+static unsigned fifo_mode = 3;
+#else
+static unsigned fifo_mode;
+#endif
+
+/* "modprobe omap_udc fifo_mode=42", or else as a kernel
+ * boot parameter "omap_udc:fifo_mode=42"
+ */
+module_param(fifo_mode, uint, 0);
+MODULE_PARM_DESC(fifo_mode, "endpoint configuration");
+
+#ifdef	USE_DMA
+static bool use_dma = 1;
+
+/* "modprobe omap_udc use_dma=y", or else as a kernel
+ * boot parameter "omap_udc:use_dma=y"
+ */
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable DMA");
+#else	/* !USE_DMA */
+
+/* save a bit of code */
+#define	use_dma		0
+#endif	/* !USE_DMA */
+
+
+static const char driver_name[] = "omap_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*-------------------------------------------------------------------------*/
+
+/* there's a notion of "current endpoint" for modifying endpoint
+ * state, and PIO access to its FIFO.
+ */
+
+static void use_ep(struct omap_ep *ep, u16 select)
+{
+	u16	num = ep->bEndpointAddress & 0x0f;
+
+	if (ep->bEndpointAddress & USB_DIR_IN)
+		num |= UDC_EP_DIR;
+	omap_writew(num | select, UDC_EP_NUM);
+	/* when select, MUST deselect later !! */
+}
+
+static inline void deselect_ep(void)
+{
+	u16 w;
+
+	w = omap_readw(UDC_EP_NUM);
+	w &= ~UDC_EP_SEL;
+	omap_writew(w, UDC_EP_NUM);
+	/* 6 wait states before TX will happen */
+}
+
+static void dma_channel_claim(struct omap_ep *ep, unsigned preferred);
+
+/*-------------------------------------------------------------------------*/
+
+static int omap_ep_enable(struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);
+	struct omap_udc	*udc;
+	unsigned long	flags;
+	u16		maxp;
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| ep->bEndpointAddress != desc->bEndpointAddress
+			|| ep->maxpacket < usb_endpoint_maxp(desc)) {
+		DBG("%s, bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+	maxp = usb_endpoint_maxp(desc);
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+				&& maxp != ep->maxpacket)
+			|| usb_endpoint_maxp(desc) > ep->maxpacket
+			|| !desc->wMaxPacketSize) {
+		DBG("%s, bad %s maxpacket\n", __func__, _ep->name);
+		return -ERANGE;
+	}
+
+#ifdef	USE_ISO
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_ISOC
+				&& desc->bInterval != 1)) {
+		/* hardware wants period = 1; USB allows 2^(Interval-1) */
+		DBG("%s, unsupported ISO period %dms\n", _ep->name,
+				1 << (desc->bInterval - 1));
+		return -EDOM;
+	}
+#else
+	if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		DBG("%s, ISO nyet\n", _ep->name);
+		return -EDOM;
+	}
+#endif
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if (ep->bmAttributes != desc->bmAttributes
+			&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+			&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+		DBG("%s, %s type mismatch\n", __func__, _ep->name);
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		DBG("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	ep->ep.desc = desc;
+	ep->irqs = 0;
+	ep->stopped = 0;
+	ep->ep.maxpacket = maxp;
+
+	/* set endpoint to initial state */
+	ep->dma_channel = 0;
+	ep->has_dma = 0;
+	ep->lch = -1;
+	use_ep(ep, UDC_EP_SEL);
+	omap_writew(udc->clr_halt, UDC_CTRL);
+	ep->ackwait = 0;
+	deselect_ep();
+
+	if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+		list_add(&ep->iso, &udc->iso);
+
+	/* maybe assign a DMA channel to this endpoint */
+	if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK)
+		/* FIXME ISO can dma, but prefers first channel */
+		dma_channel_claim(ep, 0);
+
+	/* PIO OUT may RX packets */
+	if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC
+			&& !ep->has_dma
+			&& !(ep->bEndpointAddress & USB_DIR_IN)) {
+		omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+		ep->ackwait = 1 + ep->double_buf;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	VDBG("%s enabled\n", _ep->name);
+	return 0;
+}
+
+static void nuke(struct omap_ep *, int status);
+
+static int omap_ep_disable(struct usb_ep *_ep)
+{
+	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);
+	unsigned long	flags;
+
+	if (!_ep || !ep->ep.desc) {
+		DBG("%s, %s not enabled\n", __func__,
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	ep->ep.desc = NULL;
+	nuke(ep, -ESHUTDOWN);
+	ep->ep.maxpacket = ep->maxpacket;
+	ep->has_dma = 0;
+	omap_writew(UDC_SET_HALT, UDC_CTRL);
+	list_del_init(&ep->iso);
+	del_timer(&ep->timer);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	VDBG("%s disabled\n", _ep->name);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+	struct omap_req	*req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void
+omap_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct omap_req	*req = container_of(_req, struct omap_req, req);
+
+	kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+done(struct omap_ep *ep, struct omap_req *req, int status)
+{
+	struct omap_udc		*udc = ep->udc;
+	unsigned		stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (use_dma && ep->has_dma)
+		usb_gadget_unmap_request(&udc->gadget, &req->req,
+				(ep->bEndpointAddress & USB_DIR_IN));
+
+#ifndef	USB_TRACE
+	if (status && status != -ESHUTDOWN)
+#endif
+		VDBG("complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&ep->udc->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&ep->udc->lock);
+	ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define UDC_FIFO_FULL		(UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL)
+#define UDC_FIFO_UNWRITABLE	(UDC_EP_HALTED | UDC_FIFO_FULL)
+
+#define FIFO_EMPTY	(UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY)
+#define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY)
+
+static inline int
+write_packet(u8 *buf, struct omap_req *req, unsigned max)
+{
+	unsigned	len;
+	u16		*wp;
+
+	len = min(req->req.length - req->req.actual, max);
+	req->req.actual += len;
+
+	max = len;
+	if (likely((((int)buf) & 1) == 0)) {
+		wp = (u16 *)buf;
+		while (max >= 2) {
+			omap_writew(*wp++, UDC_DATA);
+			max -= 2;
+		}
+		buf = (u8 *)wp;
+	}
+	while (max--)
+		omap_writeb(*buf++, UDC_DATA);
+	return len;
+}
+
+/* FIXME change r/w fifo calling convention */
+
+
+/* return:  0 = still running, 1 = completed, negative = errno */
+static int write_fifo(struct omap_ep *ep, struct omap_req *req)
+{
+	u8		*buf;
+	unsigned	count;
+	int		is_last;
+	u16		ep_stat;
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	/* PIO-IN isn't double buffered except for iso */
+	ep_stat = omap_readw(UDC_STAT_FLG);
+	if (ep_stat & UDC_FIFO_UNWRITABLE)
+		return 0;
+
+	count = ep->ep.maxpacket;
+	count = write_packet(buf, req, count);
+	omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+	ep->ackwait = 1;
+
+	/* last packet is often short (sometimes a zlp) */
+	if (count != ep->ep.maxpacket)
+		is_last = 1;
+	else if (req->req.length == req->req.actual
+			&& !req->req.zero)
+		is_last = 1;
+	else
+		is_last = 0;
+
+	/* NOTE:  requests complete when all IN data is in a
+	 * FIFO (or sometimes later, if a zlp was needed).
+	 * Use usb_ep_fifo_status() where needed.
+	 */
+	if (is_last)
+		done(ep, req, 0);
+	return is_last;
+}
+
+static inline int
+read_packet(u8 *buf, struct omap_req *req, unsigned avail)
+{
+	unsigned	len;
+	u16		*wp;
+
+	len = min(req->req.length - req->req.actual, avail);
+	req->req.actual += len;
+	avail = len;
+
+	if (likely((((int)buf) & 1) == 0)) {
+		wp = (u16 *)buf;
+		while (avail >= 2) {
+			*wp++ = omap_readw(UDC_DATA);
+			avail -= 2;
+		}
+		buf = (u8 *)wp;
+	}
+	while (avail--)
+		*buf++ = omap_readb(UDC_DATA);
+	return len;
+}
+
+/* return:  0 = still running, 1 = queue empty, negative = errno */
+static int read_fifo(struct omap_ep *ep, struct omap_req *req)
+{
+	u8		*buf;
+	unsigned	count, avail;
+	int		is_last;
+
+	buf = req->req.buf + req->req.actual;
+	prefetchw(buf);
+
+	for (;;) {
+		u16	ep_stat = omap_readw(UDC_STAT_FLG);
+
+		is_last = 0;
+		if (ep_stat & FIFO_EMPTY) {
+			if (!ep->double_buf)
+				break;
+			ep->fnf = 1;
+		}
+		if (ep_stat & UDC_EP_HALTED)
+			break;
+
+		if (ep_stat & UDC_FIFO_FULL)
+			avail = ep->ep.maxpacket;
+		else  {
+			avail = omap_readw(UDC_RXFSTAT);
+			ep->fnf = ep->double_buf;
+		}
+		count = read_packet(buf, req, avail);
+
+		/* partial packet reads may not be errors */
+		if (count < ep->ep.maxpacket) {
+			is_last = 1;
+			/* overflowed this request?  flush extra data */
+			if (count != avail) {
+				req->req.status = -EOVERFLOW;
+				avail -= count;
+				while (avail--)
+					omap_readw(UDC_DATA);
+			}
+		} else if (req->req.length == req->req.actual)
+			is_last = 1;
+		else
+			is_last = 0;
+
+		if (!ep->bEndpointAddress)
+			break;
+		if (is_last)
+			done(ep, req, 0);
+		break;
+	}
+	return is_last;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
+{
+	dma_addr_t	end;
+
+	/* IN-DMA needs this on fault/cancel paths, so 15xx misreports
+	 * the last transfer's bytecount by more than a FIFO's worth.
+	 */
+	if (cpu_is_omap15xx())
+		return 0;
+
+	end = omap_get_dma_src_pos(ep->lch);
+	if (end == ep->dma_counter)
+		return 0;
+
+	end |= start & (0xffff << 16);
+	if (end < start)
+		end += 0x10000;
+	return end - start;
+}
+
+static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
+{
+	dma_addr_t	end;
+
+	end = omap_get_dma_dst_pos(ep->lch);
+	if (end == ep->dma_counter)
+		return 0;
+
+	end |= start & (0xffff << 16);
+	if (cpu_is_omap15xx())
+		end++;
+	if (end < start)
+		end += 0x10000;
+	return end - start;
+}
+
+
+/* Each USB transfer request using DMA maps to one or more DMA transfers.
+ * When DMA completion isn't request completion, the UDC continues with
+ * the next DMA transfer for that USB transfer.
+ */
+
+static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
+{
+	u16		txdma_ctrl, w;
+	unsigned	length = req->req.length - req->req.actual;
+	const int	sync_mode = cpu_is_omap15xx()
+				? OMAP_DMA_SYNC_FRAME
+				: OMAP_DMA_SYNC_ELEMENT;
+	int		dma_trigger = 0;
+
+	/* measure length in either bytes or packets */
+	if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
+			|| (cpu_is_omap15xx() && length < ep->maxpacket)) {
+		txdma_ctrl = UDC_TXN_EOT | length;
+		omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
+				length, 1, sync_mode, dma_trigger, 0);
+	} else {
+		length = min(length / ep->maxpacket,
+				(unsigned) UDC_TXN_TSC + 1);
+		txdma_ctrl = length;
+		omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+				ep->ep.maxpacket >> 1, length, sync_mode,
+				dma_trigger, 0);
+		length *= ep->maxpacket;
+	}
+	omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF,
+		OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
+		0, 0);
+
+	omap_start_dma(ep->lch);
+	ep->dma_counter = omap_get_dma_src_pos(ep->lch);
+	w = omap_readw(UDC_DMA_IRQ_EN);
+	w |= UDC_TX_DONE_IE(ep->dma_channel);
+	omap_writew(w, UDC_DMA_IRQ_EN);
+	omap_writew(UDC_TXN_START | txdma_ctrl, UDC_TXDMA(ep->dma_channel));
+	req->dma_bytes = length;
+}
+
+static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
+{
+	u16 w;
+
+	if (status == 0) {
+		req->req.actual += req->dma_bytes;
+
+		/* return if this request needs to send data or zlp */
+		if (req->req.actual < req->req.length)
+			return;
+		if (req->req.zero
+				&& req->dma_bytes != 0
+				&& (req->req.actual % ep->maxpacket) == 0)
+			return;
+	} else
+		req->req.actual += dma_src_len(ep, req->req.dma
+							+ req->req.actual);
+
+	/* tx completion */
+	omap_stop_dma(ep->lch);
+	w = omap_readw(UDC_DMA_IRQ_EN);
+	w &= ~UDC_TX_DONE_IE(ep->dma_channel);
+	omap_writew(w, UDC_DMA_IRQ_EN);
+	done(ep, req, status);
+}
+
+static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
+{
+	unsigned packets = req->req.length - req->req.actual;
+	int dma_trigger = 0;
+	u16 w;
+
+	/* set up this DMA transfer, enable the fifo, start */
+	packets /= ep->ep.maxpacket;
+	packets = min(packets, (unsigned)UDC_RXN_TC + 1);
+	req->dma_bytes = packets * ep->ep.maxpacket;
+	omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+			ep->ep.maxpacket >> 1, packets,
+			OMAP_DMA_SYNC_ELEMENT,
+			dma_trigger, 0);
+	omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
+		OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
+		0, 0);
+	ep->dma_counter = omap_get_dma_dst_pos(ep->lch);
+
+	omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel));
+	w = omap_readw(UDC_DMA_IRQ_EN);
+	w |= UDC_RX_EOT_IE(ep->dma_channel);
+	omap_writew(w, UDC_DMA_IRQ_EN);
+	omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM);
+	omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+
+	omap_start_dma(ep->lch);
+}
+
+static void
+finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one)
+{
+	u16	count, w;
+
+	if (status == 0)
+		ep->dma_counter = (u16) (req->req.dma + req->req.actual);
+	count = dma_dest_len(ep, req->req.dma + req->req.actual);
+	count += req->req.actual;
+	if (one)
+		count--;
+	if (count <= req->req.length)
+		req->req.actual = count;
+
+	if (count != req->dma_bytes || status)
+		omap_stop_dma(ep->lch);
+
+	/* if this wasn't short, request may need another transfer */
+	else if (req->req.actual < req->req.length)
+		return;
+
+	/* rx completion */
+	w = omap_readw(UDC_DMA_IRQ_EN);
+	w &= ~UDC_RX_EOT_IE(ep->dma_channel);
+	omap_writew(w, UDC_DMA_IRQ_EN);
+	done(ep, req, status);
+}
+
+static void dma_irq(struct omap_udc *udc, u16 irq_src)
+{
+	u16		dman_stat = omap_readw(UDC_DMAN_STAT);
+	struct omap_ep	*ep;
+	struct omap_req	*req;
+
+	/* IN dma: tx to host */
+	if (irq_src & UDC_TXN_DONE) {
+		ep = &udc->ep[16 + UDC_DMA_TX_SRC(dman_stat)];
+		ep->irqs++;
+		/* can see TXN_DONE after dma abort */
+		if (!list_empty(&ep->queue)) {
+			req = container_of(ep->queue.next,
+						struct omap_req, queue);
+			finish_in_dma(ep, req, 0);
+		}
+		omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC);
+
+		if (!list_empty(&ep->queue)) {
+			req = container_of(ep->queue.next,
+					struct omap_req, queue);
+			next_in_dma(ep, req);
+		}
+	}
+
+	/* OUT dma: rx from host */
+	if (irq_src & UDC_RXN_EOT) {
+		ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)];
+		ep->irqs++;
+		/* can see RXN_EOT after dma abort */
+		if (!list_empty(&ep->queue)) {
+			req = container_of(ep->queue.next,
+					struct omap_req, queue);
+			finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB);
+		}
+		omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC);
+
+		if (!list_empty(&ep->queue)) {
+			req = container_of(ep->queue.next,
+					struct omap_req, queue);
+			next_out_dma(ep, req);
+		}
+	}
+
+	if (irq_src & UDC_RXN_CNT) {
+		ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)];
+		ep->irqs++;
+		/* omap15xx does this unasked... */
+		VDBG("%s, RX_CNT irq?\n", ep->ep.name);
+		omap_writew(UDC_RXN_CNT, UDC_IRQ_SRC);
+	}
+}
+
+static void dma_error(int lch, u16 ch_status, void *data)
+{
+	struct omap_ep	*ep = data;
+
+	/* if ch_status & OMAP_DMA_DROP_IRQ ... */
+	/* if ch_status & OMAP1_DMA_TOUT_IRQ ... */
+	ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status);
+
+	/* complete current transfer ... */
+}
+
+static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+{
+	u16	reg;
+	int	status, restart, is_in;
+	int	dma_channel;
+
+	is_in = ep->bEndpointAddress & USB_DIR_IN;
+	if (is_in)
+		reg = omap_readw(UDC_TXDMA_CFG);
+	else
+		reg = omap_readw(UDC_RXDMA_CFG);
+	reg |= UDC_DMA_REQ;		/* "pulse" activated */
+
+	ep->dma_channel = 0;
+	ep->lch = -1;
+	if (channel == 0 || channel > 3) {
+		if ((reg & 0x0f00) == 0)
+			channel = 3;
+		else if ((reg & 0x00f0) == 0)
+			channel = 2;
+		else if ((reg & 0x000f) == 0)	/* preferred for ISO */
+			channel = 1;
+		else {
+			status = -EMLINK;
+			goto just_restart;
+		}
+	}
+	reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1));
+	ep->dma_channel = channel;
+
+	if (is_in) {
+		dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
+		status = omap_request_dma(dma_channel,
+			ep->ep.name, dma_error, ep, &ep->lch);
+		if (status == 0) {
+			omap_writew(reg, UDC_TXDMA_CFG);
+			/* EMIFF or SDRC */
+			omap_set_dma_src_burst_mode(ep->lch,
+						OMAP_DMA_DATA_BURST_4);
+			omap_set_dma_src_data_pack(ep->lch, 1);
+			/* TIPB */
+			omap_set_dma_dest_params(ep->lch,
+				OMAP_DMA_PORT_TIPB,
+				OMAP_DMA_AMODE_CONSTANT,
+				UDC_DATA_DMA,
+				0, 0);
+		}
+	} else {
+		dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
+		status = omap_request_dma(dma_channel,
+			ep->ep.name, dma_error, ep, &ep->lch);
+		if (status == 0) {
+			omap_writew(reg, UDC_RXDMA_CFG);
+			/* TIPB */
+			omap_set_dma_src_params(ep->lch,
+				OMAP_DMA_PORT_TIPB,
+				OMAP_DMA_AMODE_CONSTANT,
+				UDC_DATA_DMA,
+				0, 0);
+			/* EMIFF or SDRC */
+			omap_set_dma_dest_burst_mode(ep->lch,
+						OMAP_DMA_DATA_BURST_4);
+			omap_set_dma_dest_data_pack(ep->lch, 1);
+		}
+	}
+	if (status)
+		ep->dma_channel = 0;
+	else {
+		ep->has_dma = 1;
+		omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
+
+		/* channel type P: hw synch (fifo) */
+		if (!cpu_is_omap15xx())
+			omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
+	}
+
+just_restart:
+	/* restart any queue, even if the claim failed  */
+	restart = !ep->stopped && !list_empty(&ep->queue);
+
+	if (status)
+		DBG("%s no dma channel: %d%s\n", ep->ep.name, status,
+			restart ? " (restart)" : "");
+	else
+		DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name,
+			is_in ? 't' : 'r',
+			ep->dma_channel - 1, ep->lch,
+			restart ? " (restart)" : "");
+
+	if (restart) {
+		struct omap_req	*req;
+		req = container_of(ep->queue.next, struct omap_req, queue);
+		if (ep->has_dma)
+			(is_in ? next_in_dma : next_out_dma)(ep, req);
+		else {
+			use_ep(ep, UDC_EP_SEL);
+			(is_in ? write_fifo : read_fifo)(ep, req);
+			deselect_ep();
+			if (!is_in) {
+				omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+				ep->ackwait = 1 + ep->double_buf;
+			}
+			/* IN: 6 wait states before it'll tx */
+		}
+	}
+}
+
+static void dma_channel_release(struct omap_ep *ep)
+{
+	int		shift = 4 * (ep->dma_channel - 1);
+	u16		mask = 0x0f << shift;
+	struct omap_req	*req;
+	int		active;
+
+	/* abort any active usb transfer request */
+	if (!list_empty(&ep->queue))
+		req = container_of(ep->queue.next, struct omap_req, queue);
+	else
+		req = NULL;
+
+	active = omap_get_dma_active_status(ep->lch);
+
+	DBG("%s release %s %cxdma%d %p\n", ep->ep.name,
+			active ? "active" : "idle",
+			(ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r',
+			ep->dma_channel - 1, req);
+
+	/* NOTE: re-setting RX_REQ/TX_REQ because of a chip bug (before
+	 * OMAP 1710 ES2.0) where reading the DMA_CFG can clear them.
+	 */
+
+	/* wait till current packet DMA finishes, and fifo empties */
+	if (ep->bEndpointAddress & USB_DIR_IN) {
+		omap_writew((omap_readw(UDC_TXDMA_CFG) & ~mask) | UDC_DMA_REQ,
+					UDC_TXDMA_CFG);
+
+		if (req) {
+			finish_in_dma(ep, req, -ECONNRESET);
+
+			/* clear FIFO; hosts probably won't empty it */
+			use_ep(ep, UDC_EP_SEL);
+			omap_writew(UDC_CLR_EP, UDC_CTRL);
+			deselect_ep();
+		}
+		while (omap_readw(UDC_TXDMA_CFG) & mask)
+			udelay(10);
+	} else {
+		omap_writew((omap_readw(UDC_RXDMA_CFG) & ~mask) | UDC_DMA_REQ,
+					UDC_RXDMA_CFG);
+
+		/* dma empties the fifo */
+		while (omap_readw(UDC_RXDMA_CFG) & mask)
+			udelay(10);
+		if (req)
+			finish_out_dma(ep, req, -ECONNRESET, 0);
+	}
+	omap_free_dma(ep->lch);
+	ep->dma_channel = 0;
+	ep->lch = -1;
+	/* has_dma still set, till endpoint is fully quiesced */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);
+	struct omap_req	*req = container_of(_req, struct omap_req, req);
+	struct omap_udc	*udc;
+	unsigned long	flags;
+	int		is_iso = 0;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+			|| !list_empty(&req->queue)) {
+		DBG("%s, bad params\n", __func__);
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->ep.desc && ep->bEndpointAddress)) {
+		DBG("%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+	if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		if (req->req.length > ep->ep.maxpacket)
+			return -EMSGSIZE;
+		is_iso = 1;
+	}
+
+	/* this isn't bogus, but OMAP DMA isn't the only hardware to
+	 * have a hard time with partial packet reads...  reject it.
+	 */
+	if (use_dma
+			&& ep->has_dma
+			&& ep->bEndpointAddress != 0
+			&& (ep->bEndpointAddress & USB_DIR_IN) == 0
+			&& (req->req.length % ep->ep.maxpacket) != 0) {
+		DBG("%s, no partial packet OUT reads\n", __func__);
+		return -EMSGSIZE;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	if (use_dma && ep->has_dma)
+		usb_gadget_map_request(&udc->gadget, &req->req,
+				(ep->bEndpointAddress & USB_DIR_IN));
+
+	VDBG("%s queue req %p, len %d buf %p\n",
+		ep->ep.name, _req, _req->length, _req->buf);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+
+	/* maybe kickstart non-iso i/o queues */
+	if (is_iso) {
+		u16 w;
+
+		w = omap_readw(UDC_IRQ_EN);
+		w |= UDC_SOF_IE;
+		omap_writew(w, UDC_IRQ_EN);
+	} else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) {
+		int	is_in;
+
+		if (ep->bEndpointAddress == 0) {
+			if (!udc->ep0_pending || !list_empty(&ep->queue)) {
+				spin_unlock_irqrestore(&udc->lock, flags);
+				return -EL2HLT;
+			}
+
+			/* empty DATA stage? */
+			is_in = udc->ep0_in;
+			if (!req->req.length) {
+
+				/* chip became CONFIGURED or ADDRESSED
+				 * earlier; drivers may already have queued
+				 * requests to non-control endpoints
+				 */
+				if (udc->ep0_set_config) {
+					u16	irq_en = omap_readw(UDC_IRQ_EN);
+
+					irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE;
+					if (!udc->ep0_reset_config)
+						irq_en |= UDC_EPN_RX_IE
+							| UDC_EPN_TX_IE;
+					omap_writew(irq_en, UDC_IRQ_EN);
+				}
+
+				/* STATUS for zero length DATA stages is
+				 * always an IN ... even for IN transfers,
+				 * a weird case which seem to stall OMAP.
+				 */
+				omap_writew(UDC_EP_SEL | UDC_EP_DIR,
+						UDC_EP_NUM);
+				omap_writew(UDC_CLR_EP, UDC_CTRL);
+				omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+				omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+
+				/* cleanup */
+				udc->ep0_pending = 0;
+				done(ep, req, 0);
+				req = NULL;
+
+			/* non-empty DATA stage */
+			} else if (is_in) {
+				omap_writew(UDC_EP_SEL | UDC_EP_DIR,
+						UDC_EP_NUM);
+			} else {
+				if (udc->ep0_setup)
+					goto irq_wait;
+				omap_writew(UDC_EP_SEL, UDC_EP_NUM);
+			}
+		} else {
+			is_in = ep->bEndpointAddress & USB_DIR_IN;
+			if (!ep->has_dma)
+				use_ep(ep, UDC_EP_SEL);
+			/* if ISO: SOF IRQs must be enabled/disabled! */
+		}
+
+		if (ep->has_dma)
+			(is_in ? next_in_dma : next_out_dma)(ep, req);
+		else if (req) {
+			if ((is_in ? write_fifo : read_fifo)(ep, req) == 1)
+				req = NULL;
+			deselect_ep();
+			if (!is_in) {
+				omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+				ep->ackwait = 1 + ep->double_buf;
+			}
+			/* IN: 6 wait states before it'll tx */
+		}
+	}
+
+irq_wait:
+	/* irq handler advances the queue */
+	if (req != NULL)
+		list_add_tail(&req->queue, &ep->queue);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);
+	struct omap_req	*req;
+	unsigned long	flags;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually 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->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) {
+		int channel = ep->dma_channel;
+
+		/* releasing the channel cancels the request,
+		 * reclaiming the channel restarts the queue
+		 */
+		dma_channel_release(ep);
+		dma_channel_claim(ep, channel);
+	} else
+		done(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int omap_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct omap_ep	*ep = container_of(_ep, struct omap_ep, ep);
+	unsigned long	flags;
+	int		status = -EOPNOTSUPP;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* just use protocol stalls for ep0; real halts are annoying */
+	if (ep->bEndpointAddress == 0) {
+		if (!ep->udc->ep0_pending)
+			status = -EINVAL;
+		else if (value) {
+			if (ep->udc->ep0_set_config) {
+				WARNING("error changing config?\n");
+				omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+			}
+			omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+			ep->udc->ep0_pending = 0;
+			status = 0;
+		} else /* NOP */
+			status = 0;
+
+	/* otherwise, all active non-ISO endpoints can halt */
+	} else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->ep.desc) {
+
+		/* IN endpoints must already be idle */
+		if ((ep->bEndpointAddress & USB_DIR_IN)
+				&& !list_empty(&ep->queue)) {
+			status = -EAGAIN;
+			goto done;
+		}
+
+		if (value) {
+			int	channel;
+
+			if (use_dma && ep->dma_channel
+					&& !list_empty(&ep->queue)) {
+				channel = ep->dma_channel;
+				dma_channel_release(ep);
+			} else
+				channel = 0;
+
+			use_ep(ep, UDC_EP_SEL);
+			if (omap_readw(UDC_STAT_FLG) & UDC_NON_ISO_FIFO_EMPTY) {
+				omap_writew(UDC_SET_HALT, UDC_CTRL);
+				status = 0;
+			} else
+				status = -EAGAIN;
+			deselect_ep();
+
+			if (channel)
+				dma_channel_claim(ep, channel);
+		} else {
+			use_ep(ep, 0);
+			omap_writew(ep->udc->clr_halt, UDC_CTRL);
+			ep->ackwait = 0;
+			if (!(ep->bEndpointAddress & USB_DIR_IN)) {
+				omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+				ep->ackwait = 1 + ep->double_buf;
+			}
+		}
+	}
+done:
+	VDBG("%s %s halt stat %d\n", ep->ep.name,
+		value ? "set" : "clear", status);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return status;
+}
+
+static const struct usb_ep_ops omap_ep_ops = {
+	.enable		= omap_ep_enable,
+	.disable	= omap_ep_disable,
+
+	.alloc_request	= omap_alloc_request,
+	.free_request	= omap_free_request,
+
+	.queue		= omap_ep_queue,
+	.dequeue	= omap_ep_dequeue,
+
+	.set_halt	= omap_ep_set_halt,
+	/* fifo_status ... report bytes in fifo */
+	/* fifo_flush ... flush fifo */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int omap_get_frame(struct usb_gadget *gadget)
+{
+	u16	sof = omap_readw(UDC_SOF);
+	return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC;
+}
+
+static int omap_wakeup(struct usb_gadget *gadget)
+{
+	struct omap_udc	*udc;
+	unsigned long	flags;
+	int		retval = -EHOSTUNREACH;
+
+	udc = container_of(gadget, struct omap_udc, gadget);
+
+	spin_lock_irqsave(&udc->lock, flags);
+	if (udc->devstat & UDC_SUS) {
+		/* NOTE:  OTG spec erratum says that OTG devices may
+		 * issue wakeups without host enable.
+		 */
+		if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) {
+			DBG("remote wakeup...\n");
+			omap_writew(UDC_RMT_WKP, UDC_SYSCON2);
+			retval = 0;
+		}
+
+	/* NOTE:  non-OTG systems may use SRP TOO... */
+	} else if (!(udc->devstat & UDC_ATT)) {
+		if (!IS_ERR_OR_NULL(udc->transceiver))
+			retval = otg_start_srp(udc->transceiver->otg);
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return retval;
+}
+
+static int
+omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+	struct omap_udc	*udc;
+	unsigned long	flags;
+	u16		syscon1;
+
+	gadget->is_selfpowered = (is_selfpowered != 0);
+	udc = container_of(gadget, struct omap_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	syscon1 = omap_readw(UDC_SYSCON1);
+	if (is_selfpowered)
+		syscon1 |= UDC_SELF_PWR;
+	else
+		syscon1 &= ~UDC_SELF_PWR;
+	omap_writew(syscon1, UDC_SYSCON1);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int can_pullup(struct omap_udc *udc)
+{
+	return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+static void pullup_enable(struct omap_udc *udc)
+{
+	u16 w;
+
+	w = omap_readw(UDC_SYSCON1);
+	w |= UDC_PULLUP_EN;
+	omap_writew(w, UDC_SYSCON1);
+	if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) {
+		u32 l;
+
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_BSESSVLD;
+		omap_writel(l, OTG_CTRL);
+	}
+	omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN);
+}
+
+static void pullup_disable(struct omap_udc *udc)
+{
+	u16 w;
+
+	if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) {
+		u32 l;
+
+		l = omap_readl(OTG_CTRL);
+		l &= ~OTG_BSESSVLD;
+		omap_writel(l, OTG_CTRL);
+	}
+	omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN);
+	w = omap_readw(UDC_SYSCON1);
+	w &= ~UDC_PULLUP_EN;
+	omap_writew(w, UDC_SYSCON1);
+}
+
+static struct omap_udc *udc;
+
+static void omap_udc_enable_clock(int enable)
+{
+	if (udc == NULL || udc->dc_clk == NULL || udc->hhc_clk == NULL)
+		return;
+
+	if (enable) {
+		clk_enable(udc->dc_clk);
+		clk_enable(udc->hhc_clk);
+		udelay(100);
+	} else {
+		clk_disable(udc->hhc_clk);
+		clk_disable(udc->dc_clk);
+	}
+}
+
+/*
+ * Called by whatever detects VBUS sessions:  external transceiver
+ * driver, or maybe GPIO0 VBUS IRQ.  May request 48 MHz clock.
+ */
+static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct omap_udc	*udc;
+	unsigned long	flags;
+	u32 l;
+
+	udc = container_of(gadget, struct omap_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	VDBG("VBUS %s\n", is_active ? "on" : "off");
+	udc->vbus_active = (is_active != 0);
+	if (cpu_is_omap15xx()) {
+		/* "software" detect, ignored if !VBUS_MODE_1510 */
+		l = omap_readl(FUNC_MUX_CTRL_0);
+		if (is_active)
+			l |= VBUS_CTRL_1510;
+		else
+			l &= ~VBUS_CTRL_1510;
+		omap_writel(l, FUNC_MUX_CTRL_0);
+	}
+	if (udc->dc_clk != NULL && is_active) {
+		if (!udc->clk_requested) {
+			omap_udc_enable_clock(1);
+			udc->clk_requested = 1;
+		}
+	}
+	if (can_pullup(udc))
+		pullup_enable(udc);
+	else
+		pullup_disable(udc);
+	if (udc->dc_clk != NULL && !is_active) {
+		if (udc->clk_requested) {
+			omap_udc_enable_clock(0);
+			udc->clk_requested = 0;
+		}
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	struct omap_udc	*udc;
+
+	udc = container_of(gadget, struct omap_udc, gadget);
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		return usb_phy_set_power(udc->transceiver, mA);
+	return -EOPNOTSUPP;
+}
+
+static int omap_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct omap_udc	*udc;
+	unsigned long	flags;
+
+	udc = container_of(gadget, struct omap_udc, gadget);
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->softconnect = (is_on != 0);
+	if (can_pullup(udc))
+		pullup_enable(udc);
+	else
+		pullup_disable(udc);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static int omap_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int omap_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops omap_gadget_ops = {
+	.get_frame		= omap_get_frame,
+	.wakeup			= omap_wakeup,
+	.set_selfpowered	= omap_set_selfpowered,
+	.vbus_session		= omap_vbus_session,
+	.vbus_draw		= omap_vbus_draw,
+	.pullup			= omap_pullup,
+	.udc_start		= omap_udc_start,
+	.udc_stop		= omap_udc_stop,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* dequeue ALL requests; caller holds udc->lock */
+static void nuke(struct omap_ep *ep, int status)
+{
+	struct omap_req	*req;
+
+	ep->stopped = 1;
+
+	if (use_dma && ep->dma_channel)
+		dma_channel_release(ep);
+
+	use_ep(ep, 0);
+	omap_writew(UDC_CLR_EP, UDC_CTRL);
+	if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)
+		omap_writew(UDC_SET_HALT, UDC_CTRL);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct omap_req, queue);
+		done(ep, req, status);
+	}
+}
+
+/* caller holds udc->lock */
+static void udc_quiesce(struct omap_udc *udc)
+{
+	struct omap_ep	*ep;
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc->ep[0], -ESHUTDOWN);
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
+		nuke(ep, -ESHUTDOWN);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void update_otg(struct omap_udc *udc)
+{
+	u16	devstat;
+
+	if (!gadget_is_otg(&udc->gadget))
+		return;
+
+	if (omap_readl(OTG_CTRL) & OTG_ID)
+		devstat = omap_readw(UDC_DEVSTAT);
+	else
+		devstat = 0;
+
+	udc->gadget.b_hnp_enable = !!(devstat & UDC_B_HNP_ENABLE);
+	udc->gadget.a_hnp_support = !!(devstat & UDC_A_HNP_SUPPORT);
+	udc->gadget.a_alt_hnp_support = !!(devstat & UDC_A_ALT_HNP_SUPPORT);
+
+	/* Enable HNP early, avoiding races on suspend irq path.
+	 * ASSUMES OTG state machine B_BUS_REQ input is true.
+	 */
+	if (udc->gadget.b_hnp_enable) {
+		u32 l;
+
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_B_HNPEN | OTG_B_BUSREQ;
+		l &= ~OTG_PULLUP;
+		omap_writel(l, OTG_CTRL);
+	}
+}
+
+static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+{
+	struct omap_ep	*ep0 = &udc->ep[0];
+	struct omap_req	*req = NULL;
+
+	ep0->irqs++;
+
+	/* Clear any pending requests and then scrub any rx/tx state
+	 * before starting to handle the SETUP request.
+	 */
+	if (irq_src & UDC_SETUP) {
+		u16	ack = irq_src & (UDC_EP0_TX|UDC_EP0_RX);
+
+		nuke(ep0, 0);
+		if (ack) {
+			omap_writew(ack, UDC_IRQ_SRC);
+			irq_src = UDC_SETUP;
+		}
+	}
+
+	/* IN/OUT packets mean we're in the DATA or STATUS stage.
+	 * This driver uses only uses protocol stalls (ep0 never halts),
+	 * and if we got this far the gadget driver already had a
+	 * chance to stall.  Tries to be forgiving of host oddities.
+	 *
+	 * NOTE:  the last chance gadget drivers have to stall control
+	 * requests is during their request completion callback.
+	 */
+	if (!list_empty(&ep0->queue))
+		req = container_of(ep0->queue.next, struct omap_req, queue);
+
+	/* IN == TX to host */
+	if (irq_src & UDC_EP0_TX) {
+		int	stat;
+
+		omap_writew(UDC_EP0_TX, UDC_IRQ_SRC);
+		omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
+		stat = omap_readw(UDC_STAT_FLG);
+		if (stat & UDC_ACK) {
+			if (udc->ep0_in) {
+				/* write next IN packet from response,
+				 * or set up the status stage.
+				 */
+				if (req)
+					stat = write_fifo(ep0, req);
+				omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+				if (!req && udc->ep0_pending) {
+					omap_writew(UDC_EP_SEL, UDC_EP_NUM);
+					omap_writew(UDC_CLR_EP, UDC_CTRL);
+					omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+					omap_writew(0, UDC_EP_NUM);
+					udc->ep0_pending = 0;
+				} /* else:  6 wait states before it'll tx */
+			} else {
+				/* ack status stage of OUT transfer */
+				omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+				if (req)
+					done(ep0, req, 0);
+			}
+			req = NULL;
+		} else if (stat & UDC_STALL) {
+			omap_writew(UDC_CLR_HALT, UDC_CTRL);
+			omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+		} else {
+			omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+		}
+	}
+
+	/* OUT == RX from host */
+	if (irq_src & UDC_EP0_RX) {
+		int	stat;
+
+		omap_writew(UDC_EP0_RX, UDC_IRQ_SRC);
+		omap_writew(UDC_EP_SEL, UDC_EP_NUM);
+		stat = omap_readw(UDC_STAT_FLG);
+		if (stat & UDC_ACK) {
+			if (!udc->ep0_in) {
+				stat = 0;
+				/* read next OUT packet of request, maybe
+				 * reactiviting the fifo; stall on errors.
+				 */
+				stat = read_fifo(ep0, req);
+				if (!req || stat < 0) {
+					omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+					udc->ep0_pending = 0;
+					stat = 0;
+				} else if (stat == 0)
+					omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+				omap_writew(0, UDC_EP_NUM);
+
+				/* activate status stage */
+				if (stat == 1) {
+					done(ep0, req, 0);
+					/* that may have STALLed ep0... */
+					omap_writew(UDC_EP_SEL | UDC_EP_DIR,
+							UDC_EP_NUM);
+					omap_writew(UDC_CLR_EP, UDC_CTRL);
+					omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+					omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+					udc->ep0_pending = 0;
+				}
+			} else {
+				/* ack status stage of IN transfer */
+				omap_writew(0, UDC_EP_NUM);
+				if (req)
+					done(ep0, req, 0);
+			}
+		} else if (stat & UDC_STALL) {
+			omap_writew(UDC_CLR_HALT, UDC_CTRL);
+			omap_writew(0, UDC_EP_NUM);
+		} else {
+			omap_writew(0, UDC_EP_NUM);
+		}
+	}
+
+	/* SETUP starts all control transfers */
+	if (irq_src & UDC_SETUP) {
+		union u {
+			u16			word[4];
+			struct usb_ctrlrequest	r;
+		} u;
+		int			status = -EINVAL;
+		struct omap_ep		*ep;
+
+		/* read the (latest) SETUP message */
+		do {
+			omap_writew(UDC_SETUP_SEL, UDC_EP_NUM);
+			/* two bytes at a time */
+			u.word[0] = omap_readw(UDC_DATA);
+			u.word[1] = omap_readw(UDC_DATA);
+			u.word[2] = omap_readw(UDC_DATA);
+			u.word[3] = omap_readw(UDC_DATA);
+			omap_writew(0, UDC_EP_NUM);
+		} while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP);
+
+#define	w_value		le16_to_cpu(u.r.wValue)
+#define	w_index		le16_to_cpu(u.r.wIndex)
+#define	w_length	le16_to_cpu(u.r.wLength)
+
+		/* Delegate almost all control requests to the gadget driver,
+		 * except for a handful of ch9 status/feature requests that
+		 * hardware doesn't autodecode _and_ the gadget API hides.
+		 */
+		udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+		udc->ep0_set_config = 0;
+		udc->ep0_pending = 1;
+		ep0->stopped = 0;
+		ep0->ackwait = 0;
+		switch (u.r.bRequest) {
+		case USB_REQ_SET_CONFIGURATION:
+			/* udc needs to know when ep != 0 is valid */
+			if (u.r.bRequestType != USB_RECIP_DEVICE)
+				goto delegate;
+			if (w_length != 0)
+				goto do_stall;
+			udc->ep0_set_config = 1;
+			udc->ep0_reset_config = (w_value == 0);
+			VDBG("set config %d\n", w_value);
+
+			/* update udc NOW since gadget driver may start
+			 * queueing requests immediately; clear config
+			 * later if it fails the request.
+			 */
+			if (udc->ep0_reset_config)
+				omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+			else
+				omap_writew(UDC_DEV_CFG, UDC_SYSCON2);
+			update_otg(udc);
+			goto delegate;
+		case USB_REQ_CLEAR_FEATURE:
+			/* clear endpoint halt */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (w_value != USB_ENDPOINT_HALT
+					|| w_length != 0)
+				goto do_stall;
+			ep = &udc->ep[w_index & 0xf];
+			if (ep != ep0) {
+				if (w_index & USB_DIR_IN)
+					ep += 16;
+				if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+						|| !ep->ep.desc)
+					goto do_stall;
+				use_ep(ep, 0);
+				omap_writew(udc->clr_halt, UDC_CTRL);
+				ep->ackwait = 0;
+				if (!(ep->bEndpointAddress & USB_DIR_IN)) {
+					omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+					ep->ackwait = 1 + ep->double_buf;
+				}
+				/* NOTE:  assumes the host behaves sanely,
+				 * only clearing real halts.  Else we may
+				 * need to kill pending transfers and then
+				 * restart the queue... very messy for DMA!
+				 */
+			}
+			VDBG("%s halt cleared by host\n", ep->name);
+			goto ep0out_status_stage;
+		case USB_REQ_SET_FEATURE:
+			/* set endpoint halt */
+			if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+				goto delegate;
+			if (w_value != USB_ENDPOINT_HALT
+					|| w_length != 0)
+				goto do_stall;
+			ep = &udc->ep[w_index & 0xf];
+			if (w_index & USB_DIR_IN)
+				ep += 16;
+			if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+					|| ep == ep0 || !ep->ep.desc)
+				goto do_stall;
+			if (use_dma && ep->has_dma) {
+				/* this has rude side-effects (aborts) and
+				 * can't really work if DMA-IN is active
+				 */
+				DBG("%s host set_halt, NYET\n", ep->name);
+				goto do_stall;
+			}
+			use_ep(ep, 0);
+			/* can't halt if fifo isn't empty... */
+			omap_writew(UDC_CLR_EP, UDC_CTRL);
+			omap_writew(UDC_SET_HALT, UDC_CTRL);
+			VDBG("%s halted by host\n", ep->name);
+ep0out_status_stage:
+			status = 0;
+			omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
+			omap_writew(UDC_CLR_EP, UDC_CTRL);
+			omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+			omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+			udc->ep0_pending = 0;
+			break;
+		case USB_REQ_GET_STATUS:
+			/* USB_ENDPOINT_HALT status? */
+			if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+				goto intf_status;
+
+			/* ep0 never stalls */
+			if (!(w_index & 0xf))
+				goto zero_status;
+
+			/* only active endpoints count */
+			ep = &udc->ep[w_index & 0xf];
+			if (w_index & USB_DIR_IN)
+				ep += 16;
+			if (!ep->ep.desc)
+				goto do_stall;
+
+			/* iso never stalls */
+			if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+				goto zero_status;
+
+			/* FIXME don't assume non-halted endpoints!! */
+			ERR("%s status, can't report\n", ep->ep.name);
+			goto do_stall;
+
+intf_status:
+			/* return interface status.  if we were pedantic,
+			 * we'd detect non-existent interfaces, and stall.
+			 */
+			if (u.r.bRequestType
+					!= (USB_DIR_IN|USB_RECIP_INTERFACE))
+				goto delegate;
+
+zero_status:
+			/* return two zero bytes */
+			omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
+			omap_writew(0, UDC_DATA);
+			omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+			omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+			status = 0;
+			VDBG("GET_STATUS, interface %d\n", w_index);
+			/* next, status stage */
+			break;
+		default:
+delegate:
+			/* activate the ep0out fifo right away */
+			if (!udc->ep0_in && w_length) {
+				omap_writew(0, UDC_EP_NUM);
+				omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+			}
+
+			/* gadget drivers see class/vendor specific requests,
+			 * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION},
+			 * and more
+			 */
+			VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
+				u.r.bRequestType, u.r.bRequest,
+				w_value, w_index, w_length);
+
+#undef	w_value
+#undef	w_index
+#undef	w_length
+
+			/* The gadget driver may return an error here,
+			 * causing an immediate protocol stall.
+			 *
+			 * Else it must issue a response, either queueing a
+			 * response buffer for the DATA stage, or halting ep0
+			 * (causing a protocol stall, not a real halt).  A
+			 * zero length buffer means no DATA stage.
+			 *
+			 * It's fine to issue that response after the setup()
+			 * call returns, and this IRQ was handled.
+			 */
+			udc->ep0_setup = 1;
+			spin_unlock(&udc->lock);
+			status = udc->driver->setup(&udc->gadget, &u.r);
+			spin_lock(&udc->lock);
+			udc->ep0_setup = 0;
+		}
+
+		if (status < 0) {
+do_stall:
+			VDBG("req %02x.%02x protocol STALL; stat %d\n",
+					u.r.bRequestType, u.r.bRequest, status);
+			if (udc->ep0_set_config) {
+				if (udc->ep0_reset_config)
+					WARNING("error resetting config?\n");
+				else
+					omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+			}
+			omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+			udc->ep0_pending = 0;
+		}
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT)
+
+static void devstate_irq(struct omap_udc *udc, u16 irq_src)
+{
+	u16	devstat, change;
+
+	devstat = omap_readw(UDC_DEVSTAT);
+	change = devstat ^ udc->devstat;
+	udc->devstat = devstat;
+
+	if (change & (UDC_USB_RESET|UDC_ATT)) {
+		udc_quiesce(udc);
+
+		if (change & UDC_ATT) {
+			/* driver for any external transceiver will
+			 * have called omap_vbus_session() already
+			 */
+			if (devstat & UDC_ATT) {
+				udc->gadget.speed = USB_SPEED_FULL;
+				VDBG("connect\n");
+				if (IS_ERR_OR_NULL(udc->transceiver))
+					pullup_enable(udc);
+				/* if (driver->connect) call it */
+			} else if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+				udc->gadget.speed = USB_SPEED_UNKNOWN;
+				if (IS_ERR_OR_NULL(udc->transceiver))
+					pullup_disable(udc);
+				DBG("disconnect, gadget %s\n",
+					udc->driver->driver.name);
+				if (udc->driver->disconnect) {
+					spin_unlock(&udc->lock);
+					udc->driver->disconnect(&udc->gadget);
+					spin_lock(&udc->lock);
+				}
+			}
+			change &= ~UDC_ATT;
+		}
+
+		if (change & UDC_USB_RESET) {
+			if (devstat & UDC_USB_RESET) {
+				VDBG("RESET=1\n");
+			} else {
+				udc->gadget.speed = USB_SPEED_FULL;
+				INFO("USB reset done, gadget %s\n",
+					udc->driver->driver.name);
+				/* ep0 traffic is legal from now on */
+				omap_writew(UDC_DS_CHG_IE | UDC_EP0_IE,
+						UDC_IRQ_EN);
+			}
+			change &= ~UDC_USB_RESET;
+		}
+	}
+	if (change & UDC_SUS) {
+		if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+			/* FIXME tell isp1301 to suspend/resume (?) */
+			if (devstat & UDC_SUS) {
+				VDBG("suspend\n");
+				update_otg(udc);
+				/* HNP could be under way already */
+				if (udc->gadget.speed == USB_SPEED_FULL
+						&& udc->driver->suspend) {
+					spin_unlock(&udc->lock);
+					udc->driver->suspend(&udc->gadget);
+					spin_lock(&udc->lock);
+				}
+				if (!IS_ERR_OR_NULL(udc->transceiver))
+					usb_phy_set_suspend(
+							udc->transceiver, 1);
+			} else {
+				VDBG("resume\n");
+				if (!IS_ERR_OR_NULL(udc->transceiver))
+					usb_phy_set_suspend(
+							udc->transceiver, 0);
+				if (udc->gadget.speed == USB_SPEED_FULL
+						&& udc->driver->resume) {
+					spin_unlock(&udc->lock);
+					udc->driver->resume(&udc->gadget);
+					spin_lock(&udc->lock);
+				}
+			}
+		}
+		change &= ~UDC_SUS;
+	}
+	if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) {
+		update_otg(udc);
+		change &= ~OTG_FLAGS;
+	}
+
+	change &= ~(UDC_CFG|UDC_DEF|UDC_ADD);
+	if (change)
+		VDBG("devstat %03x, ignore change %03x\n",
+			devstat,  change);
+
+	omap_writew(UDC_DS_CHG, UDC_IRQ_SRC);
+}
+
+static irqreturn_t omap_udc_irq(int irq, void *_udc)
+{
+	struct omap_udc	*udc = _udc;
+	u16		irq_src;
+	irqreturn_t	status = IRQ_NONE;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	irq_src = omap_readw(UDC_IRQ_SRC);
+
+	/* Device state change (usb ch9 stuff) */
+	if (irq_src & UDC_DS_CHG) {
+		devstate_irq(_udc, irq_src);
+		status = IRQ_HANDLED;
+		irq_src &= ~UDC_DS_CHG;
+	}
+
+	/* EP0 control transfers */
+	if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) {
+		ep0_irq(_udc, irq_src);
+		status = IRQ_HANDLED;
+		irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX);
+	}
+
+	/* DMA transfer completion */
+	if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) {
+		dma_irq(_udc, irq_src);
+		status = IRQ_HANDLED;
+		irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT);
+	}
+
+	irq_src &= ~(UDC_IRQ_SOF | UDC_EPN_TX|UDC_EPN_RX);
+	if (irq_src)
+		DBG("udc_irq, unhandled %03x\n", irq_src);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return status;
+}
+
+/* workaround for seemingly-lost IRQs for RX ACKs... */
+#define PIO_OUT_TIMEOUT	(jiffies + HZ/3)
+#define HALF_FULL(f)	(!((f)&(UDC_NON_ISO_FIFO_FULL|UDC_NON_ISO_FIFO_EMPTY)))
+
+static void pio_out_timer(struct timer_list *t)
+{
+	struct omap_ep	*ep = from_timer(ep, t, timer);
+	unsigned long	flags;
+	u16		stat_flg;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	if (!list_empty(&ep->queue) && ep->ackwait) {
+		use_ep(ep, UDC_EP_SEL);
+		stat_flg = omap_readw(UDC_STAT_FLG);
+
+		if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN)
+				|| (ep->double_buf && HALF_FULL(stat_flg)))) {
+			struct omap_req	*req;
+
+			VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg);
+			req = container_of(ep->queue.next,
+					struct omap_req, queue);
+			(void) read_fifo(ep, req);
+			omap_writew(ep->bEndpointAddress, UDC_EP_NUM);
+			omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+			ep->ackwait = 1 + ep->double_buf;
+		} else
+			deselect_ep();
+	}
+	mod_timer(&ep->timer, PIO_OUT_TIMEOUT);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+}
+
+static irqreturn_t omap_udc_pio_irq(int irq, void *_dev)
+{
+	u16		epn_stat, irq_src;
+	irqreturn_t	status = IRQ_NONE;
+	struct omap_ep	*ep;
+	int		epnum;
+	struct omap_udc	*udc = _dev;
+	struct omap_req	*req;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	epn_stat = omap_readw(UDC_EPN_STAT);
+	irq_src = omap_readw(UDC_IRQ_SRC);
+
+	/* handle OUT first, to avoid some wasteful NAKs */
+	if (irq_src & UDC_EPN_RX) {
+		epnum = (epn_stat >> 8) & 0x0f;
+		omap_writew(UDC_EPN_RX, UDC_IRQ_SRC);
+		status = IRQ_HANDLED;
+		ep = &udc->ep[epnum];
+		ep->irqs++;
+
+		omap_writew(epnum | UDC_EP_SEL, UDC_EP_NUM);
+		ep->fnf = 0;
+		if (omap_readw(UDC_STAT_FLG) & UDC_ACK) {
+			ep->ackwait--;
+			if (!list_empty(&ep->queue)) {
+				int stat;
+				req = container_of(ep->queue.next,
+						struct omap_req, queue);
+				stat = read_fifo(ep, req);
+				if (!ep->double_buf)
+					ep->fnf = 1;
+			}
+		}
+		/* min 6 clock delay before clearing EP_SEL ... */
+		epn_stat = omap_readw(UDC_EPN_STAT);
+		epn_stat = omap_readw(UDC_EPN_STAT);
+		omap_writew(epnum, UDC_EP_NUM);
+
+		/* enabling fifo _after_ clearing ACK, contrary to docs,
+		 * reduces lossage; timer still needed though (sigh).
+		 */
+		if (ep->fnf) {
+			omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+			ep->ackwait = 1 + ep->double_buf;
+		}
+		mod_timer(&ep->timer, PIO_OUT_TIMEOUT);
+	}
+
+	/* then IN transfers */
+	else if (irq_src & UDC_EPN_TX) {
+		epnum = epn_stat & 0x0f;
+		omap_writew(UDC_EPN_TX, UDC_IRQ_SRC);
+		status = IRQ_HANDLED;
+		ep = &udc->ep[16 + epnum];
+		ep->irqs++;
+
+		omap_writew(epnum | UDC_EP_DIR | UDC_EP_SEL, UDC_EP_NUM);
+		if (omap_readw(UDC_STAT_FLG) & UDC_ACK) {
+			ep->ackwait = 0;
+			if (!list_empty(&ep->queue)) {
+				req = container_of(ep->queue.next,
+						struct omap_req, queue);
+				(void) write_fifo(ep, req);
+			}
+		}
+		/* min 6 clock delay before clearing EP_SEL ... */
+		epn_stat = omap_readw(UDC_EPN_STAT);
+		epn_stat = omap_readw(UDC_EPN_STAT);
+		omap_writew(epnum | UDC_EP_DIR, UDC_EP_NUM);
+		/* then 6 clocks before it'd tx */
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+#ifdef	USE_ISO
+static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
+{
+	struct omap_udc	*udc = _dev;
+	struct omap_ep	*ep;
+	int		pending = 0;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* handle all non-DMA ISO transfers */
+	list_for_each_entry(ep, &udc->iso, iso) {
+		u16		stat;
+		struct omap_req	*req;
+
+		if (ep->has_dma || list_empty(&ep->queue))
+			continue;
+		req = list_entry(ep->queue.next, struct omap_req, queue);
+
+		use_ep(ep, UDC_EP_SEL);
+		stat = omap_readw(UDC_STAT_FLG);
+
+		/* NOTE: like the other controller drivers, this isn't
+		 * currently reporting lost or damaged frames.
+		 */
+		if (ep->bEndpointAddress & USB_DIR_IN) {
+			if (stat & UDC_MISS_IN)
+				/* done(ep, req, -EPROTO) */;
+			else
+				write_fifo(ep, req);
+		} else {
+			int	status = 0;
+
+			if (stat & UDC_NO_RXPACKET)
+				status = -EREMOTEIO;
+			else if (stat & UDC_ISO_ERR)
+				status = -EILSEQ;
+			else if (stat & UDC_DATA_FLUSH)
+				status = -ENOSR;
+
+			if (status)
+				/* done(ep, req, status) */;
+			else
+				read_fifo(ep, req);
+		}
+		deselect_ep();
+		/* 6 wait states before next EP */
+
+		ep->irqs++;
+		if (!list_empty(&ep->queue))
+			pending = 1;
+	}
+	if (!pending) {
+		u16 w;
+
+		w = omap_readw(UDC_IRQ_EN);
+		w &= ~UDC_SOF_IE;
+		omap_writew(w, UDC_IRQ_EN);
+	}
+	omap_writew(UDC_IRQ_SOF, UDC_IRQ_SRC);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return IRQ_HANDLED;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static inline int machine_without_vbus_sense(void)
+{
+	return machine_is_omap_innovator()
+		|| machine_is_omap_osk()
+		|| machine_is_omap_palmte()
+		|| machine_is_sx1()
+		/* No known omap7xx boards with vbus sense */
+		|| cpu_is_omap7xx();
+}
+
+static int omap_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	int		status;
+	struct omap_ep	*ep;
+	unsigned long	flags;
+
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* reset state */
+	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+		ep->irqs = 0;
+		if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+			continue;
+		use_ep(ep, 0);
+		omap_writew(UDC_SET_HALT, UDC_CTRL);
+	}
+	udc->ep0_pending = 0;
+	udc->ep[0].irqs = 0;
+	udc->softconnect = 1;
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	udc->driver = driver;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	if (udc->dc_clk != NULL)
+		omap_udc_enable_clock(1);
+
+	omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
+
+	/* connect to bus through transceiver */
+	if (!IS_ERR_OR_NULL(udc->transceiver)) {
+		status = otg_set_peripheral(udc->transceiver->otg,
+						&udc->gadget);
+		if (status < 0) {
+			ERR("can't bind to transceiver\n");
+			udc->driver = NULL;
+			goto done;
+		}
+	} else {
+		status = 0;
+		if (can_pullup(udc))
+			pullup_enable(udc);
+		else
+			pullup_disable(udc);
+	}
+
+	/* boards that don't have VBUS sensing can't autogate 48MHz;
+	 * can't enter deep sleep while a gadget driver is active.
+	 */
+	if (machine_without_vbus_sense())
+		omap_vbus_session(&udc->gadget, 1);
+
+done:
+	if (udc->dc_clk != NULL)
+		omap_udc_enable_clock(0);
+
+	return status;
+}
+
+static int omap_udc_stop(struct usb_gadget *g)
+{
+	unsigned long	flags;
+	int		status = -ENODEV;
+
+	if (udc->dc_clk != NULL)
+		omap_udc_enable_clock(1);
+
+	if (machine_without_vbus_sense())
+		omap_vbus_session(&udc->gadget, 0);
+
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		(void) otg_set_peripheral(udc->transceiver->otg, NULL);
+	else
+		pullup_disable(udc);
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc_quiesce(udc);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	udc->driver = NULL;
+
+	if (udc->dc_clk != NULL)
+		omap_udc_enable_clock(0);
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/udc";
+
+#define FOURBITS "%s%s%s%s"
+#define EIGHTBITS "%s%s%s%s%s%s%s%s"
+
+static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
+{
+	u16		stat_flg;
+	struct omap_req	*req;
+	char		buf[20];
+
+	use_ep(ep, 0);
+
+	if (use_dma && ep->has_dma)
+		snprintf(buf, sizeof buf, "(%cxdma%d lch%d) ",
+			(ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r',
+			ep->dma_channel - 1, ep->lch);
+	else
+		buf[0] = 0;
+
+	stat_flg = omap_readw(UDC_STAT_FLG);
+	seq_printf(s,
+		"\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n",
+		ep->name, buf,
+		ep->double_buf ? "dbuf " : "",
+		({ char *s;
+		switch (ep->ackwait) {
+		case 0:
+			s = "";
+			break;
+		case 1:
+			s = "(ackw) ";
+			break;
+		case 2:
+			s = "(ackw2) ";
+			break;
+		default:
+			s = "(?) ";
+			break;
+		} s; }),
+		ep->irqs, stat_flg,
+		(stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "",
+		(stat_flg & UDC_MISS_IN) ? "miss_in " : "",
+		(stat_flg & UDC_DATA_FLUSH) ? "data_flush " : "",
+		(stat_flg & UDC_ISO_ERR) ? "iso_err " : "",
+		(stat_flg & UDC_ISO_FIFO_EMPTY) ? "iso_fifo_empty " : "",
+		(stat_flg & UDC_ISO_FIFO_FULL) ? "iso_fifo_full " : "",
+		(stat_flg & UDC_EP_HALTED) ? "HALT " : "",
+		(stat_flg & UDC_STALL) ? "STALL " : "",
+		(stat_flg & UDC_NAK) ? "NAK " : "",
+		(stat_flg & UDC_ACK) ? "ACK " : "",
+		(stat_flg & UDC_FIFO_EN) ? "fifo_en " : "",
+		(stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "",
+		(stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : "");
+
+	if (list_empty(&ep->queue))
+		seq_printf(s, "\t(queue empty)\n");
+	else
+		list_for_each_entry(req, &ep->queue, queue) {
+			unsigned	length = req->req.actual;
+
+			if (use_dma && buf[0]) {
+				length += ((ep->bEndpointAddress & USB_DIR_IN)
+						? dma_src_len : dma_dest_len)
+					(ep, req->req.dma + length);
+				buf[0] = 0;
+			}
+			seq_printf(s, "\treq %p len %d/%d buf %p\n",
+					&req->req, length,
+					req->req.length, req->req.buf);
+		}
+}
+
+static char *trx_mode(unsigned m, int enabled)
+{
+	switch (m) {
+	case 0:
+		return enabled ? "*6wire" : "unused";
+	case 1:
+		return "4wire";
+	case 2:
+		return "3wire";
+	case 3:
+		return "6wire";
+	default:
+		return "unknown";
+	}
+}
+
+static int proc_otg_show(struct seq_file *s)
+{
+	u32		tmp;
+	u32		trans = 0;
+	char		*ctrl_name = "(UNKNOWN)";
+
+	tmp = omap_readl(OTG_REV);
+	ctrl_name = "tranceiver_ctrl";
+	trans = omap_readw(USB_TRANSCEIVER_CTRL);
+	seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
+		tmp >> 4, tmp & 0xf, ctrl_name, trans);
+	tmp = omap_readw(OTG_SYSCON_1);
+	seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
+			FOURBITS "\n", tmp,
+		trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R),
+		trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R),
+		(USB0_TRX_MODE(tmp) == 0 && !cpu_is_omap1710())
+			? "internal"
+			: trx_mode(USB0_TRX_MODE(tmp), 1),
+		(tmp & OTG_IDLE_EN) ? " !otg" : "",
+		(tmp & HST_IDLE_EN) ? " !host" : "",
+		(tmp & DEV_IDLE_EN) ? " !dev" : "",
+		(tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active");
+	tmp = omap_readl(OTG_SYSCON_2);
+	seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS
+			" b_ase_brst=%d hmc=%d\n", tmp,
+		(tmp & OTG_EN) ? " otg_en" : "",
+		(tmp & USBX_SYNCHRO) ? " synchro" : "",
+		/* much more SRP stuff */
+		(tmp & SRP_DATA) ? " srp_data" : "",
+		(tmp & SRP_VBUS) ? " srp_vbus" : "",
+		(tmp & OTG_PADEN) ? " otg_paden" : "",
+		(tmp & HMC_PADEN) ? " hmc_paden" : "",
+		(tmp & UHOST_EN) ? " uhost_en" : "",
+		(tmp & HMC_TLLSPEED) ? " tllspeed" : "",
+		(tmp & HMC_TLLATTACH) ? " tllattach" : "",
+		B_ASE_BRST(tmp),
+		OTG_HMC(tmp));
+	tmp = omap_readl(OTG_CTRL);
+	seq_printf(s, "otg_ctrl    %06x" EIGHTBITS EIGHTBITS "%s\n", tmp,
+		(tmp & OTG_ASESSVLD) ? " asess" : "",
+		(tmp & OTG_BSESSEND) ? " bsess_end" : "",
+		(tmp & OTG_BSESSVLD) ? " bsess" : "",
+		(tmp & OTG_VBUSVLD) ? " vbus" : "",
+		(tmp & OTG_ID) ? " id" : "",
+		(tmp & OTG_DRIVER_SEL) ? " DEVICE" : " HOST",
+		(tmp & OTG_A_SETB_HNPEN) ? " a_setb_hnpen" : "",
+		(tmp & OTG_A_BUSREQ) ? " a_bus" : "",
+		(tmp & OTG_B_HNPEN) ? " b_hnpen" : "",
+		(tmp & OTG_B_BUSREQ) ? " b_bus" : "",
+		(tmp & OTG_BUSDROP) ? " busdrop" : "",
+		(tmp & OTG_PULLDOWN) ? " down" : "",
+		(tmp & OTG_PULLUP) ? " up" : "",
+		(tmp & OTG_DRV_VBUS) ? " drv" : "",
+		(tmp & OTG_PD_VBUS) ? " pd_vb" : "",
+		(tmp & OTG_PU_VBUS) ? " pu_vb" : "",
+		(tmp & OTG_PU_ID) ? " pu_id" : ""
+		);
+	tmp = omap_readw(OTG_IRQ_EN);
+	seq_printf(s, "otg_irq_en  %04x" "\n", tmp);
+	tmp = omap_readw(OTG_IRQ_SRC);
+	seq_printf(s, "otg_irq_src %04x" "\n", tmp);
+	tmp = omap_readw(OTG_OUTCTRL);
+	seq_printf(s, "otg_outctrl %04x" "\n", tmp);
+	tmp = omap_readw(OTG_TEST);
+	seq_printf(s, "otg_test    %04x" "\n", tmp);
+	return 0;
+}
+
+static int proc_udc_show(struct seq_file *s, void *_)
+{
+	u32		tmp;
+	struct omap_ep	*ep;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	seq_printf(s, "%s, version: " DRIVER_VERSION
+#ifdef	USE_ISO
+		" (iso)"
+#endif
+		"%s\n",
+		driver_desc,
+		use_dma ?  " (dma)" : "");
+
+	tmp = omap_readw(UDC_REV) & 0xff;
+	seq_printf(s,
+		"UDC rev %d.%d, fifo mode %d, gadget %s\n"
+		"hmc %d, transceiver %s\n",
+		tmp >> 4, tmp & 0xf,
+		fifo_mode,
+		udc->driver ? udc->driver->driver.name : "(none)",
+		HMC,
+		udc->transceiver
+			? udc->transceiver->label
+			: (cpu_is_omap1710()
+				? "external" : "(none)"));
+	seq_printf(s, "ULPD control %04x req %04x status %04x\n",
+		omap_readw(ULPD_CLOCK_CTRL),
+		omap_readw(ULPD_SOFT_REQ),
+		omap_readw(ULPD_STATUS_REQ));
+
+	/* OTG controller registers */
+	if (!cpu_is_omap15xx())
+		proc_otg_show(s);
+
+	tmp = omap_readw(UDC_SYSCON1);
+	seq_printf(s, "\nsyscon1     %04x" EIGHTBITS "\n", tmp,
+		(tmp & UDC_CFG_LOCK) ? " cfg_lock" : "",
+		(tmp & UDC_DATA_ENDIAN) ? " data_endian" : "",
+		(tmp & UDC_DMA_ENDIAN) ? " dma_endian" : "",
+		(tmp & UDC_NAK_EN) ? " nak" : "",
+		(tmp & UDC_AUTODECODE_DIS) ? " autodecode_dis" : "",
+		(tmp & UDC_SELF_PWR) ? " self_pwr" : "",
+		(tmp & UDC_SOFF_DIS) ? " soff_dis" : "",
+		(tmp & UDC_PULLUP_EN) ? " PULLUP" : "");
+	/* syscon2 is write-only */
+
+	/* UDC controller registers */
+	if (!(tmp & UDC_PULLUP_EN)) {
+		seq_printf(s, "(suspended)\n");
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return 0;
+	}
+
+	tmp = omap_readw(UDC_DEVSTAT);
+	seq_printf(s, "devstat     %04x" EIGHTBITS "%s%s\n", tmp,
+		(tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "",
+		(tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "",
+		(tmp & UDC_A_ALT_HNP_SUPPORT) ? " a_alt_hnp" : "",
+		(tmp & UDC_R_WK_OK) ? " r_wk_ok" : "",
+		(tmp & UDC_USB_RESET) ? " usb_reset" : "",
+		(tmp & UDC_SUS) ? " SUS" : "",
+		(tmp & UDC_CFG) ? " CFG" : "",
+		(tmp & UDC_ADD) ? " ADD" : "",
+		(tmp & UDC_DEF) ? " DEF" : "",
+		(tmp & UDC_ATT) ? " ATT" : "");
+	seq_printf(s, "sof         %04x\n", omap_readw(UDC_SOF));
+	tmp = omap_readw(UDC_IRQ_EN);
+	seq_printf(s, "irq_en      %04x" FOURBITS "%s\n", tmp,
+		(tmp & UDC_SOF_IE) ? " sof" : "",
+		(tmp & UDC_EPN_RX_IE) ? " epn_rx" : "",
+		(tmp & UDC_EPN_TX_IE) ? " epn_tx" : "",
+		(tmp & UDC_DS_CHG_IE) ? " ds_chg" : "",
+		(tmp & UDC_EP0_IE) ? " ep0" : "");
+	tmp = omap_readw(UDC_IRQ_SRC);
+	seq_printf(s, "irq_src     %04x" EIGHTBITS "%s%s\n", tmp,
+		(tmp & UDC_TXN_DONE) ? " txn_done" : "",
+		(tmp & UDC_RXN_CNT) ? " rxn_cnt" : "",
+		(tmp & UDC_RXN_EOT) ? " rxn_eot" : "",
+		(tmp & UDC_IRQ_SOF) ? " sof" : "",
+		(tmp & UDC_EPN_RX) ? " epn_rx" : "",
+		(tmp & UDC_EPN_TX) ? " epn_tx" : "",
+		(tmp & UDC_DS_CHG) ? " ds_chg" : "",
+		(tmp & UDC_SETUP) ? " setup" : "",
+		(tmp & UDC_EP0_RX) ? " ep0out" : "",
+		(tmp & UDC_EP0_TX) ? " ep0in" : "");
+	if (use_dma) {
+		unsigned i;
+
+		tmp = omap_readw(UDC_DMA_IRQ_EN);
+		seq_printf(s, "dma_irq_en  %04x%s" EIGHTBITS "\n", tmp,
+			(tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "",
+			(tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "",
+			(tmp & UDC_RX_EOT_IE(3)) ? " rx2_eot" : "",
+
+			(tmp & UDC_TX_DONE_IE(2)) ? " tx1_done" : "",
+			(tmp & UDC_RX_CNT_IE(2)) ? " rx1_cnt" : "",
+			(tmp & UDC_RX_EOT_IE(2)) ? " rx1_eot" : "",
+
+			(tmp & UDC_TX_DONE_IE(1)) ? " tx0_done" : "",
+			(tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "",
+			(tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : "");
+
+		tmp = omap_readw(UDC_RXDMA_CFG);
+		seq_printf(s, "rxdma_cfg   %04x\n", tmp);
+		if (tmp) {
+			for (i = 0; i < 3; i++) {
+				if ((tmp & (0x0f << (i * 4))) == 0)
+					continue;
+				seq_printf(s, "rxdma[%d]    %04x\n", i,
+						omap_readw(UDC_RXDMA(i + 1)));
+			}
+		}
+		tmp = omap_readw(UDC_TXDMA_CFG);
+		seq_printf(s, "txdma_cfg   %04x\n", tmp);
+		if (tmp) {
+			for (i = 0; i < 3; i++) {
+				if (!(tmp & (0x0f << (i * 4))))
+					continue;
+				seq_printf(s, "txdma[%d]    %04x\n", i,
+						omap_readw(UDC_TXDMA(i + 1)));
+			}
+		}
+	}
+
+	tmp = omap_readw(UDC_DEVSTAT);
+	if (tmp & UDC_ATT) {
+		proc_ep_show(s, &udc->ep[0]);
+		if (tmp & UDC_ADD) {
+			list_for_each_entry(ep, &udc->gadget.ep_list,
+					ep.ep_list) {
+				if (ep->ep.desc)
+					proc_ep_show(s, ep);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+static void create_proc_file(void)
+{
+	proc_create_single(proc_filename, 0, NULL, proc_udc_show);
+}
+
+static void remove_proc_file(void)
+{
+	remove_proc_entry(proc_filename, NULL);
+}
+
+#else
+
+static inline void create_proc_file(void) {}
+static inline void remove_proc_file(void) {}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Before this controller can enumerate, we need to pick an endpoint
+ * configuration, or "fifo_mode"  That involves allocating 2KB of packet
+ * buffer space among the endpoints we'll be operating.
+ *
+ * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when
+ * UDC_SYSCON_1.CFG_LOCK is set can now work.  We won't use that
+ * capability yet though.
+ */
+static unsigned
+omap_ep_setup(char *name, u8 addr, u8 type,
+		unsigned buf, unsigned maxp, int dbuf)
+{
+	struct omap_ep	*ep;
+	u16		epn_rxtx = 0;
+
+	/* OUT endpoints first, then IN */
+	ep = &udc->ep[addr & 0xf];
+	if (addr & USB_DIR_IN)
+		ep += 16;
+
+	/* in case of ep init table bugs */
+	BUG_ON(ep->name[0]);
+
+	/* chip setup ... bit values are same for IN, OUT */
+	if (type == USB_ENDPOINT_XFER_ISOC) {
+		switch (maxp) {
+		case 8:
+			epn_rxtx = 0 << 12;
+			break;
+		case 16:
+			epn_rxtx = 1 << 12;
+			break;
+		case 32:
+			epn_rxtx = 2 << 12;
+			break;
+		case 64:
+			epn_rxtx = 3 << 12;
+			break;
+		case 128:
+			epn_rxtx = 4 << 12;
+			break;
+		case 256:
+			epn_rxtx = 5 << 12;
+			break;
+		case 512:
+			epn_rxtx = 6 << 12;
+			break;
+		default:
+			BUG();
+		}
+		epn_rxtx |= UDC_EPN_RX_ISO;
+		dbuf = 1;
+	} else {
+		/* double-buffering "not supported" on 15xx,
+		 * and ignored for PIO-IN on newer chips
+		 * (for more reliable behavior)
+		 */
+		if (!use_dma || cpu_is_omap15xx())
+			dbuf = 0;
+
+		switch (maxp) {
+		case 8:
+			epn_rxtx = 0 << 12;
+			break;
+		case 16:
+			epn_rxtx = 1 << 12;
+			break;
+		case 32:
+			epn_rxtx = 2 << 12;
+			break;
+		case 64:
+			epn_rxtx = 3 << 12;
+			break;
+		default:
+			BUG();
+		}
+		if (dbuf && addr)
+			epn_rxtx |= UDC_EPN_RX_DB;
+		timer_setup(&ep->timer, pio_out_timer, 0);
+	}
+	if (addr)
+		epn_rxtx |= UDC_EPN_RX_VALID;
+	BUG_ON(buf & 0x07);
+	epn_rxtx |= buf >> 3;
+
+	DBG("%s addr %02x rxtx %04x maxp %d%s buf %d\n",
+		name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf);
+
+	if (addr & USB_DIR_IN)
+		omap_writew(epn_rxtx, UDC_EP_TX(addr & 0xf));
+	else
+		omap_writew(epn_rxtx, UDC_EP_RX(addr));
+
+	/* next endpoint's buffer starts after this one's */
+	buf += maxp;
+	if (dbuf)
+		buf += maxp;
+	BUG_ON(buf > 2048);
+
+	/* set up driver data structures */
+	BUG_ON(strlen(name) >= sizeof ep->name);
+	strlcpy(ep->name, name, sizeof ep->name);
+	INIT_LIST_HEAD(&ep->queue);
+	INIT_LIST_HEAD(&ep->iso);
+	ep->bEndpointAddress = addr;
+	ep->bmAttributes = type;
+	ep->double_buf = dbuf;
+	ep->udc = udc;
+
+	switch (type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		ep->ep.caps.type_control = true;
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		ep->ep.caps.type_iso = true;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep->ep.caps.type_bulk = true;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		ep->ep.caps.type_int = true;
+		break;
+	};
+
+	if (addr & USB_DIR_IN)
+		ep->ep.caps.dir_in = true;
+	else
+		ep->ep.caps.dir_out = true;
+
+	ep->ep.name = ep->name;
+	ep->ep.ops = &omap_ep_ops;
+	ep->maxpacket = maxp;
+	usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
+	list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+	return buf;
+}
+
+static void omap_udc_release(struct device *dev)
+{
+	pullup_disable(udc);
+	if (!IS_ERR_OR_NULL(udc->transceiver)) {
+		usb_put_phy(udc->transceiver);
+		udc->transceiver = NULL;
+	}
+	omap_writew(0, UDC_SYSCON1);
+	remove_proc_file();
+	if (udc->dc_clk) {
+		if (udc->clk_requested)
+			omap_udc_enable_clock(0);
+		clk_put(udc->hhc_clk);
+		clk_put(udc->dc_clk);
+	}
+	if (udc->done)
+		complete(udc->done);
+	kfree(udc);
+}
+
+static int
+omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
+{
+	unsigned	tmp, buf;
+
+	/* abolish any previous hardware state */
+	omap_writew(0, UDC_SYSCON1);
+	omap_writew(0, UDC_IRQ_EN);
+	omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
+	omap_writew(0, UDC_DMA_IRQ_EN);
+	omap_writew(0, UDC_RXDMA_CFG);
+	omap_writew(0, UDC_TXDMA_CFG);
+
+	/* UDC_PULLUP_EN gates the chip clock */
+	/* OTG_SYSCON_1 |= DEV_IDLE_EN; */
+
+	udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	spin_lock_init(&udc->lock);
+
+	udc->gadget.ops = &omap_gadget_ops;
+	udc->gadget.ep0 = &udc->ep[0].ep;
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+	INIT_LIST_HEAD(&udc->iso);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->gadget.max_speed = USB_SPEED_FULL;
+	udc->gadget.name = driver_name;
+	udc->gadget.quirk_ep_out_aligned_size = 1;
+	udc->transceiver = xceiv;
+
+	/* ep0 is special; put it right after the SETUP buffer */
+	buf = omap_ep_setup("ep0", 0, USB_ENDPOINT_XFER_CONTROL,
+			8 /* after SETUP */, 64 /* maxpacket */, 0);
+	list_del_init(&udc->ep[0].ep.ep_list);
+
+	/* initially disable all non-ep0 endpoints */
+	for (tmp = 1; tmp < 15; tmp++) {
+		omap_writew(0, UDC_EP_RX(tmp));
+		omap_writew(0, UDC_EP_TX(tmp));
+	}
+
+#define OMAP_BULK_EP(name, addr) \
+	buf = omap_ep_setup(name "-bulk", addr, \
+			USB_ENDPOINT_XFER_BULK, buf, 64, 1);
+#define OMAP_INT_EP(name, addr, maxp) \
+	buf = omap_ep_setup(name "-int", addr, \
+			USB_ENDPOINT_XFER_INT, buf, maxp, 0);
+#define OMAP_ISO_EP(name, addr, maxp) \
+	buf = omap_ep_setup(name "-iso", addr, \
+			USB_ENDPOINT_XFER_ISOC, buf, maxp, 1);
+
+	switch (fifo_mode) {
+	case 0:
+		OMAP_BULK_EP("ep1in",  USB_DIR_IN  | 1);
+		OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2);
+		OMAP_INT_EP("ep3in",   USB_DIR_IN  | 3, 16);
+		break;
+	case 1:
+		OMAP_BULK_EP("ep1in",  USB_DIR_IN  | 1);
+		OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2);
+		OMAP_INT_EP("ep9in",   USB_DIR_IN  | 9, 16);
+
+		OMAP_BULK_EP("ep3in",  USB_DIR_IN  | 3);
+		OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4);
+		OMAP_INT_EP("ep10in",  USB_DIR_IN  | 10, 16);
+
+		OMAP_BULK_EP("ep5in",  USB_DIR_IN  | 5);
+		OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5);
+		OMAP_INT_EP("ep11in",  USB_DIR_IN  | 11, 16);
+
+		OMAP_BULK_EP("ep6in",  USB_DIR_IN  | 6);
+		OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6);
+		OMAP_INT_EP("ep12in",  USB_DIR_IN  | 12, 16);
+
+		OMAP_BULK_EP("ep7in",  USB_DIR_IN  | 7);
+		OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7);
+		OMAP_INT_EP("ep13in",  USB_DIR_IN  | 13, 16);
+		OMAP_INT_EP("ep13out", USB_DIR_OUT | 13, 16);
+
+		OMAP_BULK_EP("ep8in",  USB_DIR_IN  | 8);
+		OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8);
+		OMAP_INT_EP("ep14in",  USB_DIR_IN  | 14, 16);
+		OMAP_INT_EP("ep14out", USB_DIR_OUT | 14, 16);
+
+		OMAP_BULK_EP("ep15in",  USB_DIR_IN  | 15);
+		OMAP_BULK_EP("ep15out", USB_DIR_OUT | 15);
+
+		break;
+
+#ifdef	USE_ISO
+	case 2:			/* mixed iso/bulk */
+		OMAP_ISO_EP("ep1in",   USB_DIR_IN  | 1, 256);
+		OMAP_ISO_EP("ep2out",  USB_DIR_OUT | 2, 256);
+		OMAP_ISO_EP("ep3in",   USB_DIR_IN  | 3, 128);
+		OMAP_ISO_EP("ep4out",  USB_DIR_OUT | 4, 128);
+
+		OMAP_INT_EP("ep5in",   USB_DIR_IN  | 5, 16);
+
+		OMAP_BULK_EP("ep6in",  USB_DIR_IN  | 6);
+		OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7);
+		OMAP_INT_EP("ep8in",   USB_DIR_IN  | 8, 16);
+		break;
+	case 3:			/* mixed bulk/iso */
+		OMAP_BULK_EP("ep1in",  USB_DIR_IN  | 1);
+		OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2);
+		OMAP_INT_EP("ep3in",   USB_DIR_IN  | 3, 16);
+
+		OMAP_BULK_EP("ep4in",  USB_DIR_IN  | 4);
+		OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5);
+		OMAP_INT_EP("ep6in",   USB_DIR_IN  | 6, 16);
+
+		OMAP_ISO_EP("ep7in",   USB_DIR_IN  | 7, 256);
+		OMAP_ISO_EP("ep8out",  USB_DIR_OUT | 8, 256);
+		OMAP_INT_EP("ep9in",   USB_DIR_IN  | 9, 16);
+		break;
+#endif
+
+	/* add more modes as needed */
+
+	default:
+		ERR("unsupported fifo_mode #%d\n", fifo_mode);
+		return -ENODEV;
+	}
+	omap_writew(UDC_CFG_LOCK|UDC_SELF_PWR, UDC_SYSCON1);
+	INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf);
+	return 0;
+}
+
+static int omap_udc_probe(struct platform_device *pdev)
+{
+	int			status = -ENODEV;
+	int			hmc;
+	struct usb_phy		*xceiv = NULL;
+	const char		*type = NULL;
+	struct omap_usb_config	*config = dev_get_platdata(&pdev->dev);
+	struct clk		*dc_clk = NULL;
+	struct clk		*hhc_clk = NULL;
+
+	if (cpu_is_omap7xx())
+		use_dma = 0;
+
+	/* NOTE:  "knows" the order of the resources! */
+	if (!request_mem_region(pdev->resource[0].start,
+			pdev->resource[0].end - pdev->resource[0].start + 1,
+			driver_name)) {
+		DBG("request_mem_region failed\n");
+		return -EBUSY;
+	}
+
+	if (cpu_is_omap16xx()) {
+		dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
+		hhc_clk = clk_get(&pdev->dev, "usb_hhc_ck");
+		BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
+		/* can't use omap_udc_enable_clock yet */
+		clk_enable(dc_clk);
+		clk_enable(hhc_clk);
+		udelay(100);
+	}
+
+	if (cpu_is_omap7xx()) {
+		dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
+		hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
+		BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
+		/* can't use omap_udc_enable_clock yet */
+		clk_enable(dc_clk);
+		clk_enable(hhc_clk);
+		udelay(100);
+	}
+
+	INFO("OMAP UDC rev %d.%d%s\n",
+		omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf,
+		config->otg ? ", Mini-AB" : "");
+
+	/* use the mode given to us by board init code */
+	if (cpu_is_omap15xx()) {
+		hmc = HMC_1510;
+		type = "(unknown)";
+
+		if (machine_without_vbus_sense()) {
+			/* just set up software VBUS detect, and then
+			 * later rig it so we always report VBUS.
+			 * FIXME without really sensing VBUS, we can't
+			 * know when to turn PULLUP_EN on/off; and that
+			 * means we always "need" the 48MHz clock.
+			 */
+			u32 tmp = omap_readl(FUNC_MUX_CTRL_0);
+			tmp &= ~VBUS_CTRL_1510;
+			omap_writel(tmp, FUNC_MUX_CTRL_0);
+			tmp |= VBUS_MODE_1510;
+			tmp &= ~VBUS_CTRL_1510;
+			omap_writel(tmp, FUNC_MUX_CTRL_0);
+		}
+	} else {
+		/* The transceiver may package some GPIO logic or handle
+		 * loopback and/or transceiverless setup; if we find one,
+		 * use it.  Except for OTG, we don't _need_ to talk to one;
+		 * but not having one probably means no VBUS detection.
+		 */
+		xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+		if (!IS_ERR_OR_NULL(xceiv))
+			type = xceiv->label;
+		else if (config->otg) {
+			DBG("OTG requires external transceiver!\n");
+			goto cleanup0;
+		}
+
+		hmc = HMC_1610;
+
+		switch (hmc) {
+		case 0:			/* POWERUP DEFAULT == 0 */
+		case 4:
+		case 12:
+		case 20:
+			if (!cpu_is_omap1710()) {
+				type = "integrated";
+				break;
+			}
+			/* FALL THROUGH */
+		case 3:
+		case 11:
+		case 16:
+		case 19:
+		case 25:
+			if (IS_ERR_OR_NULL(xceiv)) {
+				DBG("external transceiver not registered!\n");
+				type = "unknown";
+			}
+			break;
+		case 21:			/* internal loopback */
+			type = "loopback";
+			break;
+		case 14:			/* transceiverless */
+			if (cpu_is_omap1710())
+				goto bad_on_1710;
+			/* FALL THROUGH */
+		case 13:
+		case 15:
+			type = "no";
+			break;
+
+		default:
+bad_on_1710:
+			ERR("unrecognized UDC HMC mode %d\n", hmc);
+			goto cleanup0;
+		}
+	}
+
+	INFO("hmc mode %d, %s transceiver\n", hmc, type);
+
+	/* a "gadget" abstracts/virtualizes the controller */
+	status = omap_udc_setup(pdev, xceiv);
+	if (status)
+		goto cleanup0;
+
+	xceiv = NULL;
+	/* "udc" is now valid */
+	pullup_disable(udc);
+#if	IS_ENABLED(CONFIG_USB_OHCI_HCD)
+	udc->gadget.is_otg = (config->otg != 0);
+#endif
+
+	/* starting with omap1710 es2.0, clear toggle is a separate bit */
+	if (omap_readw(UDC_REV) >= 0x61)
+		udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE;
+	else
+		udc->clr_halt = UDC_RESET_EP;
+
+	/* USB general purpose IRQ:  ep0, state changes, dma, etc */
+	status = devm_request_irq(&pdev->dev, pdev->resource[1].start,
+				  omap_udc_irq, 0, driver_name, udc);
+	if (status != 0) {
+		ERR("can't get irq %d, err %d\n",
+			(int) pdev->resource[1].start, status);
+		goto cleanup1;
+	}
+
+	/* USB "non-iso" IRQ (PIO for all but ep0) */
+	status = devm_request_irq(&pdev->dev, pdev->resource[2].start,
+				  omap_udc_pio_irq, 0, "omap_udc pio", udc);
+	if (status != 0) {
+		ERR("can't get irq %d, err %d\n",
+			(int) pdev->resource[2].start, status);
+		goto cleanup1;
+	}
+#ifdef	USE_ISO
+	status = devm_request_irq(&pdev->dev, pdev->resource[3].start,
+				  omap_udc_iso_irq, 0, "omap_udc iso", udc);
+	if (status != 0) {
+		ERR("can't get irq %d, err %d\n",
+			(int) pdev->resource[3].start, status);
+		goto cleanup1;
+	}
+#endif
+	if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
+		udc->dc_clk = dc_clk;
+		udc->hhc_clk = hhc_clk;
+		clk_disable(hhc_clk);
+		clk_disable(dc_clk);
+	}
+
+	create_proc_file();
+	return usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+					  omap_udc_release);
+
+cleanup1:
+	kfree(udc);
+	udc = NULL;
+
+cleanup0:
+	if (!IS_ERR_OR_NULL(xceiv))
+		usb_put_phy(xceiv);
+
+	if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
+		clk_disable(hhc_clk);
+		clk_disable(dc_clk);
+		clk_put(hhc_clk);
+		clk_put(dc_clk);
+	}
+
+	release_mem_region(pdev->resource[0].start,
+			pdev->resource[0].end - pdev->resource[0].start + 1);
+
+	return status;
+}
+
+static int omap_udc_remove(struct platform_device *pdev)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	udc->done = &done;
+
+	usb_del_gadget_udc(&udc->gadget);
+
+	wait_for_completion(&done);
+
+	release_mem_region(pdev->resource[0].start,
+			pdev->resource[0].end - pdev->resource[0].start + 1);
+
+	return 0;
+}
+
+/* suspend/resume/wakeup from sysfs (echo > power/state) or when the
+ * system is forced into deep sleep
+ *
+ * REVISIT we should probably reject suspend requests when there's a host
+ * session active, rather than disconnecting, at least on boards that can
+ * report VBUS irqs (UDC_DEVSTAT.UDC_ATT).  And in any case, we need to
+ * make host resumes and VBUS detection trigger OMAP wakeup events; that
+ * may involve talking to an external transceiver (e.g. isp1301).
+ */
+
+static int omap_udc_suspend(struct platform_device *dev, pm_message_t message)
+{
+	u32	devstat;
+
+	devstat = omap_readw(UDC_DEVSTAT);
+
+	/* we're requesting 48 MHz clock if the pullup is enabled
+	 * (== we're attached to the host) and we're not suspended,
+	 * which would prevent entry to deep sleep...
+	 */
+	if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) {
+		WARNING("session active; suspend requires disconnect\n");
+		omap_pullup(&udc->gadget, 0);
+	}
+
+	return 0;
+}
+
+static int omap_udc_resume(struct platform_device *dev)
+{
+	DBG("resume + wakeup/SRP\n");
+	omap_pullup(&udc->gadget, 1);
+
+	/* maybe the host would enumerate us if we nudged it */
+	msleep(100);
+	return omap_wakeup(&udc->gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+	.probe		= omap_udc_probe,
+	.remove		= omap_udc_remove,
+	.suspend	= omap_udc_suspend,
+	.resume		= omap_udc_resume,
+	.driver		= {
+		.name	= (char *) driver_name,
+	},
+};
+
+module_platform_driver(udc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap_udc");
diff --git a/drivers/usb/gadget/udc/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h
new file mode 100644
index 0000000..00f9e60
--- /dev/null
+++ b/drivers/usb/gadget/udc/omap_udc.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * omap_udc.h -- for omap 3.2 udc, with OTG support
+ *
+ * 2004 (C) Texas Instruments, Inc.
+ * 2004 (C) David Brownell
+ */
+
+/*
+ * USB device/endpoint management registers
+ */
+
+#define	UDC_REV				(UDC_BASE + 0x0)	/* Revision */
+#define	UDC_EP_NUM			(UDC_BASE + 0x4)	/* Which endpoint */
+#	define	UDC_SETUP_SEL		(1 << 6)
+#	define	UDC_EP_SEL		(1 << 5)
+#	define	UDC_EP_DIR		(1 << 4)
+	/* low 4 bits for endpoint number */
+#define	UDC_DATA			(UDC_BASE + 0x08)	/* Endpoint FIFO */
+#define	UDC_CTRL			(UDC_BASE + 0x0C)	/* Endpoint control */
+#	define	UDC_CLR_HALT		(1 << 7)
+#	define	UDC_SET_HALT		(1 << 6)
+#	define	UDC_CLRDATA_TOGGLE	(1 << 3)
+#	define	UDC_SET_FIFO_EN		(1 << 2)
+#	define	UDC_CLR_EP		(1 << 1)
+#	define	UDC_RESET_EP		(1 << 0)
+#define	UDC_STAT_FLG			(UDC_BASE + 0x10)	/* Endpoint status */
+#	define	UDC_NO_RXPACKET		(1 << 15)
+#	define	UDC_MISS_IN		(1 << 14)
+#	define	UDC_DATA_FLUSH		(1 << 13)
+#	define	UDC_ISO_ERR		(1 << 12)
+#	define	UDC_ISO_FIFO_EMPTY	(1 << 9)
+#	define	UDC_ISO_FIFO_FULL	(1 << 8)
+#	define	UDC_EP_HALTED		(1 << 6)
+#	define	UDC_STALL		(1 << 5)
+#	define	UDC_NAK			(1 << 4)
+#	define	UDC_ACK			(1 << 3)
+#	define	UDC_FIFO_EN		(1 << 2)
+#	define	UDC_NON_ISO_FIFO_EMPTY	(1 << 1)
+#	define	UDC_NON_ISO_FIFO_FULL	(1 << 0)
+#define	UDC_RXFSTAT			(UDC_BASE + 0x14)	/* OUT bytecount */
+#define	UDC_SYSCON1			(UDC_BASE + 0x18)	/* System config 1 */
+#	define	UDC_CFG_LOCK		(1 << 8)
+#	define	UDC_DATA_ENDIAN		(1 << 7)
+#	define	UDC_DMA_ENDIAN		(1 << 6)
+#	define	UDC_NAK_EN		(1 << 4)
+#	define	UDC_AUTODECODE_DIS	(1 << 3)
+#	define	UDC_SELF_PWR		(1 << 2)
+#	define	UDC_SOFF_DIS		(1 << 1)
+#	define	UDC_PULLUP_EN		(1 << 0)
+#define	UDC_SYSCON2			(UDC_BASE + 0x1C)	/* System config 2 */
+#	define	UDC_RMT_WKP		(1 << 6)
+#	define	UDC_STALL_CMD		(1 << 5)
+#	define	UDC_DEV_CFG		(1 << 3)
+#	define	UDC_CLR_CFG		(1 << 2)
+#define	UDC_DEVSTAT			(UDC_BASE + 0x20)	/* Device status */
+#	define	UDC_B_HNP_ENABLE	(1 << 9)
+#	define	UDC_A_HNP_SUPPORT	(1 << 8)
+#	define	UDC_A_ALT_HNP_SUPPORT	(1 << 7)
+#	define	UDC_R_WK_OK		(1 << 6)
+#	define	UDC_USB_RESET		(1 << 5)
+#	define	UDC_SUS			(1 << 4)
+#	define	UDC_CFG			(1 << 3)
+#	define	UDC_ADD			(1 << 2)
+#	define	UDC_DEF			(1 << 1)
+#	define	UDC_ATT			(1 << 0)
+#define	UDC_SOF				(UDC_BASE + 0x24)	/* Start of frame */
+#	define	UDC_FT_LOCK		(1 << 12)
+#	define	UDC_TS_OK		(1 << 11)
+#	define	UDC_TS			0x03ff
+#define	UDC_IRQ_EN			(UDC_BASE + 0x28)	/* Interrupt enable */
+#	define	UDC_SOF_IE		(1 << 7)
+#	define	UDC_EPN_RX_IE		(1 << 5)
+#	define	UDC_EPN_TX_IE		(1 << 4)
+#	define	UDC_DS_CHG_IE		(1 << 3)
+#	define	UDC_EP0_IE		(1 << 0)
+#define	UDC_DMA_IRQ_EN			(UDC_BASE + 0x2C)	/* DMA irq enable */
+	/* rx/tx dma channels numbered 1-3 not 0-2 */
+#	define	UDC_TX_DONE_IE(n)	(1 << (4 * (n) - 2))
+#	define	UDC_RX_CNT_IE(n)	(1 << (4 * (n) - 3))
+#	define	UDC_RX_EOT_IE(n)	(1 << (4 * (n) - 4))
+#define	UDC_IRQ_SRC			(UDC_BASE + 0x30)	/* Interrupt source */
+#	define	UDC_TXN_DONE		(1 << 10)
+#	define	UDC_RXN_CNT		(1 << 9)
+#	define	UDC_RXN_EOT		(1 << 8)
+#	define	UDC_IRQ_SOF		(1 << 7)
+#	define	UDC_EPN_RX		(1 << 5)
+#	define	UDC_EPN_TX		(1 << 4)
+#	define	UDC_DS_CHG		(1 << 3)
+#	define	UDC_SETUP		(1 << 2)
+#	define	UDC_EP0_RX		(1 << 1)
+#	define	UDC_EP0_TX		(1 << 0)
+#	define	UDC_IRQ_SRC_MASK	0x7bf
+#define	UDC_EPN_STAT			(UDC_BASE + 0x34)	/* EP irq status */
+#define	UDC_DMAN_STAT			(UDC_BASE + 0x38)	/* DMA irq status */
+#	define	UDC_DMA_RX_SB		(1 << 12)
+#	define	UDC_DMA_RX_SRC(x)	(((x)>>8) & 0xf)
+#	define	UDC_DMA_TX_SRC(x)	(((x)>>0) & 0xf)
+
+
+/* DMA configuration registers:  up to three channels in each direction.  */
+#define	UDC_RXDMA_CFG			(UDC_BASE + 0x40)	/* 3 eps for RX DMA */
+#	define	UDC_DMA_REQ		(1 << 12)
+#define	UDC_TXDMA_CFG			(UDC_BASE + 0x44)	/* 3 eps for TX DMA */
+#define	UDC_DATA_DMA			(UDC_BASE + 0x48)	/* rx/tx fifo addr */
+
+/* rx/tx dma control, numbering channels 1-3 not 0-2 */
+#define	UDC_TXDMA(chan)			(UDC_BASE + 0x50 - 4 + 4 * (chan))
+#	define UDC_TXN_EOT		(1 << 15)	/* bytes vs packets */
+#	define UDC_TXN_START		(1 << 14)	/* start transfer */
+#	define UDC_TXN_TSC		0x03ff		/* units in xfer */
+#define	UDC_RXDMA(chan)			(UDC_BASE + 0x60 - 4 + 4 * (chan))
+#	define UDC_RXN_STOP		(1 << 15)	/* enable EOT irq */
+#	define UDC_RXN_TC		0x00ff		/* packets in xfer */
+
+
+/*
+ * Endpoint configuration registers (used before CFG_LOCK is set)
+ * UDC_EP_TX(0) is unused
+ */
+#define	UDC_EP_RX(endpoint)		(UDC_BASE + 0x80 + (endpoint)*4)
+#	define	UDC_EPN_RX_VALID	(1 << 15)
+#	define	UDC_EPN_RX_DB		(1 << 14)
+	/* buffer size in bits 13, 12 */
+#	define	UDC_EPN_RX_ISO		(1 << 11)
+	/* buffer pointer in low 11 bits */
+#define	UDC_EP_TX(endpoint)		(UDC_BASE + 0xc0 + (endpoint)*4)
+	/* same bitfields as in RX */
+
+/*-------------------------------------------------------------------------*/
+
+struct omap_req {
+	struct usb_request		req;
+	struct list_head		queue;
+	unsigned			dma_bytes;
+	unsigned			mapped:1;
+};
+
+struct omap_ep {
+	struct usb_ep			ep;
+	struct list_head		queue;
+	unsigned long			irqs;
+	struct list_head		iso;
+	char				name[14];
+	u16				maxpacket;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+	unsigned			double_buf:1;
+	unsigned			stopped:1;
+	unsigned			fnf:1;
+	unsigned			has_dma:1;
+	u8				ackwait;
+	u8				dma_channel;
+	u16				dma_counter;
+	int				lch;
+	struct omap_udc			*udc;
+	struct timer_list		timer;
+};
+
+struct omap_udc {
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	spinlock_t			lock;
+	struct omap_ep			ep[32];
+	u16				devstat;
+	u16				clr_halt;
+	struct usb_phy			*transceiver;
+	struct list_head		iso;
+	unsigned			softconnect:1;
+	unsigned			vbus_active:1;
+	unsigned			ep0_pending:1;
+	unsigned			ep0_in:1;
+	unsigned			ep0_set_config:1;
+	unsigned			ep0_reset_config:1;
+	unsigned			ep0_setup:1;
+	struct completion		*done;
+	struct clk			*dc_clk;
+	struct clk			*hhc_clk;
+	unsigned			clk_requested:1;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef VERBOSE
+#    define VDBG		DBG
+#else
+#    define VDBG(stuff...)	do{}while(0)
+#endif
+
+#define ERR(stuff...)		pr_err("udc: " stuff)
+#define WARNING(stuff...)	pr_warn("udc: " stuff)
+#define INFO(stuff...)		pr_info("udc: " stuff)
+#define DBG(stuff...)		pr_debug("udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* MOD_CONF_CTRL_0 */
+#define VBUS_W2FC_1510		(1 << 17)	/* 0 gpio0, 1 dvdd2 pin */
+
+/* FUNC_MUX_CTRL_0 */
+#define	VBUS_CTRL_1510		(1 << 19)	/* 1 connected (software) */
+#define	VBUS_MODE_1510		(1 << 18)	/* 0 hardware, 1 software */
+
+#define	HMC_1510	((omap_readl(MOD_CONF_CTRL_0) >> 1) & 0x3f)
+#define	HMC_1610	(omap_readl(OTG_SYSCON_2) & 0x3f)
+#define	HMC		(cpu_is_omap15xx() ? HMC_1510 : HMC_1610)
+
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
new file mode 100644
index 0000000..afaea11
--- /dev/null
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -0,0 +1,3185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+
+/* GPIO port for VBUS detecting */
+static int vbus_gpio_port = -1;		/* GPIO port number (-1:Not used) */
+
+#define PCH_VBUS_PERIOD		3000	/* VBUS polling period (msec) */
+#define PCH_VBUS_INTERVAL	10	/* VBUS polling interval (msec) */
+
+/* Address offset of Registers */
+#define UDC_EP_REG_SHIFT	0x20	/* Offset to next EP */
+
+#define UDC_EPCTL_ADDR		0x00	/* Endpoint control */
+#define UDC_EPSTS_ADDR		0x04	/* Endpoint status */
+#define UDC_BUFIN_FRAMENUM_ADDR	0x08	/* buffer size in / frame number out */
+#define UDC_BUFOUT_MAXPKT_ADDR	0x0C	/* buffer size out / maxpkt in */
+#define UDC_SUBPTR_ADDR		0x10	/* setup buffer pointer */
+#define UDC_DESPTR_ADDR		0x14	/* Data descriptor pointer */
+#define UDC_CONFIRM_ADDR	0x18	/* Write/Read confirmation */
+
+#define UDC_DEVCFG_ADDR		0x400	/* Device configuration */
+#define UDC_DEVCTL_ADDR		0x404	/* Device control */
+#define UDC_DEVSTS_ADDR		0x408	/* Device status */
+#define UDC_DEVIRQSTS_ADDR	0x40C	/* Device irq status */
+#define UDC_DEVIRQMSK_ADDR	0x410	/* Device irq mask */
+#define UDC_EPIRQSTS_ADDR	0x414	/* Endpoint irq status */
+#define UDC_EPIRQMSK_ADDR	0x418	/* Endpoint irq mask */
+#define UDC_DEVLPM_ADDR		0x41C	/* LPM control / status */
+#define UDC_CSR_BUSY_ADDR	0x4f0	/* UDC_CSR_BUSY Status register */
+#define UDC_SRST_ADDR		0x4fc	/* SOFT RESET register */
+#define UDC_CSR_ADDR		0x500	/* USB_DEVICE endpoint register */
+
+/* Endpoint control register */
+/* Bit position */
+#define UDC_EPCTL_MRXFLUSH		(1 << 12)
+#define UDC_EPCTL_RRDY			(1 << 9)
+#define UDC_EPCTL_CNAK			(1 << 8)
+#define UDC_EPCTL_SNAK			(1 << 7)
+#define UDC_EPCTL_NAK			(1 << 6)
+#define UDC_EPCTL_P			(1 << 3)
+#define UDC_EPCTL_F			(1 << 1)
+#define UDC_EPCTL_S			(1 << 0)
+#define UDC_EPCTL_ET_SHIFT		4
+/* Mask patern */
+#define UDC_EPCTL_ET_MASK		0x00000030
+/* Value for ET field */
+#define UDC_EPCTL_ET_CONTROL		0
+#define UDC_EPCTL_ET_ISO		1
+#define UDC_EPCTL_ET_BULK		2
+#define UDC_EPCTL_ET_INTERRUPT		3
+
+/* Endpoint status register */
+/* Bit position */
+#define UDC_EPSTS_XFERDONE		(1 << 27)
+#define UDC_EPSTS_RSS			(1 << 26)
+#define UDC_EPSTS_RCS			(1 << 25)
+#define UDC_EPSTS_TXEMPTY		(1 << 24)
+#define UDC_EPSTS_TDC			(1 << 10)
+#define UDC_EPSTS_HE			(1 << 9)
+#define UDC_EPSTS_MRXFIFO_EMP		(1 << 8)
+#define UDC_EPSTS_BNA			(1 << 7)
+#define UDC_EPSTS_IN			(1 << 6)
+#define UDC_EPSTS_OUT_SHIFT		4
+/* Mask patern */
+#define UDC_EPSTS_OUT_MASK		0x00000030
+#define UDC_EPSTS_ALL_CLR_MASK		0x1F0006F0
+/* Value for OUT field */
+#define UDC_EPSTS_OUT_SETUP		2
+#define UDC_EPSTS_OUT_DATA		1
+
+/* Device configuration register */
+/* Bit position */
+#define UDC_DEVCFG_CSR_PRG		(1 << 17)
+#define UDC_DEVCFG_SP			(1 << 3)
+/* SPD Valee */
+#define UDC_DEVCFG_SPD_HS		0x0
+#define UDC_DEVCFG_SPD_FS		0x1
+#define UDC_DEVCFG_SPD_LS		0x2
+
+/* Device control register */
+/* Bit position */
+#define UDC_DEVCTL_THLEN_SHIFT		24
+#define UDC_DEVCTL_BRLEN_SHIFT		16
+#define UDC_DEVCTL_CSR_DONE		(1 << 13)
+#define UDC_DEVCTL_SD			(1 << 10)
+#define UDC_DEVCTL_MODE			(1 << 9)
+#define UDC_DEVCTL_BREN			(1 << 8)
+#define UDC_DEVCTL_THE			(1 << 7)
+#define UDC_DEVCTL_DU			(1 << 4)
+#define UDC_DEVCTL_TDE			(1 << 3)
+#define UDC_DEVCTL_RDE			(1 << 2)
+#define UDC_DEVCTL_RES			(1 << 0)
+
+/* Device status register */
+/* Bit position */
+#define UDC_DEVSTS_TS_SHIFT		18
+#define UDC_DEVSTS_ENUM_SPEED_SHIFT	13
+#define UDC_DEVSTS_ALT_SHIFT		8
+#define UDC_DEVSTS_INTF_SHIFT		4
+#define UDC_DEVSTS_CFG_SHIFT		0
+/* Mask patern */
+#define UDC_DEVSTS_TS_MASK		0xfffc0000
+#define UDC_DEVSTS_ENUM_SPEED_MASK	0x00006000
+#define UDC_DEVSTS_ALT_MASK		0x00000f00
+#define UDC_DEVSTS_INTF_MASK		0x000000f0
+#define UDC_DEVSTS_CFG_MASK		0x0000000f
+/* value for maximum speed for SPEED field */
+#define UDC_DEVSTS_ENUM_SPEED_FULL	1
+#define UDC_DEVSTS_ENUM_SPEED_HIGH	0
+#define UDC_DEVSTS_ENUM_SPEED_LOW	2
+#define UDC_DEVSTS_ENUM_SPEED_FULLX	3
+
+/* Device irq register */
+/* Bit position */
+#define UDC_DEVINT_RWKP			(1 << 7)
+#define UDC_DEVINT_ENUM			(1 << 6)
+#define UDC_DEVINT_SOF			(1 << 5)
+#define UDC_DEVINT_US			(1 << 4)
+#define UDC_DEVINT_UR			(1 << 3)
+#define UDC_DEVINT_ES			(1 << 2)
+#define UDC_DEVINT_SI			(1 << 1)
+#define UDC_DEVINT_SC			(1 << 0)
+/* Mask patern */
+#define UDC_DEVINT_MSK			0x7f
+
+/* Endpoint irq register */
+/* Bit position */
+#define UDC_EPINT_IN_SHIFT		0
+#define UDC_EPINT_OUT_SHIFT		16
+#define UDC_EPINT_IN_EP0		(1 << 0)
+#define UDC_EPINT_OUT_EP0		(1 << 16)
+/* Mask patern */
+#define UDC_EPINT_MSK_DISABLE_ALL	0xffffffff
+
+/* UDC_CSR_BUSY Status register */
+/* Bit position */
+#define UDC_CSR_BUSY			(1 << 0)
+
+/* SOFT RESET register */
+/* Bit position */
+#define UDC_PSRST			(1 << 1)
+#define UDC_SRST			(1 << 0)
+
+/* USB_DEVICE endpoint register */
+/* Bit position */
+#define UDC_CSR_NE_NUM_SHIFT		0
+#define UDC_CSR_NE_DIR_SHIFT		4
+#define UDC_CSR_NE_TYPE_SHIFT		5
+#define UDC_CSR_NE_CFG_SHIFT		7
+#define UDC_CSR_NE_INTF_SHIFT		11
+#define UDC_CSR_NE_ALT_SHIFT		15
+#define UDC_CSR_NE_MAX_PKT_SHIFT	19
+/* Mask patern */
+#define UDC_CSR_NE_NUM_MASK		0x0000000f
+#define UDC_CSR_NE_DIR_MASK		0x00000010
+#define UDC_CSR_NE_TYPE_MASK		0x00000060
+#define UDC_CSR_NE_CFG_MASK		0x00000780
+#define UDC_CSR_NE_INTF_MASK		0x00007800
+#define UDC_CSR_NE_ALT_MASK		0x00078000
+#define UDC_CSR_NE_MAX_PKT_MASK		0x3ff80000
+
+#define PCH_UDC_CSR(ep)	(UDC_CSR_ADDR + ep*4)
+#define PCH_UDC_EPINT(in, num)\
+		(1 << (num + (in ? UDC_EPINT_IN_SHIFT : UDC_EPINT_OUT_SHIFT)))
+
+/* Index of endpoint */
+#define UDC_EP0IN_IDX		0
+#define UDC_EP0OUT_IDX		1
+#define UDC_EPIN_IDX(ep)	(ep * 2)
+#define UDC_EPOUT_IDX(ep)	(ep * 2 + 1)
+#define PCH_UDC_EP0		0
+#define PCH_UDC_EP1		1
+#define PCH_UDC_EP2		2
+#define PCH_UDC_EP3		3
+
+/* Number of endpoint */
+#define PCH_UDC_EP_NUM		32	/* Total number of EPs (16 IN,16 OUT) */
+#define PCH_UDC_USED_EP_NUM	4	/* EP number of EP's really used */
+/* Length Value */
+#define PCH_UDC_BRLEN		0x0F	/* Burst length */
+#define PCH_UDC_THLEN		0x1F	/* Threshold length */
+/* Value of EP Buffer Size */
+#define UDC_EP0IN_BUFF_SIZE	16
+#define UDC_EPIN_BUFF_SIZE	256
+#define UDC_EP0OUT_BUFF_SIZE	16
+#define UDC_EPOUT_BUFF_SIZE	256
+/* Value of EP maximum packet size */
+#define UDC_EP0IN_MAX_PKT_SIZE	64
+#define UDC_EP0OUT_MAX_PKT_SIZE	64
+#define UDC_BULK_MAX_PKT_SIZE	512
+
+/* DMA */
+#define DMA_DIR_RX		1	/* DMA for data receive */
+#define DMA_DIR_TX		2	/* DMA for data transmit */
+#define DMA_ADDR_INVALID	(~(dma_addr_t)0)
+#define UDC_DMA_MAXPACKET	65536	/* maximum packet size for DMA */
+
+/**
+ * struct pch_udc_data_dma_desc - Structure to hold DMA descriptor information
+ *				  for data
+ * @status:		Status quadlet
+ * @reserved:		Reserved
+ * @dataptr:		Buffer descriptor
+ * @next:		Next descriptor
+ */
+struct pch_udc_data_dma_desc {
+	u32 status;
+	u32 reserved;
+	u32 dataptr;
+	u32 next;
+};
+
+/**
+ * struct pch_udc_stp_dma_desc - Structure to hold DMA descriptor information
+ *				 for control data
+ * @status:	Status
+ * @reserved:	Reserved
+ * @data12:	First setup word
+ * @data34:	Second setup word
+ */
+struct pch_udc_stp_dma_desc {
+	u32 status;
+	u32 reserved;
+	struct usb_ctrlrequest request;
+} __attribute((packed));
+
+/* DMA status definitions */
+/* Buffer status */
+#define PCH_UDC_BUFF_STS	0xC0000000
+#define PCH_UDC_BS_HST_RDY	0x00000000
+#define PCH_UDC_BS_DMA_BSY	0x40000000
+#define PCH_UDC_BS_DMA_DONE	0x80000000
+#define PCH_UDC_BS_HST_BSY	0xC0000000
+/*  Rx/Tx Status */
+#define PCH_UDC_RXTX_STS	0x30000000
+#define PCH_UDC_RTS_SUCC	0x00000000
+#define PCH_UDC_RTS_DESERR	0x10000000
+#define PCH_UDC_RTS_BUFERR	0x30000000
+/* Last Descriptor Indication */
+#define PCH_UDC_DMA_LAST	0x08000000
+/* Number of Rx/Tx Bytes Mask */
+#define PCH_UDC_RXTX_BYTES	0x0000ffff
+
+/**
+ * struct pch_udc_cfg_data - Structure to hold current configuration
+ *			     and interface information
+ * @cur_cfg:	current configuration in use
+ * @cur_intf:	current interface in use
+ * @cur_alt:	current alt interface in use
+ */
+struct pch_udc_cfg_data {
+	u16 cur_cfg;
+	u16 cur_intf;
+	u16 cur_alt;
+};
+
+/**
+ * struct pch_udc_ep - Structure holding a PCH USB device Endpoint information
+ * @ep:			embedded ep request
+ * @td_stp_phys:	for setup request
+ * @td_data_phys:	for data request
+ * @td_stp:		for setup request
+ * @td_data:		for data request
+ * @dev:		reference to device struct
+ * @offset_addr:	offset address of ep register
+ * @desc:		for this ep
+ * @queue:		queue for requests
+ * @num:		endpoint number
+ * @in:			endpoint is IN
+ * @halted:		endpoint halted?
+ * @epsts:		Endpoint status
+ */
+struct pch_udc_ep {
+	struct usb_ep			ep;
+	dma_addr_t			td_stp_phys;
+	dma_addr_t			td_data_phys;
+	struct pch_udc_stp_dma_desc	*td_stp;
+	struct pch_udc_data_dma_desc	*td_data;
+	struct pch_udc_dev		*dev;
+	unsigned long			offset_addr;
+	struct list_head		queue;
+	unsigned			num:5,
+					in:1,
+					halted:1;
+	unsigned long			epsts;
+};
+
+/**
+ * struct pch_vbus_gpio_data - Structure holding GPIO informaton
+ *					for detecting VBUS
+ * @port:		gpio port number
+ * @intr:		gpio interrupt number
+ * @irq_work_fall	Structure for WorkQueue
+ * @irq_work_rise	Structure for WorkQueue
+ */
+struct pch_vbus_gpio_data {
+	int			port;
+	int			intr;
+	struct work_struct	irq_work_fall;
+	struct work_struct	irq_work_rise;
+};
+
+/**
+ * struct pch_udc_dev - Structure holding complete information
+ *			of the PCH USB device
+ * @gadget:		gadget driver data
+ * @driver:		reference to gadget driver bound
+ * @pdev:		reference to the PCI device
+ * @ep:			array of endpoints
+ * @lock:		protects all state
+ * @stall:		stall requested
+ * @prot_stall:		protcol stall requested
+ * @registered:		driver registered with system
+ * @suspended:		driver in suspended state
+ * @connected:		gadget driver associated
+ * @vbus_session:	required vbus_session state
+ * @set_cfg_not_acked:	pending acknowledgement 4 setup
+ * @waiting_zlp_ack:	pending acknowledgement 4 ZLP
+ * @data_requests:	DMA pool for data requests
+ * @stp_requests:	DMA pool for setup requests
+ * @dma_addr:		DMA pool for received
+ * @setup_data:		Received setup data
+ * @base_addr:		for mapped device memory
+ * @cfg_data:		current cfg, intf, and alt in use
+ * @vbus_gpio:		GPIO informaton for detecting VBUS
+ */
+struct pch_udc_dev {
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct pci_dev			*pdev;
+	struct pch_udc_ep		ep[PCH_UDC_EP_NUM];
+	spinlock_t			lock; /* protects all state */
+	unsigned
+			stall:1,
+			prot_stall:1,
+			suspended:1,
+			connected:1,
+			vbus_session:1,
+			set_cfg_not_acked:1,
+			waiting_zlp_ack:1;
+	struct dma_pool		*data_requests;
+	struct dma_pool		*stp_requests;
+	dma_addr_t			dma_addr;
+	struct usb_ctrlrequest		setup_data;
+	void __iomem			*base_addr;
+	struct pch_udc_cfg_data		cfg_data;
+	struct pch_vbus_gpio_data	vbus_gpio;
+};
+#define to_pch_udc(g)	(container_of((g), struct pch_udc_dev, gadget))
+
+#define PCH_UDC_PCI_BAR_QUARK_X1000	0
+#define PCH_UDC_PCI_BAR			1
+
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC	0x0939
+#define PCI_DEVICE_ID_INTEL_EG20T_UDC		0x8808
+
+#define PCI_VENDOR_ID_ROHM		0x10DB
+#define PCI_DEVICE_ID_ML7213_IOH_UDC	0x801D
+#define PCI_DEVICE_ID_ML7831_IOH_UDC	0x8808
+
+static const char	ep0_string[] = "ep0in";
+static DEFINE_SPINLOCK(udc_stall_spinlock);	/* stall spin lock */
+static bool speed_fs;
+module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
+MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
+
+/**
+ * struct pch_udc_request - Structure holding a PCH USB device request packet
+ * @req:		embedded ep request
+ * @td_data_phys:	phys. address
+ * @td_data:		first dma desc. of chain
+ * @td_data_last:	last dma desc. of chain
+ * @queue:		associated queue
+ * @dma_going:		DMA in progress for request
+ * @dma_mapped:		DMA memory mapped for request
+ * @dma_done:		DMA completed for request
+ * @chain_len:		chain length
+ * @buf:		Buffer memory for align adjustment
+ * @dma:		DMA memory for align adjustment
+ */
+struct pch_udc_request {
+	struct usb_request		req;
+	dma_addr_t			td_data_phys;
+	struct pch_udc_data_dma_desc	*td_data;
+	struct pch_udc_data_dma_desc	*td_data_last;
+	struct list_head		queue;
+	unsigned			dma_going:1,
+					dma_mapped:1,
+					dma_done:1;
+	unsigned			chain_len;
+	void				*buf;
+	dma_addr_t			dma;
+};
+
+static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg)
+{
+	return ioread32(dev->base_addr + reg);
+}
+
+static inline void pch_udc_writel(struct pch_udc_dev *dev,
+				    unsigned long val, unsigned long reg)
+{
+	iowrite32(val, dev->base_addr + reg);
+}
+
+static inline void pch_udc_bit_set(struct pch_udc_dev *dev,
+				     unsigned long reg,
+				     unsigned long bitmask)
+{
+	pch_udc_writel(dev, pch_udc_readl(dev, reg) | bitmask, reg);
+}
+
+static inline void pch_udc_bit_clr(struct pch_udc_dev *dev,
+				     unsigned long reg,
+				     unsigned long bitmask)
+{
+	pch_udc_writel(dev, pch_udc_readl(dev, reg) & ~(bitmask), reg);
+}
+
+static inline u32 pch_udc_ep_readl(struct pch_udc_ep *ep, unsigned long reg)
+{
+	return ioread32(ep->dev->base_addr + ep->offset_addr + reg);
+}
+
+static inline void pch_udc_ep_writel(struct pch_udc_ep *ep,
+				    unsigned long val, unsigned long reg)
+{
+	iowrite32(val, ep->dev->base_addr + ep->offset_addr + reg);
+}
+
+static inline void pch_udc_ep_bit_set(struct pch_udc_ep *ep,
+				     unsigned long reg,
+				     unsigned long bitmask)
+{
+	pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) | bitmask, reg);
+}
+
+static inline void pch_udc_ep_bit_clr(struct pch_udc_ep *ep,
+				     unsigned long reg,
+				     unsigned long bitmask)
+{
+	pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) & ~(bitmask), reg);
+}
+
+/**
+ * pch_udc_csr_busy() - Wait till idle.
+ * @dev:	Reference to pch_udc_dev structure
+ */
+static void pch_udc_csr_busy(struct pch_udc_dev *dev)
+{
+	unsigned int count = 200;
+
+	/* Wait till idle */
+	while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY)
+		&& --count)
+		cpu_relax();
+	if (!count)
+		dev_err(&dev->pdev->dev, "%s: wait error\n", __func__);
+}
+
+/**
+ * pch_udc_write_csr() - Write the command and status registers.
+ * @dev:	Reference to pch_udc_dev structure
+ * @val:	value to be written to CSR register
+ * @addr:	address of CSR register
+ */
+static void pch_udc_write_csr(struct pch_udc_dev *dev, unsigned long val,
+			       unsigned int ep)
+{
+	unsigned long reg = PCH_UDC_CSR(ep);
+
+	pch_udc_csr_busy(dev);		/* Wait till idle */
+	pch_udc_writel(dev, val, reg);
+	pch_udc_csr_busy(dev);		/* Wait till idle */
+}
+
+/**
+ * pch_udc_read_csr() - Read the command and status registers.
+ * @dev:	Reference to pch_udc_dev structure
+ * @addr:	address of CSR register
+ *
+ * Return codes:	content of CSR register
+ */
+static u32 pch_udc_read_csr(struct pch_udc_dev *dev, unsigned int ep)
+{
+	unsigned long reg = PCH_UDC_CSR(ep);
+
+	pch_udc_csr_busy(dev);		/* Wait till idle */
+	pch_udc_readl(dev, reg);	/* Dummy read */
+	pch_udc_csr_busy(dev);		/* Wait till idle */
+	return pch_udc_readl(dev, reg);
+}
+
+/**
+ * pch_udc_rmt_wakeup() - Initiate for remote wakeup
+ * @dev:	Reference to pch_udc_dev structure
+ */
+static inline void pch_udc_rmt_wakeup(struct pch_udc_dev *dev)
+{
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+	mdelay(1);
+	pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+}
+
+/**
+ * pch_udc_get_frame() - Get the current frame from device status register
+ * @dev:	Reference to pch_udc_dev structure
+ * Retern	current frame
+ */
+static inline int pch_udc_get_frame(struct pch_udc_dev *dev)
+{
+	u32 frame = pch_udc_readl(dev, UDC_DEVSTS_ADDR);
+	return (frame & UDC_DEVSTS_TS_MASK) >> UDC_DEVSTS_TS_SHIFT;
+}
+
+/**
+ * pch_udc_clear_selfpowered() - Clear the self power control
+ * @dev:	Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_clear_selfpowered(struct pch_udc_dev *dev)
+{
+	pch_udc_bit_clr(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
+}
+
+/**
+ * pch_udc_set_selfpowered() - Set the self power control
+ * @dev:	Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_set_selfpowered(struct pch_udc_dev *dev)
+{
+	pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
+}
+
+/**
+ * pch_udc_set_disconnect() - Set the disconnect status.
+ * @dev:	Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_set_disconnect(struct pch_udc_dev *dev)
+{
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
+}
+
+/**
+ * pch_udc_clear_disconnect() - Clear the disconnect status.
+ * @dev:	Reference to pch_udc_regs structure
+ */
+static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
+{
+	/* Clear the disconnect */
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+	pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
+	mdelay(1);
+	/* Resume USB signalling */
+	pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+}
+
+/**
+ * pch_udc_reconnect() - This API initializes usb device controller,
+ *						and clear the disconnect status.
+ * @dev:		Reference to pch_udc_regs structure
+ */
+static void pch_udc_init(struct pch_udc_dev *dev);
+static void pch_udc_reconnect(struct pch_udc_dev *dev)
+{
+	pch_udc_init(dev);
+
+	/* enable device interrupts */
+	/* pch_udc_enable_interrupts() */
+	pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR,
+			UDC_DEVINT_UR | UDC_DEVINT_ENUM);
+
+	/* Clear the disconnect */
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+	pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
+	mdelay(1);
+	/* Resume USB signalling */
+	pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+}
+
+/**
+ * pch_udc_vbus_session() - set or clearr the disconnect status.
+ * @dev:	Reference to pch_udc_regs structure
+ * @is_active:	Parameter specifying the action
+ *		  0:   indicating VBUS power is ending
+ *		  !0:  indicating VBUS power is starting
+ */
+static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
+					  int is_active)
+{
+	if (is_active) {
+		pch_udc_reconnect(dev);
+		dev->vbus_session = 1;
+	} else {
+		if (dev->driver && dev->driver->disconnect) {
+			spin_lock(&dev->lock);
+			dev->driver->disconnect(&dev->gadget);
+			spin_unlock(&dev->lock);
+		}
+		pch_udc_set_disconnect(dev);
+		dev->vbus_session = 0;
+	}
+}
+
+/**
+ * pch_udc_ep_set_stall() - Set the stall of endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static void pch_udc_ep_set_stall(struct pch_udc_ep *ep)
+{
+	if (ep->in) {
+		pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
+		pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+	} else {
+		pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+	}
+}
+
+/**
+ * pch_udc_ep_clear_stall() - Clear the stall of endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_clear_stall(struct pch_udc_ep *ep)
+{
+	/* Clear the stall */
+	pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+	/* Clear NAK by writing CNAK */
+	pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
+}
+
+/**
+ * pch_udc_ep_set_trfr_type() - Set the transfer type of endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @type:	Type of endpoint
+ */
+static inline void pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep,
+					u8 type)
+{
+	pch_udc_ep_writel(ep, ((type << UDC_EPCTL_ET_SHIFT) &
+				UDC_EPCTL_ET_MASK), UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @buf_size:	The buffer word size
+ */
+static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
+						 u32 buf_size, u32 ep_in)
+{
+	u32 data;
+	if (ep_in) {
+		data = pch_udc_ep_readl(ep, UDC_BUFIN_FRAMENUM_ADDR);
+		data = (data & 0xffff0000) | (buf_size & 0xffff);
+		pch_udc_ep_writel(ep, data, UDC_BUFIN_FRAMENUM_ADDR);
+	} else {
+		data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
+		data = (buf_size << 16) | (data & 0xffff);
+		pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
+	}
+}
+
+/**
+ * pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @pkt_size:	The packet byte size
+ */
+static void pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size)
+{
+	u32 data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
+	data = (data & 0xffff0000) | (pkt_size & 0xffff);
+	pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_subptr() - Set the Setup buffer pointer for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @addr:	Address of the register
+ */
+static inline void pch_udc_ep_set_subptr(struct pch_udc_ep *ep, u32 addr)
+{
+	pch_udc_ep_writel(ep, addr, UDC_SUBPTR_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_ddptr() - Set the Data descriptor pointer for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @addr:	Address of the register
+ */
+static inline void pch_udc_ep_set_ddptr(struct pch_udc_ep *ep, u32 addr)
+{
+	pch_udc_ep_writel(ep, addr, UDC_DESPTR_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_pd() - Set the poll demand bit for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_pd(struct pch_udc_ep *ep)
+{
+	pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_P);
+}
+
+/**
+ * pch_udc_ep_set_rrdy() - Set the receive ready bit for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_rrdy(struct pch_udc_ep *ep)
+{
+	pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
+}
+
+/**
+ * pch_udc_ep_clear_rrdy() - Clear the receive ready bit for the endpoint
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_clear_rrdy(struct pch_udc_ep *ep)
+{
+	pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
+}
+
+/**
+ * pch_udc_set_dma() - Set the 'TDE' or RDE bit of device control
+ *			register depending on the direction specified
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @dir:	whether Tx or Rx
+ *		  DMA_DIR_RX: Receive
+ *		  DMA_DIR_TX: Transmit
+ */
+static inline void pch_udc_set_dma(struct pch_udc_dev *dev, int dir)
+{
+	if (dir == DMA_DIR_RX)
+		pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE);
+	else if (dir == DMA_DIR_TX)
+		pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
+}
+
+/**
+ * pch_udc_clear_dma() - Clear the 'TDE' or RDE bit of device control
+ *				 register depending on the direction specified
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @dir:	Whether Tx or Rx
+ *		  DMA_DIR_RX: Receive
+ *		  DMA_DIR_TX: Transmit
+ */
+static inline void pch_udc_clear_dma(struct pch_udc_dev *dev, int dir)
+{
+	if (dir == DMA_DIR_RX)
+		pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE);
+	else if (dir == DMA_DIR_TX)
+		pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
+}
+
+/**
+ * pch_udc_set_csr_done() - Set the device control register
+ *				CSR done field (bit 13)
+ * @dev:	reference to structure of type pch_udc_regs
+ */
+static inline void pch_udc_set_csr_done(struct pch_udc_dev *dev)
+{
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_CSR_DONE);
+}
+
+/**
+ * pch_udc_disable_interrupts() - Disables the specified interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @mask:	Mask to disable interrupts
+ */
+static inline void pch_udc_disable_interrupts(struct pch_udc_dev *dev,
+					    u32 mask)
+{
+	pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_enable_interrupts() - Enable the specified interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @mask:	Mask to enable interrupts
+ */
+static inline void pch_udc_enable_interrupts(struct pch_udc_dev *dev,
+					   u32 mask)
+{
+	pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_disable_ep_interrupts() - Disable endpoint interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @mask:	Mask to disable interrupts
+ */
+static inline void pch_udc_disable_ep_interrupts(struct pch_udc_dev *dev,
+						u32 mask)
+{
+	pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_enable_ep_interrupts() - Enable endpoint interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @mask:	Mask to enable interrupts
+ */
+static inline void pch_udc_enable_ep_interrupts(struct pch_udc_dev *dev,
+					      u32 mask)
+{
+	pch_udc_bit_clr(dev, UDC_EPIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_read_device_interrupts() - Read the device interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * Retern	The device interrupts
+ */
+static inline u32 pch_udc_read_device_interrupts(struct pch_udc_dev *dev)
+{
+	return pch_udc_readl(dev, UDC_DEVIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_write_device_interrupts() - Write device interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @val:	The value to be written to interrupt register
+ */
+static inline void pch_udc_write_device_interrupts(struct pch_udc_dev *dev,
+						     u32 val)
+{
+	pch_udc_writel(dev, val, UDC_DEVIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_interrupts() - Read the endpoint interrupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * Retern	The endpoint interrupt
+ */
+static inline u32 pch_udc_read_ep_interrupts(struct pch_udc_dev *dev)
+{
+	return pch_udc_readl(dev, UDC_EPIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_write_ep_interrupts() - Clear endpoint interupts
+ * @dev:	Reference to structure of type pch_udc_regs
+ * @val:	The value to be written to interrupt register
+ */
+static inline void pch_udc_write_ep_interrupts(struct pch_udc_dev *dev,
+					     u32 val)
+{
+	pch_udc_writel(dev, val, UDC_EPIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_device_status() - Read the device status
+ * @dev:	Reference to structure of type pch_udc_regs
+ * Retern	The device status
+ */
+static inline u32 pch_udc_read_device_status(struct pch_udc_dev *dev)
+{
+	return pch_udc_readl(dev, UDC_DEVSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_control() - Read the endpoint control
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * Retern	The endpoint control register value
+ */
+static inline u32 pch_udc_read_ep_control(struct pch_udc_ep *ep)
+{
+	return pch_udc_ep_readl(ep, UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_clear_ep_control() - Clear the endpoint control register
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * Retern	The endpoint control register value
+ */
+static inline void pch_udc_clear_ep_control(struct pch_udc_ep *ep)
+{
+	return pch_udc_ep_writel(ep, 0, UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_status() - Read the endpoint status
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * Retern	The endpoint status
+ */
+static inline u32 pch_udc_read_ep_status(struct pch_udc_ep *ep)
+{
+	return pch_udc_ep_readl(ep, UDC_EPSTS_ADDR);
+}
+
+/**
+ * pch_udc_clear_ep_status() - Clear the endpoint status
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ * @stat:	Endpoint status
+ */
+static inline void pch_udc_clear_ep_status(struct pch_udc_ep *ep,
+					 u32 stat)
+{
+	return pch_udc_ep_writel(ep, stat, UDC_EPSTS_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_nak() - Set the bit 7 (SNAK field)
+ *				of the endpoint control register
+ * @ep:		Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_nak(struct pch_udc_ep *ep)
+{
+	pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_SNAK);
+}
+
+/**
+ * pch_udc_ep_clear_nak() - Set the bit 8 (CNAK field)
+ *				of the endpoint control register
+ * @ep:		reference to structure of type pch_udc_ep_regs
+ */
+static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
+{
+	unsigned int loopcnt = 0;
+	struct pch_udc_dev *dev = ep->dev;
+
+	if (!(pch_udc_ep_readl(ep, UDC_EPCTL_ADDR) & UDC_EPCTL_NAK))
+		return;
+	if (!ep->in) {
+		loopcnt = 10000;
+		while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
+			--loopcnt)
+			udelay(5);
+		if (!loopcnt)
+			dev_err(&dev->pdev->dev, "%s: RxFIFO not Empty\n",
+				__func__);
+	}
+	loopcnt = 10000;
+	while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_NAK) && --loopcnt) {
+		pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
+		udelay(5);
+	}
+	if (!loopcnt)
+		dev_err(&dev->pdev->dev, "%s: Clear NAK not set for ep%d%s\n",
+			__func__, ep->num, (ep->in ? "in" : "out"));
+}
+
+/**
+ * pch_udc_ep_fifo_flush() - Flush the endpoint fifo
+ * @ep:	reference to structure of type pch_udc_ep_regs
+ * @dir:	direction of endpoint
+ *		  0:  endpoint is OUT
+ *		  !0: endpoint is IN
+ */
+static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
+{
+	if (dir) {	/* IN ep */
+		pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
+		return;
+	}
+}
+
+/**
+ * pch_udc_ep_enable() - This api enables endpoint
+ * @regs:	Reference to structure pch_udc_ep_regs
+ * @desc:	endpoint descriptor
+ */
+static void pch_udc_ep_enable(struct pch_udc_ep *ep,
+			       struct pch_udc_cfg_data *cfg,
+			       const struct usb_endpoint_descriptor *desc)
+{
+	u32 val = 0;
+	u32 buff_size = 0;
+
+	pch_udc_ep_set_trfr_type(ep, desc->bmAttributes);
+	if (ep->in)
+		buff_size = UDC_EPIN_BUFF_SIZE;
+	else
+		buff_size = UDC_EPOUT_BUFF_SIZE;
+	pch_udc_ep_set_bufsz(ep, buff_size, ep->in);
+	pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc));
+	pch_udc_ep_set_nak(ep);
+	pch_udc_ep_fifo_flush(ep, ep->in);
+	/* Configure the endpoint */
+	val = ep->num << UDC_CSR_NE_NUM_SHIFT | ep->in << UDC_CSR_NE_DIR_SHIFT |
+	      ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) <<
+		UDC_CSR_NE_TYPE_SHIFT) |
+	      (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) |
+	      (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) |
+	      (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) |
+	      usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT;
+
+	if (ep->in)
+		pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num));
+	else
+		pch_udc_write_csr(ep->dev, val, UDC_EPOUT_IDX(ep->num));
+}
+
+/**
+ * pch_udc_ep_disable() - This api disables endpoint
+ * @regs:	Reference to structure pch_udc_ep_regs
+ */
+static void pch_udc_ep_disable(struct pch_udc_ep *ep)
+{
+	if (ep->in) {
+		/* flush the fifo */
+		pch_udc_ep_writel(ep, UDC_EPCTL_F, UDC_EPCTL_ADDR);
+		/* set NAK */
+		pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
+		pch_udc_ep_bit_set(ep, UDC_EPSTS_ADDR, UDC_EPSTS_IN);
+	} else {
+		/* set NAK */
+		pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
+	}
+	/* reset desc pointer */
+	pch_udc_ep_writel(ep, 0, UDC_DESPTR_ADDR);
+}
+
+/**
+ * pch_udc_wait_ep_stall() - Wait EP stall.
+ * @dev:	Reference to pch_udc_dev structure
+ */
+static void pch_udc_wait_ep_stall(struct pch_udc_ep *ep)
+{
+	unsigned int count = 10000;
+
+	/* Wait till idle */
+	while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_S) && --count)
+		udelay(5);
+	if (!count)
+		dev_err(&ep->dev->pdev->dev, "%s: wait error\n", __func__);
+}
+
+/**
+ * pch_udc_init() - This API initializes usb device controller
+ * @dev:	Rreference to pch_udc_regs structure
+ */
+static void pch_udc_init(struct pch_udc_dev *dev)
+{
+	if (NULL == dev) {
+		pr_err("%s: Invalid address\n", __func__);
+		return;
+	}
+	/* Soft Reset and Reset PHY */
+	pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
+	pch_udc_writel(dev, UDC_SRST | UDC_PSRST, UDC_SRST_ADDR);
+	mdelay(1);
+	pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
+	pch_udc_writel(dev, 0x00, UDC_SRST_ADDR);
+	mdelay(1);
+	/* mask and clear all device interrupts */
+	pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK);
+	pch_udc_bit_set(dev, UDC_DEVIRQSTS_ADDR, UDC_DEVINT_MSK);
+
+	/* mask and clear all ep interrupts */
+	pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+	pch_udc_bit_set(dev, UDC_EPIRQSTS_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+
+	/* enable dynamic CSR programmingi, self powered and device speed */
+	if (speed_fs)
+		pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+				UDC_DEVCFG_SP | UDC_DEVCFG_SPD_FS);
+	else /* defaul high speed */
+		pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+				UDC_DEVCFG_SP | UDC_DEVCFG_SPD_HS);
+	pch_udc_bit_set(dev, UDC_DEVCTL_ADDR,
+			(PCH_UDC_THLEN << UDC_DEVCTL_THLEN_SHIFT) |
+			(PCH_UDC_BRLEN << UDC_DEVCTL_BRLEN_SHIFT) |
+			UDC_DEVCTL_MODE | UDC_DEVCTL_BREN |
+			UDC_DEVCTL_THE);
+}
+
+/**
+ * pch_udc_exit() - This API exit usb device controller
+ * @dev:	Reference to pch_udc_regs structure
+ */
+static void pch_udc_exit(struct pch_udc_dev *dev)
+{
+	/* mask all device interrupts */
+	pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK);
+	/* mask all ep interrupts */
+	pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+	/* put device in disconnected state */
+	pch_udc_set_disconnect(dev);
+}
+
+/**
+ * pch_udc_pcd_get_frame() - This API is invoked to get the current frame number
+ * @gadget:	Reference to the gadget driver
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:	If the gadget passed is NULL
+ */
+static int pch_udc_pcd_get_frame(struct usb_gadget *gadget)
+{
+	struct pch_udc_dev	*dev;
+
+	if (!gadget)
+		return -EINVAL;
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	return pch_udc_get_frame(dev);
+}
+
+/**
+ * pch_udc_pcd_wakeup() - This API is invoked to initiate a remote wakeup
+ * @gadget:	Reference to the gadget driver
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:	If the gadget passed is NULL
+ */
+static int pch_udc_pcd_wakeup(struct usb_gadget *gadget)
+{
+	struct pch_udc_dev	*dev;
+	unsigned long		flags;
+
+	if (!gadget)
+		return -EINVAL;
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	spin_lock_irqsave(&dev->lock, flags);
+	pch_udc_rmt_wakeup(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 0;
+}
+
+/**
+ * pch_udc_pcd_selfpowered() - This API is invoked to specify whether the device
+ *				is self powered or not
+ * @gadget:	Reference to the gadget driver
+ * @value:	Specifies self powered or not
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:	If the gadget passed is NULL
+ */
+static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
+{
+	struct pch_udc_dev	*dev;
+
+	if (!gadget)
+		return -EINVAL;
+	gadget->is_selfpowered = (value != 0);
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	if (value)
+		pch_udc_set_selfpowered(dev);
+	else
+		pch_udc_clear_selfpowered(dev);
+	return 0;
+}
+
+/**
+ * pch_udc_pcd_pullup() - This API is invoked to make the device
+ *				visible/invisible to the host
+ * @gadget:	Reference to the gadget driver
+ * @is_on:	Specifies whether the pull up is made active or inactive
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:	If the gadget passed is NULL
+ */
+static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct pch_udc_dev	*dev;
+
+	if (!gadget)
+		return -EINVAL;
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	if (is_on) {
+		pch_udc_reconnect(dev);
+	} else {
+		if (dev->driver && dev->driver->disconnect) {
+			spin_lock(&dev->lock);
+			dev->driver->disconnect(&dev->gadget);
+			spin_unlock(&dev->lock);
+		}
+		pch_udc_set_disconnect(dev);
+	}
+
+	return 0;
+}
+
+/**
+ * pch_udc_pcd_vbus_session() - This API is used by a driver for an external
+ *				transceiver (or GPIO) that
+ *				detects a VBUS power session starting/ending
+ * @gadget:	Reference to the gadget driver
+ * @is_active:	specifies whether the session is starting or ending
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:	If the gadget passed is NULL
+ */
+static int pch_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct pch_udc_dev	*dev;
+
+	if (!gadget)
+		return -EINVAL;
+	dev = container_of(gadget, struct pch_udc_dev, gadget);
+	pch_udc_vbus_session(dev, is_active);
+	return 0;
+}
+
+/**
+ * pch_udc_pcd_vbus_draw() - This API is used by gadget drivers during
+ *				SET_CONFIGURATION calls to
+ *				specify how much power the device can consume
+ * @gadget:	Reference to the gadget driver
+ * @mA:		specifies the current limit in 2mA unit
+ *
+ * Return codes:
+ *	-EINVAL:	If the gadget passed is NULL
+ *	-EOPNOTSUPP:
+ */
+static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pch_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int pch_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops pch_udc_ops = {
+	.get_frame = pch_udc_pcd_get_frame,
+	.wakeup = pch_udc_pcd_wakeup,
+	.set_selfpowered = pch_udc_pcd_selfpowered,
+	.pullup = pch_udc_pcd_pullup,
+	.vbus_session = pch_udc_pcd_vbus_session,
+	.vbus_draw = pch_udc_pcd_vbus_draw,
+	.udc_start = pch_udc_start,
+	.udc_stop = pch_udc_stop,
+};
+
+/**
+ * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status.
+ * @dev:	Reference to the driver structure
+ *
+ * Return value:
+ *	1: VBUS is high
+ *	0: VBUS is low
+ *     -1: It is not enable to detect VBUS using GPIO
+ */
+static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev)
+{
+	int vbus = 0;
+
+	if (dev->vbus_gpio.port)
+		vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0;
+	else
+		vbus = -1;
+
+	return vbus;
+}
+
+/**
+ * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low.
+ *                             If VBUS is Low, disconnect is processed
+ * @irq_work:	Structure for WorkQueue
+ *
+ */
+static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
+{
+	struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
+		struct pch_vbus_gpio_data, irq_work_fall);
+	struct pch_udc_dev *dev =
+		container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
+	int vbus_saved = -1;
+	int vbus;
+	int count;
+
+	if (!dev->vbus_gpio.port)
+		return;
+
+	for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL);
+		count++) {
+		vbus = pch_vbus_gpio_get_value(dev);
+
+		if ((vbus_saved == vbus) && (vbus == 0)) {
+			dev_dbg(&dev->pdev->dev, "VBUS fell");
+			if (dev->driver
+				&& dev->driver->disconnect) {
+				dev->driver->disconnect(
+					&dev->gadget);
+			}
+			if (dev->vbus_gpio.intr)
+				pch_udc_init(dev);
+			else
+				pch_udc_reconnect(dev);
+			return;
+		}
+		vbus_saved = vbus;
+		mdelay(PCH_VBUS_INTERVAL);
+	}
+}
+
+/**
+ * pch_vbus_gpio_work_rise() - This API checks VBUS is High.
+ *                             If VBUS is High, connect is processed
+ * @irq_work:	Structure for WorkQueue
+ *
+ */
+static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
+{
+	struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
+		struct pch_vbus_gpio_data, irq_work_rise);
+	struct pch_udc_dev *dev =
+		container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
+	int vbus;
+
+	if (!dev->vbus_gpio.port)
+		return;
+
+	mdelay(PCH_VBUS_INTERVAL);
+	vbus = pch_vbus_gpio_get_value(dev);
+
+	if (vbus == 1) {
+		dev_dbg(&dev->pdev->dev, "VBUS rose");
+		pch_udc_reconnect(dev);
+		return;
+	}
+}
+
+/**
+ * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
+ * @irq:	Interrupt request number
+ * @dev:	Reference to the device structure
+ *
+ * Return codes:
+ *	0: Success
+ *	-EINVAL: GPIO port is invalid or can't be initialized.
+ */
+static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
+{
+	struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
+
+	if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr)
+		return IRQ_NONE;
+
+	if (pch_vbus_gpio_get_value(dev))
+		schedule_work(&dev->vbus_gpio.irq_work_rise);
+	else
+		schedule_work(&dev->vbus_gpio.irq_work_fall);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
+ * @dev:	Reference to the driver structure
+ * @vbus_gpio	Number of GPIO port to detect gpio
+ *
+ * Return codes:
+ *	0: Success
+ *	-EINVAL: GPIO port is invalid or can't be initialized.
+ */
+static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
+{
+	int err;
+	int irq_num = 0;
+
+	dev->vbus_gpio.port = 0;
+	dev->vbus_gpio.intr = 0;
+
+	if (vbus_gpio_port <= -1)
+		return -EINVAL;
+
+	err = gpio_is_valid(vbus_gpio_port);
+	if (!err) {
+		pr_err("%s: gpio port %d is invalid\n",
+			__func__, vbus_gpio_port);
+		return -EINVAL;
+	}
+
+	err = gpio_request(vbus_gpio_port, "pch_vbus");
+	if (err) {
+		pr_err("%s: can't request gpio port %d, err: %d\n",
+			__func__, vbus_gpio_port, err);
+		return -EINVAL;
+	}
+
+	dev->vbus_gpio.port = vbus_gpio_port;
+	gpio_direction_input(vbus_gpio_port);
+	INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
+
+	irq_num = gpio_to_irq(vbus_gpio_port);
+	if (irq_num > 0) {
+		irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
+		err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
+			"vbus_detect", dev);
+		if (!err) {
+			dev->vbus_gpio.intr = irq_num;
+			INIT_WORK(&dev->vbus_gpio.irq_work_rise,
+				pch_vbus_gpio_work_rise);
+		} else {
+			pr_err("%s: can't request irq %d, err: %d\n",
+				__func__, irq_num, err);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * pch_vbus_gpio_free() - This API frees resources of GPIO port
+ * @dev:	Reference to the driver structure
+ */
+static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
+{
+	if (dev->vbus_gpio.intr)
+		free_irq(dev->vbus_gpio.intr, dev);
+
+	if (dev->vbus_gpio.port)
+		gpio_free(dev->vbus_gpio.port);
+}
+
+/**
+ * complete_req() - This API is invoked from the driver when processing
+ *			of a request is complete
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request structure
+ * @status:	Indicates the success/failure of completion
+ */
+static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
+								 int status)
+	__releases(&dev->lock)
+	__acquires(&dev->lock)
+{
+	struct pch_udc_dev	*dev;
+	unsigned halted = ep->halted;
+
+	list_del_init(&req->queue);
+
+	/* set new status if pending */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	dev = ep->dev;
+	if (req->dma_mapped) {
+		if (req->dma == DMA_ADDR_INVALID) {
+			if (ep->in)
+				dma_unmap_single(&dev->pdev->dev, req->req.dma,
+						 req->req.length,
+						 DMA_TO_DEVICE);
+			else
+				dma_unmap_single(&dev->pdev->dev, req->req.dma,
+						 req->req.length,
+						 DMA_FROM_DEVICE);
+			req->req.dma = DMA_ADDR_INVALID;
+		} else {
+			if (ep->in)
+				dma_unmap_single(&dev->pdev->dev, req->dma,
+						 req->req.length,
+						 DMA_TO_DEVICE);
+			else {
+				dma_unmap_single(&dev->pdev->dev, req->dma,
+						 req->req.length,
+						 DMA_FROM_DEVICE);
+				memcpy(req->req.buf, req->buf, req->req.length);
+			}
+			kfree(req->buf);
+			req->dma = DMA_ADDR_INVALID;
+		}
+		req->dma_mapped = 0;
+	}
+	ep->halted = 1;
+	spin_unlock(&dev->lock);
+	if (!ep->in)
+		pch_udc_ep_clear_rrdy(ep);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&dev->lock);
+	ep->halted = halted;
+}
+
+/**
+ * empty_req_queue() - This API empties the request queue of an endpoint
+ * @ep:		Reference to the endpoint structure
+ */
+static void empty_req_queue(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request	*req;
+
+	ep->halted = 1;
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+		complete_req(ep, req, -ESHUTDOWN);	/* Remove from list */
+	}
+}
+
+/**
+ * pch_udc_free_dma_chain() - This function frees the DMA chain created
+ *				for the request
+ * @dev		Reference to the driver structure
+ * @req		Reference to the request to be freed
+ *
+ * Return codes:
+ *	0: Success
+ */
+static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
+				   struct pch_udc_request *req)
+{
+	struct pch_udc_data_dma_desc *td = req->td_data;
+	unsigned i = req->chain_len;
+
+	dma_addr_t addr2;
+	dma_addr_t addr = (dma_addr_t)td->next;
+	td->next = 0x00;
+	for (; i > 1; --i) {
+		/* do not free first desc., will be done by free for request */
+		td = phys_to_virt(addr);
+		addr2 = (dma_addr_t)td->next;
+		dma_pool_free(dev->data_requests, td, addr);
+		td->next = 0x00;
+		addr = addr2;
+	}
+	req->chain_len = 1;
+}
+
+/**
+ * pch_udc_create_dma_chain() - This function creates or reinitializes
+ *				a DMA chain
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request
+ * @buf_len:	The buffer length
+ * @gfp_flags:	Flags to be used while mapping the data buffer
+ *
+ * Return codes:
+ *	0:		success,
+ *	-ENOMEM:	dma_pool_alloc invocation fails
+ */
+static int pch_udc_create_dma_chain(struct pch_udc_ep *ep,
+				    struct pch_udc_request *req,
+				    unsigned long buf_len,
+				    gfp_t gfp_flags)
+{
+	struct pch_udc_data_dma_desc *td = req->td_data, *last;
+	unsigned long bytes = req->req.length, i = 0;
+	dma_addr_t dma_addr;
+	unsigned len = 1;
+
+	if (req->chain_len > 1)
+		pch_udc_free_dma_chain(ep->dev, req);
+
+	if (req->dma == DMA_ADDR_INVALID)
+		td->dataptr = req->req.dma;
+	else
+		td->dataptr = req->dma;
+
+	td->status = PCH_UDC_BS_HST_BSY;
+	for (; ; bytes -= buf_len, ++len) {
+		td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
+		if (bytes <= buf_len)
+			break;
+		last = td;
+		td = dma_pool_alloc(ep->dev->data_requests, gfp_flags,
+				    &dma_addr);
+		if (!td)
+			goto nomem;
+		i += buf_len;
+		td->dataptr = req->td_data->dataptr + i;
+		last->next = dma_addr;
+	}
+
+	req->td_data_last = td;
+	td->status |= PCH_UDC_DMA_LAST;
+	td->next = req->td_data_phys;
+	req->chain_len = len;
+	return 0;
+
+nomem:
+	if (len > 1) {
+		req->chain_len = len;
+		pch_udc_free_dma_chain(ep->dev, req);
+	}
+	req->chain_len = 1;
+	return -ENOMEM;
+}
+
+/**
+ * prepare_dma() - This function creates and initializes the DMA chain
+ *			for the request
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request
+ * @gfp:	Flag to be used while mapping the data buffer
+ *
+ * Return codes:
+ *	0:		Success
+ *	Other 0:	linux error number on failure
+ */
+static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
+			  gfp_t gfp)
+{
+	int	retval;
+
+	/* Allocate and create a DMA chain */
+	retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+	if (retval) {
+		pr_err("%s: could not create DMA chain:%d\n", __func__, retval);
+		return retval;
+	}
+	if (ep->in)
+		req->td_data->status = (req->td_data->status &
+				~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_RDY;
+	return 0;
+}
+
+/**
+ * process_zlp() - This function process zero length packets
+ *			from the gadget driver
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request
+ */
+static void process_zlp(struct pch_udc_ep *ep, struct pch_udc_request *req)
+{
+	struct pch_udc_dev	*dev = ep->dev;
+
+	/* IN zlp's are handled by hardware */
+	complete_req(ep, req, 0);
+
+	/* if set_config or set_intf is waiting for ack by zlp
+	 * then set CSR_DONE
+	 */
+	if (dev->set_cfg_not_acked) {
+		pch_udc_set_csr_done(dev);
+		dev->set_cfg_not_acked = 0;
+	}
+	/* setup command is ACK'ed now by zlp */
+	if (!dev->stall && dev->waiting_zlp_ack) {
+		pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
+		dev->waiting_zlp_ack = 0;
+	}
+}
+
+/**
+ * pch_udc_start_rxrequest() - This function starts the receive requirement.
+ * @ep:		Reference to the endpoint structure
+ * @req:	Reference to the request structure
+ */
+static void pch_udc_start_rxrequest(struct pch_udc_ep *ep,
+					 struct pch_udc_request *req)
+{
+	struct pch_udc_data_dma_desc *td_data;
+
+	pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+	td_data = req->td_data;
+	/* Set the status bits for all descriptors */
+	while (1) {
+		td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
+				    PCH_UDC_BS_HST_RDY;
+		if ((td_data->status & PCH_UDC_DMA_LAST) ==  PCH_UDC_DMA_LAST)
+			break;
+		td_data = phys_to_virt(td_data->next);
+	}
+	/* Write the descriptor pointer */
+	pch_udc_ep_set_ddptr(ep, req->td_data_phys);
+	req->dma_going = 1;
+	pch_udc_enable_ep_interrupts(ep->dev, UDC_EPINT_OUT_EP0 << ep->num);
+	pch_udc_set_dma(ep->dev, DMA_DIR_RX);
+	pch_udc_ep_clear_nak(ep);
+	pch_udc_ep_set_rrdy(ep);
+}
+
+/**
+ * pch_udc_pcd_ep_enable() - This API enables the endpoint. It is called
+ *				from gadget driver
+ * @usbep:	Reference to the USB endpoint structure
+ * @desc:	Reference to the USB endpoint descriptor structure
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:
+ *	-ESHUTDOWN:
+ */
+static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
+				    const struct usb_endpoint_descriptor *desc)
+{
+	struct pch_udc_ep	*ep;
+	struct pch_udc_dev	*dev;
+	unsigned long		iflags;
+
+	if (!usbep || (usbep->name == ep0_string) || !desc ||
+	    (desc->bDescriptorType != USB_DT_ENDPOINT) || !desc->wMaxPacketSize)
+		return -EINVAL;
+
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	dev = ep->dev;
+	if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+	spin_lock_irqsave(&dev->lock, iflags);
+	ep->ep.desc = desc;
+	ep->halted = 0;
+	pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
+	ep->ep.maxpacket = usb_endpoint_maxp(desc);
+	pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+	spin_unlock_irqrestore(&dev->lock, iflags);
+	return 0;
+}
+
+/**
+ * pch_udc_pcd_ep_disable() - This API disables endpoint and is called
+ *				from gadget driver
+ * @usbep	Reference to the USB endpoint structure
+ *
+ * Return codes:
+ *	0:		Success
+ *	-EINVAL:
+ */
+static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
+{
+	struct pch_udc_ep	*ep;
+	unsigned long	iflags;
+
+	if (!usbep)
+		return -EINVAL;
+
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	if ((usbep->name == ep0_string) || !ep->ep.desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->dev->lock, iflags);
+	empty_req_queue(ep);
+	ep->halted = 1;
+	pch_udc_ep_disable(ep);
+	pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+	ep->ep.desc = NULL;
+	INIT_LIST_HEAD(&ep->queue);
+	spin_unlock_irqrestore(&ep->dev->lock, iflags);
+	return 0;
+}
+
+/**
+ * pch_udc_alloc_request() - This function allocates request structure.
+ *				It is called by gadget driver
+ * @usbep:	Reference to the USB endpoint structure
+ * @gfp:	Flag to be used while allocating memory
+ *
+ * Return codes:
+ *	NULL:			Failure
+ *	Allocated address:	Success
+ */
+static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
+						  gfp_t gfp)
+{
+	struct pch_udc_request		*req;
+	struct pch_udc_ep		*ep;
+	struct pch_udc_data_dma_desc	*dma_desc;
+
+	if (!usbep)
+		return NULL;
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	req = kzalloc(sizeof *req, gfp);
+	if (!req)
+		return NULL;
+	req->req.dma = DMA_ADDR_INVALID;
+	req->dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD(&req->queue);
+	if (!ep->dev->dma_addr)
+		return &req->req;
+	/* ep0 in requests are allocated from data pool here */
+	dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp,
+				  &req->td_data_phys);
+	if (NULL == dma_desc) {
+		kfree(req);
+		return NULL;
+	}
+	/* prevent from using desc. - set HOST BUSY */
+	dma_desc->status |= PCH_UDC_BS_HST_BSY;
+	dma_desc->dataptr = cpu_to_le32(DMA_ADDR_INVALID);
+	req->td_data = dma_desc;
+	req->td_data_last = dma_desc;
+	req->chain_len = 1;
+	return &req->req;
+}
+
+/**
+ * pch_udc_free_request() - This function frees request structure.
+ *				It is called by gadget driver
+ * @usbep:	Reference to the USB endpoint structure
+ * @usbreq:	Reference to the USB request
+ */
+static void pch_udc_free_request(struct usb_ep *usbep,
+				  struct usb_request *usbreq)
+{
+	struct pch_udc_ep	*ep;
+	struct pch_udc_request	*req;
+	struct pch_udc_dev	*dev;
+
+	if (!usbep || !usbreq)
+		return;
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	req = container_of(usbreq, struct pch_udc_request, req);
+	dev = ep->dev;
+	if (!list_empty(&req->queue))
+		dev_err(&dev->pdev->dev, "%s: %s req=0x%p queue not empty\n",
+			__func__, usbep->name, req);
+	if (req->td_data != NULL) {
+		if (req->chain_len > 1)
+			pch_udc_free_dma_chain(ep->dev, req);
+		dma_pool_free(ep->dev->data_requests, req->td_data,
+			      req->td_data_phys);
+	}
+	kfree(req);
+}
+
+/**
+ * pch_udc_pcd_queue() - This function queues a request packet. It is called
+ *			by gadget driver
+ * @usbep:	Reference to the USB endpoint structure
+ * @usbreq:	Reference to the USB request
+ * @gfp:	Flag to be used while mapping the data buffer
+ *
+ * Return codes:
+ *	0:			Success
+ *	linux error number:	Failure
+ */
+static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
+								 gfp_t gfp)
+{
+	int retval = 0;
+	struct pch_udc_ep	*ep;
+	struct pch_udc_dev	*dev;
+	struct pch_udc_request	*req;
+	unsigned long	iflags;
+
+	if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf)
+		return -EINVAL;
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	dev = ep->dev;
+	if (!ep->ep.desc && ep->num)
+		return -EINVAL;
+	req = container_of(usbreq, struct pch_udc_request, req);
+	if (!list_empty(&req->queue))
+		return -EINVAL;
+	if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+	spin_lock_irqsave(&dev->lock, iflags);
+	/* map the buffer for dma */
+	if (usbreq->length &&
+	    ((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
+		if (!((unsigned long)(usbreq->buf) & 0x03)) {
+			if (ep->in)
+				usbreq->dma = dma_map_single(&dev->pdev->dev,
+							     usbreq->buf,
+							     usbreq->length,
+							     DMA_TO_DEVICE);
+			else
+				usbreq->dma = dma_map_single(&dev->pdev->dev,
+							     usbreq->buf,
+							     usbreq->length,
+							     DMA_FROM_DEVICE);
+		} else {
+			req->buf = kzalloc(usbreq->length, GFP_ATOMIC);
+			if (!req->buf) {
+				retval = -ENOMEM;
+				goto probe_end;
+			}
+			if (ep->in) {
+				memcpy(req->buf, usbreq->buf, usbreq->length);
+				req->dma = dma_map_single(&dev->pdev->dev,
+							  req->buf,
+							  usbreq->length,
+							  DMA_TO_DEVICE);
+			} else
+				req->dma = dma_map_single(&dev->pdev->dev,
+							  req->buf,
+							  usbreq->length,
+							  DMA_FROM_DEVICE);
+		}
+		req->dma_mapped = 1;
+	}
+	if (usbreq->length > 0) {
+		retval = prepare_dma(ep, req, GFP_ATOMIC);
+		if (retval)
+			goto probe_end;
+	}
+	usbreq->actual = 0;
+	usbreq->status = -EINPROGRESS;
+	req->dma_done = 0;
+	if (list_empty(&ep->queue) && !ep->halted) {
+		/* no pending transfer, so start this req */
+		if (!usbreq->length) {
+			process_zlp(ep, req);
+			retval = 0;
+			goto probe_end;
+		}
+		if (!ep->in) {
+			pch_udc_start_rxrequest(ep, req);
+		} else {
+			/*
+			* For IN trfr the descriptors will be programmed and
+			* P bit will be set when
+			* we get an IN token
+			*/
+			pch_udc_wait_ep_stall(ep);
+			pch_udc_ep_clear_nak(ep);
+			pch_udc_enable_ep_interrupts(ep->dev, (1 << ep->num));
+		}
+	}
+	/* Now add this request to the ep's pending requests */
+	if (req != NULL)
+		list_add_tail(&req->queue, &ep->queue);
+
+probe_end:
+	spin_unlock_irqrestore(&dev->lock, iflags);
+	return retval;
+}
+
+/**
+ * pch_udc_pcd_dequeue() - This function de-queues a request packet.
+ *				It is called by gadget driver
+ * @usbep:	Reference to the USB endpoint structure
+ * @usbreq:	Reference to the USB request
+ *
+ * Return codes:
+ *	0:			Success
+ *	linux error number:	Failure
+ */
+static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
+				struct usb_request *usbreq)
+{
+	struct pch_udc_ep	*ep;
+	struct pch_udc_request	*req;
+	unsigned long		flags;
+	int ret = -EINVAL;
+
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	if (!usbep || !usbreq || (!ep->ep.desc && ep->num))
+		return ret;
+	req = container_of(usbreq, struct pch_udc_request, req);
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	/* make sure it's still queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == usbreq) {
+			pch_udc_ep_set_nak(ep);
+			if (!list_empty(&req->queue))
+				complete_req(ep, req, -ECONNRESET);
+			ret = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return ret;
+}
+
+/**
+ * pch_udc_pcd_set_halt() - This function Sets or clear the endpoint halt
+ *			    feature
+ * @usbep:	Reference to the USB endpoint structure
+ * @halt:	Specifies whether to set or clear the feature
+ *
+ * Return codes:
+ *	0:			Success
+ *	linux error number:	Failure
+ */
+static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
+{
+	struct pch_udc_ep	*ep;
+	unsigned long iflags;
+	int ret;
+
+	if (!usbep)
+		return -EINVAL;
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	if (!ep->ep.desc && !ep->num)
+		return -EINVAL;
+	if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+	spin_lock_irqsave(&udc_stall_spinlock, iflags);
+	if (list_empty(&ep->queue)) {
+		if (halt) {
+			if (ep->num == PCH_UDC_EP0)
+				ep->dev->stall = 1;
+			pch_udc_ep_set_stall(ep);
+			pch_udc_enable_ep_interrupts(
+				ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+		} else {
+			pch_udc_ep_clear_stall(ep);
+		}
+		ret = 0;
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+	return ret;
+}
+
+/**
+ * pch_udc_pcd_set_wedge() - This function Sets or clear the endpoint
+ *				halt feature
+ * @usbep:	Reference to the USB endpoint structure
+ * @halt:	Specifies whether to set or clear the feature
+ *
+ * Return codes:
+ *	0:			Success
+ *	linux error number:	Failure
+ */
+static int pch_udc_pcd_set_wedge(struct usb_ep *usbep)
+{
+	struct pch_udc_ep	*ep;
+	unsigned long iflags;
+	int ret;
+
+	if (!usbep)
+		return -EINVAL;
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	if (!ep->ep.desc && !ep->num)
+		return -EINVAL;
+	if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+	spin_lock_irqsave(&udc_stall_spinlock, iflags);
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+	} else {
+		if (ep->num == PCH_UDC_EP0)
+			ep->dev->stall = 1;
+		pch_udc_ep_set_stall(ep);
+		pch_udc_enable_ep_interrupts(ep->dev,
+					     PCH_UDC_EPINT(ep->in, ep->num));
+		ep->dev->prot_stall = 1;
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+	return ret;
+}
+
+/**
+ * pch_udc_pcd_fifo_flush() - This function Flush the FIFO of specified endpoint
+ * @usbep:	Reference to the USB endpoint structure
+ */
+static void pch_udc_pcd_fifo_flush(struct usb_ep *usbep)
+{
+	struct pch_udc_ep  *ep;
+
+	if (!usbep)
+		return;
+
+	ep = container_of(usbep, struct pch_udc_ep, ep);
+	if (ep->ep.desc || !ep->num)
+		pch_udc_ep_fifo_flush(ep, ep->in);
+}
+
+static const struct usb_ep_ops pch_udc_ep_ops = {
+	.enable		= pch_udc_pcd_ep_enable,
+	.disable	= pch_udc_pcd_ep_disable,
+	.alloc_request	= pch_udc_alloc_request,
+	.free_request	= pch_udc_free_request,
+	.queue		= pch_udc_pcd_queue,
+	.dequeue	= pch_udc_pcd_dequeue,
+	.set_halt	= pch_udc_pcd_set_halt,
+	.set_wedge	= pch_udc_pcd_set_wedge,
+	.fifo_status	= NULL,
+	.fifo_flush	= pch_udc_pcd_fifo_flush,
+};
+
+/**
+ * pch_udc_init_setup_buff() - This function initializes the SETUP buffer
+ * @td_stp:	Reference to the SETP buffer structure
+ */
+static void pch_udc_init_setup_buff(struct pch_udc_stp_dma_desc *td_stp)
+{
+	static u32	pky_marker;
+
+	if (!td_stp)
+		return;
+	td_stp->reserved = ++pky_marker;
+	memset(&td_stp->request, 0xFF, sizeof td_stp->request);
+	td_stp->status = PCH_UDC_BS_HST_RDY;
+}
+
+/**
+ * pch_udc_start_next_txrequest() - This function starts
+ *					the next transmission requirement
+ * @ep:	Reference to the endpoint structure
+ */
+static void pch_udc_start_next_txrequest(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request *req;
+	struct pch_udc_data_dma_desc *td_data;
+
+	if (pch_udc_read_ep_control(ep) & UDC_EPCTL_P)
+		return;
+
+	if (list_empty(&ep->queue))
+		return;
+
+	/* next request */
+	req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+	if (req->dma_going)
+		return;
+	if (!req->td_data)
+		return;
+	pch_udc_wait_ep_stall(ep);
+	req->dma_going = 1;
+	pch_udc_ep_set_ddptr(ep, 0);
+	td_data = req->td_data;
+	while (1) {
+		td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
+				   PCH_UDC_BS_HST_RDY;
+		if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST)
+			break;
+		td_data = phys_to_virt(td_data->next);
+	}
+	pch_udc_ep_set_ddptr(ep, req->td_data_phys);
+	pch_udc_set_dma(ep->dev, DMA_DIR_TX);
+	pch_udc_ep_set_pd(ep);
+	pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+	pch_udc_ep_clear_nak(ep);
+}
+
+/**
+ * pch_udc_complete_transfer() - This function completes a transfer
+ * @ep:		Reference to the endpoint structure
+ */
+static void pch_udc_complete_transfer(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request *req;
+	struct pch_udc_dev *dev = ep->dev;
+
+	if (list_empty(&ep->queue))
+		return;
+	req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+	if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+	    PCH_UDC_BS_DMA_DONE)
+		return;
+	if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
+	     PCH_UDC_RTS_SUCC) {
+		dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
+			"epstatus=0x%08x\n",
+		       (req->td_data_last->status & PCH_UDC_RXTX_STS),
+		       (int)(ep->epsts));
+		return;
+	}
+
+	req->req.actual = req->req.length;
+	req->td_data_last->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST;
+	req->td_data->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST;
+	complete_req(ep, req, 0);
+	req->dma_going = 0;
+	if (!list_empty(&ep->queue)) {
+		pch_udc_wait_ep_stall(ep);
+		pch_udc_ep_clear_nak(ep);
+		pch_udc_enable_ep_interrupts(ep->dev,
+					     PCH_UDC_EPINT(ep->in, ep->num));
+	} else {
+		pch_udc_disable_ep_interrupts(ep->dev,
+					      PCH_UDC_EPINT(ep->in, ep->num));
+	}
+}
+
+/**
+ * pch_udc_complete_receiver() - This function completes a receiver
+ * @ep:		Reference to the endpoint structure
+ */
+static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
+{
+	struct pch_udc_request *req;
+	struct pch_udc_dev *dev = ep->dev;
+	unsigned int count;
+	struct pch_udc_data_dma_desc *td;
+	dma_addr_t addr;
+
+	if (list_empty(&ep->queue))
+		return;
+	/* next request */
+	req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+	pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+	pch_udc_ep_set_ddptr(ep, 0);
+	if ((req->td_data_last->status & PCH_UDC_BUFF_STS) ==
+	    PCH_UDC_BS_DMA_DONE)
+		td = req->td_data_last;
+	else
+		td = req->td_data;
+
+	while (1) {
+		if ((td->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) {
+			dev_err(&dev->pdev->dev, "Invalid RXTX status=0x%08x "
+				"epstatus=0x%08x\n",
+				(req->td_data->status & PCH_UDC_RXTX_STS),
+				(int)(ep->epsts));
+			return;
+		}
+		if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
+			if (td->status & PCH_UDC_DMA_LAST) {
+				count = td->status & PCH_UDC_RXTX_BYTES;
+				break;
+			}
+		if (td == req->td_data_last) {
+			dev_err(&dev->pdev->dev, "Not complete RX descriptor");
+			return;
+		}
+		addr = (dma_addr_t)td->next;
+		td = phys_to_virt(addr);
+	}
+	/* on 64k packets the RXBYTES field is zero */
+	if (!count && (req->req.length == UDC_DMA_MAXPACKET))
+		count = UDC_DMA_MAXPACKET;
+	req->td_data->status |= PCH_UDC_DMA_LAST;
+	td->status |= PCH_UDC_BS_HST_BSY;
+
+	req->dma_going = 0;
+	req->req.actual = count;
+	complete_req(ep, req, 0);
+	/* If there is a new/failed requests try that now */
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+		pch_udc_start_rxrequest(ep, req);
+	}
+}
+
+/**
+ * pch_udc_svc_data_in() - This function process endpoint interrupts
+ *				for IN endpoints
+ * @dev:	Reference to the device structure
+ * @ep_num:	Endpoint that generated the interrupt
+ */
+static void pch_udc_svc_data_in(struct pch_udc_dev *dev, int ep_num)
+{
+	u32	epsts;
+	struct pch_udc_ep	*ep;
+
+	ep = &dev->ep[UDC_EPIN_IDX(ep_num)];
+	epsts = ep->epsts;
+	ep->epsts = 0;
+
+	if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA  | UDC_EPSTS_HE |
+		       UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
+		       UDC_EPSTS_RSS | UDC_EPSTS_XFERDONE)))
+		return;
+	if ((epsts & UDC_EPSTS_BNA))
+		return;
+	if (epsts & UDC_EPSTS_HE)
+		return;
+	if (epsts & UDC_EPSTS_RSS) {
+		pch_udc_ep_set_stall(ep);
+		pch_udc_enable_ep_interrupts(ep->dev,
+					     PCH_UDC_EPINT(ep->in, ep->num));
+	}
+	if (epsts & UDC_EPSTS_RCS) {
+		if (!dev->prot_stall) {
+			pch_udc_ep_clear_stall(ep);
+		} else {
+			pch_udc_ep_set_stall(ep);
+			pch_udc_enable_ep_interrupts(ep->dev,
+						PCH_UDC_EPINT(ep->in, ep->num));
+		}
+	}
+	if (epsts & UDC_EPSTS_TDC)
+		pch_udc_complete_transfer(ep);
+	/* On IN interrupt, provide data if we have any */
+	if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_RSS) &&
+	    !(epsts & UDC_EPSTS_TDC) && !(epsts & UDC_EPSTS_TXEMPTY))
+		pch_udc_start_next_txrequest(ep);
+}
+
+/**
+ * pch_udc_svc_data_out() - Handles interrupts from OUT endpoint
+ * @dev:	Reference to the device structure
+ * @ep_num:	Endpoint that generated the interrupt
+ */
+static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num)
+{
+	u32			epsts;
+	struct pch_udc_ep		*ep;
+	struct pch_udc_request		*req = NULL;
+
+	ep = &dev->ep[UDC_EPOUT_IDX(ep_num)];
+	epsts = ep->epsts;
+	ep->epsts = 0;
+
+	if ((epsts & UDC_EPSTS_BNA) && (!list_empty(&ep->queue))) {
+		/* next request */
+		req = list_entry(ep->queue.next, struct pch_udc_request,
+				 queue);
+		if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+		     PCH_UDC_BS_DMA_DONE) {
+			if (!req->dma_going)
+				pch_udc_start_rxrequest(ep, req);
+			return;
+		}
+	}
+	if (epsts & UDC_EPSTS_HE)
+		return;
+	if (epsts & UDC_EPSTS_RSS) {
+		pch_udc_ep_set_stall(ep);
+		pch_udc_enable_ep_interrupts(ep->dev,
+					     PCH_UDC_EPINT(ep->in, ep->num));
+	}
+	if (epsts & UDC_EPSTS_RCS) {
+		if (!dev->prot_stall) {
+			pch_udc_ep_clear_stall(ep);
+		} else {
+			pch_udc_ep_set_stall(ep);
+			pch_udc_enable_ep_interrupts(ep->dev,
+						PCH_UDC_EPINT(ep->in, ep->num));
+		}
+	}
+	if (((epsts & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+	    UDC_EPSTS_OUT_DATA) {
+		if (ep->dev->prot_stall == 1) {
+			pch_udc_ep_set_stall(ep);
+			pch_udc_enable_ep_interrupts(ep->dev,
+						PCH_UDC_EPINT(ep->in, ep->num));
+		} else {
+			pch_udc_complete_receiver(ep);
+		}
+	}
+	if (list_empty(&ep->queue))
+		pch_udc_set_dma(dev, DMA_DIR_RX);
+}
+
+/**
+ * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_svc_control_in(struct pch_udc_dev *dev)
+{
+	u32	epsts;
+	struct pch_udc_ep	*ep;
+	struct pch_udc_ep	*ep_out;
+
+	ep = &dev->ep[UDC_EP0IN_IDX];
+	ep_out = &dev->ep[UDC_EP0OUT_IDX];
+	epsts = ep->epsts;
+	ep->epsts = 0;
+
+	if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE |
+		       UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
+		       UDC_EPSTS_XFERDONE)))
+		return;
+	if ((epsts & UDC_EPSTS_BNA))
+		return;
+	if (epsts & UDC_EPSTS_HE)
+		return;
+	if ((epsts & UDC_EPSTS_TDC) && (!dev->stall)) {
+		pch_udc_complete_transfer(ep);
+		pch_udc_clear_dma(dev, DMA_DIR_RX);
+		ep_out->td_data->status = (ep_out->td_data->status &
+					~PCH_UDC_BUFF_STS) |
+					PCH_UDC_BS_HST_RDY;
+		pch_udc_ep_clear_nak(ep_out);
+		pch_udc_set_dma(dev, DMA_DIR_RX);
+		pch_udc_ep_set_rrdy(ep_out);
+	}
+	/* On IN interrupt, provide data if we have any */
+	if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_TDC) &&
+	     !(epsts & UDC_EPSTS_TXEMPTY))
+		pch_udc_start_next_txrequest(ep);
+}
+
+/**
+ * pch_udc_svc_control_out() - Routine that handle Control
+ *					OUT endpoint interrupts
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
+	__releases(&dev->lock)
+	__acquires(&dev->lock)
+{
+	u32	stat;
+	int setup_supported;
+	struct pch_udc_ep	*ep;
+
+	ep = &dev->ep[UDC_EP0OUT_IDX];
+	stat = ep->epsts;
+	ep->epsts = 0;
+
+	/* If setup data */
+	if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+	    UDC_EPSTS_OUT_SETUP) {
+		dev->stall = 0;
+		dev->ep[UDC_EP0IN_IDX].halted = 0;
+		dev->ep[UDC_EP0OUT_IDX].halted = 0;
+		dev->setup_data = ep->td_stp->request;
+		pch_udc_init_setup_buff(ep->td_stp);
+		pch_udc_clear_dma(dev, DMA_DIR_RX);
+		pch_udc_ep_fifo_flush(&(dev->ep[UDC_EP0IN_IDX]),
+				      dev->ep[UDC_EP0IN_IDX].in);
+		if ((dev->setup_data.bRequestType & USB_DIR_IN))
+			dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
+		else /* OUT */
+			dev->gadget.ep0 = &ep->ep;
+		spin_lock(&dev->lock);
+		/* If Mass storage Reset */
+		if ((dev->setup_data.bRequestType == 0x21) &&
+		    (dev->setup_data.bRequest == 0xFF))
+			dev->prot_stall = 0;
+		/* call gadget with setup data received */
+		setup_supported = dev->driver->setup(&dev->gadget,
+						     &dev->setup_data);
+		spin_unlock(&dev->lock);
+
+		if (dev->setup_data.bRequestType & USB_DIR_IN) {
+			ep->td_data->status = (ep->td_data->status &
+						~PCH_UDC_BUFF_STS) |
+						PCH_UDC_BS_HST_RDY;
+			pch_udc_ep_set_ddptr(ep, ep->td_data_phys);
+		}
+		/* ep0 in returns data on IN phase */
+		if (setup_supported >= 0 && setup_supported <
+					    UDC_EP0IN_MAX_PKT_SIZE) {
+			pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
+			/* Gadget would have queued a request when
+			 * we called the setup */
+			if (!(dev->setup_data.bRequestType & USB_DIR_IN)) {
+				pch_udc_set_dma(dev, DMA_DIR_RX);
+				pch_udc_ep_clear_nak(ep);
+			}
+		} else if (setup_supported < 0) {
+			/* if unsupported request, then stall */
+			pch_udc_ep_set_stall(&(dev->ep[UDC_EP0IN_IDX]));
+			pch_udc_enable_ep_interrupts(ep->dev,
+						PCH_UDC_EPINT(ep->in, ep->num));
+			dev->stall = 0;
+			pch_udc_set_dma(dev, DMA_DIR_RX);
+		} else {
+			dev->waiting_zlp_ack = 1;
+		}
+	} else if ((((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+		     UDC_EPSTS_OUT_DATA) && !dev->stall) {
+		pch_udc_clear_dma(dev, DMA_DIR_RX);
+		pch_udc_ep_set_ddptr(ep, 0);
+		if (!list_empty(&ep->queue)) {
+			ep->epsts = stat;
+			pch_udc_svc_data_out(dev, PCH_UDC_EP0);
+		}
+		pch_udc_set_dma(dev, DMA_DIR_RX);
+	}
+	pch_udc_ep_set_rrdy(ep);
+}
+
+
+/**
+ * pch_udc_postsvc_epinters() - This function enables end point interrupts
+ *				and clears NAK status
+ * @dev:	Reference to the device structure
+ * @ep_num:	End point number
+ */
+static void pch_udc_postsvc_epinters(struct pch_udc_dev *dev, int ep_num)
+{
+	struct pch_udc_ep	*ep = &dev->ep[UDC_EPIN_IDX(ep_num)];
+	if (list_empty(&ep->queue))
+		return;
+	pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+	pch_udc_ep_clear_nak(ep);
+}
+
+/**
+ * pch_udc_read_all_epstatus() - This function read all endpoint status
+ * @dev:	Reference to the device structure
+ * @ep_intr:	Status of endpoint interrupt
+ */
+static void pch_udc_read_all_epstatus(struct pch_udc_dev *dev, u32 ep_intr)
+{
+	int i;
+	struct pch_udc_ep	*ep;
+
+	for (i = 0; i < PCH_UDC_USED_EP_NUM; i++) {
+		/* IN */
+		if (ep_intr & (0x1 << i)) {
+			ep = &dev->ep[UDC_EPIN_IDX(i)];
+			ep->epsts = pch_udc_read_ep_status(ep);
+			pch_udc_clear_ep_status(ep, ep->epsts);
+		}
+		/* OUT */
+		if (ep_intr & (0x10000 << i)) {
+			ep = &dev->ep[UDC_EPOUT_IDX(i)];
+			ep->epsts = pch_udc_read_ep_status(ep);
+			pch_udc_clear_ep_status(ep, ep->epsts);
+		}
+	}
+}
+
+/**
+ * pch_udc_activate_control_ep() - This function enables the control endpoints
+ *					for traffic after a reset
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_activate_control_ep(struct pch_udc_dev *dev)
+{
+	struct pch_udc_ep	*ep;
+	u32 val;
+
+	/* Setup the IN endpoint */
+	ep = &dev->ep[UDC_EP0IN_IDX];
+	pch_udc_clear_ep_control(ep);
+	pch_udc_ep_fifo_flush(ep, ep->in);
+	pch_udc_ep_set_bufsz(ep, UDC_EP0IN_BUFF_SIZE, ep->in);
+	pch_udc_ep_set_maxpkt(ep, UDC_EP0IN_MAX_PKT_SIZE);
+	/* Initialize the IN EP Descriptor */
+	ep->td_data      = NULL;
+	ep->td_stp       = NULL;
+	ep->td_data_phys = 0;
+	ep->td_stp_phys  = 0;
+
+	/* Setup the OUT endpoint */
+	ep = &dev->ep[UDC_EP0OUT_IDX];
+	pch_udc_clear_ep_control(ep);
+	pch_udc_ep_fifo_flush(ep, ep->in);
+	pch_udc_ep_set_bufsz(ep, UDC_EP0OUT_BUFF_SIZE, ep->in);
+	pch_udc_ep_set_maxpkt(ep, UDC_EP0OUT_MAX_PKT_SIZE);
+	val = UDC_EP0OUT_MAX_PKT_SIZE << UDC_CSR_NE_MAX_PKT_SHIFT;
+	pch_udc_write_csr(ep->dev, val, UDC_EP0OUT_IDX);
+
+	/* Initialize the SETUP buffer */
+	pch_udc_init_setup_buff(ep->td_stp);
+	/* Write the pointer address of dma descriptor */
+	pch_udc_ep_set_subptr(ep, ep->td_stp_phys);
+	/* Write the pointer address of Setup descriptor */
+	pch_udc_ep_set_ddptr(ep, ep->td_data_phys);
+
+	/* Initialize the dma descriptor */
+	ep->td_data->status  = PCH_UDC_DMA_LAST;
+	ep->td_data->dataptr = dev->dma_addr;
+	ep->td_data->next    = ep->td_data_phys;
+
+	pch_udc_ep_clear_nak(ep);
+}
+
+
+/**
+ * pch_udc_svc_ur_interrupt() - This function handles a USB reset interrupt
+ * @dev:	Reference to driver structure
+ */
+static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
+{
+	struct pch_udc_ep	*ep;
+	int i;
+
+	pch_udc_clear_dma(dev, DMA_DIR_TX);
+	pch_udc_clear_dma(dev, DMA_DIR_RX);
+	/* Mask all endpoint interrupts */
+	pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+	/* clear all endpoint interrupts */
+	pch_udc_write_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+	for (i = 0; i < PCH_UDC_EP_NUM; i++) {
+		ep = &dev->ep[i];
+		pch_udc_clear_ep_status(ep, UDC_EPSTS_ALL_CLR_MASK);
+		pch_udc_clear_ep_control(ep);
+		pch_udc_ep_set_ddptr(ep, 0);
+		pch_udc_write_csr(ep->dev, 0x00, i);
+	}
+	dev->stall = 0;
+	dev->prot_stall = 0;
+	dev->waiting_zlp_ack = 0;
+	dev->set_cfg_not_acked = 0;
+
+	/* disable ep to empty req queue. Skip the control EP's */
+	for (i = 0; i < (PCH_UDC_USED_EP_NUM*2); i++) {
+		ep = &dev->ep[i];
+		pch_udc_ep_set_nak(ep);
+		pch_udc_ep_fifo_flush(ep, ep->in);
+		/* Complete request queue */
+		empty_req_queue(ep);
+	}
+	if (dev->driver) {
+		spin_unlock(&dev->lock);
+		usb_gadget_udc_reset(&dev->gadget, dev->driver);
+		spin_lock(&dev->lock);
+	}
+}
+
+/**
+ * pch_udc_svc_enum_interrupt() - This function handles a USB speed enumeration
+ *				done interrupt
+ * @dev:	Reference to driver structure
+ */
+static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev)
+{
+	u32 dev_stat, dev_speed;
+	u32 speed = USB_SPEED_FULL;
+
+	dev_stat = pch_udc_read_device_status(dev);
+	dev_speed = (dev_stat & UDC_DEVSTS_ENUM_SPEED_MASK) >>
+						 UDC_DEVSTS_ENUM_SPEED_SHIFT;
+	switch (dev_speed) {
+	case UDC_DEVSTS_ENUM_SPEED_HIGH:
+		speed = USB_SPEED_HIGH;
+		break;
+	case  UDC_DEVSTS_ENUM_SPEED_FULL:
+		speed = USB_SPEED_FULL;
+		break;
+	case  UDC_DEVSTS_ENUM_SPEED_LOW:
+		speed = USB_SPEED_LOW;
+		break;
+	default:
+		BUG();
+	}
+	dev->gadget.speed = speed;
+	pch_udc_activate_control_ep(dev);
+	pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 | UDC_EPINT_OUT_EP0);
+	pch_udc_set_dma(dev, DMA_DIR_TX);
+	pch_udc_set_dma(dev, DMA_DIR_RX);
+	pch_udc_ep_set_rrdy(&(dev->ep[UDC_EP0OUT_IDX]));
+
+	/* enable device interrupts */
+	pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US |
+					UDC_DEVINT_ES | UDC_DEVINT_ENUM |
+					UDC_DEVINT_SI | UDC_DEVINT_SC);
+}
+
+/**
+ * pch_udc_svc_intf_interrupt() - This function handles a set interface
+ *				  interrupt
+ * @dev:	Reference to driver structure
+ */
+static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
+{
+	u32 reg, dev_stat = 0;
+	int i;
+
+	dev_stat = pch_udc_read_device_status(dev);
+	dev->cfg_data.cur_intf = (dev_stat & UDC_DEVSTS_INTF_MASK) >>
+							 UDC_DEVSTS_INTF_SHIFT;
+	dev->cfg_data.cur_alt = (dev_stat & UDC_DEVSTS_ALT_MASK) >>
+							 UDC_DEVSTS_ALT_SHIFT;
+	dev->set_cfg_not_acked = 1;
+	/* Construct the usb request for gadget driver and inform it */
+	memset(&dev->setup_data, 0 , sizeof dev->setup_data);
+	dev->setup_data.bRequest = USB_REQ_SET_INTERFACE;
+	dev->setup_data.bRequestType = USB_RECIP_INTERFACE;
+	dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_alt);
+	dev->setup_data.wIndex = cpu_to_le16(dev->cfg_data.cur_intf);
+	/* programm the Endpoint Cfg registers */
+	/* Only one end point cfg register */
+	reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX);
+	reg = (reg & ~UDC_CSR_NE_INTF_MASK) |
+	      (dev->cfg_data.cur_intf << UDC_CSR_NE_INTF_SHIFT);
+	reg = (reg & ~UDC_CSR_NE_ALT_MASK) |
+	      (dev->cfg_data.cur_alt << UDC_CSR_NE_ALT_SHIFT);
+	pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX);
+	for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) {
+		/* clear stall bits */
+		pch_udc_ep_clear_stall(&(dev->ep[i]));
+		dev->ep[i].halted = 0;
+	}
+	dev->stall = 0;
+	spin_unlock(&dev->lock);
+	dev->driver->setup(&dev->gadget, &dev->setup_data);
+	spin_lock(&dev->lock);
+}
+
+/**
+ * pch_udc_svc_cfg_interrupt() - This function handles a set configuration
+ *				interrupt
+ * @dev:	Reference to driver structure
+ */
+static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
+{
+	int i;
+	u32 reg, dev_stat = 0;
+
+	dev_stat = pch_udc_read_device_status(dev);
+	dev->set_cfg_not_acked = 1;
+	dev->cfg_data.cur_cfg = (dev_stat & UDC_DEVSTS_CFG_MASK) >>
+				UDC_DEVSTS_CFG_SHIFT;
+	/* make usb request for gadget driver */
+	memset(&dev->setup_data, 0 , sizeof dev->setup_data);
+	dev->setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_cfg);
+	/* program the NE registers */
+	/* Only one end point cfg register */
+	reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX);
+	reg = (reg & ~UDC_CSR_NE_CFG_MASK) |
+	      (dev->cfg_data.cur_cfg << UDC_CSR_NE_CFG_SHIFT);
+	pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX);
+	for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) {
+		/* clear stall bits */
+		pch_udc_ep_clear_stall(&(dev->ep[i]));
+		dev->ep[i].halted = 0;
+	}
+	dev->stall = 0;
+
+	/* call gadget zero with setup data received */
+	spin_unlock(&dev->lock);
+	dev->driver->setup(&dev->gadget, &dev->setup_data);
+	spin_lock(&dev->lock);
+}
+
+/**
+ * pch_udc_dev_isr() - This function services device interrupts
+ *			by invoking appropriate routines.
+ * @dev:	Reference to the device structure
+ * @dev_intr:	The Device interrupt status.
+ */
+static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
+{
+	int vbus;
+
+	/* USB Reset Interrupt */
+	if (dev_intr & UDC_DEVINT_UR) {
+		pch_udc_svc_ur_interrupt(dev);
+		dev_dbg(&dev->pdev->dev, "USB_RESET\n");
+	}
+	/* Enumeration Done Interrupt */
+	if (dev_intr & UDC_DEVINT_ENUM) {
+		pch_udc_svc_enum_interrupt(dev);
+		dev_dbg(&dev->pdev->dev, "USB_ENUM\n");
+	}
+	/* Set Interface Interrupt */
+	if (dev_intr & UDC_DEVINT_SI)
+		pch_udc_svc_intf_interrupt(dev);
+	/* Set Config Interrupt */
+	if (dev_intr & UDC_DEVINT_SC)
+		pch_udc_svc_cfg_interrupt(dev);
+	/* USB Suspend interrupt */
+	if (dev_intr & UDC_DEVINT_US) {
+		if (dev->driver
+			&& dev->driver->suspend) {
+			spin_unlock(&dev->lock);
+			dev->driver->suspend(&dev->gadget);
+			spin_lock(&dev->lock);
+		}
+
+		vbus = pch_vbus_gpio_get_value(dev);
+		if ((dev->vbus_session == 0)
+			&& (vbus != 1)) {
+			if (dev->driver && dev->driver->disconnect) {
+				spin_unlock(&dev->lock);
+				dev->driver->disconnect(&dev->gadget);
+				spin_lock(&dev->lock);
+			}
+			pch_udc_reconnect(dev);
+		} else if ((dev->vbus_session == 0)
+			&& (vbus == 1)
+			&& !dev->vbus_gpio.intr)
+			schedule_work(&dev->vbus_gpio.irq_work_fall);
+
+		dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
+	}
+	/* Clear the SOF interrupt, if enabled */
+	if (dev_intr & UDC_DEVINT_SOF)
+		dev_dbg(&dev->pdev->dev, "SOF\n");
+	/* ES interrupt, IDLE > 3ms on the USB */
+	if (dev_intr & UDC_DEVINT_ES)
+		dev_dbg(&dev->pdev->dev, "ES\n");
+	/* RWKP interrupt */
+	if (dev_intr & UDC_DEVINT_RWKP)
+		dev_dbg(&dev->pdev->dev, "RWKP\n");
+}
+
+/**
+ * pch_udc_isr() - This function handles interrupts from the PCH USB Device
+ * @irq:	Interrupt request number
+ * @dev:	Reference to the device structure
+ */
+static irqreturn_t pch_udc_isr(int irq, void *pdev)
+{
+	struct pch_udc_dev *dev = (struct pch_udc_dev *) pdev;
+	u32 dev_intr, ep_intr;
+	int i;
+
+	dev_intr = pch_udc_read_device_interrupts(dev);
+	ep_intr = pch_udc_read_ep_interrupts(dev);
+
+	/* For a hot plug, this find that the controller is hung up. */
+	if (dev_intr == ep_intr)
+		if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) {
+			dev_dbg(&dev->pdev->dev, "UDC: Hung up\n");
+			/* The controller is reset */
+			pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
+			return IRQ_HANDLED;
+		}
+	if (dev_intr)
+		/* Clear device interrupts */
+		pch_udc_write_device_interrupts(dev, dev_intr);
+	if (ep_intr)
+		/* Clear ep interrupts */
+		pch_udc_write_ep_interrupts(dev, ep_intr);
+	if (!dev_intr && !ep_intr)
+		return IRQ_NONE;
+	spin_lock(&dev->lock);
+	if (dev_intr)
+		pch_udc_dev_isr(dev, dev_intr);
+	if (ep_intr) {
+		pch_udc_read_all_epstatus(dev, ep_intr);
+		/* Process Control In interrupts, if present */
+		if (ep_intr & UDC_EPINT_IN_EP0) {
+			pch_udc_svc_control_in(dev);
+			pch_udc_postsvc_epinters(dev, 0);
+		}
+		/* Process Control Out interrupts, if present */
+		if (ep_intr & UDC_EPINT_OUT_EP0)
+			pch_udc_svc_control_out(dev);
+		/* Process data in end point interrupts */
+		for (i = 1; i < PCH_UDC_USED_EP_NUM; i++) {
+			if (ep_intr & (1 <<  i)) {
+				pch_udc_svc_data_in(dev, i);
+				pch_udc_postsvc_epinters(dev, i);
+			}
+		}
+		/* Process data out end point interrupts */
+		for (i = UDC_EPINT_OUT_SHIFT + 1; i < (UDC_EPINT_OUT_SHIFT +
+						 PCH_UDC_USED_EP_NUM); i++)
+			if (ep_intr & (1 <<  i))
+				pch_udc_svc_data_out(dev, i -
+							 UDC_EPINT_OUT_SHIFT);
+	}
+	spin_unlock(&dev->lock);
+	return IRQ_HANDLED;
+}
+
+/**
+ * pch_udc_setup_ep0() - This function enables control endpoint for traffic
+ * @dev:	Reference to the device structure
+ */
+static void pch_udc_setup_ep0(struct pch_udc_dev *dev)
+{
+	/* enable ep0 interrupts */
+	pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 |
+						UDC_EPINT_OUT_EP0);
+	/* enable device interrupts */
+	pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US |
+				       UDC_DEVINT_ES | UDC_DEVINT_ENUM |
+				       UDC_DEVINT_SI | UDC_DEVINT_SC);
+}
+
+/**
+ * pch_udc_pcd_reinit() - This API initializes the endpoint structures
+ * @dev:	Reference to the driver structure
+ */
+static void pch_udc_pcd_reinit(struct pch_udc_dev *dev)
+{
+	const char *const ep_string[] = {
+		ep0_string, "ep0out", "ep1in", "ep1out", "ep2in", "ep2out",
+		"ep3in", "ep3out", "ep4in", "ep4out", "ep5in", "ep5out",
+		"ep6in", "ep6out", "ep7in", "ep7out", "ep8in", "ep8out",
+		"ep9in", "ep9out", "ep10in", "ep10out", "ep11in", "ep11out",
+		"ep12in", "ep12out", "ep13in", "ep13out", "ep14in", "ep14out",
+		"ep15in", "ep15out",
+	};
+	int i;
+
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+	/* Initialize the endpoints structures */
+	memset(dev->ep, 0, sizeof dev->ep);
+	for (i = 0; i < PCH_UDC_EP_NUM; i++) {
+		struct pch_udc_ep *ep = &dev->ep[i];
+		ep->dev = dev;
+		ep->halted = 1;
+		ep->num = i / 2;
+		ep->in = ~i & 1;
+		ep->ep.name = ep_string[i];
+		ep->ep.ops = &pch_udc_ep_ops;
+		if (ep->in) {
+			ep->offset_addr = ep->num * UDC_EP_REG_SHIFT;
+			ep->ep.caps.dir_in = true;
+		} else {
+			ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) *
+					  UDC_EP_REG_SHIFT;
+			ep->ep.caps.dir_out = true;
+		}
+		if (i == UDC_EP0IN_IDX || i == UDC_EP0OUT_IDX) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+		/* need to set ep->ep.maxpacket and set Default Configuration?*/
+		usb_ep_set_maxpacket_limit(&ep->ep, UDC_BULK_MAX_PKT_SIZE);
+		list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+		INIT_LIST_HEAD(&ep->queue);
+	}
+	usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IDX].ep, UDC_EP0IN_MAX_PKT_SIZE);
+	usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IDX].ep, UDC_EP0OUT_MAX_PKT_SIZE);
+
+	/* remove ep0 in and out from the list.  They have own pointer */
+	list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list);
+	list_del_init(&dev->ep[UDC_EP0OUT_IDX].ep.ep_list);
+
+	dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+}
+
+/**
+ * pch_udc_pcd_init() - This API initializes the driver structure
+ * @dev:	Reference to the driver structure
+ *
+ * Return codes:
+ *	0: Success
+ */
+static int pch_udc_pcd_init(struct pch_udc_dev *dev)
+{
+	pch_udc_init(dev);
+	pch_udc_pcd_reinit(dev);
+	pch_vbus_gpio_init(dev, vbus_gpio_port);
+	return 0;
+}
+
+/**
+ * init_dma_pools() - create dma pools during initialization
+ * @pdev:	reference to struct pci_dev
+ */
+static int init_dma_pools(struct pch_udc_dev *dev)
+{
+	struct pch_udc_stp_dma_desc	*td_stp;
+	struct pch_udc_data_dma_desc	*td_data;
+	void				*ep0out_buf;
+
+	/* DMA setup */
+	dev->data_requests = dma_pool_create("data_requests", &dev->pdev->dev,
+		sizeof(struct pch_udc_data_dma_desc), 0, 0);
+	if (!dev->data_requests) {
+		dev_err(&dev->pdev->dev, "%s: can't get request data pool\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	/* dma desc for setup data */
+	dev->stp_requests = dma_pool_create("setup requests", &dev->pdev->dev,
+		sizeof(struct pch_udc_stp_dma_desc), 0, 0);
+	if (!dev->stp_requests) {
+		dev_err(&dev->pdev->dev, "%s: can't get setup request pool\n",
+			__func__);
+		return -ENOMEM;
+	}
+	/* setup */
+	td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+				&dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
+	if (!td_stp) {
+		dev_err(&dev->pdev->dev,
+			"%s: can't allocate setup dma descriptor\n", __func__);
+		return -ENOMEM;
+	}
+	dev->ep[UDC_EP0OUT_IDX].td_stp = td_stp;
+
+	/* data: 0 packets !? */
+	td_data = dma_pool_alloc(dev->data_requests, GFP_KERNEL,
+				&dev->ep[UDC_EP0OUT_IDX].td_data_phys);
+	if (!td_data) {
+		dev_err(&dev->pdev->dev,
+			"%s: can't allocate data dma descriptor\n", __func__);
+		return -ENOMEM;
+	}
+	dev->ep[UDC_EP0OUT_IDX].td_data = td_data;
+	dev->ep[UDC_EP0IN_IDX].td_stp = NULL;
+	dev->ep[UDC_EP0IN_IDX].td_stp_phys = 0;
+	dev->ep[UDC_EP0IN_IDX].td_data = NULL;
+	dev->ep[UDC_EP0IN_IDX].td_data_phys = 0;
+
+	ep0out_buf = devm_kzalloc(&dev->pdev->dev, UDC_EP0OUT_BUFF_SIZE * 4,
+				  GFP_KERNEL);
+	if (!ep0out_buf)
+		return -ENOMEM;
+	dev->dma_addr = dma_map_single(&dev->pdev->dev, ep0out_buf,
+				       UDC_EP0OUT_BUFF_SIZE * 4,
+				       DMA_FROM_DEVICE);
+	return 0;
+}
+
+static int pch_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct pch_udc_dev	*dev = to_pch_udc(g);
+
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+
+	/* get ready for ep0 traffic */
+	pch_udc_setup_ep0(dev);
+
+	/* clear SD */
+	if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr)
+		pch_udc_clear_disconnect(dev);
+
+	dev->connected = 1;
+	return 0;
+}
+
+static int pch_udc_stop(struct usb_gadget *g)
+{
+	struct pch_udc_dev	*dev = to_pch_udc(g);
+
+	pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+
+	/* Assures that there are no pending requests with this driver */
+	dev->driver = NULL;
+	dev->connected = 0;
+
+	/* set SD */
+	pch_udc_set_disconnect(dev);
+
+	return 0;
+}
+
+static void pch_udc_shutdown(struct pci_dev *pdev)
+{
+	struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+
+	pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+	pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+	/* disable the pullup so the host will think we're gone */
+	pch_udc_set_disconnect(dev);
+}
+
+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,
+			"%s: gadget driver still bound!!!\n", __func__);
+	/* dma pool cleanup */
+	dma_pool_destroy(dev->data_requests);
+
+	if (dev->stp_requests) {
+		/* cleanup DMA desc's for ep0in */
+		if (dev->ep[UDC_EP0OUT_IDX].td_stp) {
+			dma_pool_free(dev->stp_requests,
+				dev->ep[UDC_EP0OUT_IDX].td_stp,
+				dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
+		}
+		if (dev->ep[UDC_EP0OUT_IDX].td_data) {
+			dma_pool_free(dev->stp_requests,
+				dev->ep[UDC_EP0OUT_IDX].td_data,
+				dev->ep[UDC_EP0OUT_IDX].td_data_phys);
+		}
+		dma_pool_destroy(dev->stp_requests);
+	}
+
+	if (dev->dma_addr)
+		dma_unmap_single(&dev->pdev->dev, dev->dma_addr,
+				 UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE);
+
+	pch_vbus_gpio_free(dev);
+
+	pch_udc_exit(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pch_udc_suspend(struct device *d)
+{
+	struct pci_dev *pdev = to_pci_dev(d);
+	struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+
+	pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+	pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+	return 0;
+}
+
+static int pch_udc_resume(struct device *d)
+{
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pch_udc_pm, pch_udc_suspend, pch_udc_resume);
+#define PCH_UDC_PM_OPS		(&pch_udc_pm)
+#else
+#define PCH_UDC_PM_OPS		NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static int pch_udc_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *id)
+{
+	int			bar;
+	int			retval;
+	struct pch_udc_dev	*dev;
+
+	/* init */
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	/* pci setup */
+	retval = pcim_enable_device(pdev);
+	if (retval)
+		return retval;
+
+	pci_set_drvdata(pdev, dev);
+
+	/* Determine BAR based on PCI ID */
+	if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC)
+		bar = PCH_UDC_PCI_BAR_QUARK_X1000;
+	else
+		bar = PCH_UDC_PCI_BAR;
+
+	/* PCI resource allocation */
+	retval = pcim_iomap_regions(pdev, 1 << bar, pci_name(pdev));
+	if (retval)
+		return retval;
+
+	dev->base_addr = pcim_iomap_table(pdev)[bar];
+
+	/* initialize the hardware */
+	if (pch_udc_pcd_init(dev))
+		return -ENODEV;
+
+	pci_enable_msi(pdev);
+
+	retval = devm_request_irq(&pdev->dev, pdev->irq, pch_udc_isr,
+				  IRQF_SHARED, KBUILD_MODNAME, dev);
+	if (retval) {
+		dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__,
+			pdev->irq);
+		goto finished;
+	}
+
+	pci_set_master(pdev);
+	pci_try_set_mwi(pdev);
+
+	/* device struct setup */
+	spin_lock_init(&dev->lock);
+	dev->pdev = pdev;
+	dev->gadget.ops = &pch_udc_ops;
+
+	retval = init_dma_pools(dev);
+	if (retval)
+		goto finished;
+
+	dev->gadget.name = KBUILD_MODNAME;
+	dev->gadget.max_speed = USB_SPEED_HIGH;
+
+	/* 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:
+	pch_udc_remove(pdev);
+	return retval;
+}
+
+static const struct pci_device_id pch_udc_pcidev_id[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_INTEL,
+			   PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC),
+		.class = PCI_CLASS_SERIAL_USB_DEVICE,
+		.class_mask = 0xffffffff,
+	},
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
+		.class = PCI_CLASS_SERIAL_USB_DEVICE,
+		.class_mask = 0xffffffff,
+	},
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7213_IOH_UDC),
+		.class = PCI_CLASS_SERIAL_USB_DEVICE,
+		.class_mask = 0xffffffff,
+	},
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7831_IOH_UDC),
+		.class = PCI_CLASS_SERIAL_USB_DEVICE,
+		.class_mask = 0xffffffff,
+	},
+	{ 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, pch_udc_pcidev_id);
+
+static struct pci_driver pch_udc_driver = {
+	.name =	KBUILD_MODNAME,
+	.id_table =	pch_udc_pcidev_id,
+	.probe =	pch_udc_probe,
+	.remove =	pch_udc_remove,
+	.shutdown =	pch_udc_shutdown,
+	.driver = {
+		.pm = PCH_UDC_PM_OPS,
+	},
+};
+
+module_pci_driver(pch_udc_driver);
+
+MODULE_DESCRIPTION("Intel EG20T USB Device Controller");
+MODULE_AUTHOR("LAPIS Semiconductor, <tomoya-linux@dsn.lapis-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
new file mode 100644
index 0000000..d4be535
--- /dev/null
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -0,0 +1,2548 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
+ * Copyright (C) 2003 Robert Schwebel, Pengutronix
+ * Copyright (C) 2003 Benedikt Spranger, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003 Joshua Wise
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/platform_data/pxa2xx_udc.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/prefetch.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#ifdef CONFIG_ARCH_LUBBOCK
+#include <mach/lubbock.h>
+#endif
+
+#define UDCCR	 0x0000 /* UDC Control Register */
+#define UDC_RES1 0x0004 /* UDC Undocumented - Reserved1 */
+#define UDC_RES2 0x0008 /* UDC Undocumented - Reserved2 */
+#define UDC_RES3 0x000C /* UDC Undocumented - Reserved3 */
+#define UDCCS0	 0x0010 /* UDC Endpoint 0 Control/Status Register */
+#define UDCCS1	 0x0014 /* UDC Endpoint 1 (IN) Control/Status Register */
+#define UDCCS2	 0x0018 /* UDC Endpoint 2 (OUT) Control/Status Register */
+#define UDCCS3	 0x001C /* UDC Endpoint 3 (IN) Control/Status Register */
+#define UDCCS4	 0x0020 /* UDC Endpoint 4 (OUT) Control/Status Register */
+#define UDCCS5	 0x0024 /* UDC Endpoint 5 (Interrupt) Control/Status Register */
+#define UDCCS6	 0x0028 /* UDC Endpoint 6 (IN) Control/Status Register */
+#define UDCCS7	 0x002C /* UDC Endpoint 7 (OUT) Control/Status Register */
+#define UDCCS8	 0x0030 /* UDC Endpoint 8 (IN) Control/Status Register */
+#define UDCCS9	 0x0034 /* UDC Endpoint 9 (OUT) Control/Status Register */
+#define UDCCS10	 0x0038 /* UDC Endpoint 10 (Interrupt) Control/Status Register */
+#define UDCCS11	 0x003C /* UDC Endpoint 11 (IN) Control/Status Register */
+#define UDCCS12	 0x0040 /* UDC Endpoint 12 (OUT) Control/Status Register */
+#define UDCCS13	 0x0044 /* UDC Endpoint 13 (IN) Control/Status Register */
+#define UDCCS14	 0x0048 /* UDC Endpoint 14 (OUT) Control/Status Register */
+#define UDCCS15	 0x004C /* UDC Endpoint 15 (Interrupt) Control/Status Register */
+#define UFNRH	 0x0060 /* UDC Frame Number Register High */
+#define UFNRL	 0x0064 /* UDC Frame Number Register Low */
+#define UBCR2	 0x0068 /* UDC Byte Count Reg 2 */
+#define UBCR4	 0x006c /* UDC Byte Count Reg 4 */
+#define UBCR7	 0x0070 /* UDC Byte Count Reg 7 */
+#define UBCR9	 0x0074 /* UDC Byte Count Reg 9 */
+#define UBCR12	 0x0078 /* UDC Byte Count Reg 12 */
+#define UBCR14	 0x007c /* UDC Byte Count Reg 14 */
+#define UDDR0	 0x0080 /* UDC Endpoint 0 Data Register */
+#define UDDR1	 0x0100 /* UDC Endpoint 1 Data Register */
+#define UDDR2	 0x0180 /* UDC Endpoint 2 Data Register */
+#define UDDR3	 0x0200 /* UDC Endpoint 3 Data Register */
+#define UDDR4	 0x0400 /* UDC Endpoint 4 Data Register */
+#define UDDR5	 0x00A0 /* UDC Endpoint 5 Data Register */
+#define UDDR6	 0x0600 /* UDC Endpoint 6 Data Register */
+#define UDDR7	 0x0680 /* UDC Endpoint 7 Data Register */
+#define UDDR8	 0x0700 /* UDC Endpoint 8 Data Register */
+#define UDDR9	 0x0900 /* UDC Endpoint 9 Data Register */
+#define UDDR10	 0x00C0 /* UDC Endpoint 10 Data Register */
+#define UDDR11	 0x0B00 /* UDC Endpoint 11 Data Register */
+#define UDDR12	 0x0B80 /* UDC Endpoint 12 Data Register */
+#define UDDR13	 0x0C00 /* UDC Endpoint 13 Data Register */
+#define UDDR14	 0x0E00 /* UDC Endpoint 14 Data Register */
+#define UDDR15	 0x00E0 /* UDC Endpoint 15 Data Register */
+
+#define UICR0	 0x0050 /* UDC Interrupt Control Register 0 */
+#define UICR1	 0x0054 /* UDC Interrupt Control Register 1 */
+
+#define USIR0	 0x0058 /* UDC Status Interrupt Register 0 */
+#define USIR1	 0x005C /* UDC Status Interrupt Register 1 */
+
+#define UDCCR_UDE	(1 << 0)	/* UDC enable */
+#define UDCCR_UDA	(1 << 1)	/* UDC active */
+#define UDCCR_RSM	(1 << 2)	/* Device resume */
+#define UDCCR_RESIR	(1 << 3)	/* Resume interrupt request */
+#define UDCCR_SUSIR	(1 << 4)	/* Suspend interrupt request */
+#define UDCCR_SRM	(1 << 5)	/* Suspend/resume interrupt mask */
+#define UDCCR_RSTIR	(1 << 6)	/* Reset interrupt request */
+#define UDCCR_REM	(1 << 7)	/* Reset interrupt mask */
+
+#define UDCCS0_OPR	(1 << 0)	/* OUT packet ready */
+#define UDCCS0_IPR	(1 << 1)	/* IN packet ready */
+#define UDCCS0_FTF	(1 << 2)	/* Flush Tx FIFO */
+#define UDCCS0_DRWF	(1 << 3)	/* Device remote wakeup feature */
+#define UDCCS0_SST	(1 << 4)	/* Sent stall */
+#define UDCCS0_FST	(1 << 5)	/* Force stall */
+#define UDCCS0_RNE	(1 << 6)	/* Receive FIFO no empty */
+#define UDCCS0_SA	(1 << 7)	/* Setup active */
+
+#define UDCCS_BI_TFS	(1 << 0)	/* Transmit FIFO service */
+#define UDCCS_BI_TPC	(1 << 1)	/* Transmit packet complete */
+#define UDCCS_BI_FTF	(1 << 2)	/* Flush Tx FIFO */
+#define UDCCS_BI_TUR	(1 << 3)	/* Transmit FIFO underrun */
+#define UDCCS_BI_SST	(1 << 4)	/* Sent stall */
+#define UDCCS_BI_FST	(1 << 5)	/* Force stall */
+#define UDCCS_BI_TSP	(1 << 7)	/* Transmit short packet */
+
+#define UDCCS_BO_RFS	(1 << 0)	/* Receive FIFO service */
+#define UDCCS_BO_RPC	(1 << 1)	/* Receive packet complete */
+#define UDCCS_BO_DME	(1 << 3)	/* DMA enable */
+#define UDCCS_BO_SST	(1 << 4)	/* Sent stall */
+#define UDCCS_BO_FST	(1 << 5)	/* Force stall */
+#define UDCCS_BO_RNE	(1 << 6)	/* Receive FIFO not empty */
+#define UDCCS_BO_RSP	(1 << 7)	/* Receive short packet */
+
+#define UDCCS_II_TFS	(1 << 0)	/* Transmit FIFO service */
+#define UDCCS_II_TPC	(1 << 1)	/* Transmit packet complete */
+#define UDCCS_II_FTF	(1 << 2)	/* Flush Tx FIFO */
+#define UDCCS_II_TUR	(1 << 3)	/* Transmit FIFO underrun */
+#define UDCCS_II_TSP	(1 << 7)	/* Transmit short packet */
+
+#define UDCCS_IO_RFS	(1 << 0)	/* Receive FIFO service */
+#define UDCCS_IO_RPC	(1 << 1)	/* Receive packet complete */
+#ifdef CONFIG_ARCH_IXP4XX /* FIXME: is this right?, datasheed says '2' */
+#define UDCCS_IO_ROF	(1 << 3)	/* Receive overflow */
+#endif
+#ifdef CONFIG_ARCH_PXA
+#define UDCCS_IO_ROF	(1 << 2)	/* Receive overflow */
+#endif
+#define UDCCS_IO_DME	(1 << 3)	/* DMA enable */
+#define UDCCS_IO_RNE	(1 << 6)	/* Receive FIFO not empty */
+#define UDCCS_IO_RSP	(1 << 7)	/* Receive short packet */
+
+#define UDCCS_INT_TFS	(1 << 0)	/* Transmit FIFO service */
+#define UDCCS_INT_TPC	(1 << 1)	/* Transmit packet complete */
+#define UDCCS_INT_FTF	(1 << 2)	/* Flush Tx FIFO */
+#define UDCCS_INT_TUR	(1 << 3)	/* Transmit FIFO underrun */
+#define UDCCS_INT_SST	(1 << 4)	/* Sent stall */
+#define UDCCS_INT_FST	(1 << 5)	/* Force stall */
+#define UDCCS_INT_TSP	(1 << 7)	/* Transmit short packet */
+
+#define UICR0_IM0	(1 << 0)	/* Interrupt mask ep 0 */
+#define UICR0_IM1	(1 << 1)	/* Interrupt mask ep 1 */
+#define UICR0_IM2	(1 << 2)	/* Interrupt mask ep 2 */
+#define UICR0_IM3	(1 << 3)	/* Interrupt mask ep 3 */
+#define UICR0_IM4	(1 << 4)	/* Interrupt mask ep 4 */
+#define UICR0_IM5	(1 << 5)	/* Interrupt mask ep 5 */
+#define UICR0_IM6	(1 << 6)	/* Interrupt mask ep 6 */
+#define UICR0_IM7	(1 << 7)	/* Interrupt mask ep 7 */
+
+#define UICR1_IM8	(1 << 0)	/* Interrupt mask ep 8 */
+#define UICR1_IM9	(1 << 1)	/* Interrupt mask ep 9 */
+#define UICR1_IM10	(1 << 2)	/* Interrupt mask ep 10 */
+#define UICR1_IM11	(1 << 3)	/* Interrupt mask ep 11 */
+#define UICR1_IM12	(1 << 4)	/* Interrupt mask ep 12 */
+#define UICR1_IM13	(1 << 5)	/* Interrupt mask ep 13 */
+#define UICR1_IM14	(1 << 6)	/* Interrupt mask ep 14 */
+#define UICR1_IM15	(1 << 7)	/* Interrupt mask ep 15 */
+
+#define USIR0_IR0	(1 << 0)	/* Interrupt request ep 0 */
+#define USIR0_IR1	(1 << 1)	/* Interrupt request ep 1 */
+#define USIR0_IR2	(1 << 2)	/* Interrupt request ep 2 */
+#define USIR0_IR3	(1 << 3)	/* Interrupt request ep 3 */
+#define USIR0_IR4	(1 << 4)	/* Interrupt request ep 4 */
+#define USIR0_IR5	(1 << 5)	/* Interrupt request ep 5 */
+#define USIR0_IR6	(1 << 6)	/* Interrupt request ep 6 */
+#define USIR0_IR7	(1 << 7)	/* Interrupt request ep 7 */
+
+#define USIR1_IR8	(1 << 0)	/* Interrupt request ep 8 */
+#define USIR1_IR9	(1 << 1)	/* Interrupt request ep 9 */
+#define USIR1_IR10	(1 << 2)	/* Interrupt request ep 10 */
+#define USIR1_IR11	(1 << 3)	/* Interrupt request ep 11 */
+#define USIR1_IR12	(1 << 4)	/* Interrupt request ep 12 */
+#define USIR1_IR13	(1 << 5)	/* Interrupt request ep 13 */
+#define USIR1_IR14	(1 << 6)	/* Interrupt request ep 14 */
+#define USIR1_IR15	(1 << 7)	/* Interrupt request ep 15 */
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
+ * series processors.  The UDC for the IXP 4xx series is very similar.
+ * There are fifteen endpoints, in addition to ep0.
+ *
+ * Such controller drivers work with a gadget driver.  The gadget driver
+ * returns descriptors, implements configuration and data protocols used
+ * by the host to interact with this device, and allocates endpoints to
+ * the different protocol interfaces.  The controller driver virtualizes
+ * usb hardware so that the gadget drivers will be more portable.
+ *
+ * This UDC hardware wants to implement a bit too much USB protocol, so
+ * it constrains the sorts of USB configuration change events that work.
+ * The errata for these chips are misleading; some "fixed" bugs from
+ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
+ *
+ * Note that the UDC hardware supports DMA (except on IXP) but that's
+ * not used here.  IN-DMA (to host) is simple enough, when the data is
+ * suitably aligned (16 bytes) ... the network stack doesn't do that,
+ * other software can.  OUT-DMA is buggy in most chip versions, as well
+ * as poorly designed (data toggle not automatic).  So this driver won't
+ * bother using DMA.  (Mostly-working IN-DMA support was available in
+ * kernels before 2.6.23, but was never enabled or well tested.)
+ */
+
+#define	DRIVER_VERSION	"30-June-2007"
+#define	DRIVER_DESC	"PXA 25x USB Device Controller driver"
+
+
+static const char driver_name [] = "pxa25x_udc";
+
+static const char ep0name [] = "ep0";
+
+
+#ifdef CONFIG_ARCH_IXP4XX
+
+/* cpu-specific register addresses are compiled in to this code */
+#ifdef CONFIG_ARCH_PXA
+#error "Can't configure both IXP and PXA"
+#endif
+
+/* IXP doesn't yet support <linux/clk.h> */
+#define clk_get(dev,name)	NULL
+#define clk_enable(clk)		do { } while (0)
+#define clk_disable(clk)	do { } while (0)
+#define clk_put(clk)		do { } while (0)
+
+#endif
+
+#include "pxa25x_udc.h"
+
+
+#ifdef	CONFIG_USB_PXA25X_SMALL
+#define SIZE_STR	" (small)"
+#else
+#define SIZE_STR	""
+#endif
+
+/* ---------------------------------------------------------------------------
+ *	endpoint related parts of the api to the usb controller hardware,
+ *	used by gadget driver; and the inner talker-to-hardware core.
+ * ---------------------------------------------------------------------------
+ */
+
+static void pxa25x_ep_fifo_flush (struct usb_ep *ep);
+static void nuke (struct pxa25x_ep *, int status);
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+	int off_level = mach->gpio_pullup_inverted;
+
+	if (gpio_is_valid(mach->gpio_pullup))
+		gpio_set_value(mach->gpio_pullup, off_level);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+	int on_level = !mach->gpio_pullup_inverted;
+
+	if (gpio_is_valid(mach->gpio_pullup))
+		gpio_set_value(mach->gpio_pullup, on_level);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
+#if defined(CONFIG_CPU_BIG_ENDIAN)
+/*
+ * IXP4xx has its buses wired up in a way that relies on never doing any
+ * byte swaps, independent of whether it runs in big-endian or little-endian
+ * mode, as explained by Krzysztof Hałasa.
+ *
+ * We only support pxa25x in little-endian mode, but it is very likely
+ * that it works the same way.
+ */
+static inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val)
+{
+	iowrite32be(val, dev->regs + reg);
+}
+
+static inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg)
+{
+	return ioread32be(dev->regs + reg);
+}
+#else
+static inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val)
+{
+	writel(val, dev->regs + reg);
+}
+
+static inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg)
+{
+	return readl(dev->regs + reg);
+}
+#endif
+
+static void pio_irq_enable(struct pxa25x_ep *ep)
+{
+	u32 bEndpointAddress = ep->bEndpointAddress & 0xf;
+
+        if (bEndpointAddress < 8)
+		udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) &
+						~(1 << bEndpointAddress));
+        else {
+                bEndpointAddress -= 8;
+		udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) &
+						~(1 << bEndpointAddress));
+	}
+}
+
+static void pio_irq_disable(struct pxa25x_ep *ep)
+{
+	u32 bEndpointAddress = ep->bEndpointAddress & 0xf;
+
+        if (bEndpointAddress < 8)
+                udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) |
+						(1 << bEndpointAddress));
+        else {
+                bEndpointAddress -= 8;
+                udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) |
+						(1 << bEndpointAddress));
+        }
+}
+
+/* The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+#define UDCCR_MASK_BITS         (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)
+
+static inline void udc_set_mask_UDCCR(struct pxa25x_udc *dev, int mask)
+{
+	u32 udccr = udc_get_reg(dev, UDCCR);
+
+	udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR);
+}
+
+static inline void udc_clear_mask_UDCCR(struct pxa25x_udc *dev, int mask)
+{
+	u32 udccr = udc_get_reg(dev, UDCCR);
+
+	udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR);
+}
+
+static inline void udc_ack_int_UDCCR(struct pxa25x_udc *dev, int mask)
+{
+	/* udccr contains the bits we dont want to change */
+	u32 udccr = udc_get_reg(dev, UDCCR) & UDCCR_MASK_BITS;
+
+	udc_set_reg(dev, udccr | (mask & ~UDCCR_MASK_BITS), UDCCR);
+}
+
+static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *ep)
+{
+	return udc_get_reg(ep->dev, ep->regoff_udccs);
+}
+
+static inline void udc_ep_set_UDCCS(struct pxa25x_ep *ep, u32 data)
+{
+	udc_set_reg(ep->dev, data, ep->regoff_udccs);
+}
+
+static inline u32 udc_ep0_get_UDCCS(struct pxa25x_udc *dev)
+{
+	return udc_get_reg(dev, UDCCS0);
+}
+
+static inline void udc_ep0_set_UDCCS(struct pxa25x_udc *dev, u32 data)
+{
+	udc_set_reg(dev, data, UDCCS0);
+}
+
+static inline u32 udc_ep_get_UDDR(struct pxa25x_ep *ep)
+{
+	return udc_get_reg(ep->dev, ep->regoff_uddr);
+}
+
+static inline void udc_ep_set_UDDR(struct pxa25x_ep *ep, u32 data)
+{
+	udc_set_reg(ep->dev, data, ep->regoff_uddr);
+}
+
+static inline u32 udc_ep_get_UBCR(struct pxa25x_ep *ep)
+{
+	return udc_get_reg(ep->dev, ep->regoff_ubcr);
+}
+
+/*
+ * endpoint enable/disable
+ *
+ * we need to verify the descriptors used to enable endpoints.  since pxa25x
+ * endpoint configurations are fixed, and are pretty much always enabled,
+ * there's not a lot to manage here.
+ *
+ * because pxa25x can't selectively initialize bulk (or interrupt) endpoints,
+ * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except
+ * for a single interface (with only the default altsetting) and for gadget
+ * drivers that don't halt endpoints (not reset by set_interface).  that also
+ * means that if you use ISO, you must violate the USB spec rule that all
+ * iso endpoints must be in non-default altsettings.
+ */
+static int pxa25x_ep_enable (struct usb_ep *_ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct pxa25x_ep        *ep;
+	struct pxa25x_udc       *dev;
+
+	ep = container_of (_ep, struct pxa25x_ep, ep);
+	if (!_ep || !desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT
+			|| ep->bEndpointAddress != desc->bEndpointAddress
+			|| ep->fifo_size < usb_endpoint_maxp (desc)) {
+		DMSG("%s, bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if (ep->bmAttributes != desc->bmAttributes
+			&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+			&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+		DMSG("%s, %s type mismatch\n", __func__, _ep->name);
+		return -EINVAL;
+	}
+
+	/* hardware _could_ do smaller, but driver doesn't */
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+				&& usb_endpoint_maxp (desc)
+						!= BULK_FIFO_SIZE)
+			|| !desc->wMaxPacketSize) {
+		DMSG("%s, bad %s maxpacket\n", __func__, _ep->name);
+		return -ERANGE;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		DMSG("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	ep->ep.desc = desc;
+	ep->stopped = 0;
+	ep->pio_irqs = 0;
+	ep->ep.maxpacket = usb_endpoint_maxp (desc);
+
+	/* flush fifo (mostly for OUT buffers) */
+	pxa25x_ep_fifo_flush (_ep);
+
+	/* ... reset halt state too, if we could ... */
+
+	DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+	return 0;
+}
+
+static int pxa25x_ep_disable (struct usb_ep *_ep)
+{
+	struct pxa25x_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of (_ep, struct pxa25x_ep, ep);
+	if (!_ep || !ep->ep.desc) {
+		DMSG("%s, %s not enabled\n", __func__,
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+	local_irq_save(flags);
+
+	nuke (ep, -ESHUTDOWN);
+
+	/* flush fifo (mostly for IN buffers) */
+	pxa25x_ep_fifo_flush (_ep);
+
+	ep->ep.desc = NULL;
+	ep->stopped = 1;
+
+	local_irq_restore(flags);
+	DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* for the pxa25x, these can just wrap kmalloc/kfree.  gadget drivers
+ * must still pass correctly initialized endpoints, since other controller
+ * drivers may care about how it's currently set up (dma issues etc).
+ */
+
+/*
+ *	pxa25x_ep_alloc_request - allocate a request data structure
+ */
+static struct usb_request *
+pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct pxa25x_request *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD (&req->queue);
+	return &req->req;
+}
+
+
+/*
+ *	pxa25x_ep_free_request - deallocate a request data structure
+ */
+static void
+pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa25x_request	*req;
+
+	req = container_of (_req, struct pxa25x_request, req);
+	WARN_ON(!list_empty (&req->queue));
+	kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *	done - retire a request; caller blocked irqs
+ */
+static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status)
+{
+	unsigned		stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+
+	if (likely (req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	ep->stopped = stopped;
+}
+
+
+static inline void ep0_idle (struct pxa25x_udc *dev)
+{
+	dev->ep0state = EP0_IDLE;
+}
+
+static int
+write_packet(struct pxa25x_ep *ep, struct pxa25x_request *req, unsigned max)
+{
+	u8		*buf;
+	unsigned	length, count;
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	/* how big will this packet be? */
+	length = min(req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+	count = length;
+	while (likely(count--))
+		udc_ep_set_UDDR(ep, *buf++);
+
+	return length;
+}
+
+/*
+ * write to an IN endpoint fifo, as many packets as possible.
+ * irqs will use this to write the rest later.
+ * caller guarantees at least one packet buffer is ready (or a zlp).
+ */
+static int
+write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+	unsigned		max;
+
+	max = usb_endpoint_maxp(ep->ep.desc);
+	do {
+		unsigned	count;
+		int		is_last, is_short;
+
+		count = write_packet(ep, req, max);
+
+		/* last packet is usually short (or a zlp) */
+		if (unlikely (count != max))
+			is_last = is_short = 1;
+		else {
+			if (likely(req->req.length != req->req.actual)
+					|| req->req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+			/* interrupt/iso maxpacket may not fill the fifo */
+			is_short = unlikely (max < ep->fifo_size);
+		}
+
+		DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n",
+			ep->ep.name, count,
+			is_last ? "/L" : "", is_short ? "/S" : "",
+			req->req.length - req->req.actual, req);
+
+		/* let loose that packet. maybe try writing another one,
+		 * double buffering might work.  TSP, TPC, and TFS
+		 * bit values are the same for all normal IN endpoints.
+		 */
+		udc_ep_set_UDCCS(ep, UDCCS_BI_TPC);
+		if (is_short)
+			udc_ep_set_UDCCS(ep, UDCCS_BI_TSP);
+
+		/* requests complete when all IN data is in the FIFO */
+		if (is_last) {
+			done (ep, req, 0);
+			if (list_empty(&ep->queue))
+				pio_irq_disable(ep);
+			return 1;
+		}
+
+		// TODO experiment: how robust can fifo mode tweaking be?
+		// double buffering is off in the default fifo mode, which
+		// prevents TFS from being set here.
+
+	} while (udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS);
+	return 0;
+}
+
+/* caller asserts req->pending (ep0 irq status nyet cleared); starts
+ * ep0 data stage.  these chips want very simple state transitions.
+ */
+static inline
+void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag)
+{
+	udc_ep0_set_UDCCS(dev, flags|UDCCS0_SA|UDCCS0_OPR);
+	udc_set_reg(dev, USIR0, USIR0_IR0);
+	dev->req_pending = 0;
+	DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
+		__func__, tag, udc_ep0_get_UDCCS(dev), flags);
+}
+
+static int
+write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+	struct pxa25x_udc *dev = ep->dev;
+	unsigned	count;
+	int		is_short;
+
+	count = write_packet(&dev->ep[0], req, EP0_FIFO_SIZE);
+	ep->dev->stats.write.bytes += count;
+
+	/* last packet "must be" short (or a zlp) */
+	is_short = (count != EP0_FIFO_SIZE);
+
+	DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
+		req->req.length - req->req.actual, req);
+
+	if (unlikely (is_short)) {
+		if (ep->dev->req_pending)
+			ep0start(ep->dev, UDCCS0_IPR, "short IN");
+		else
+			udc_ep0_set_UDCCS(dev, UDCCS0_IPR);
+
+		count = req->req.length;
+		done (ep, req, 0);
+		ep0_idle(ep->dev);
+#ifndef CONFIG_ARCH_IXP4XX
+#if 1
+		/* This seems to get rid of lost status irqs in some cases:
+		 * host responds quickly, or next request involves config
+		 * change automagic, or should have been hidden, or ...
+		 *
+		 * FIXME get rid of all udelays possible...
+		 */
+		if (count >= EP0_FIFO_SIZE) {
+			count = 100;
+			do {
+				if ((udc_ep0_get_UDCCS(dev) & UDCCS0_OPR) != 0) {
+					/* clear OPR, generate ack */
+					udc_ep0_set_UDCCS(dev, UDCCS0_OPR);
+					break;
+				}
+				count--;
+				udelay(1);
+			} while (count);
+		}
+#endif
+#endif
+	} else if (ep->dev->req_pending)
+		ep0start(ep->dev, 0, "IN");
+	return is_short;
+}
+
+
+/*
+ * read_fifo -  unload packet(s) from the fifo we use for usb OUT
+ * transfers and put them into the request.  caller should have made
+ * sure there's at least one packet ready.
+ *
+ * returns true if the request completed because of short packet or the
+ * request buffer having filled (and maybe overran till end-of-packet).
+ */
+static int
+read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+	for (;;) {
+		u32		udccs;
+		u8		*buf;
+		unsigned	bufferspace, count, is_short;
+
+		/* make sure there's a packet in the FIFO.
+		 * UDCCS_{BO,IO}_RPC are all the same bit value.
+		 * UDCCS_{BO,IO}_RNE are all the same bit value.
+		 */
+		udccs = udc_ep_get_UDCCS(ep);
+		if (unlikely ((udccs & UDCCS_BO_RPC) == 0))
+			break;
+		buf = req->req.buf + req->req.actual;
+		prefetchw(buf);
+		bufferspace = req->req.length - req->req.actual;
+
+		/* read all bytes from this packet */
+		if (likely (udccs & UDCCS_BO_RNE)) {
+			count = 1 + (0x0ff & udc_ep_get_UBCR(ep));
+			req->req.actual += min (count, bufferspace);
+		} else /* zlp */
+			count = 0;
+		is_short = (count < ep->ep.maxpacket);
+		DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",
+			ep->ep.name, udccs, count,
+			is_short ? "/S" : "",
+			req, req->req.actual, req->req.length);
+		while (likely (count-- != 0)) {
+			u8	byte = (u8) udc_ep_get_UDDR(ep);
+
+			if (unlikely (bufferspace == 0)) {
+				/* this happens when the driver's buffer
+				 * is smaller than what the host sent.
+				 * discard the extra data.
+				 */
+				if (req->req.status != -EOVERFLOW)
+					DMSG("%s overflow %d\n",
+						ep->ep.name, count);
+				req->req.status = -EOVERFLOW;
+			} else {
+				*buf++ = byte;
+				bufferspace--;
+			}
+		}
+		udc_ep_set_UDCCS(ep, UDCCS_BO_RPC);
+		/* RPC/RSP/RNE could now reflect the other packet buffer */
+
+		/* iso is one request per packet */
+		if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+			if (udccs & UDCCS_IO_ROF)
+				req->req.status = -EHOSTUNREACH;
+			/* more like "is_done" */
+			is_short = 1;
+		}
+
+		/* completion */
+		if (is_short || req->req.actual == req->req.length) {
+			done (ep, req, 0);
+			if (list_empty(&ep->queue))
+				pio_irq_disable(ep);
+			return 1;
+		}
+
+		/* finished that packet.  the next one may be waiting... */
+	}
+	return 0;
+}
+
+/*
+ * special ep0 version of the above.  no UBCR0 or double buffering; status
+ * handshaking is magic.  most device protocols don't need control-OUT.
+ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
+ * protocols do use them.
+ */
+static int
+read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
+{
+	u8		*buf, byte;
+	unsigned	bufferspace;
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+
+	while (udc_ep_get_UDCCS(ep) & UDCCS0_RNE) {
+		byte = (u8) UDDR0;
+
+		if (unlikely (bufferspace == 0)) {
+			/* this happens when the driver's buffer
+			 * is smaller than what the host sent.
+			 * discard the extra data.
+			 */
+			if (req->req.status != -EOVERFLOW)
+				DMSG("%s overflow\n", ep->ep.name);
+			req->req.status = -EOVERFLOW;
+		} else {
+			*buf++ = byte;
+			req->req.actual++;
+			bufferspace--;
+		}
+	}
+
+	udc_ep_set_UDCCS(ep, UDCCS0_OPR | UDCCS0_IPR);
+
+	/* completion */
+	if (req->req.actual >= req->req.length)
+		return 1;
+
+	/* finished that packet.  the next one may be waiting... */
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+	struct pxa25x_request	*req;
+	struct pxa25x_ep	*ep;
+	struct pxa25x_udc	*dev;
+	unsigned long		flags;
+
+	req = container_of(_req, struct pxa25x_request, req);
+	if (unlikely (!_req || !_req->complete || !_req->buf
+			|| !list_empty(&req->queue))) {
+		DMSG("%s, bad params\n", __func__);
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct pxa25x_ep, ep);
+	if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
+		DMSG("%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		DMSG("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	/* iso is always one packet per request, that's the only way
+	 * we can report per-packet status.  that also helps with dma.
+	 */
+	if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+			&& req->req.length > usb_endpoint_maxp(ep->ep.desc)))
+		return -EMSGSIZE;
+
+	DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
+		_ep->name, _req, _req->length, _req->buf);
+
+	local_irq_save(flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		if (ep->ep.desc == NULL/* ep0 */) {
+			unsigned	length = _req->length;
+
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				dev->stats.write.ops++;
+				if (write_ep0_fifo(ep, req))
+					req = NULL;
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				dev->stats.read.ops++;
+				/* messy ... */
+				if (dev->req_config) {
+					DBG(DBG_VERBOSE, "ep0 config ack%s\n",
+						dev->has_cfr ?  "" : " raced");
+					if (dev->has_cfr)
+						udc_set_reg(dev, UDCCFR, UDCCFR_AREN |
+							    UDCCFR_ACM | UDCCFR_MB1);
+					done(ep, req, 0);
+					dev->ep0state = EP0_END_XFER;
+					local_irq_restore (flags);
+					return 0;
+				}
+				if (dev->req_pending)
+					ep0start(dev, UDCCS0_IPR, "OUT");
+				if (length == 0 || ((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0
+						&& read_ep0_fifo(ep, req))) {
+					ep0_idle(dev);
+					done(ep, req, 0);
+					req = NULL;
+				}
+				break;
+
+			default:
+				DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
+				local_irq_restore (flags);
+				return -EL2HLT;
+			}
+		/* can the FIFO can satisfy the request immediately? */
+		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+			if ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) != 0
+					&& write_fifo(ep, req))
+				req = NULL;
+		} else if ((udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) != 0
+				&& read_fifo(ep, req)) {
+			req = NULL;
+		}
+
+		if (likely(req && ep->ep.desc))
+			pio_irq_enable(ep);
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely(req != NULL))
+		list_add_tail(&req->queue, &ep->queue);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+
+/*
+ *	nuke - dequeue ALL requests
+ */
+static void nuke(struct pxa25x_ep *ep, int status)
+{
+	struct pxa25x_request *req;
+
+	/* called with irqs blocked */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+				struct pxa25x_request,
+				queue);
+		done(ep, req, status);
+	}
+	if (ep->ep.desc)
+		pio_irq_disable(ep);
+}
+
+
+/* dequeue JUST ONE request */
+static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa25x_ep	*ep;
+	struct pxa25x_request	*req;
+	unsigned long		flags;
+
+	ep = container_of(_ep, struct pxa25x_ep, ep);
+	if (!_ep || ep->ep.name == ep0name)
+		return -EINVAL;
+
+	local_irq_save(flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pxa25x_ep	*ep;
+	unsigned long		flags;
+
+	ep = container_of(_ep, struct pxa25x_ep, ep);
+	if (unlikely (!_ep
+			|| (!ep->ep.desc && ep->ep.name != ep0name))
+			|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		DMSG("%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+	if (value == 0) {
+		/* this path (reset toggle+halt) is needed to implement
+		 * SET_INTERFACE on normal hardware.  but it can't be
+		 * done from software on the PXA UDC, and the hardware
+		 * forgets to do it as part of SET_INTERFACE automagic.
+		 */
+		DMSG("only host can clear %s halt\n", _ep->name);
+		return -EROFS;
+	}
+
+	local_irq_save(flags);
+
+	if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+			&& ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) == 0
+			   || !list_empty(&ep->queue))) {
+		local_irq_restore(flags);
+		return -EAGAIN;
+	}
+
+	/* FST bit is the same for control, bulk in, bulk out, interrupt in */
+	udc_ep_set_UDCCS(ep, UDCCS_BI_FST|UDCCS_BI_FTF);
+
+	/* ep0 needs special care */
+	if (!ep->ep.desc) {
+		start_watchdog(ep->dev);
+		ep->dev->req_pending = 0;
+		ep->dev->ep0state = EP0_STALL;
+
+	/* and bulk/intr endpoints like dropping stalls too */
+	} else {
+		unsigned i;
+		for (i = 0; i < 1000; i += 20) {
+			if (udc_ep_get_UDCCS(ep) & UDCCS_BI_SST)
+				break;
+			udelay(20);
+		}
+	}
+	local_irq_restore(flags);
+
+	DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+	return 0;
+}
+
+static int pxa25x_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct pxa25x_ep        *ep;
+
+	ep = container_of(_ep, struct pxa25x_ep, ep);
+	if (!_ep) {
+		DMSG("%s, bad ep\n", __func__);
+		return -ENODEV;
+	}
+	/* pxa can't report unclaimed bytes from IN fifos */
+	if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
+		return -EOPNOTSUPP;
+	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+			|| (udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) == 0)
+		return 0;
+	else
+		return (udc_ep_get_UBCR(ep) & 0xfff) + 1;
+}
+
+static void pxa25x_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct pxa25x_ep        *ep;
+
+	ep = container_of(_ep, struct pxa25x_ep, ep);
+	if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+		DMSG("%s, bad ep\n", __func__);
+		return;
+	}
+
+	/* toggle and halt bits stay unchanged */
+
+	/* for OUT, just read and discard the FIFO contents. */
+	if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
+		while (((udc_ep_get_UDCCS(ep)) & UDCCS_BO_RNE) != 0)
+			(void)udc_ep_get_UDDR(ep);
+		return;
+	}
+
+	/* most IN status is the same, but ISO can't stall */
+	udc_ep_set_UDCCS(ep, UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
+		| (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+			? 0 : UDCCS_BI_SST));
+}
+
+
+static struct usb_ep_ops pxa25x_ep_ops = {
+	.enable		= pxa25x_ep_enable,
+	.disable	= pxa25x_ep_disable,
+
+	.alloc_request	= pxa25x_ep_alloc_request,
+	.free_request	= pxa25x_ep_free_request,
+
+	.queue		= pxa25x_ep_queue,
+	.dequeue	= pxa25x_ep_dequeue,
+
+	.set_halt	= pxa25x_ep_set_halt,
+	.fifo_status	= pxa25x_ep_fifo_status,
+	.fifo_flush	= pxa25x_ep_fifo_flush,
+};
+
+
+/* ---------------------------------------------------------------------------
+ *	device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int pxa25x_udc_get_frame(struct usb_gadget *_gadget)
+{
+	struct pxa25x_udc	*dev;
+
+	dev = container_of(_gadget, struct pxa25x_udc, gadget);
+	return ((udc_get_reg(dev, UFNRH) & 0x07) << 8) |
+		(udc_get_reg(dev, UFNRL) & 0xff);
+}
+
+static int pxa25x_udc_wakeup(struct usb_gadget *_gadget)
+{
+	struct pxa25x_udc	*udc;
+
+	udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+	/* host may not have enabled remote wakeup */
+	if ((udc_ep0_get_UDCCS(udc) & UDCCS0_DRWF) == 0)
+		return -EHOSTUNREACH;
+	udc_set_mask_UDCCR(udc, UDCCR_RSM);
+	return 0;
+}
+
+static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *);
+static void udc_enable (struct pxa25x_udc *);
+static void udc_disable(struct pxa25x_udc *);
+
+/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
+ * in active use.
+ */
+static int pullup(struct pxa25x_udc *udc)
+{
+	int is_active = udc->vbus && udc->pullup && !udc->suspended;
+	DMSG("%s\n", is_active ? "active" : "inactive");
+	if (is_active) {
+		if (!udc->active) {
+			udc->active = 1;
+			/* Enable clock for USB device */
+			clk_enable(udc->clk);
+			udc_enable(udc);
+		}
+	} else {
+		if (udc->active) {
+			if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+				DMSG("disconnect %s\n", udc->driver
+					? udc->driver->driver.name
+					: "(no driver)");
+				stop_activity(udc, udc->driver);
+			}
+			udc_disable(udc);
+			/* Disable clock for USB device */
+			clk_disable(udc->clk);
+			udc->active = 0;
+		}
+
+	}
+	return 0;
+}
+
+/* VBUS reporting logically comes from a transceiver */
+static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa25x_udc	*udc;
+
+	udc = container_of(_gadget, struct pxa25x_udc, gadget);
+	udc->vbus = is_active;
+	DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
+	pullup(udc);
+	return 0;
+}
+
+/* drivers may have software control over D+ pullup */
+static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa25x_udc	*udc;
+
+	udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+	/* not all boards support pullup control */
+	if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
+		return -EOPNOTSUPP;
+
+	udc->pullup = (is_active != 0);
+	pullup(udc);
+	return 0;
+}
+
+/* boards may consume current from VBUS, up to 100-500mA based on config.
+ * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs
+ * violate USB specs.
+ */
+static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+	struct pxa25x_udc	*udc;
+
+	udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		return usb_phy_set_power(udc->transceiver, mA);
+	return -EOPNOTSUPP;
+}
+
+static int pxa25x_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int pxa25x_udc_stop(struct usb_gadget *g);
+
+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,
+	.udc_start	= pxa25x_udc_start,
+	.udc_stop	= pxa25x_udc_stop,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+
+static int udc_debug_show(struct seq_file *m, void *_d)
+{
+	struct pxa25x_udc	*dev = m->private;
+	unsigned long		flags;
+	int			i;
+	u32			tmp;
+
+	local_irq_save(flags);
+
+	/* basic device status */
+	seq_printf(m, DRIVER_DESC "\n"
+		"%s version: %s\nGadget driver: %s\nHost %s\n\n",
+		driver_name, DRIVER_VERSION SIZE_STR "(pio)",
+		dev->driver ? dev->driver->driver.name : "(none)",
+		dev->gadget.speed == USB_SPEED_FULL ? "full speed" : "disconnected");
+
+	/* registers for device and ep0 */
+	seq_printf(m,
+		"uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+		udc_get_reg(dev, UICR1), udc_get_reg(dev, UICR0),
+		udc_get_reg(dev, USIR1), udc_get_reg(dev, USIR0),
+		udc_get_reg(dev, UFNRH), udc_get_reg(dev, UFNRL));
+
+	tmp = udc_get_reg(dev, UDCCR);
+	seq_printf(m,
+		"udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
+		(tmp & UDCCR_REM) ? " rem" : "",
+		(tmp & UDCCR_RSTIR) ? " rstir" : "",
+		(tmp & UDCCR_SRM) ? " srm" : "",
+		(tmp & UDCCR_SUSIR) ? " susir" : "",
+		(tmp & UDCCR_RESIR) ? " resir" : "",
+		(tmp & UDCCR_RSM) ? " rsm" : "",
+		(tmp & UDCCR_UDA) ? " uda" : "",
+		(tmp & UDCCR_UDE) ? " ude" : "");
+
+	tmp = udc_ep0_get_UDCCS(dev);
+	seq_printf(m,
+		"udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
+		(tmp & UDCCS0_SA) ? " sa" : "",
+		(tmp & UDCCS0_RNE) ? " rne" : "",
+		(tmp & UDCCS0_FST) ? " fst" : "",
+		(tmp & UDCCS0_SST) ? " sst" : "",
+		(tmp & UDCCS0_DRWF) ? " dwrf" : "",
+		(tmp & UDCCS0_FTF) ? " ftf" : "",
+		(tmp & UDCCS0_IPR) ? " ipr" : "",
+		(tmp & UDCCS0_OPR) ? " opr" : "");
+
+	if (dev->has_cfr) {
+		tmp = udc_get_reg(dev, UDCCFR);
+		seq_printf(m,
+			"udccfr %02X =%s%s\n", tmp,
+			(tmp & UDCCFR_AREN) ? " aren" : "",
+			(tmp & UDCCFR_ACM) ? " acm" : "");
+	}
+
+	if (dev->gadget.speed != USB_SPEED_FULL || !dev->driver)
+		goto done;
+
+	seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+		dev->stats.write.bytes, dev->stats.write.ops,
+		dev->stats.read.bytes, dev->stats.read.ops,
+		dev->stats.irqs);
+
+	/* dump endpoint queues */
+	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+		struct pxa25x_ep	*ep = &dev->ep [i];
+		struct pxa25x_request	*req;
+
+		if (i != 0) {
+			const struct usb_endpoint_descriptor	*desc;
+
+			desc = ep->ep.desc;
+			if (!desc)
+				continue;
+			tmp = udc_ep_get_UDCCS(&dev->ep[i]);
+			seq_printf(m,
+				"%s max %d %s udccs %02x irqs %lu\n",
+				ep->ep.name, usb_endpoint_maxp(desc),
+				"pio", tmp, ep->pio_irqs);
+			/* TODO translate all five groups of udccs bits! */
+
+		} else /* ep0 should only have one transfer queued */
+			seq_printf(m, "ep0 max 16 pio irqs %lu\n",
+				ep->pio_irqs);
+
+		if (list_empty(&ep->queue)) {
+			seq_printf(m, "\t(nothing queued)\n");
+			continue;
+		}
+		list_for_each_entry(req, &ep->queue, queue) {
+			seq_printf(m,
+					"\treq %p len %d/%d buf %p\n",
+					&req->req, req->req.actual,
+					req->req.length, req->req.buf);
+		}
+	}
+
+done:
+	local_irq_restore(flags);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(udc_debug);
+
+#define create_debug_files(dev) \
+	do { \
+		dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
+			S_IRUGO, NULL, dev, &udc_debug_fops); \
+	} while (0)
+#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc)
+
+#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_debug_files(dev) do {} while (0)
+#define remove_debug_files(dev) do {} while (0)
+
+#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ *	udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pxa25x_udc *dev)
+{
+	/* block all irqs */
+	udc_set_mask_UDCCR(dev, UDCCR_SRM|UDCCR_REM);
+	udc_set_reg(dev, UICR0, 0xff);
+	udc_set_reg(dev, UICR1, 0xff);
+	udc_set_reg(dev, UFNRH, UFNRH_SIM);
+
+	/* if hardware supports it, disconnect from usb */
+	pullup_off();
+
+	udc_clear_mask_UDCCR(dev, UDCCR_UDE);
+
+	ep0_idle (dev);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+
+/*
+ *	udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pxa25x_udc *dev)
+{
+	u32	i;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+	dev->ep0state = EP0_IDLE;
+	dev->gadget.quirk_altset_not_supp = 1;
+
+	/* basic endpoint records init */
+	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+		struct pxa25x_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->ep.desc = NULL;
+		ep->stopped = 0;
+		INIT_LIST_HEAD (&ep->queue);
+		ep->pio_irqs = 0;
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket);
+	}
+
+	/* the rest was statically initialized, and is read-only */
+}
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static void udc_enable (struct pxa25x_udc *dev)
+{
+	udc_clear_mask_UDCCR(dev, UDCCR_UDE);
+
+	/* try to clear these bits before we enable the udc */
+	udc_ack_int_UDCCR(dev, UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
+
+	ep0_idle(dev);
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->stats.irqs = 0;
+
+	/*
+	 * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual:
+	 * - enable UDC
+	 * - if RESET is already in progress, ack interrupt
+	 * - unmask reset interrupt
+	 */
+	udc_set_mask_UDCCR(dev, UDCCR_UDE);
+	if (!(udc_get_reg(dev, UDCCR) & UDCCR_UDA))
+		udc_ack_int_UDCCR(dev, UDCCR_RSTIR);
+
+	if (dev->has_cfr /* UDC_RES2 is defined */) {
+		/* pxa255 (a0+) can avoid a set_config race that could
+		 * prevent gadget drivers from configuring correctly
+		 */
+		udc_set_reg(dev, UDCCFR, UDCCFR_ACM | UDCCFR_MB1);
+	} else {
+		/* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1)
+		 * which could result in missing packets and interrupts.
+		 * supposedly one bit per endpoint, controlling whether it
+		 * double buffers or not; ACM/AREN bits fit into the holes.
+		 * zero bits (like USIR0_IRx) disable double buffering.
+		 */
+		udc_set_reg(dev, UDC_RES1, 0x00);
+		udc_set_reg(dev, UDC_RES2, 0x00);
+	}
+
+	/* enable suspend/resume and reset irqs */
+	udc_clear_mask_UDCCR(dev, UDCCR_SRM | UDCCR_REM);
+
+	/* enable ep0 irqs */
+	udc_set_reg(dev, UICR0, udc_get_reg(dev, UICR0) & ~UICR0_IM0);
+
+	/* if hardware supports it, pullup D+ and wait for reset */
+	pullup_on();
+}
+
+
+/* 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 pxa25x_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct pxa25x_udc	*dev = to_pxa25x(g);
+	int			retval;
+
+	/* first hook up the driver ... */
+	dev->driver = driver;
+	dev->pullup = 1;
+
+	/* ... then enable host detection and ep0; and we're ready
+	 * for set_configuration as well as eventual disconnect.
+	 */
+	/* connect to bus through transceiver */
+	if (!IS_ERR_OR_NULL(dev->transceiver)) {
+		retval = otg_set_peripheral(dev->transceiver->otg,
+						&dev->gadget);
+		if (retval)
+			goto bind_fail;
+	}
+
+	dump_state(dev);
+	return 0;
+bind_fail:
+	return retval;
+}
+
+static void
+reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+		struct pxa25x_ep *ep = &dev->ep[i];
+
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+	del_timer_sync(&dev->timer);
+
+	/* report reset; the driver is already quiesced */
+	if (driver)
+		usb_gadget_udc_reset(&dev->gadget, driver);
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+static void
+stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+		struct pxa25x_ep *ep = &dev->ep[i];
+
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+	del_timer_sync(&dev->timer);
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver)
+		driver->disconnect(&dev->gadget);
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+static int pxa25x_udc_stop(struct usb_gadget*g)
+{
+	struct pxa25x_udc	*dev = to_pxa25x(g);
+
+	local_irq_disable();
+	dev->pullup = 0;
+	stop_activity(dev, NULL);
+	local_irq_enable();
+
+	if (!IS_ERR_OR_NULL(dev->transceiver))
+		(void) otg_set_peripheral(dev->transceiver->otg, NULL);
+
+	dev->driver = NULL;
+
+	dump_state(dev);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_LUBBOCK
+
+/* Lubbock has separate connect and disconnect irqs.  More typical designs
+ * use one GPIO as the VBUS IRQ, and another to control the D+ pullup.
+ */
+
+static irqreturn_t
+lubbock_vbus_irq(int irq, void *_dev)
+{
+	struct pxa25x_udc	*dev = _dev;
+	int			vbus;
+
+	dev->stats.irqs++;
+	switch (irq) {
+	case LUBBOCK_USB_IRQ:
+		vbus = 1;
+		disable_irq(LUBBOCK_USB_IRQ);
+		enable_irq(LUBBOCK_USB_DISC_IRQ);
+		break;
+	case LUBBOCK_USB_DISC_IRQ:
+		vbus = 0;
+		disable_irq(LUBBOCK_USB_DISC_IRQ);
+		enable_irq(LUBBOCK_USB_IRQ);
+		break;
+	default:
+		return IRQ_NONE;
+	}
+
+	pxa25x_udc_vbus_session(&dev->gadget, vbus);
+	return IRQ_HANDLED;
+}
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline void clear_ep_state (struct pxa25x_udc *dev)
+{
+	unsigned i;
+
+	/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
+	 * fifos, and pending transactions mustn't be continued in any case.
+	 */
+	for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)
+		nuke(&dev->ep[i], -ECONNABORTED);
+}
+
+static void udc_watchdog(struct timer_list *t)
+{
+	struct pxa25x_udc	*dev = from_timer(dev, t, timer);
+
+	local_irq_disable();
+	if (dev->ep0state == EP0_STALL
+			&& (udc_ep0_get_UDCCS(dev) & UDCCS0_FST) == 0
+			&& (udc_ep0_get_UDCCS(dev) & UDCCS0_SST) == 0) {
+		udc_ep0_set_UDCCS(dev, UDCCS0_FST|UDCCS0_FTF);
+		DBG(DBG_VERBOSE, "ep0 re-stall\n");
+		start_watchdog(dev);
+	}
+	local_irq_enable();
+}
+
+static void handle_ep0 (struct pxa25x_udc *dev)
+{
+	u32			udccs0 = udc_ep0_get_UDCCS(dev);
+	struct pxa25x_ep	*ep = &dev->ep [0];
+	struct pxa25x_request	*req;
+	union {
+		struct usb_ctrlrequest	r;
+		u8			raw [8];
+		u32			word [2];
+	} u;
+
+	if (list_empty(&ep->queue))
+		req = NULL;
+	else
+		req = list_entry(ep->queue.next, struct pxa25x_request, queue);
+
+	/* clear stall status */
+	if (udccs0 & UDCCS0_SST) {
+		nuke(ep, -EPIPE);
+		udc_ep0_set_UDCCS(dev, UDCCS0_SST);
+		del_timer(&dev->timer);
+		ep0_idle(dev);
+	}
+
+	/* previous request unfinished?  non-error iff back-to-back ... */
+	if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+		nuke(ep, 0);
+		del_timer(&dev->timer);
+		ep0_idle(dev);
+	}
+
+	switch (dev->ep0state) {
+	case EP0_IDLE:
+		/* late-breaking status? */
+		udccs0 = udc_ep0_get_UDCCS(dev);
+
+		/* start control request? */
+		if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))
+				== (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {
+			int i;
+
+			nuke (ep, -EPROTO);
+
+			/* read SETUP packet */
+			for (i = 0; i < 8; i++) {
+				if (unlikely(!(udc_ep0_get_UDCCS(dev) & UDCCS0_RNE))) {
+bad_setup:
+					DMSG("SETUP %d!\n", i);
+					goto stall;
+				}
+				u.raw [i] = (u8) UDDR0;
+			}
+			if (unlikely((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0))
+				goto bad_setup;
+
+got_setup:
+			DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+				u.r.bRequestType, u.r.bRequest,
+				le16_to_cpu(u.r.wValue),
+				le16_to_cpu(u.r.wIndex),
+				le16_to_cpu(u.r.wLength));
+
+			/* cope with automagic for some standard requests. */
+			dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
+						== USB_TYPE_STANDARD;
+			dev->req_config = 0;
+			dev->req_pending = 1;
+			switch (u.r.bRequest) {
+			/* hardware restricts gadget drivers here! */
+			case USB_REQ_SET_CONFIGURATION:
+				if (u.r.bRequestType == USB_RECIP_DEVICE) {
+					/* reflect hardware's automagic
+					 * up to the gadget driver.
+					 */
+config_change:
+					dev->req_config = 1;
+					clear_ep_state(dev);
+					/* if !has_cfr, there's no synch
+					 * else use AREN (later) not SA|OPR
+					 * USIR0_IR0 acts edge sensitive
+					 */
+				}
+				break;
+			/* ... and here, even more ... */
+			case USB_REQ_SET_INTERFACE:
+				if (u.r.bRequestType == USB_RECIP_INTERFACE) {
+					/* udc hardware is broken by design:
+					 *  - altsetting may only be zero;
+					 *  - hw resets all interfaces' eps;
+					 *  - ep reset doesn't include halt(?).
+					 */
+					DMSG("broken set_interface (%d/%d)\n",
+						le16_to_cpu(u.r.wIndex),
+						le16_to_cpu(u.r.wValue));
+					goto config_change;
+				}
+				break;
+			/* hardware was supposed to hide this */
+			case USB_REQ_SET_ADDRESS:
+				if (u.r.bRequestType == USB_RECIP_DEVICE) {
+					ep0start(dev, 0, "address");
+					return;
+				}
+				break;
+			}
+
+			if (u.r.bRequestType & USB_DIR_IN)
+				dev->ep0state = EP0_IN_DATA_PHASE;
+			else
+				dev->ep0state = EP0_OUT_DATA_PHASE;
+
+			i = dev->driver->setup(&dev->gadget, &u.r);
+			if (i < 0) {
+				/* hardware automagic preventing STALL... */
+				if (dev->req_config) {
+					/* hardware sometimes neglects to tell
+					 * tell us about config change events,
+					 * so later ones may fail...
+					 */
+					WARNING("config change %02x fail %d?\n",
+						u.r.bRequest, i);
+					return;
+					/* TODO experiment:  if has_cfr,
+					 * hardware didn't ACK; maybe we
+					 * could actually STALL!
+					 */
+				}
+				DBG(DBG_VERBOSE, "protocol STALL, "
+					"%02x err %d\n", udc_ep0_get_UDCCS(dev), i);
+stall:
+				/* the watchdog timer helps deal with cases
+				 * where udc seems to clear FST wrongly, and
+				 * then NAKs instead of STALLing.
+				 */
+				ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
+				start_watchdog(dev);
+				dev->ep0state = EP0_STALL;
+
+			/* deferred i/o == no response yet */
+			} else if (dev->req_pending) {
+				if (likely(dev->ep0state == EP0_IN_DATA_PHASE
+						|| dev->req_std || u.r.wLength))
+					ep0start(dev, 0, "defer");
+				else
+					ep0start(dev, UDCCS0_IPR, "defer/IPR");
+			}
+
+			/* expect at least one data or status stage irq */
+			return;
+
+		} else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))
+				== (UDCCS0_OPR|UDCCS0_SA))) {
+			unsigned i;
+
+			/* pxa210/250 erratum 131 for B0/B1 says RNE lies.
+			 * still observed on a pxa255 a0.
+			 */
+			DBG(DBG_VERBOSE, "e131\n");
+			nuke(ep, -EPROTO);
+
+			/* read SETUP data, but don't trust it too much */
+			for (i = 0; i < 8; i++)
+				u.raw [i] = (u8) UDDR0;
+			if ((u.r.bRequestType & USB_RECIP_MASK)
+					> USB_RECIP_OTHER)
+				goto stall;
+			if (u.word [0] == 0 && u.word [1] == 0)
+				goto stall;
+			goto got_setup;
+		} else {
+			/* some random early IRQ:
+			 * - we acked FST
+			 * - IPR cleared
+			 * - OPR got set, without SA (likely status stage)
+			 */
+			udc_ep0_set_UDCCS(dev, udccs0 & (UDCCS0_SA|UDCCS0_OPR));
+		}
+		break;
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+		if (udccs0 & UDCCS0_OPR) {
+			udc_ep0_set_UDCCS(dev, UDCCS0_OPR|UDCCS0_FTF);
+			DBG(DBG_VERBOSE, "ep0in premature status\n");
+			if (req)
+				done(ep, req, 0);
+			ep0_idle(dev);
+		} else /* irq was IPR clearing */ {
+			if (req) {
+				/* this IN packet might finish the request */
+				(void) write_ep0_fifo(ep, req);
+			} /* else IN token before response was written */
+		}
+		break;
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+		if (udccs0 & UDCCS0_OPR) {
+			if (req) {
+				/* this OUT packet might finish the request */
+				if (read_ep0_fifo(ep, req))
+					done(ep, req, 0);
+				/* else more OUT packets expected */
+			} /* else OUT token before read was issued */
+		} else /* irq was IPR clearing */ {
+			DBG(DBG_VERBOSE, "ep0out premature status\n");
+			if (req)
+				done(ep, req, 0);
+			ep0_idle(dev);
+		}
+		break;
+	case EP0_END_XFER:
+		if (req)
+			done(ep, req, 0);
+		/* ack control-IN status (maybe in-zlp was skipped)
+		 * also appears after some config change events.
+		 */
+		if (udccs0 & UDCCS0_OPR)
+			udc_ep0_set_UDCCS(dev, UDCCS0_OPR);
+		ep0_idle(dev);
+		break;
+	case EP0_STALL:
+		udc_ep0_set_UDCCS(dev, UDCCS0_FST);
+		break;
+	}
+	udc_set_reg(dev, USIR0, USIR0_IR0);
+}
+
+static void handle_ep(struct pxa25x_ep *ep)
+{
+	struct pxa25x_request	*req;
+	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
+	int			completed;
+	u32			udccs, tmp;
+
+	do {
+		completed = 0;
+		if (likely (!list_empty(&ep->queue)))
+			req = list_entry(ep->queue.next,
+					struct pxa25x_request, queue);
+		else
+			req = NULL;
+
+		// TODO check FST handling
+
+		udccs = udc_ep_get_UDCCS(ep);
+		if (unlikely(is_in)) {	/* irq from TPC, SST, or (ISO) TUR */
+			tmp = UDCCS_BI_TUR;
+			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+				tmp |= UDCCS_BI_SST;
+			tmp &= udccs;
+			if (likely (tmp))
+				udc_ep_set_UDCCS(ep, tmp);
+			if (req && likely ((udccs & UDCCS_BI_TFS) != 0))
+				completed = write_fifo(ep, req);
+
+		} else {	/* irq from RPC (or for ISO, ROF) */
+			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+				tmp = UDCCS_BO_SST | UDCCS_BO_DME;
+			else
+				tmp = UDCCS_IO_ROF | UDCCS_IO_DME;
+			tmp &= udccs;
+			if (likely(tmp))
+				udc_ep_set_UDCCS(ep, tmp);
+
+			/* fifos can hold packets, ready for reading... */
+			if (likely(req)) {
+				completed = read_fifo(ep, req);
+			} else
+				pio_irq_disable(ep);
+		}
+		ep->pio_irqs++;
+	} while (completed);
+}
+
+/*
+ *	pxa25x_udc_irq - interrupt handler
+ *
+ * avoid delays in ep0 processing. the control handshaking isn't always
+ * under software control (pxa250c0 and the pxa255 are better), and delays
+ * could cause usb protocol errors.
+ */
+static irqreturn_t
+pxa25x_udc_irq(int irq, void *_dev)
+{
+	struct pxa25x_udc	*dev = _dev;
+	int			handled;
+
+	dev->stats.irqs++;
+	do {
+		u32		udccr = udc_get_reg(dev, UDCCR);
+
+		handled = 0;
+
+		/* SUSpend Interrupt Request */
+		if (unlikely(udccr & UDCCR_SUSIR)) {
+			udc_ack_int_UDCCR(dev, UDCCR_SUSIR);
+			handled = 1;
+			DBG(DBG_VERBOSE, "USB suspend\n");
+
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+					&& dev->driver
+					&& dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+			ep0_idle (dev);
+		}
+
+		/* RESume Interrupt Request */
+		if (unlikely(udccr & UDCCR_RESIR)) {
+			udc_ack_int_UDCCR(dev, UDCCR_RESIR);
+			handled = 1;
+			DBG(DBG_VERBOSE, "USB resume\n");
+
+			if (dev->gadget.speed != USB_SPEED_UNKNOWN
+					&& dev->driver
+					&& dev->driver->resume)
+				dev->driver->resume(&dev->gadget);
+		}
+
+		/* ReSeT Interrupt Request - USB reset */
+		if (unlikely(udccr & UDCCR_RSTIR)) {
+			udc_ack_int_UDCCR(dev, UDCCR_RSTIR);
+			handled = 1;
+
+			if ((udc_get_reg(dev, UDCCR) & UDCCR_UDA) == 0) {
+				DBG(DBG_VERBOSE, "USB reset start\n");
+
+				/* reset driver and endpoints,
+				 * in case that's not yet done
+				 */
+				reset_gadget(dev, dev->driver);
+
+			} else {
+				DBG(DBG_VERBOSE, "USB reset end\n");
+				dev->gadget.speed = USB_SPEED_FULL;
+				memset(&dev->stats, 0, sizeof dev->stats);
+				/* driver and endpoints are still reset */
+			}
+
+		} else {
+			u32	usir0 = udc_get_reg(dev, USIR0) &
+					~udc_get_reg(dev, UICR0);
+			u32	usir1 = udc_get_reg(dev, USIR1) &
+					~udc_get_reg(dev, UICR1);
+			int	i;
+
+			if (unlikely (!usir0 && !usir1))
+				continue;
+
+			DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0);
+
+			/* control traffic */
+			if (usir0 & USIR0_IR0) {
+				dev->ep[0].pio_irqs++;
+				handle_ep0(dev);
+				handled = 1;
+			}
+
+			/* endpoint data transfers */
+			for (i = 0; i < 8; i++) {
+				u32	tmp = 1 << i;
+
+				if (i && (usir0 & tmp)) {
+					handle_ep(&dev->ep[i]);
+					udc_set_reg(dev, USIR0,
+						udc_get_reg(dev, USIR0) | tmp);
+					handled = 1;
+				}
+#ifndef	CONFIG_USB_PXA25X_SMALL
+				if (usir1 & tmp) {
+					handle_ep(&dev->ep[i+8]);
+					udc_set_reg(dev, USIR1,
+						udc_get_reg(dev, USIR1) | tmp);
+					handled = 1;
+				}
+#endif
+			}
+		}
+
+		/* we could also ask for 1 msec SOF (SIR) interrupts */
+
+	} while (handled);
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void nop_release (struct device *dev)
+{
+	DMSG("%s %s\n", __func__, dev_name(dev));
+}
+
+/* this uses load-time allocation and initialization (instead of
+ * doing it at run-time) to save code, eliminate fault paths, and
+ * be more obviously correct.
+ */
+static struct pxa25x_udc memory = {
+	.gadget = {
+		.ops		= &pxa25x_udc_ops,
+		.ep0		= &memory.ep[0].ep,
+		.name		= driver_name,
+		.dev = {
+			.init_name	= "gadget",
+			.release	= nop_release,
+		},
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.ep = {
+			.name		= ep0name,
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+		.regoff_udccs	= UDCCS0,
+		.regoff_uddr	= UDDR0,
+	},
+
+	/* first group of endpoints */
+	.ep[1] = {
+		.ep = {
+			.name		= "ep1in-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 1,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS1,
+		.regoff_uddr	= UDDR1,
+	},
+	.ep[2] = {
+		.ep = {
+			.name		= "ep2out-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 2,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS2,
+		.regoff_ubcr	= UBCR2,
+		.regoff_uddr	= UDDR2,
+	},
+#ifndef CONFIG_USB_PXA25X_SMALL
+	.ep[3] = {
+		.ep = {
+			.name		= "ep3in-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 3,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS3,
+		.regoff_uddr	= UDDR3,
+	},
+	.ep[4] = {
+		.ep = {
+			.name		= "ep4out-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = 4,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS4,
+		.regoff_ubcr	= UBCR4,
+		.regoff_uddr	= UDDR4,
+	},
+	.ep[5] = {
+		.ep = {
+			.name		= "ep5in-int",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= INT_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(0, 0),
+		},
+		.dev		= &memory,
+		.fifo_size	= INT_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 5,
+		.bmAttributes	= USB_ENDPOINT_XFER_INT,
+		.regoff_udccs	= UDCCS5,
+		.regoff_uddr	= UDDR5,
+	},
+
+	/* second group of endpoints */
+	.ep[6] = {
+		.ep = {
+			.name		= "ep6in-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 6,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS6,
+		.regoff_uddr	= UDDR6,
+	},
+	.ep[7] = {
+		.ep = {
+			.name		= "ep7out-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 7,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS7,
+		.regoff_ubcr	= UBCR7,
+		.regoff_uddr	= UDDR7,
+	},
+	.ep[8] = {
+		.ep = {
+			.name		= "ep8in-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 8,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS8,
+		.regoff_uddr	= UDDR8,
+	},
+	.ep[9] = {
+		.ep = {
+			.name		= "ep9out-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = 9,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS9,
+		.regoff_ubcr	= UBCR9,
+		.regoff_uddr	= UDDR9,
+	},
+	.ep[10] = {
+		.ep = {
+			.name		= "ep10in-int",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= INT_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(0, 0),
+		},
+		.dev		= &memory,
+		.fifo_size	= INT_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 10,
+		.bmAttributes	= USB_ENDPOINT_XFER_INT,
+		.regoff_udccs	= UDCCS10,
+		.regoff_uddr	= UDDR10,
+	},
+
+	/* third group of endpoints */
+	.ep[11] = {
+		.ep = {
+			.name		= "ep11in-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 11,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS11,
+		.regoff_uddr	= UDDR11,
+	},
+	.ep[12] = {
+		.ep = {
+			.name		= "ep12out-bulk",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= BULK_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= BULK_FIFO_SIZE,
+		.bEndpointAddress = 12,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+		.regoff_udccs	= UDCCS12,
+		.regoff_ubcr	= UBCR12,
+		.regoff_uddr	= UDDR12,
+	},
+	.ep[13] = {
+		.ep = {
+			.name		= "ep13in-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_IN),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 13,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS13,
+		.regoff_uddr	= UDDR13,
+	},
+	.ep[14] = {
+		.ep = {
+			.name		= "ep14out-iso",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= ISO_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO,
+						USB_EP_CAPS_DIR_OUT),
+		},
+		.dev		= &memory,
+		.fifo_size	= ISO_FIFO_SIZE,
+		.bEndpointAddress = 14,
+		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
+		.regoff_udccs	= UDCCS14,
+		.regoff_ubcr	= UBCR14,
+		.regoff_uddr	= UDDR14,
+	},
+	.ep[15] = {
+		.ep = {
+			.name		= "ep15in-int",
+			.ops		= &pxa25x_ep_ops,
+			.maxpacket	= INT_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(0, 0),
+		},
+		.dev		= &memory,
+		.fifo_size	= INT_FIFO_SIZE,
+		.bEndpointAddress = USB_DIR_IN | 15,
+		.bmAttributes	= USB_ENDPOINT_XFER_INT,
+		.regoff_udccs	= UDCCS15,
+		.regoff_uddr	= UDDR15,
+	},
+#endif /* !CONFIG_USB_PXA25X_SMALL */
+};
+
+#define CP15R0_VENDOR_MASK	0xffffe000
+
+#if	defined(CONFIG_ARCH_PXA)
+#define CP15R0_XSCALE_VALUE	0x69052000	/* intel/arm/xscale */
+
+#elif	defined(CONFIG_ARCH_IXP4XX)
+#define CP15R0_XSCALE_VALUE	0x69054000	/* intel/arm/ixp4xx */
+
+#endif
+
+#define CP15R0_PROD_MASK	0x000003f0
+#define PXA25x			0x00000100	/* and PXA26x */
+#define PXA210			0x00000120
+
+#define CP15R0_REV_MASK		0x0000000f
+
+#define CP15R0_PRODREV_MASK	(CP15R0_PROD_MASK | CP15R0_REV_MASK)
+
+#define PXA255_A0		0x00000106	/* or PXA260_B1 */
+#define PXA250_C0		0x00000105	/* or PXA26x_B0 */
+#define PXA250_B2		0x00000104
+#define PXA250_B1		0x00000103	/* or PXA260_A0 */
+#define PXA250_B0		0x00000102
+#define PXA250_A1		0x00000101
+#define PXA250_A0		0x00000100
+
+#define PXA210_C0		0x00000125
+#define PXA210_B2		0x00000124
+#define PXA210_B1		0x00000123
+#define PXA210_B0		0x00000122
+#define IXP425_A0		0x000001c1
+#define IXP425_B0		0x000001f1
+#define IXP465_AD		0x00000200
+
+/*
+ *	probe - binds to the platform device
+ */
+static int pxa25x_udc_probe(struct platform_device *pdev)
+{
+	struct pxa25x_udc *dev = &memory;
+	int retval, irq;
+	u32 chiprev;
+	struct resource *res;
+
+	pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
+
+	/* insist on Intel/ARM/XScale */
+	asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
+	if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
+		pr_err("%s: not XScale!\n", driver_name);
+		return -ENODEV;
+	}
+
+	/* trigger chiprev-specific logic */
+	switch (chiprev & CP15R0_PRODREV_MASK) {
+#if	defined(CONFIG_ARCH_PXA)
+	case PXA255_A0:
+		dev->has_cfr = 1;
+		break;
+	case PXA250_A0:
+	case PXA250_A1:
+		/* A0/A1 "not released"; ep 13, 15 unusable */
+		/* fall through */
+	case PXA250_B2: case PXA210_B2:
+	case PXA250_B1: case PXA210_B1:
+	case PXA250_B0: case PXA210_B0:
+		/* OUT-DMA is broken ... */
+		/* fall through */
+	case PXA250_C0: case PXA210_C0:
+		break;
+#elif	defined(CONFIG_ARCH_IXP4XX)
+	case IXP425_A0:
+	case IXP425_B0:
+	case IXP465_AD:
+		dev->has_cfr = 1;
+		break;
+#endif
+	default:
+		pr_err("%s: unrecognized processor: %08x\n",
+			driver_name, chiprev);
+		/* iop3xx, ixp4xx, ... */
+		return -ENODEV;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dev->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dev->regs))
+		return PTR_ERR(dev->regs);
+
+	dev->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk))
+		return PTR_ERR(dev->clk);
+
+	pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
+		dev->has_cfr ? "" : " (!cfr)",
+		SIZE_STR "(pio)"
+		);
+
+	/* other non-static parts of init */
+	dev->dev = &pdev->dev;
+	dev->mach = dev_get_platdata(&pdev->dev);
+
+	dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
+	if (gpio_is_valid(dev->mach->gpio_pullup)) {
+		retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup,
+					   "pca25x_udc GPIO PULLUP");
+		if (retval) {
+			dev_dbg(&pdev->dev,
+				"can't get pullup gpio %d, err: %d\n",
+				dev->mach->gpio_pullup, retval);
+			goto err;
+		}
+		gpio_direction_output(dev->mach->gpio_pullup, 0);
+	}
+
+	timer_setup(&dev->timer, udc_watchdog, 0);
+
+	the_controller = dev;
+	platform_set_drvdata(pdev, dev);
+
+	udc_disable(dev);
+	udc_reinit(dev);
+
+	dev->vbus = 0;
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0,
+				  driver_name, dev);
+	if (retval != 0) {
+		pr_err("%s: can't get irq %d, err %d\n",
+			driver_name, irq, retval);
+		goto err;
+	}
+	dev->got_irq = 1;
+
+#ifdef CONFIG_ARCH_LUBBOCK
+	if (machine_is_lubbock()) {
+		retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ,
+					  lubbock_vbus_irq, 0, driver_name,
+					  dev);
+		if (retval != 0) {
+			pr_err("%s: can't get irq %i, err %d\n",
+				driver_name, LUBBOCK_USB_DISC_IRQ, retval);
+			goto err;
+		}
+		retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ,
+					  lubbock_vbus_irq, 0, driver_name,
+					  dev);
+		if (retval != 0) {
+			pr_err("%s: can't get irq %i, err %d\n",
+				driver_name, LUBBOCK_USB_IRQ, retval);
+			goto err;
+		}
+	} else
+#endif
+	create_debug_files(dev);
+
+	retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+	if (!retval)
+		return retval;
+
+	remove_debug_files(dev);
+ err:
+	if (!IS_ERR_OR_NULL(dev->transceiver))
+		dev->transceiver = NULL;
+	return retval;
+}
+
+static void pxa25x_udc_shutdown(struct platform_device *_dev)
+{
+	pullup_off();
+}
+
+static int pxa25x_udc_remove(struct platform_device *pdev)
+{
+	struct pxa25x_udc *dev = platform_get_drvdata(pdev);
+
+	if (dev->driver)
+		return -EBUSY;
+
+	usb_del_gadget_udc(&dev->gadget);
+	dev->pullup = 0;
+	pullup(dev);
+
+	remove_debug_files(dev);
+
+	if (!IS_ERR_OR_NULL(dev->transceiver))
+		dev->transceiver = NULL;
+
+	the_controller = NULL;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+/* USB suspend (controlled by the host) and system suspend (controlled
+ * by the PXA) don't necessarily work well together.  If USB is active,
+ * the 48 MHz clock is required; so the system can't enter 33 MHz idle
+ * mode, or any deeper PM saving state.
+ *
+ * For now, we punt and forcibly disconnect from the USB host when PXA
+ * enters any suspend state.  While we're disconnected, we always disable
+ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
+ * Boards without software pullup control shouldn't use those states.
+ * VBUS IRQs should probably be ignored so that the PXA device just acts
+ * "dead" to USB hosts until system resume.
+ */
+static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct pxa25x_udc	*udc = platform_get_drvdata(dev);
+	unsigned long flags;
+
+	if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
+		WARNING("USB host won't detect disconnect!\n");
+	udc->suspended = 1;
+
+	local_irq_save(flags);
+	pullup(udc);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static int pxa25x_udc_resume(struct platform_device *dev)
+{
+	struct pxa25x_udc	*udc = platform_get_drvdata(dev);
+	unsigned long flags;
+
+	udc->suspended = 0;
+	local_irq_save(flags);
+	pullup(udc);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+#else
+#define	pxa25x_udc_suspend	NULL
+#define	pxa25x_udc_resume	NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+	.shutdown	= pxa25x_udc_shutdown,
+	.probe		= pxa25x_udc_probe,
+	.remove		= pxa25x_udc_remove,
+	.suspend	= pxa25x_udc_suspend,
+	.resume		= pxa25x_udc_resume,
+	.driver		= {
+		.name	= "pxa25x-udc",
+	},
+};
+
+module_platform_driver(udc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa25x-udc");
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
new file mode 100644
index 0000000..ccc6b92
--- /dev/null
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Intel PXA25x on-chip full speed USB device controller
+ *
+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA25X_H
+#define __LINUX_USB_GADGET_PXA25X_H
+
+#include <linux/types.h>
+
+/*-------------------------------------------------------------------------*/
+
+/* pxa25x has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
+#define UFNRH_SIR	(1 << 7)	/* SOF interrupt request */
+#define UFNRH_SIM	(1 << 6)	/* SOF interrupt mask */
+#define UFNRH_IPE14	(1 << 5)	/* ISO packet error, ep14 */
+#define UFNRH_IPE9	(1 << 4)	/* ISO packet error, ep9 */
+#define UFNRH_IPE4	(1 << 3)	/* ISO packet error, ep4 */
+
+/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
+#define	UDCCFR		UDC_RES2	/* UDC Control Function Register */
+#define UDCCFR_AREN	(1 << 7)	/* ACK response enable (now) */
+#define UDCCFR_ACM	(1 << 2)	/* ACK control mode (wait for AREN) */
+
+/* latest pxa255 errata define new "must be one" bits in UDCCFR */
+#define	UDCCFR_MB1	(0xff & ~(UDCCFR_AREN|UDCCFR_ACM))
+
+/*-------------------------------------------------------------------------*/
+
+struct pxa25x_udc;
+
+struct pxa25x_ep {
+	struct usb_ep				ep;
+	struct pxa25x_udc			*dev;
+
+	struct list_head			queue;
+	unsigned long				pio_irqs;
+
+	unsigned short				fifo_size;
+	u8					bEndpointAddress;
+	u8					bmAttributes;
+
+	unsigned				stopped : 1;
+	unsigned				dma_fixup : 1;
+
+	/* UDCCS = UDC Control/Status for this EP
+	 * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+	 * UDDR = UDC Endpoint Data Register (the fifo)
+	 * DRCM = DMA Request Channel Map
+	 */
+	u32					regoff_udccs;
+	u32					regoff_ubcr;
+	u32					regoff_uddr;
+};
+
+struct pxa25x_request {
+	struct usb_request			req;
+	struct list_head			queue;
+};
+
+enum ep0_state {
+	EP0_IDLE,
+	EP0_IN_DATA_PHASE,
+	EP0_OUT_DATA_PHASE,
+	EP0_END_XFER,
+	EP0_STALL,
+};
+
+#define EP0_FIFO_SIZE	((unsigned)16)
+#define BULK_FIFO_SIZE	((unsigned)64)
+#define ISO_FIFO_SIZE	((unsigned)256)
+#define INT_FIFO_SIZE	((unsigned)8)
+
+struct udc_stats {
+	struct ep0stats {
+		unsigned long		ops;
+		unsigned long		bytes;
+	} read, write;
+	unsigned long			irqs;
+};
+
+#ifdef CONFIG_USB_PXA25X_SMALL
+/* when memory's tight, SMALL config saves code+data.  */
+#define	PXA_UDC_NUM_ENDPOINTS	3
+#endif
+
+#ifndef	PXA_UDC_NUM_ENDPOINTS
+#define	PXA_UDC_NUM_ENDPOINTS	16
+#endif
+
+struct pxa25x_udc {
+	struct usb_gadget			gadget;
+	struct usb_gadget_driver		*driver;
+
+	enum ep0_state				ep0state;
+	struct udc_stats			stats;
+	unsigned				got_irq : 1,
+						vbus : 1,
+						pullup : 1,
+						has_cfr : 1,
+						req_pending : 1,
+						req_std : 1,
+						req_config : 1,
+						suspended : 1,
+						active : 1;
+
+#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+	struct timer_list			timer;
+
+	struct device				*dev;
+	struct clk				*clk;
+	struct pxa2xx_udc_mach_info		*mach;
+	struct usb_phy				*transceiver;
+	u64					dma_mask;
+	struct pxa25x_ep			ep [PXA_UDC_NUM_ENDPOINTS];
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+	struct dentry				*debugfs_udc;
+#endif
+	void __iomem				*regs;
+};
+#define to_pxa25x(g)	(container_of((g), struct pxa25x_udc, gadget))
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_LUBBOCK
+#include <mach/lubbock.h>
+/* lubbock can also report usb connect/disconnect irqs */
+#endif
+
+static struct pxa25x_udc *the_controller;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Debugging support vanishes in non-debug builds.  DBG_NORMAL should be
+ * mostly silent during normal use/testing, with no timing side-effects.
+ */
+#define DBG_NORMAL	1	/* error paths, device state transitions */
+#define DBG_VERBOSE	2	/* add some success path trace info */
+#define DBG_NOISY	3	/* ... even more: request level */
+#define DBG_VERY_NOISY	4	/* ... even more: packet level */
+
+#define DMSG(stuff...)	pr_debug("udc: " stuff)
+
+#ifdef DEBUG
+
+static const char *state_name[] = {
+	"EP0_IDLE",
+	"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+	"EP0_END_XFER", "EP0_STALL"
+};
+
+#ifdef VERBOSE_DEBUG
+#    define UDC_DEBUG DBG_VERBOSE
+#else
+#    define UDC_DEBUG DBG_NORMAL
+#endif
+
+static void __maybe_unused
+dump_udccr(const char *label)
+{
+	u32	udccr = UDCCR;
+	DMSG("%s %02X =%s%s%s%s%s%s%s%s\n",
+		label, udccr,
+		(udccr & UDCCR_REM) ? " rem" : "",
+		(udccr & UDCCR_RSTIR) ? " rstir" : "",
+		(udccr & UDCCR_SRM) ? " srm" : "",
+		(udccr & UDCCR_SUSIR) ? " susir" : "",
+		(udccr & UDCCR_RESIR) ? " resir" : "",
+		(udccr & UDCCR_RSM) ? " rsm" : "",
+		(udccr & UDCCR_UDA) ? " uda" : "",
+		(udccr & UDCCR_UDE) ? " ude" : "");
+}
+
+static void __maybe_unused
+dump_udccs0(const char *label)
+{
+	u32		udccs0 = UDCCS0;
+
+	DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n",
+		label, state_name[the_controller->ep0state], udccs0,
+		(udccs0 & UDCCS0_SA) ? " sa" : "",
+		(udccs0 & UDCCS0_RNE) ? " rne" : "",
+		(udccs0 & UDCCS0_FST) ? " fst" : "",
+		(udccs0 & UDCCS0_SST) ? " sst" : "",
+		(udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
+		(udccs0 & UDCCS0_FTF) ? " ftf" : "",
+		(udccs0 & UDCCS0_IPR) ? " ipr" : "",
+		(udccs0 & UDCCS0_OPR) ? " opr" : "");
+}
+
+static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *);
+
+static void __maybe_unused
+dump_state(struct pxa25x_udc *dev)
+{
+	u32		tmp;
+	unsigned	i;
+
+	DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+		state_name[dev->ep0state],
+		UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
+	dump_udccr("udccr");
+	if (dev->has_cfr) {
+		tmp = UDCCFR;
+		DMSG("udccfr %02X =%s%s\n", tmp,
+			(tmp & UDCCFR_AREN) ? " aren" : "",
+			(tmp & UDCCFR_ACM) ? " acm" : "");
+	}
+
+	if (!dev->driver) {
+		DMSG("no gadget driver bound\n");
+		return;
+	} else
+		DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+
+	dump_udccs0 ("udccs0");
+	DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
+		dev->stats.write.bytes, dev->stats.write.ops,
+		dev->stats.read.bytes, dev->stats.read.ops);
+
+	for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+		if (dev->ep[i].ep.desc == NULL)
+			continue;
+		DMSG ("udccs%d = %02x\n", i, udc_ep_get_UDCCS(&dev->ep[i]));
+	}
+}
+
+#else
+
+#define	dump_udccr(x)	do{}while(0)
+#define	dump_udccs0(x)	do{}while(0)
+#define	dump_state(x)	do{}while(0)
+
+#define UDC_DEBUG ((unsigned)0)
+
+#endif
+
+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+
+#define ERR(stuff...)		pr_err("udc: " stuff)
+#define WARNING(stuff...)	pr_warn("udc: " stuff)
+#define INFO(stuff...)		pr_info("udc: " stuff)
+
+
+#endif /* __LINUX_USB_GADGET_PXA25X_H */
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
new file mode 100644
index 0000000..0142332
--- /dev/null
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -0,0 +1,2567 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Handles the Intel 27x USB Device Controller (UDC)
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <linux/prefetch.h>
+#include <linux/byteorder/generic.h>
+#include <linux/platform_data/pxa2xx_udc.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/phy.h>
+
+#include "pxa27x_udc.h"
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x
+ * series processors.
+ *
+ * Such controller drivers work with a gadget driver.  The gadget driver
+ * returns descriptors, implements configuration and data protocols used
+ * by the host to interact with this device, and allocates endpoints to
+ * the different protocol interfaces.  The controller driver virtualizes
+ * usb hardware so that the gadget drivers will be more portable.
+ *
+ * This UDC hardware wants to implement a bit too much USB protocol. The
+ * biggest issues are:  that the endpoints have to be set up before the
+ * controller can be enabled (minor, and not uncommon); and each endpoint
+ * can only have one configuration, interface and alternative interface
+ * number (major, and very unusual). Once set up, these cannot be changed
+ * without a controller reset.
+ *
+ * The workaround is to setup all combinations necessary for the gadgets which
+ * will work with this driver. This is done in pxa_udc structure, statically.
+ * See pxa_udc, udc_usb_ep versus pxa_ep, and matching function find_pxa_ep.
+ * (You could modify this if needed.  Some drivers have a "fifo_mode" module
+ * parameter to facilitate such changes.)
+ *
+ * The combinations have been tested with these gadgets :
+ *  - zero gadget
+ *  - file storage gadget
+ *  - ether gadget
+ *
+ * The driver doesn't use DMA, only IO access and IRQ callbacks. No use is
+ * made of UDC's double buffering either. USB "On-The-Go" is not implemented.
+ *
+ * All the requests are handled the same way :
+ *  - the drivers tries to handle the request directly to the IO
+ *  - if the IO fifo is not big enough, the remaining is send/received in
+ *    interrupt handling.
+ */
+
+#define	DRIVER_VERSION	"2008-04-18"
+#define	DRIVER_DESC	"PXA 27x USB Device Controller driver"
+
+static const char driver_name[] = "pxa27x_udc";
+static struct pxa_udc *the_controller;
+
+static void handle_ep(struct pxa_ep *ep);
+
+/*
+ * Debug filesystem
+ */
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+static int state_dbg_show(struct seq_file *s, void *p)
+{
+	struct pxa_udc *udc = s->private;
+	u32 tmp;
+
+	if (!udc->driver)
+		return -ENODEV;
+
+	/* basic device status */
+	seq_printf(s, DRIVER_DESC "\n"
+		   "%s version: %s\n"
+		   "Gadget driver: %s\n",
+		   driver_name, DRIVER_VERSION,
+		   udc->driver ? udc->driver->driver.name : "(none)");
+
+	tmp = udc_readl(udc, UDCCR);
+	seq_printf(s,
+		   "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
+		   tmp,
+		   (tmp & UDCCR_OEN) ? " oen":"",
+		   (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
+		   (tmp & UDCCR_AHNP) ? " rem" : "",
+		   (tmp & UDCCR_BHNP) ? " rstir" : "",
+		   (tmp & UDCCR_DWRE) ? " dwre" : "",
+		   (tmp & UDCCR_SMAC) ? " smac" : "",
+		   (tmp & UDCCR_EMCE) ? " emce" : "",
+		   (tmp & UDCCR_UDR) ? " udr" : "",
+		   (tmp & UDCCR_UDA) ? " uda" : "",
+		   (tmp & UDCCR_UDE) ? " ude" : "",
+		   (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+		   (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+		   (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
+	/* registers for device and ep0 */
+	seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
+		   udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
+	seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
+		   udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
+	seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
+	seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
+		   udc->stats.irqs_reset, udc->stats.irqs_suspend,
+		   udc->stats.irqs_resume, udc->stats.irqs_reconfig);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(state_dbg);
+
+static int queues_dbg_show(struct seq_file *s, void *p)
+{
+	struct pxa_udc *udc = s->private;
+	struct pxa_ep *ep;
+	struct pxa27x_request *req;
+	int i, maxpkt;
+
+	if (!udc->driver)
+		return -ENODEV;
+
+	/* dump endpoint queues */
+	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &udc->pxa_ep[i];
+		maxpkt = ep->fifo_size;
+		seq_printf(s,  "%-12s max_pkt=%d %s\n",
+			   EPNAME(ep), maxpkt, "pio");
+
+		if (list_empty(&ep->queue)) {
+			seq_puts(s, "\t(nothing queued)\n");
+			continue;
+		}
+
+		list_for_each_entry(req, &ep->queue, queue) {
+			seq_printf(s,  "\treq %p len %d/%d buf %p\n",
+				   &req->req, req->req.actual,
+				   req->req.length, req->req.buf);
+		}
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(queues_dbg);
+
+static int eps_dbg_show(struct seq_file *s, void *p)
+{
+	struct pxa_udc *udc = s->private;
+	struct pxa_ep *ep;
+	int i;
+	u32 tmp;
+
+	if (!udc->driver)
+		return -ENODEV;
+
+	ep = &udc->pxa_ep[0];
+	tmp = udc_ep_readl(ep, UDCCSR);
+	seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
+		   tmp,
+		   (tmp & UDCCSR0_SA) ? " sa" : "",
+		   (tmp & UDCCSR0_RNE) ? " rne" : "",
+		   (tmp & UDCCSR0_FST) ? " fst" : "",
+		   (tmp & UDCCSR0_SST) ? " sst" : "",
+		   (tmp & UDCCSR0_DME) ? " dme" : "",
+		   (tmp & UDCCSR0_IPR) ? " ipr" : "",
+		   (tmp & UDCCSR0_OPC) ? " opc" : "");
+	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &udc->pxa_ep[i];
+		tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
+		seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
+			   EPNAME(ep),
+			   ep->stats.in_bytes, ep->stats.in_ops,
+			   ep->stats.out_bytes, ep->stats.out_ops,
+			   ep->stats.irqs,
+			   tmp, udc_ep_readl(ep, UDCCSR),
+			   udc_ep_readl(ep, UDCBCR));
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(eps_dbg);
+
+static void pxa_init_debugfs(struct pxa_udc *udc)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(udc->gadget.name, NULL);
+	udc->debugfs_root = root;
+
+	debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops);
+	debugfs_create_file("queues", 0400, root, udc, &queues_dbg_fops);
+	debugfs_create_file("epstate", 0400, root, udc, &eps_dbg_fops);
+}
+
+static void pxa_cleanup_debugfs(struct pxa_udc *udc)
+{
+	debugfs_remove_recursive(udc->debugfs_root);
+}
+
+#else
+static inline void pxa_init_debugfs(struct pxa_udc *udc)
+{
+}
+
+static inline void pxa_cleanup_debugfs(struct pxa_udc *udc)
+{
+}
+#endif
+
+/**
+ * is_match_usb_pxa - check if usb_ep and pxa_ep match
+ * @udc_usb_ep: usb endpoint
+ * @ep: pxa endpoint
+ * @config: configuration required in pxa_ep
+ * @interface: interface required in pxa_ep
+ * @altsetting: altsetting required in pxa_ep
+ *
+ * Returns 1 if all criteria match between pxa and usb endpoint, 0 otherwise
+ */
+static int is_match_usb_pxa(struct udc_usb_ep *udc_usb_ep, struct pxa_ep *ep,
+		int config, int interface, int altsetting)
+{
+	if (usb_endpoint_num(&udc_usb_ep->desc) != ep->addr)
+		return 0;
+	if (usb_endpoint_dir_in(&udc_usb_ep->desc) != ep->dir_in)
+		return 0;
+	if (usb_endpoint_type(&udc_usb_ep->desc) != ep->type)
+		return 0;
+	if ((ep->config != config) || (ep->interface != interface)
+			|| (ep->alternate != altsetting))
+		return 0;
+	return 1;
+}
+
+/**
+ * find_pxa_ep - find pxa_ep structure matching udc_usb_ep
+ * @udc: pxa udc
+ * @udc_usb_ep: udc_usb_ep structure
+ *
+ * Match udc_usb_ep and all pxa_ep available, to see if one matches.
+ * This is necessary because of the strong pxa hardware restriction requiring
+ * that once pxa endpoints are initialized, their configuration is freezed, and
+ * no change can be made to their address, direction, or in which configuration,
+ * interface or altsetting they are active ... which differs from more usual
+ * models which have endpoints be roughly just addressable fifos, and leave
+ * configuration events up to gadget drivers (like all control messages).
+ *
+ * Note that there is still a blurred point here :
+ *   - we rely on UDCCR register "active interface" and "active altsetting".
+ *     This is a nonsense in regard of USB spec, where multiple interfaces are
+ *     active at the same time.
+ *   - if we knew for sure that the pxa can handle multiple interface at the
+ *     same time, assuming Intel's Developer Guide is wrong, this function
+ *     should be reviewed, and a cache of couples (iface, altsetting) should
+ *     be kept in the pxa_udc structure. In this case this function would match
+ *     against the cache of couples instead of the "last altsetting" set up.
+ *
+ * Returns the matched pxa_ep structure or NULL if none found
+ */
+static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc,
+		struct udc_usb_ep *udc_usb_ep)
+{
+	int i;
+	struct pxa_ep *ep;
+	int cfg = udc->config;
+	int iface = udc->last_interface;
+	int alt = udc->last_alternate;
+
+	if (udc_usb_ep == &udc->udc_usb_ep[0])
+		return &udc->pxa_ep[0];
+
+	for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &udc->pxa_ep[i];
+		if (is_match_usb_pxa(udc_usb_ep, ep, cfg, iface, alt))
+			return ep;
+	}
+	return NULL;
+}
+
+/**
+ * update_pxa_ep_matches - update pxa_ep cached values in all udc_usb_ep
+ * @udc: pxa udc
+ *
+ * Context: in_interrupt()
+ *
+ * Updates all pxa_ep fields in udc_usb_ep structures, if this field was
+ * previously set up (and is not NULL). The update is necessary is a
+ * configuration change or altsetting change was issued by the USB host.
+ */
+static void update_pxa_ep_matches(struct pxa_udc *udc)
+{
+	int i;
+	struct udc_usb_ep *udc_usb_ep;
+
+	for (i = 1; i < NR_USB_ENDPOINTS; i++) {
+		udc_usb_ep = &udc->udc_usb_ep[i];
+		if (udc_usb_ep->pxa_ep)
+			udc_usb_ep->pxa_ep = find_pxa_ep(udc, udc_usb_ep);
+	}
+}
+
+/**
+ * pio_irq_enable - Enables irq generation for one endpoint
+ * @ep: udc endpoint
+ */
+static void pio_irq_enable(struct pxa_ep *ep)
+{
+	struct pxa_udc *udc = ep->dev;
+	int index = EPIDX(ep);
+	u32 udcicr0 = udc_readl(udc, UDCICR0);
+	u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+	if (index < 16)
+		udc_writel(udc, UDCICR0, udcicr0 | (3 << (index * 2)));
+	else
+		udc_writel(udc, UDCICR1, udcicr1 | (3 << ((index - 16) * 2)));
+}
+
+/**
+ * pio_irq_disable - Disables irq generation for one endpoint
+ * @ep: udc endpoint
+ */
+static void pio_irq_disable(struct pxa_ep *ep)
+{
+	struct pxa_udc *udc = ep->dev;
+	int index = EPIDX(ep);
+	u32 udcicr0 = udc_readl(udc, UDCICR0);
+	u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+	if (index < 16)
+		udc_writel(udc, UDCICR0, udcicr0 & ~(3 << (index * 2)));
+	else
+		udc_writel(udc, UDCICR1, udcicr1 & ~(3 << ((index - 16) * 2)));
+}
+
+/**
+ * udc_set_mask_UDCCR - set bits in UDCCR
+ * @udc: udc device
+ * @mask: bits to set in UDCCR
+ *
+ * Sets bits in UDCCR, leaving DME and FST bits as they were.
+ */
+static inline void udc_set_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	udc_writel(udc, UDCCR,
+			(udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS));
+}
+
+/**
+ * udc_clear_mask_UDCCR - clears bits in UDCCR
+ * @udc: udc device
+ * @mask: bit to clear in UDCCR
+ *
+ * Clears bits in UDCCR, leaving DME and FST bits as they were.
+ */
+static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	udc_writel(udc, UDCCR,
+			(udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS));
+}
+
+/**
+ * ep_write_UDCCSR - set bits in UDCCSR
+ * @udc: udc device
+ * @mask: bits to set in UDCCR
+ *
+ * Sets bits in UDCCSR (UDCCSR0 and UDCCSR*).
+ *
+ * A specific case is applied to ep0 : the ACM bit is always set to 1, for
+ * SET_INTERFACE and SET_CONFIGURATION.
+ */
+static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask)
+{
+	if (is_ep0(ep))
+		mask |= UDCCSR0_ACM;
+	udc_ep_writel(ep, UDCCSR, mask);
+}
+
+/**
+ * ep_count_bytes_remain - get how many bytes in udc endpoint
+ * @ep: udc endpoint
+ *
+ * Returns number of bytes in OUT fifos. Broken for IN fifos (-EOPNOTSUPP)
+ */
+static int ep_count_bytes_remain(struct pxa_ep *ep)
+{
+	if (ep->dir_in)
+		return -EOPNOTSUPP;
+	return udc_ep_readl(ep, UDCBCR) & 0x3ff;
+}
+
+/**
+ * ep_is_empty - checks if ep has byte ready for reading
+ * @ep: udc endpoint
+ *
+ * If endpoint is the control endpoint, checks if there are bytes in the
+ * control endpoint fifo. If endpoint is a data endpoint, checks if bytes
+ * are ready for reading on OUT endpoint.
+ *
+ * Returns 0 if ep not empty, 1 if ep empty, -EOPNOTSUPP if IN endpoint
+ */
+static int ep_is_empty(struct pxa_ep *ep)
+{
+	int ret;
+
+	if (!is_ep0(ep) && ep->dir_in)
+		return -EOPNOTSUPP;
+	if (is_ep0(ep))
+		ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR0_RNE);
+	else
+		ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNE);
+	return ret;
+}
+
+/**
+ * ep_is_full - checks if ep has place to write bytes
+ * @ep: udc endpoint
+ *
+ * If endpoint is not the control endpoint and is an IN endpoint, checks if
+ * there is place to write bytes into the endpoint.
+ *
+ * Returns 0 if ep not full, 1 if ep full, -EOPNOTSUPP if OUT endpoint
+ */
+static int ep_is_full(struct pxa_ep *ep)
+{
+	if (is_ep0(ep))
+		return (udc_ep_readl(ep, UDCCSR) & UDCCSR0_IPR);
+	if (!ep->dir_in)
+		return -EOPNOTSUPP;
+	return (!(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNF));
+}
+
+/**
+ * epout_has_pkt - checks if OUT endpoint fifo has a packet available
+ * @ep: pxa endpoint
+ *
+ * Returns 1 if a complete packet is available, 0 if not, -EOPNOTSUPP for IN ep.
+ */
+static int epout_has_pkt(struct pxa_ep *ep)
+{
+	if (!is_ep0(ep) && ep->dir_in)
+		return -EOPNOTSUPP;
+	if (is_ep0(ep))
+		return (udc_ep_readl(ep, UDCCSR) & UDCCSR0_OPC);
+	return (udc_ep_readl(ep, UDCCSR) & UDCCSR_PC);
+}
+
+/**
+ * set_ep0state - Set ep0 automata state
+ * @dev: udc device
+ * @state: state
+ */
+static void set_ep0state(struct pxa_udc *udc, int state)
+{
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+	char *old_stname = EP0_STNAME(udc);
+
+	udc->ep0state = state;
+	ep_dbg(ep, "state=%s->%s, udccsr0=0x%03x, udcbcr=%d\n", old_stname,
+		EP0_STNAME(udc), udc_ep_readl(ep, UDCCSR),
+		udc_ep_readl(ep, UDCBCR));
+}
+
+/**
+ * ep0_idle - Put control endpoint into idle state
+ * @dev: udc device
+ */
+static void ep0_idle(struct pxa_udc *dev)
+{
+	set_ep0state(dev, WAIT_FOR_SETUP);
+}
+
+/**
+ * inc_ep_stats_reqs - Update ep stats counts
+ * @ep: physical endpoint
+ * @req: usb request
+ * @is_in: ep direction (USB_DIR_IN or 0)
+ *
+ */
+static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in)
+{
+	if (is_in)
+		ep->stats.in_ops++;
+	else
+		ep->stats.out_ops++;
+}
+
+/**
+ * inc_ep_stats_bytes - Update ep stats counts
+ * @ep: physical endpoint
+ * @count: bytes transferred on endpoint
+ * @is_in: ep direction (USB_DIR_IN or 0)
+ */
+static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in)
+{
+	if (is_in)
+		ep->stats.in_bytes += count;
+	else
+		ep->stats.out_bytes += count;
+}
+
+/**
+ * pxa_ep_setup - Sets up an usb physical endpoint
+ * @ep: pxa27x physical endpoint
+ *
+ * Find the physical pxa27x ep, and setup its UDCCR
+ */
+static void pxa_ep_setup(struct pxa_ep *ep)
+{
+	u32 new_udccr;
+
+	new_udccr = ((ep->config << UDCCONR_CN_S) & UDCCONR_CN)
+		| ((ep->interface << UDCCONR_IN_S) & UDCCONR_IN)
+		| ((ep->alternate << UDCCONR_AISN_S) & UDCCONR_AISN)
+		| ((EPADDR(ep) << UDCCONR_EN_S) & UDCCONR_EN)
+		| ((EPXFERTYPE(ep) << UDCCONR_ET_S) & UDCCONR_ET)
+		| ((ep->dir_in) ? UDCCONR_ED : 0)
+		| ((ep->fifo_size << UDCCONR_MPS_S) & UDCCONR_MPS)
+		| UDCCONR_EE;
+
+	udc_ep_writel(ep, UDCCR, new_udccr);
+}
+
+/**
+ * pxa_eps_setup - Sets up all usb physical endpoints
+ * @dev: udc device
+ *
+ * Setup all pxa physical endpoints, except ep0
+ */
+static void pxa_eps_setup(struct pxa_udc *dev)
+{
+	unsigned int i;
+
+	dev_dbg(dev->dev, "%s: dev=%p\n", __func__, dev);
+
+	for (i = 1; i < NR_PXA_ENDPOINTS; i++)
+		pxa_ep_setup(&dev->pxa_ep[i]);
+}
+
+/**
+ * pxa_ep_alloc_request - Allocate usb request
+ * @_ep: usb endpoint
+ * @gfp_flags:
+ *
+ * For the pxa27x, these can just wrap kmalloc/kfree.  gadget drivers
+ * must still pass correctly initialized endpoints, since other controller
+ * drivers may care about how it's currently set up (dma issues etc).
+  */
+static struct usb_request *
+pxa_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+	struct pxa27x_request *req;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	req->in_use = 0;
+	req->udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+	return &req->req;
+}
+
+/**
+ * pxa_ep_free_request - Free usb request
+ * @_ep: usb endpoint
+ * @_req: usb request
+ *
+ * Wrapper around kfree to free _req
+ */
+static void pxa_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa27x_request *req;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+/**
+ * ep_add_request - add a request to the endpoint's queue
+ * @ep: usb endpoint
+ * @req: usb request
+ *
+ * Context: ep->lock held
+ *
+ * Queues the request in the endpoint's queue, and enables the interrupts
+ * on the endpoint.
+ */
+static void ep_add_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	if (unlikely(!req))
+		return;
+	ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+		req->req.length, udc_ep_readl(ep, UDCCSR));
+
+	req->in_use = 1;
+	list_add_tail(&req->queue, &ep->queue);
+	pio_irq_enable(ep);
+}
+
+/**
+ * ep_del_request - removes a request from the endpoint's queue
+ * @ep: usb endpoint
+ * @req: usb request
+ *
+ * Context: ep->lock held
+ *
+ * Unqueue the request from the endpoint's queue. If there are no more requests
+ * on the endpoint, and if it's not the control endpoint, interrupts are
+ * disabled on the endpoint.
+ */
+static void ep_del_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	if (unlikely(!req))
+		return;
+	ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+		req->req.length, udc_ep_readl(ep, UDCCSR));
+
+	list_del_init(&req->queue);
+	req->in_use = 0;
+	if (!is_ep0(ep) && list_empty(&ep->queue))
+		pio_irq_disable(ep);
+}
+
+/**
+ * req_done - Complete an usb request
+ * @ep: pxa physical endpoint
+ * @req: pxa request
+ * @status: usb request status sent to gadget API
+ * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held
+ *
+ * Context: ep->lock held if flags not NULL, else ep->lock released
+ *
+ * Retire a pxa27x usb request. Endpoint must be locked.
+ */
+static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status,
+	unsigned long *pflags)
+{
+	unsigned long	flags;
+
+	ep_del_request(ep, req);
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN)
+		ep_dbg(ep, "complete req %p stat %d len %u/%u\n",
+			&req->req, status,
+			req->req.actual, req->req.length);
+
+	if (pflags)
+		spin_unlock_irqrestore(&ep->lock, *pflags);
+	local_irq_save(flags);
+	usb_gadget_giveback_request(&req->udc_usb_ep->usb_ep, &req->req);
+	local_irq_restore(flags);
+	if (pflags)
+		spin_lock_irqsave(&ep->lock, *pflags);
+}
+
+/**
+ * ep_end_out_req - Ends endpoint OUT request
+ * @ep: physical endpoint
+ * @req: pxa request
+ * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held
+ *
+ * Context: ep->lock held or released (see req_done())
+ *
+ * Ends endpoint OUT request (completes usb request).
+ */
+static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req,
+	unsigned long *pflags)
+{
+	inc_ep_stats_reqs(ep, !USB_DIR_IN);
+	req_done(ep, req, 0, pflags);
+}
+
+/**
+ * ep0_end_out_req - Ends control endpoint OUT request (ends data stage)
+ * @ep: physical endpoint
+ * @req: pxa request
+ * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held
+ *
+ * Context: ep->lock held or released (see req_done())
+ *
+ * Ends control endpoint OUT request (completes usb request), and puts
+ * control endpoint into idle state
+ */
+static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req,
+	unsigned long *pflags)
+{
+	set_ep0state(ep->dev, OUT_STATUS_STAGE);
+	ep_end_out_req(ep, req, pflags);
+	ep0_idle(ep->dev);
+}
+
+/**
+ * ep_end_in_req - Ends endpoint IN request
+ * @ep: physical endpoint
+ * @req: pxa request
+ * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held
+ *
+ * Context: ep->lock held or released (see req_done())
+ *
+ * Ends endpoint IN request (completes usb request).
+ */
+static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req,
+	unsigned long *pflags)
+{
+	inc_ep_stats_reqs(ep, USB_DIR_IN);
+	req_done(ep, req, 0, pflags);
+}
+
+/**
+ * ep0_end_in_req - Ends control endpoint IN request (ends data stage)
+ * @ep: physical endpoint
+ * @req: pxa request
+ * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held
+ *
+ * Context: ep->lock held or released (see req_done())
+ *
+ * Ends control endpoint IN request (completes usb request), and puts
+ * control endpoint into status state
+ */
+static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req,
+	unsigned long *pflags)
+{
+	set_ep0state(ep->dev, IN_STATUS_STAGE);
+	ep_end_in_req(ep, req, pflags);
+}
+
+/**
+ * nuke - Dequeue all requests
+ * @ep: pxa endpoint
+ * @status: usb request status
+ *
+ * Context: ep->lock released
+ *
+ * Dequeues all requests on an endpoint. As a side effect, interrupts will be
+ * disabled on that endpoint (because no more requests).
+ */
+static void nuke(struct pxa_ep *ep, int status)
+{
+	struct pxa27x_request	*req;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+		req_done(ep, req, status, &flags);
+	}
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/**
+ * read_packet - transfer 1 packet from an OUT endpoint into request
+ * @ep: pxa physical endpoint
+ * @req: usb request
+ *
+ * Takes bytes from OUT endpoint and transfers them info the usb request.
+ * If there is less space in request than bytes received in OUT endpoint,
+ * bytes are left in the OUT endpoint.
+ *
+ * Returns how many bytes were actually transferred
+ */
+static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	u32 *buf;
+	int bytes_ep, bufferspace, count, i;
+
+	bytes_ep = ep_count_bytes_remain(ep);
+	bufferspace = req->req.length - req->req.actual;
+
+	buf = (u32 *)(req->req.buf + req->req.actual);
+	prefetchw(buf);
+
+	if (likely(!ep_is_empty(ep)))
+		count = min(bytes_ep, bufferspace);
+	else /* zlp */
+		count = 0;
+
+	for (i = count; i > 0; i -= 4)
+		*buf++ = udc_ep_readl(ep, UDCDR);
+	req->req.actual += count;
+
+	ep_write_UDCCSR(ep, UDCCSR_PC);
+
+	return count;
+}
+
+/**
+ * write_packet - transfer 1 packet from request into an IN endpoint
+ * @ep: pxa physical endpoint
+ * @req: usb request
+ * @max: max bytes that fit into endpoint
+ *
+ * Takes bytes from usb request, and transfers them into the physical
+ * endpoint. If there are no bytes to transfer, doesn't write anything
+ * to physical endpoint.
+ *
+ * Returns how many bytes were actually transferred.
+ */
+static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req,
+			unsigned int max)
+{
+	int length, count, remain, i;
+	u32 *buf;
+	u8 *buf_8;
+
+	buf = (u32 *)(req->req.buf + req->req.actual);
+	prefetch(buf);
+
+	length = min(req->req.length - req->req.actual, max);
+	req->req.actual += length;
+
+	remain = length & 0x3;
+	count = length & ~(0x3);
+	for (i = count; i > 0 ; i -= 4)
+		udc_ep_writel(ep, UDCDR, *buf++);
+
+	buf_8 = (u8 *)buf;
+	for (i = remain; i > 0; i--)
+		udc_ep_writeb(ep, UDCDR, *buf_8++);
+
+	ep_vdbg(ep, "length=%d+%d, udccsr=0x%03x\n", count, remain,
+		udc_ep_readl(ep, UDCCSR));
+
+	return length;
+}
+
+/**
+ * read_fifo - Transfer packets from OUT endpoint into usb request
+ * @ep: pxa physical endpoint
+ * @req: usb request
+ *
+ * Context: callable when in_interrupt()
+ *
+ * Unload as many packets as possible from the fifo we use for usb OUT
+ * transfers and put them into the request. Caller should have made sure
+ * there's at least one packet ready.
+ * Doesn't complete the request, that's the caller's job
+ *
+ * Returns 1 if the request completed, 0 otherwise
+ */
+static int read_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	int count, is_short, completed = 0;
+
+	while (epout_has_pkt(ep)) {
+		count = read_packet(ep, req);
+		inc_ep_stats_bytes(ep, count, !USB_DIR_IN);
+
+		is_short = (count < ep->fifo_size);
+		ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+			udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+			&req->req, req->req.actual, req->req.length);
+
+		/* completion */
+		if (is_short || req->req.actual == req->req.length) {
+			completed = 1;
+			break;
+		}
+		/* finished that packet.  the next one may be waiting... */
+	}
+	return completed;
+}
+
+/**
+ * write_fifo - transfer packets from usb request into an IN endpoint
+ * @ep: pxa physical endpoint
+ * @req: pxa usb request
+ *
+ * Write to an IN endpoint fifo, as many packets as possible.
+ * irqs will use this to write the rest later.
+ * caller guarantees at least one packet buffer is ready (or a zlp).
+ * Doesn't complete the request, that's the caller's job
+ *
+ * Returns 1 if request fully transferred, 0 if partial transfer
+ */
+static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	unsigned max;
+	int count, is_short, is_last = 0, completed = 0, totcount = 0;
+	u32 udccsr;
+
+	max = ep->fifo_size;
+	do {
+		udccsr = udc_ep_readl(ep, UDCCSR);
+		if (udccsr & UDCCSR_PC) {
+			ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
+				udccsr);
+			ep_write_UDCCSR(ep, UDCCSR_PC);
+		}
+		if (udccsr & UDCCSR_TRN) {
+			ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n",
+				udccsr);
+			ep_write_UDCCSR(ep, UDCCSR_TRN);
+		}
+
+		count = write_packet(ep, req, max);
+		inc_ep_stats_bytes(ep, count, USB_DIR_IN);
+		totcount += count;
+
+		/* last packet is usually short (or a zlp) */
+		if (unlikely(count < max)) {
+			is_last = 1;
+			is_short = 1;
+		} else {
+			if (likely(req->req.length > req->req.actual)
+					|| req->req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+			/* interrupt/iso maxpacket may not fill the fifo */
+			is_short = unlikely(max < ep->fifo_size);
+		}
+
+		if (is_short)
+			ep_write_UDCCSR(ep, UDCCSR_SP);
+
+		/* requests complete when all IN data is in the FIFO */
+		if (is_last) {
+			completed = 1;
+			break;
+		}
+	} while (!ep_is_full(ep));
+
+	ep_dbg(ep, "wrote count:%d bytes%s%s, left:%d req=%p\n",
+			totcount, is_last ? "/L" : "", is_short ? "/S" : "",
+			req->req.length - req->req.actual, &req->req);
+
+	return completed;
+}
+
+/**
+ * read_ep0_fifo - Transfer packets from control endpoint into usb request
+ * @ep: control endpoint
+ * @req: pxa usb request
+ *
+ * Special ep0 version of the above read_fifo. Reads as many bytes from control
+ * endpoint as can be read, and stores them into usb request (limited by request
+ * maximum length).
+ *
+ * Returns 0 if usb request only partially filled, 1 if fully filled
+ */
+static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	int count, is_short, completed = 0;
+
+	while (epout_has_pkt(ep)) {
+		count = read_packet(ep, req);
+		ep_write_UDCCSR(ep, UDCCSR0_OPC);
+		inc_ep_stats_bytes(ep, count, !USB_DIR_IN);
+
+		is_short = (count < ep->fifo_size);
+		ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+			udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+			&req->req, req->req.actual, req->req.length);
+
+		if (is_short || req->req.actual >= req->req.length) {
+			completed = 1;
+			break;
+		}
+	}
+
+	return completed;
+}
+
+/**
+ * write_ep0_fifo - Send a request to control endpoint (ep0 in)
+ * @ep: control endpoint
+ * @req: request
+ *
+ * Context: callable when in_interrupt()
+ *
+ * Sends a request (or a part of the request) to the control endpoint (ep0 in).
+ * If the request doesn't fit, the remaining part will be sent from irq.
+ * The request is considered fully written only if either :
+ *   - last write transferred all remaining bytes, but fifo was not fully filled
+ *   - last write was a 0 length write
+ *
+ * Returns 1 if request fully written, 0 if request only partially sent
+ */
+static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+	unsigned	count;
+	int		is_last, is_short;
+
+	count = write_packet(ep, req, EP0_FIFO_SIZE);
+	inc_ep_stats_bytes(ep, count, USB_DIR_IN);
+
+	is_short = (count < EP0_FIFO_SIZE);
+	is_last = ((count == 0) || (count < EP0_FIFO_SIZE));
+
+	/* Sends either a short packet or a 0 length packet */
+	if (unlikely(is_short))
+		ep_write_UDCCSR(ep, UDCCSR0_IPR);
+
+	ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n",
+		count, is_short ? "/S" : "", is_last ? "/L" : "",
+		req->req.length - req->req.actual,
+		&req->req, udc_ep_readl(ep, UDCCSR));
+
+	return is_last;
+}
+
+/**
+ * pxa_ep_queue - Queue a request into an IN endpoint
+ * @_ep: usb endpoint
+ * @_req: usb request
+ * @gfp_flags: flags
+ *
+ * Context: normally called when !in_interrupt, but callable when in_interrupt()
+ * in the special case of ep0 setup :
+ *   (irq->handle_ep0_ctrl_req->gadget_setup->pxa_ep_queue)
+ *
+ * Returns 0 if succedeed, error otherwise
+ */
+static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa_ep		*ep;
+	struct pxa27x_request	*req;
+	struct pxa_udc		*dev;
+	unsigned long		flags;
+	int			rc = 0;
+	int			is_first_req;
+	unsigned		length;
+	int			recursion_detected;
+
+	req = container_of(_req, struct pxa27x_request, req);
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+	if (unlikely(!_req || !_req->complete || !_req->buf))
+		return -EINVAL;
+
+	if (unlikely(!_ep))
+		return -EINVAL;
+
+	ep = udc_usb_ep->pxa_ep;
+	if (unlikely(!ep))
+		return -EINVAL;
+
+	dev = ep->dev;
+	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		ep_dbg(ep, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	/* iso is always one packet per request, that's the only way
+	 * we can report per-packet status.  that also helps with dma.
+	 */
+	if (unlikely(EPXFERTYPE_is_ISO(ep)
+			&& req->req.length > ep->fifo_size))
+		return -EMSGSIZE;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	recursion_detected = ep->in_handle_ep;
+
+	is_first_req = list_empty(&ep->queue);
+	ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n",
+			_req, is_first_req ? "yes" : "no",
+			_req->length, _req->buf);
+
+	if (!ep->enabled) {
+		_req->status = -ESHUTDOWN;
+		rc = -ESHUTDOWN;
+		goto out_locked;
+	}
+
+	if (req->in_use) {
+		ep_err(ep, "refusing to queue req %p (already queued)\n", req);
+		goto out_locked;
+	}
+
+	length = _req->length;
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	ep_add_request(ep, req);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	if (is_ep0(ep)) {
+		switch (dev->ep0state) {
+		case WAIT_ACK_SET_CONF_INTERF:
+			if (length == 0) {
+				ep_end_in_req(ep, req, NULL);
+			} else {
+				ep_err(ep, "got a request of %d bytes while"
+					"in state WAIT_ACK_SET_CONF_INTERF\n",
+					length);
+				ep_del_request(ep, req);
+				rc = -EL2HLT;
+			}
+			ep0_idle(ep->dev);
+			break;
+		case IN_DATA_STAGE:
+			if (!ep_is_full(ep))
+				if (write_ep0_fifo(ep, req))
+					ep0_end_in_req(ep, req, NULL);
+			break;
+		case OUT_DATA_STAGE:
+			if ((length == 0) || !epout_has_pkt(ep))
+				if (read_ep0_fifo(ep, req))
+					ep0_end_out_req(ep, req, NULL);
+			break;
+		default:
+			ep_err(ep, "odd state %s to send me a request\n",
+				EP0_STNAME(ep->dev));
+			ep_del_request(ep, req);
+			rc = -EL2HLT;
+			break;
+		}
+	} else {
+		if (!recursion_detected)
+			handle_ep(ep);
+	}
+
+out:
+	return rc;
+out_locked:
+	spin_unlock_irqrestore(&ep->lock, flags);
+	goto out;
+}
+
+/**
+ * pxa_ep_dequeue - Dequeue one request
+ * @_ep: usb endpoint
+ * @_req: usb request
+ *
+ * Return 0 if no error, -EINVAL or -ECONNRESET otherwise
+ */
+static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa27x_request	*req;
+	unsigned long		flags;
+	int			rc = -EINVAL;
+
+	if (!_ep)
+		return rc;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return rc;
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			rc = 0;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+	if (!rc)
+		req_done(ep, req, -ECONNRESET, NULL);
+	return rc;
+}
+
+/**
+ * pxa_ep_set_halt - Halts operations on one endpoint
+ * @_ep: usb endpoint
+ * @value:
+ *
+ * Returns 0 if no error, -EINVAL, -EROFS, -EAGAIN otherwise
+ */
+static int pxa_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	unsigned long flags;
+	int rc;
+
+
+	if (!_ep)
+		return -EINVAL;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return -EINVAL;
+
+	if (value == 0) {
+		/*
+		 * This path (reset toggle+halt) is needed to implement
+		 * SET_INTERFACE on normal hardware.  but it can't be
+		 * done from software on the PXA UDC, and the hardware
+		 * forgets to do it as part of SET_INTERFACE automagic.
+		 */
+		ep_dbg(ep, "only host can clear halt\n");
+		return -EROFS;
+	}
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	rc = -EAGAIN;
+	if (ep->dir_in	&& (ep_is_full(ep) || !list_empty(&ep->queue)))
+		goto out;
+
+	/* FST, FEF bits are the same for control and non control endpoints */
+	rc = 0;
+	ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF);
+	if (is_ep0(ep))
+		set_ep0state(ep->dev, STALL);
+
+out:
+	spin_unlock_irqrestore(&ep->lock, flags);
+	return rc;
+}
+
+/**
+ * pxa_ep_fifo_status - Get how many bytes in physical endpoint
+ * @_ep: usb endpoint
+ *
+ * Returns number of bytes in OUT fifos. Broken for IN fifos.
+ */
+static int pxa_ep_fifo_status(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+
+	if (!_ep)
+		return -ENODEV;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return -ENODEV;
+
+	if (ep->dir_in)
+		return -EOPNOTSUPP;
+	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || ep_is_empty(ep))
+		return 0;
+	else
+		return ep_count_bytes_remain(ep) + 1;
+}
+
+/**
+ * pxa_ep_fifo_flush - Flushes one endpoint
+ * @_ep: usb endpoint
+ *
+ * Discards all data in one endpoint(IN or OUT), except control endpoint.
+ */
+static void pxa_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	unsigned long		flags;
+
+	if (!_ep)
+		return;
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep))
+		return;
+
+	spin_lock_irqsave(&ep->lock, flags);
+
+	if (unlikely(!list_empty(&ep->queue)))
+		ep_dbg(ep, "called while queue list not empty\n");
+	ep_dbg(ep, "called\n");
+
+	/* for OUT, just read and discard the FIFO contents. */
+	if (!ep->dir_in) {
+		while (!ep_is_empty(ep))
+			udc_ep_readl(ep, UDCDR);
+	} else {
+		/* most IN status is the same, but ISO can't stall */
+		ep_write_UDCCSR(ep,
+				UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN
+				| (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST));
+	}
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/**
+ * pxa_ep_enable - Enables usb endpoint
+ * @_ep: usb endpoint
+ * @desc: usb endpoint descriptor
+ *
+ * Nothing much to do here, as ep configuration is done once and for all
+ * before udc is enabled. After udc enable, no physical endpoint configuration
+ * can be changed.
+ * Function makes sanity checks and flushes the endpoint.
+ */
+static int pxa_ep_enable(struct usb_ep *_ep,
+	const struct usb_endpoint_descriptor *desc)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+	struct pxa_udc		*udc;
+
+	if (!_ep || !desc)
+		return -EINVAL;
+
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	if (udc_usb_ep->pxa_ep) {
+		ep = udc_usb_ep->pxa_ep;
+		ep_warn(ep, "usb_ep %s already enabled, doing nothing\n",
+			_ep->name);
+	} else {
+		ep = find_pxa_ep(udc_usb_ep->dev, udc_usb_ep);
+	}
+
+	if (!ep || is_ep0(ep)) {
+		dev_err(udc_usb_ep->dev->dev,
+			"unable to match pxa_ep for ep %s\n",
+			_ep->name);
+		return -EINVAL;
+	}
+
+	if ((desc->bDescriptorType != USB_DT_ENDPOINT)
+			|| (ep->type != usb_endpoint_type(desc))) {
+		ep_err(ep, "type mismatch\n");
+		return -EINVAL;
+	}
+
+	if (ep->fifo_size < usb_endpoint_maxp(desc)) {
+		ep_err(ep, "bad maxpacket\n");
+		return -ERANGE;
+	}
+
+	udc_usb_ep->pxa_ep = ep;
+	udc = ep->dev;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		ep_err(ep, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	ep->enabled = 1;
+
+	/* flush fifo (mostly for OUT buffers) */
+	pxa_ep_fifo_flush(_ep);
+
+	ep_dbg(ep, "enabled\n");
+	return 0;
+}
+
+/**
+ * pxa_ep_disable - Disable usb endpoint
+ * @_ep: usb endpoint
+ *
+ * Same as for pxa_ep_enable, no physical endpoint configuration can be
+ * changed.
+ * Function flushes the endpoint and related requests.
+ */
+static int pxa_ep_disable(struct usb_ep *_ep)
+{
+	struct pxa_ep		*ep;
+	struct udc_usb_ep	*udc_usb_ep;
+
+	if (!_ep)
+		return -EINVAL;
+
+	udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+	ep = udc_usb_ep->pxa_ep;
+	if (!ep || is_ep0(ep) || !list_empty(&ep->queue))
+		return -EINVAL;
+
+	ep->enabled = 0;
+	nuke(ep, -ESHUTDOWN);
+
+	pxa_ep_fifo_flush(_ep);
+	udc_usb_ep->pxa_ep = NULL;
+
+	ep_dbg(ep, "disabled\n");
+	return 0;
+}
+
+static const struct usb_ep_ops pxa_ep_ops = {
+	.enable		= pxa_ep_enable,
+	.disable	= pxa_ep_disable,
+
+	.alloc_request	= pxa_ep_alloc_request,
+	.free_request	= pxa_ep_free_request,
+
+	.queue		= pxa_ep_queue,
+	.dequeue	= pxa_ep_dequeue,
+
+	.set_halt	= pxa_ep_set_halt,
+	.fifo_status	= pxa_ep_fifo_status,
+	.fifo_flush	= pxa_ep_fifo_flush,
+};
+
+/**
+ * dplus_pullup - Connect or disconnect pullup resistor to D+ pin
+ * @udc: udc device
+ * @on: 0 if disconnect pullup resistor, 1 otherwise
+ * Context: any
+ *
+ * Handle D+ pullup resistor, make the device visible to the usb bus, and
+ * declare it as a full speed usb device
+ */
+static void dplus_pullup(struct pxa_udc *udc, int on)
+{
+	if (udc->gpiod) {
+		gpiod_set_value(udc->gpiod, on);
+	} else if (udc->udc_command) {
+		if (on)
+			udc->udc_command(PXA2XX_UDC_CMD_CONNECT);
+		else
+			udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+	}
+	udc->pullup_on = on;
+}
+
+/**
+ * pxa_udc_get_frame - Returns usb frame number
+ * @_gadget: usb gadget
+ */
+static int pxa_udc_get_frame(struct usb_gadget *_gadget)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	return (udc_readl(udc, UDCFNR) & 0x7ff);
+}
+
+/**
+ * pxa_udc_wakeup - Force udc device out of suspend
+ * @_gadget: usb gadget
+ *
+ * Returns 0 if successful, error code otherwise
+ */
+static int pxa_udc_wakeup(struct usb_gadget *_gadget)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	/* host may not have enabled remote wakeup */
+	if ((udc_readl(udc, UDCCR) & UDCCR_DWRE) == 0)
+		return -EHOSTUNREACH;
+	udc_set_mask_UDCCR(udc, UDCCR_UDR);
+	return 0;
+}
+
+static void udc_enable(struct pxa_udc *udc);
+static void udc_disable(struct pxa_udc *udc);
+
+/**
+ * should_enable_udc - Tells if UDC should be enabled
+ * @udc: udc device
+ * Context: any
+ *
+ * The UDC should be enabled if :
+
+ *  - the pullup resistor is connected
+ *  - and a gadget driver is bound
+ *  - and vbus is sensed (or no vbus sense is available)
+ *
+ * Returns 1 if UDC should be enabled, 0 otherwise
+ */
+static int should_enable_udc(struct pxa_udc *udc)
+{
+	int put_on;
+
+	put_on = ((udc->pullup_on) && (udc->driver));
+	put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver)));
+	return put_on;
+}
+
+/**
+ * should_disable_udc - Tells if UDC should be disabled
+ * @udc: udc device
+ * Context: any
+ *
+ * The UDC should be disabled if :
+ *  - the pullup resistor is not connected
+ *  - or no gadget driver is bound
+ *  - or no vbus is sensed (when vbus sesing is available)
+ *
+ * Returns 1 if UDC should be disabled
+ */
+static int should_disable_udc(struct pxa_udc *udc)
+{
+	int put_off;
+
+	put_off = ((!udc->pullup_on) || (!udc->driver));
+	put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver)));
+	return put_off;
+}
+
+/**
+ * pxa_udc_pullup - Offer manual D+ pullup control
+ * @_gadget: usb gadget using the control
+ * @is_active: 0 if disconnect, else connect D+ pullup resistor
+ * Context: !in_interrupt()
+ *
+ * Returns 0 if OK, -EOPNOTSUPP if udc driver doesn't handle D+ pullup
+ */
+static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	if (!udc->gpiod && !udc->udc_command)
+		return -EOPNOTSUPP;
+
+	dplus_pullup(udc, is_active);
+
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	if (should_disable_udc(udc))
+		udc_disable(udc);
+	return 0;
+}
+
+/**
+ * pxa_udc_vbus_session - Called by external transceiver to enable/disable udc
+ * @_gadget: usb gadget
+ * @is_active: 0 if should disable the udc, 1 if should enable
+ *
+ * Enables the udc, and optionnaly activates D+ pullup resistor. Or disables the
+ * udc, and deactivates D+ pullup resistor.
+ *
+ * Returns 0
+ */
+static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+	udc->vbus_sensed = is_active;
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	if (should_disable_udc(udc))
+		udc_disable(udc);
+
+	return 0;
+}
+
+/**
+ * pxa_udc_vbus_draw - Called by gadget driver after SET_CONFIGURATION completed
+ * @_gadget: usb gadget
+ * @mA: current drawn
+ *
+ * Context: !in_interrupt()
+ *
+ * Called after a configuration was chosen by a USB host, to inform how much
+ * current can be drawn by the device from VBus line.
+ *
+ * Returns 0 or -EOPNOTSUPP if no transceiver is handling the udc
+ */
+static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+	struct pxa_udc *udc;
+
+	udc = to_gadget_udc(_gadget);
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		return usb_phy_set_power(udc->transceiver, mA);
+	return -EOPNOTSUPP;
+}
+
+/**
+ * pxa_udc_phy_event - Called by phy upon VBus event
+ * @nb: notifier block
+ * @action: phy action, is vbus connect or disconnect
+ * @data: the usb_gadget structure in pxa_udc
+ *
+ * Called by the USB Phy when a cable connect or disconnect is sensed.
+ *
+ * Returns 0
+ */
+static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action,
+			     void *data)
+{
+	struct usb_gadget *gadget = data;
+
+	switch (action) {
+	case USB_EVENT_VBUS:
+		usb_gadget_vbus_connect(gadget);
+		return NOTIFY_OK;
+	case USB_EVENT_NONE:
+		usb_gadget_vbus_disconnect(gadget);
+		return NOTIFY_OK;
+	default:
+		return NOTIFY_DONE;
+	}
+}
+
+static struct notifier_block pxa27x_udc_phy = {
+	.notifier_call = pxa_udc_phy_event,
+};
+
+static int pxa27x_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int pxa27x_udc_stop(struct usb_gadget *g);
+
+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,
+	.udc_start	= pxa27x_udc_start,
+	.udc_stop	= pxa27x_udc_stop,
+};
+
+/**
+ * udc_disable - disable udc device controller
+ * @udc: udc device
+ * Context: any
+ *
+ * Disables the udc device : disables clocks, udc interrupts, control endpoint
+ * interrupts.
+ */
+static void udc_disable(struct pxa_udc *udc)
+{
+	if (!udc->enabled)
+		return;
+
+	udc_writel(udc, UDCICR0, 0);
+	udc_writel(udc, UDCICR1, 0);
+
+	udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+
+	ep0_idle(udc);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	clk_disable(udc->clk);
+
+	udc->enabled = 0;
+}
+
+/**
+ * udc_init_data - Initialize udc device data structures
+ * @dev: udc device
+ *
+ * Initializes gadget endpoint list, endpoints locks. No action is taken
+ * on the hardware.
+ */
+static void udc_init_data(struct pxa_udc *dev)
+{
+	int i;
+	struct pxa_ep *ep;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->udc_usb_ep[0].pxa_ep = &dev->pxa_ep[0];
+	dev->gadget.quirk_altset_not_supp = 1;
+	ep0_idle(dev);
+
+	/* PXA endpoints init */
+	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
+		ep = &dev->pxa_ep[i];
+
+		ep->enabled = is_ep0(ep);
+		INIT_LIST_HEAD(&ep->queue);
+		spin_lock_init(&ep->lock);
+	}
+
+	/* USB endpoints init */
+	for (i = 1; i < NR_USB_ENDPOINTS; i++) {
+		list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list,
+				&dev->gadget.ep_list);
+		usb_ep_set_maxpacket_limit(&dev->udc_usb_ep[i].usb_ep,
+					   dev->udc_usb_ep[i].usb_ep.maxpacket);
+	}
+}
+
+/**
+ * udc_enable - Enables the udc device
+ * @dev: udc device
+ *
+ * Enables the udc device : enables clocks, udc interrupts, control endpoint
+ * interrupts, sets usb as UDC client and setups endpoints.
+ */
+static void udc_enable(struct pxa_udc *udc)
+{
+	if (udc->enabled)
+		return;
+
+	clk_enable(udc->clk);
+	udc_writel(udc, UDCICR0, 0);
+	udc_writel(udc, UDCICR1, 0);
+	udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+
+	ep0_idle(udc);
+	udc->gadget.speed = USB_SPEED_FULL;
+	memset(&udc->stats, 0, sizeof(udc->stats));
+
+	pxa_eps_setup(udc);
+	udc_set_mask_UDCCR(udc, UDCCR_UDE);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
+	udelay(2);
+	if (udc_readl(udc, UDCCR) & UDCCR_EMCE)
+		dev_err(udc->dev, "Configuration errors, udc disabled\n");
+
+	/*
+	 * Caller must be able to sleep in order to cope with startup transients
+	 */
+	msleep(100);
+
+	/* enable suspend/resume and reset irqs */
+	udc_writel(udc, UDCICR1,
+			UDCICR1_IECC | UDCICR1_IERU
+			| UDCICR1_IESU | UDCICR1_IERS);
+
+	/* enable ep0 irqs */
+	pio_irq_enable(&udc->pxa_ep[0]);
+
+	udc->enabled = 1;
+}
+
+/**
+ * pxa27x_start - Register gadget driver
+ * @driver: gadget driver
+ * @bind: bind function
+ *
+ * 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.
+ *
+ * Note that the udc is not automatically enabled. Check function
+ * should_enable_udc().
+ *
+ * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
+ */
+static int pxa27x_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct pxa_udc *udc = to_pxa(g);
+	int retval;
+
+	/* first hook up the driver ... */
+	udc->driver = driver;
+
+	if (!IS_ERR_OR_NULL(udc->transceiver)) {
+		retval = otg_set_peripheral(udc->transceiver->otg,
+						&udc->gadget);
+		if (retval) {
+			dev_err(udc->dev, "can't bind to transceiver\n");
+			goto fail;
+		}
+	}
+
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	return 0;
+
+fail:
+	udc->driver = NULL;
+	return retval;
+}
+
+/**
+ * stop_activity - Stops udc endpoints
+ * @udc: udc device
+ * @driver: gadget driver
+ *
+ * Disables all udc endpoints (even control endpoint), report disconnect to
+ * the gadget user.
+ */
+static void stop_activity(struct pxa_udc *udc)
+{
+	int i;
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (i = 0; i < NR_USB_ENDPOINTS; i++)
+		pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
+}
+
+/**
+ * pxa27x_udc_stop - Unregister the gadget driver
+ * @driver: gadget driver
+ *
+ * Returns 0 if no error, -ENODEV, -EINVAL otherwise
+ */
+static int pxa27x_udc_stop(struct usb_gadget *g)
+{
+	struct pxa_udc *udc = to_pxa(g);
+
+	stop_activity(udc);
+	udc_disable(udc);
+
+	udc->driver = NULL;
+
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		return otg_set_peripheral(udc->transceiver->otg, NULL);
+	return 0;
+}
+
+/**
+ * handle_ep0_ctrl_req - handle control endpoint control request
+ * @udc: udc device
+ * @req: control request
+ */
+static void handle_ep0_ctrl_req(struct pxa_udc *udc,
+				struct pxa27x_request *req)
+{
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+	union {
+		struct usb_ctrlrequest	r;
+		u32			word[2];
+	} u;
+	int i;
+	int have_extrabytes = 0;
+	unsigned long flags;
+
+	nuke(ep, -EPROTO);
+	spin_lock_irqsave(&ep->lock, flags);
+
+	/*
+	 * In the PXA320 manual, in the section about Back-to-Back setup
+	 * packets, it describes this situation.  The solution is to set OPC to
+	 * get rid of the status packet, and then continue with the setup
+	 * packet. Generalize to pxa27x CPUs.
+	 */
+	if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0))
+		ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
+	/* read SETUP packet */
+	for (i = 0; i < 2; i++) {
+		if (unlikely(ep_is_empty(ep)))
+			goto stall;
+		u.word[i] = udc_ep_readl(ep, UDCDR);
+	}
+
+	have_extrabytes = !ep_is_empty(ep);
+	while (!ep_is_empty(ep)) {
+		i = udc_ep_readl(ep, UDCDR);
+		ep_err(ep, "wrong to have extra bytes for setup : 0x%08x\n", i);
+	}
+
+	ep_dbg(ep, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+		u.r.bRequestType, u.r.bRequest,
+		le16_to_cpu(u.r.wValue), le16_to_cpu(u.r.wIndex),
+		le16_to_cpu(u.r.wLength));
+	if (unlikely(have_extrabytes))
+		goto stall;
+
+	if (u.r.bRequestType & USB_DIR_IN)
+		set_ep0state(udc, IN_DATA_STAGE);
+	else
+		set_ep0state(udc, OUT_DATA_STAGE);
+
+	/* Tell UDC to enter Data Stage */
+	ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC);
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+	i = udc->driver->setup(&udc->gadget, &u.r);
+	spin_lock_irqsave(&ep->lock, flags);
+	if (i < 0)
+		goto stall;
+out:
+	spin_unlock_irqrestore(&ep->lock, flags);
+	return;
+stall:
+	ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n",
+		udc_ep_readl(ep, UDCCSR), i);
+	ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF);
+	set_ep0state(udc, STALL);
+	goto out;
+}
+
+/**
+ * handle_ep0 - Handle control endpoint data transfers
+ * @udc: udc device
+ * @fifo_irq: 1 if triggered by fifo service type irq
+ * @opc_irq: 1 if triggered by output packet complete type irq
+ *
+ * Context : when in_interrupt() or with ep->lock held
+ *
+ * Tries to transfer all pending request data into the endpoint and/or
+ * transfer all pending data in the endpoint into usb requests.
+ * Handles states of ep0 automata.
+ *
+ * PXA27x hardware handles several standard usb control requests without
+ * driver notification.  The requests fully handled by hardware are :
+ *  SET_ADDRESS, SET_FEATURE, CLEAR_FEATURE, GET_CONFIGURATION, GET_INTERFACE,
+ *  GET_STATUS
+ * The requests handled by hardware, but with irq notification are :
+ *  SYNCH_FRAME, SET_CONFIGURATION, SET_INTERFACE
+ * The remaining standard requests really handled by handle_ep0 are :
+ *  GET_DESCRIPTOR, SET_DESCRIPTOR, specific requests.
+ * Requests standardized outside of USB 2.0 chapter 9 are handled more
+ * uniformly, by gadget drivers.
+ *
+ * The control endpoint state machine is _not_ USB spec compliant, it's even
+ * hardly compliant with Intel PXA270 developers guide.
+ * The key points which inferred this state machine are :
+ *   - on every setup token, bit UDCCSR0_SA is raised and held until cleared by
+ *     software.
+ *   - on every OUT packet received, UDCCSR0_OPC is raised and held until
+ *     cleared by software.
+ *   - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it
+ *     before reading ep0.
+ *     This is true only for PXA27x. This is not true anymore for PXA3xx family
+ *     (check Back-to-Back setup packet in developers guide).
+ *   - irq can be called on a "packet complete" event (opc_irq=1), while
+ *     UDCCSR0_OPC is not yet raised (delta can be as big as 100ms
+ *     from experimentation).
+ *   - as UDCCSR0_SA can be activated while in irq handling, and clearing
+ *     UDCCSR0_OPC would flush the setup data, we almost never clear UDCCSR0_OPC
+ *     => we never actually read the "status stage" packet of an IN data stage
+ *     => this is not documented in Intel documentation
+ *   - hardware as no idea of STATUS STAGE, it only handle SETUP STAGE and DATA
+ *     STAGE. The driver add STATUS STAGE to send last zero length packet in
+ *     OUT_STATUS_STAGE.
+ *   - special attention was needed for IN_STATUS_STAGE. If a packet complete
+ *     event is detected, we terminate the status stage without ackowledging the
+ *     packet (not to risk to loose a potential SETUP packet)
+ */
+static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
+{
+	u32			udccsr0;
+	struct pxa_ep		*ep = &udc->pxa_ep[0];
+	struct pxa27x_request	*req = NULL;
+	int			completed = 0;
+
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+	udccsr0 = udc_ep_readl(ep, UDCCSR);
+	ep_dbg(ep, "state=%s, req=%p, udccsr0=0x%03x, udcbcr=%d, irq_msk=%x\n",
+		EP0_STNAME(udc), req, udccsr0, udc_ep_readl(ep, UDCBCR),
+		(fifo_irq << 1 | opc_irq));
+
+	if (udccsr0 & UDCCSR0_SST) {
+		ep_dbg(ep, "clearing stall status\n");
+		nuke(ep, -EPIPE);
+		ep_write_UDCCSR(ep, UDCCSR0_SST);
+		ep0_idle(udc);
+	}
+
+	if (udccsr0 & UDCCSR0_SA) {
+		nuke(ep, 0);
+		set_ep0state(udc, SETUP_STAGE);
+	}
+
+	switch (udc->ep0state) {
+	case WAIT_FOR_SETUP:
+		/*
+		 * Hardware bug : beware, we cannot clear OPC, since we would
+		 * miss a potential OPC irq for a setup packet.
+		 * So, we only do ... nothing, and hope for a next irq with
+		 * UDCCSR0_SA set.
+		 */
+		break;
+	case SETUP_STAGE:
+		udccsr0 &= UDCCSR0_CTRL_REQ_MASK;
+		if (likely(udccsr0 == UDCCSR0_CTRL_REQ_MASK))
+			handle_ep0_ctrl_req(udc, req);
+		break;
+	case IN_DATA_STAGE:			/* GET_DESCRIPTOR */
+		if (epout_has_pkt(ep))
+			ep_write_UDCCSR(ep, UDCCSR0_OPC);
+		if (req && !ep_is_full(ep))
+			completed = write_ep0_fifo(ep, req);
+		if (completed)
+			ep0_end_in_req(ep, req, NULL);
+		break;
+	case OUT_DATA_STAGE:			/* SET_DESCRIPTOR */
+		if (epout_has_pkt(ep) && req)
+			completed = read_ep0_fifo(ep, req);
+		if (completed)
+			ep0_end_out_req(ep, req, NULL);
+		break;
+	case STALL:
+		ep_write_UDCCSR(ep, UDCCSR0_FST);
+		break;
+	case IN_STATUS_STAGE:
+		/*
+		 * Hardware bug : beware, we cannot clear OPC, since we would
+		 * miss a potential PC irq for a setup packet.
+		 * So, we only put the ep0 into WAIT_FOR_SETUP state.
+		 */
+		if (opc_irq)
+			ep0_idle(udc);
+		break;
+	case OUT_STATUS_STAGE:
+	case WAIT_ACK_SET_CONF_INTERF:
+		ep_warn(ep, "should never get in %s state here!!!\n",
+				EP0_STNAME(ep->dev));
+		ep0_idle(udc);
+		break;
+	}
+}
+
+/**
+ * handle_ep - Handle endpoint data tranfers
+ * @ep: pxa physical endpoint
+ *
+ * Tries to transfer all pending request data into the endpoint and/or
+ * transfer all pending data in the endpoint into usb requests.
+ *
+ * Is always called when in_interrupt() and with ep->lock released.
+ */
+static void handle_ep(struct pxa_ep *ep)
+{
+	struct pxa27x_request	*req;
+	int completed;
+	u32 udccsr;
+	int is_in = ep->dir_in;
+	int loop = 0;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	if (ep->in_handle_ep)
+		goto recursion_detected;
+	ep->in_handle_ep = 1;
+
+	do {
+		completed = 0;
+		udccsr = udc_ep_readl(ep, UDCCSR);
+
+		if (likely(!list_empty(&ep->queue)))
+			req = list_entry(ep->queue.next,
+					struct pxa27x_request, queue);
+		else
+			req = NULL;
+
+		ep_dbg(ep, "req:%p, udccsr 0x%03x loop=%d\n",
+				req, udccsr, loop++);
+
+		if (unlikely(udccsr & (UDCCSR_SST | UDCCSR_TRN)))
+			udc_ep_writel(ep, UDCCSR,
+					udccsr & (UDCCSR_SST | UDCCSR_TRN));
+		if (!req)
+			break;
+
+		if (unlikely(is_in)) {
+			if (likely(!ep_is_full(ep)))
+				completed = write_fifo(ep, req);
+		} else {
+			if (likely(epout_has_pkt(ep)))
+				completed = read_fifo(ep, req);
+		}
+
+		if (completed) {
+			if (is_in)
+				ep_end_in_req(ep, req, &flags);
+			else
+				ep_end_out_req(ep, req, &flags);
+		}
+	} while (completed);
+
+	ep->in_handle_ep = 0;
+recursion_detected:
+	spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+/**
+ * pxa27x_change_configuration - Handle SET_CONF usb request notification
+ * @udc: udc device
+ * @config: usb configuration
+ *
+ * Post the request to upper level.
+ * Don't use any pxa specific harware configuration capabilities
+ */
+static void pxa27x_change_configuration(struct pxa_udc *udc, int config)
+{
+	struct usb_ctrlrequest req ;
+
+	dev_dbg(udc->dev, "config=%d\n", config);
+
+	udc->config = config;
+	udc->last_interface = 0;
+	udc->last_alternate = 0;
+
+	req.bRequestType = 0;
+	req.bRequest = USB_REQ_SET_CONFIGURATION;
+	req.wValue = config;
+	req.wIndex = 0;
+	req.wLength = 0;
+
+	set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+	udc->driver->setup(&udc->gadget, &req);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+}
+
+/**
+ * pxa27x_change_interface - Handle SET_INTERF usb request notification
+ * @udc: udc device
+ * @iface: interface number
+ * @alt: alternate setting number
+ *
+ * Post the request to upper level.
+ * Don't use any pxa specific harware configuration capabilities
+ */
+static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt)
+{
+	struct usb_ctrlrequest  req;
+
+	dev_dbg(udc->dev, "interface=%d, alternate setting=%d\n", iface, alt);
+
+	udc->last_interface = iface;
+	udc->last_alternate = alt;
+
+	req.bRequestType = USB_RECIP_INTERFACE;
+	req.bRequest = USB_REQ_SET_INTERFACE;
+	req.wValue = alt;
+	req.wIndex = iface;
+	req.wLength = 0;
+
+	set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+	udc->driver->setup(&udc->gadget, &req);
+	ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+}
+
+/*
+ * irq_handle_data - Handle data transfer
+ * @irq: irq IRQ number
+ * @udc: dev pxa_udc device structure
+ *
+ * Called from irq handler, transferts data to or from endpoint to queue
+ */
+static void irq_handle_data(int irq, struct pxa_udc *udc)
+{
+	int i;
+	struct pxa_ep *ep;
+	u32 udcisr0 = udc_readl(udc, UDCISR0) & UDCCISR0_EP_MASK;
+	u32 udcisr1 = udc_readl(udc, UDCISR1) & UDCCISR1_EP_MASK;
+
+	if (udcisr0 & UDCISR_INT_MASK) {
+		udc->pxa_ep[0].stats.irqs++;
+		udc_writel(udc, UDCISR0, UDCISR_INT(0, UDCISR_INT_MASK));
+		handle_ep0(udc, !!(udcisr0 & UDCICR_FIFOERR),
+				!!(udcisr0 & UDCICR_PKTCOMPL));
+	}
+
+	udcisr0 >>= 2;
+	for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) {
+		if (!(udcisr0 & UDCISR_INT_MASK))
+			continue;
+
+		udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK));
+
+		WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+		if (i < ARRAY_SIZE(udc->pxa_ep)) {
+			ep = &udc->pxa_ep[i];
+			ep->stats.irqs++;
+			handle_ep(ep);
+		}
+	}
+
+	for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) {
+		udc_writel(udc, UDCISR1, UDCISR_INT(i - 16, UDCISR_INT_MASK));
+		if (!(udcisr1 & UDCISR_INT_MASK))
+			continue;
+
+		WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+		if (i < ARRAY_SIZE(udc->pxa_ep)) {
+			ep = &udc->pxa_ep[i];
+			ep->stats.irqs++;
+			handle_ep(ep);
+		}
+	}
+
+}
+
+/**
+ * irq_udc_suspend - Handle IRQ "UDC Suspend"
+ * @udc: udc device
+ */
+static void irq_udc_suspend(struct pxa_udc *udc)
+{
+	udc_writel(udc, UDCISR1, UDCISR1_IRSU);
+	udc->stats.irqs_suspend++;
+
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN
+			&& udc->driver && udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+	ep0_idle(udc);
+}
+
+/**
+  * irq_udc_resume - Handle IRQ "UDC Resume"
+  * @udc: udc device
+  */
+static void irq_udc_resume(struct pxa_udc *udc)
+{
+	udc_writel(udc, UDCISR1, UDCISR1_IRRU);
+	udc->stats.irqs_resume++;
+
+	if (udc->gadget.speed != USB_SPEED_UNKNOWN
+			&& udc->driver && udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+/**
+ * irq_udc_reconfig - Handle IRQ "UDC Change Configuration"
+ * @udc: udc device
+ */
+static void irq_udc_reconfig(struct pxa_udc *udc)
+{
+	unsigned config, interface, alternate, config_change;
+	u32 udccr = udc_readl(udc, UDCCR);
+
+	udc_writel(udc, UDCISR1, UDCISR1_IRCC);
+	udc->stats.irqs_reconfig++;
+
+	config = (udccr & UDCCR_ACN) >> UDCCR_ACN_S;
+	config_change = (config != udc->config);
+	pxa27x_change_configuration(udc, config);
+
+	interface = (udccr & UDCCR_AIN) >> UDCCR_AIN_S;
+	alternate = (udccr & UDCCR_AAISN) >> UDCCR_AAISN_S;
+	pxa27x_change_interface(udc, interface, alternate);
+
+	if (config_change)
+		update_pxa_ep_matches(udc);
+	udc_set_mask_UDCCR(udc, UDCCR_SMAC);
+}
+
+/**
+ * irq_udc_reset - Handle IRQ "UDC Reset"
+ * @udc: udc device
+ */
+static void irq_udc_reset(struct pxa_udc *udc)
+{
+	u32 udccr = udc_readl(udc, UDCCR);
+	struct pxa_ep *ep = &udc->pxa_ep[0];
+
+	dev_info(udc->dev, "USB reset\n");
+	udc_writel(udc, UDCISR1, UDCISR1_IRRS);
+	udc->stats.irqs_reset++;
+
+	if ((udccr & UDCCR_UDA) == 0) {
+		dev_dbg(udc->dev, "USB reset start\n");
+		stop_activity(udc);
+	}
+	udc->gadget.speed = USB_SPEED_FULL;
+	memset(&udc->stats, 0, sizeof udc->stats);
+
+	nuke(ep, -EPROTO);
+	ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC);
+	ep0_idle(udc);
+}
+
+/**
+ * pxa_udc_irq - Main irq handler
+ * @irq: irq number
+ * @_dev: udc device
+ *
+ * Handles all udc interrupts
+ */
+static irqreturn_t pxa_udc_irq(int irq, void *_dev)
+{
+	struct pxa_udc *udc = _dev;
+	u32 udcisr0 = udc_readl(udc, UDCISR0);
+	u32 udcisr1 = udc_readl(udc, UDCISR1);
+	u32 udccr = udc_readl(udc, UDCCR);
+	u32 udcisr1_spec;
+
+	dev_vdbg(udc->dev, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+		 "UDCCR:0x%08x\n", udcisr0, udcisr1, udccr);
+
+	udcisr1_spec = udcisr1 & 0xf8000000;
+	if (unlikely(udcisr1_spec & UDCISR1_IRSU))
+		irq_udc_suspend(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRRU))
+		irq_udc_resume(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRCC))
+		irq_udc_reconfig(udc);
+	if (unlikely(udcisr1_spec & UDCISR1_IRRS))
+		irq_udc_reset(udc);
+
+	if ((udcisr0 & UDCCISR0_EP_MASK) | (udcisr1 & UDCCISR1_EP_MASK))
+		irq_handle_data(irq, udc);
+
+	return IRQ_HANDLED;
+}
+
+static struct pxa_udc memory = {
+	.gadget = {
+		.ops		= &pxa_udc_ops,
+		.ep0		= &memory.udc_usb_ep[0].usb_ep,
+		.name		= driver_name,
+		.dev = {
+			.init_name	= "gadget",
+		},
+	},
+
+	.udc_usb_ep = {
+		USB_EP_CTRL,
+		USB_EP_OUT_BULK(1),
+		USB_EP_IN_BULK(2),
+		USB_EP_IN_ISO(3),
+		USB_EP_OUT_ISO(4),
+		USB_EP_IN_INT(5),
+	},
+
+	.pxa_ep = {
+		PXA_EP_CTRL,
+		/* Endpoints for gadget zero */
+		PXA_EP_OUT_BULK(1, 1, 3, 0, 0),
+		PXA_EP_IN_BULK(2,  2, 3, 0, 0),
+		/* Endpoints for ether gadget, file storage gadget */
+		PXA_EP_OUT_BULK(3, 1, 1, 0, 0),
+		PXA_EP_IN_BULK(4,  2, 1, 0, 0),
+		PXA_EP_IN_ISO(5,   3, 1, 0, 0),
+		PXA_EP_OUT_ISO(6,  4, 1, 0, 0),
+		PXA_EP_IN_INT(7,   5, 1, 0, 0),
+		/* Endpoints for RNDIS, serial */
+		PXA_EP_OUT_BULK(8, 1, 2, 0, 0),
+		PXA_EP_IN_BULK(9,  2, 2, 0, 0),
+		PXA_EP_IN_INT(10,  5, 2, 0, 0),
+		/*
+		 * All the following endpoints are only for completion.  They
+		 * won't never work, as multiple interfaces are really broken on
+		 * the pxa.
+		*/
+		PXA_EP_OUT_BULK(11, 1, 2, 1, 0),
+		PXA_EP_IN_BULK(12,  2, 2, 1, 0),
+		/* Endpoint for CDC Ether */
+		PXA_EP_OUT_BULK(13, 1, 1, 1, 1),
+		PXA_EP_IN_BULK(14,  2, 1, 1, 1),
+	}
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id udc_pxa_dt_ids[] = {
+	{ .compatible = "marvell,pxa270-udc" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids);
+#endif
+
+/**
+ * pxa_udc_probe - probes the udc device
+ * @_dev: platform device
+ *
+ * Perform basic init : allocates udc clock, creates sysfs files, requests
+ * irq.
+ */
+static int pxa_udc_probe(struct platform_device *pdev)
+{
+	struct resource *regs;
+	struct pxa_udc *udc = &memory;
+	int retval = 0, gpio;
+	struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev);
+	unsigned long gpio_flags;
+
+	if (mach) {
+		gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0;
+		gpio = mach->gpio_pullup;
+		if (gpio_is_valid(gpio)) {
+			retval = devm_gpio_request_one(&pdev->dev, gpio,
+						       gpio_flags,
+						       "USB D+ pullup");
+			if (retval)
+				return retval;
+			udc->gpiod = gpio_to_desc(mach->gpio_pullup);
+		}
+		udc->udc_command = mach->udc_command;
+	} else {
+		udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->regs = devm_ioremap_resource(&pdev->dev, regs);
+	if (IS_ERR(udc->regs))
+		return PTR_ERR(udc->regs);
+	udc->irq = platform_get_irq(pdev, 0);
+	if (udc->irq < 0)
+		return udc->irq;
+
+	udc->dev = &pdev->dev;
+	if (of_have_populated_dt()) {
+		udc->transceiver =
+			devm_usb_get_phy_by_phandle(udc->dev, "phys", 0);
+		if (IS_ERR(udc->transceiver))
+			return PTR_ERR(udc->transceiver);
+	} else {
+		udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+	}
+
+	if (IS_ERR(udc->gpiod)) {
+		dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
+			PTR_ERR(udc->gpiod));
+		return PTR_ERR(udc->gpiod);
+	}
+	if (udc->gpiod)
+		gpiod_direction_output(udc->gpiod, 0);
+
+	udc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(udc->clk))
+		return PTR_ERR(udc->clk);
+
+	retval = clk_prepare(udc->clk);
+	if (retval)
+		return retval;
+
+	udc->vbus_sensed = 0;
+
+	the_controller = udc;
+	platform_set_drvdata(pdev, udc);
+	udc_init_data(udc);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = devm_request_irq(&pdev->dev, udc->irq, pxa_udc_irq,
+				  IRQF_SHARED, driver_name, udc);
+	if (retval != 0) {
+		dev_err(udc->dev, "%s: can't get irq %i, err %d\n",
+			driver_name, udc->irq, retval);
+		goto err;
+	}
+
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		usb_register_notifier(udc->transceiver, &pxa27x_udc_phy);
+	retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+	if (retval)
+		goto err_add_gadget;
+
+	pxa_init_debugfs(udc);
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	return 0;
+
+err_add_gadget:
+	if (!IS_ERR_OR_NULL(udc->transceiver))
+		usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
+err:
+	clk_unprepare(udc->clk);
+	return retval;
+}
+
+/**
+ * pxa_udc_remove - removes the udc device driver
+ * @_dev: platform device
+ */
+static int pxa_udc_remove(struct platform_device *_dev)
+{
+	struct pxa_udc *udc = platform_get_drvdata(_dev);
+
+	usb_del_gadget_udc(&udc->gadget);
+	pxa_cleanup_debugfs(udc);
+
+	if (!IS_ERR_OR_NULL(udc->transceiver)) {
+		usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
+		usb_put_phy(udc->transceiver);
+	}
+
+	udc->transceiver = NULL;
+	the_controller = NULL;
+	clk_unprepare(udc->clk);
+
+	return 0;
+}
+
+static void pxa_udc_shutdown(struct platform_device *_dev)
+{
+	struct pxa_udc *udc = platform_get_drvdata(_dev);
+
+	if (udc_readl(udc, UDCCR) & UDCCR_UDE)
+		udc_disable(udc);
+}
+
+#ifdef CONFIG_PXA27x
+extern void pxa27x_clear_otgph(void);
+#else
+#define pxa27x_clear_otgph()   do {} while (0)
+#endif
+
+#ifdef CONFIG_PM
+/**
+ * pxa_udc_suspend - Suspend udc device
+ * @_dev: platform device
+ * @state: suspend state
+ *
+ * Suspends udc : saves configuration registers (UDCCR*), then disables the udc
+ * device.
+ */
+static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
+{
+	struct pxa_udc *udc = platform_get_drvdata(_dev);
+	struct pxa_ep *ep;
+
+	ep = &udc->pxa_ep[0];
+	udc->udccsr0 = udc_ep_readl(ep, UDCCSR);
+
+	udc_disable(udc);
+	udc->pullup_resume = udc->pullup_on;
+	dplus_pullup(udc, 0);
+
+	if (udc->driver)
+		udc->driver->disconnect(&udc->gadget);
+
+	return 0;
+}
+
+/**
+ * pxa_udc_resume - Resume udc device
+ * @_dev: platform device
+ *
+ * Resumes udc : restores configuration registers (UDCCR*), then enables the udc
+ * device.
+ */
+static int pxa_udc_resume(struct platform_device *_dev)
+{
+	struct pxa_udc *udc = platform_get_drvdata(_dev);
+	struct pxa_ep *ep;
+
+	ep = &udc->pxa_ep[0];
+	udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME));
+
+	dplus_pullup(udc, udc->pullup_resume);
+	if (should_enable_udc(udc))
+		udc_enable(udc);
+	/*
+	 * We do not handle OTG yet.
+	 *
+	 * OTGPH bit is set when sleep mode is entered.
+	 * it indicates that OTG pad is retaining its state.
+	 * Upon exit from sleep mode and before clearing OTGPH,
+	 * Software must configure the USB OTG pad, UDC, and UHC
+	 * to the state they were in before entering sleep mode.
+	 */
+	pxa27x_clear_otgph();
+
+	return 0;
+}
+#endif
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:pxa27x-udc");
+
+static struct platform_driver udc_driver = {
+	.driver		= {
+		.name	= "pxa27x-udc",
+		.of_match_table = of_match_ptr(udc_pxa_dt_ids),
+	},
+	.probe		= pxa_udc_probe,
+	.remove		= pxa_udc_remove,
+	.shutdown	= pxa_udc_shutdown,
+#ifdef CONFIG_PM
+	.suspend	= pxa_udc_suspend,
+	.resume		= pxa_udc_resume
+#endif
+};
+
+module_platform_driver(udc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Robert Jarzmik");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
new file mode 100644
index 0000000..13b2977
--- /dev/null
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+
+/*
+ * Register definitions
+ */
+/* Offsets */
+#define UDCCR		0x0000		/* UDC Control Register */
+#define UDCICR0		0x0004		/* UDC Interrupt Control Register0 */
+#define UDCICR1		0x0008		/* UDC Interrupt Control Register1 */
+#define UDCISR0		0x000C		/* UDC Interrupt Status Register 0 */
+#define UDCISR1		0x0010		/* UDC Interrupt Status Register 1 */
+#define UDCFNR		0x0014		/* UDC Frame Number Register */
+#define UDCOTGICR	0x0018		/* UDC On-The-Go interrupt control */
+#define UP2OCR		0x0020		/* USB Port 2 Output Control register */
+#define UP3OCR		0x0024		/* USB Port 3 Output Control register */
+#define UDCCSRn(x)	(0x0100 + ((x)<<2)) /* UDC Control/Status register */
+#define UDCBCRn(x)	(0x0200 + ((x)<<2)) /* UDC Byte Count Register */
+#define UDCDRn(x)	(0x0300 + ((x)<<2)) /* UDC Data Register  */
+#define UDCCRn(x)	(0x0400 + ((x)<<2)) /* UDC Control Register */
+
+#define UDCCR_OEN	(1 << 31)	/* On-the-Go Enable */
+#define UDCCR_AALTHNP	(1 << 30)	/* A-device Alternate Host Negotiation
+					   Protocol Port Support */
+#define UDCCR_AHNP	(1 << 29)	/* A-device Host Negotiation Protocol
+					   Support */
+#define UDCCR_BHNP	(1 << 28)	/* B-device Host Negotiation Protocol
+					   Enable */
+#define UDCCR_DWRE	(1 << 16)	/* Device Remote Wake-up Enable */
+#define UDCCR_ACN	(0x03 << 11)	/* Active UDC configuration Number */
+#define UDCCR_ACN_S	11
+#define UDCCR_AIN	(0x07 << 8)	/* Active UDC interface Number */
+#define UDCCR_AIN_S	8
+#define UDCCR_AAISN	(0x07 << 5)	/* Active UDC Alternate Interface
+					   Setting Number */
+#define UDCCR_AAISN_S	5
+#define UDCCR_SMAC	(1 << 4)	/* Switch Endpoint Memory to Active
+					   Configuration */
+#define UDCCR_EMCE	(1 << 3)	/* Endpoint Memory Configuration
+					   Error */
+#define UDCCR_UDR	(1 << 2)	/* UDC Resume */
+#define UDCCR_UDA	(1 << 1)	/* UDC Active */
+#define UDCCR_UDE	(1 << 0)	/* UDC Enable */
+
+#define UDCICR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCICR1_IECC	(1 << 31)	/* IntEn - Configuration Change */
+#define UDCICR1_IESOF	(1 << 30)	/* IntEn - Start of Frame */
+#define UDCICR1_IERU	(1 << 29)	/* IntEn - Resume */
+#define UDCICR1_IESU	(1 << 28)	/* IntEn - Suspend */
+#define UDCICR1_IERS	(1 << 27)	/* IntEn - Reset */
+#define UDCICR_FIFOERR	(1 << 1)	/* FIFO Error interrupt for EP */
+#define UDCICR_PKTCOMPL	(1 << 0)	/* Packet Complete interrupt for EP */
+#define UDCICR_INT_MASK	(UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCISR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCISR1_IRCC	(1 << 31)	/* IntReq - Configuration Change */
+#define UDCISR1_IRSOF	(1 << 30)	/* IntReq - Start of Frame */
+#define UDCISR1_IRRU	(1 << 29)	/* IntReq - Resume */
+#define UDCISR1_IRSU	(1 << 28)	/* IntReq - Suspend */
+#define UDCISR1_IRRS	(1 << 27)	/* IntReq - Reset */
+#define UDCISR_INT_MASK	(UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCOTGICR_IESF	(1 << 24)	/* OTG SET_FEATURE command recvd */
+#define UDCOTGICR_IEXR	(1 << 17)	/* Extra Transceiver Interrupt
+					   Rising Edge Interrupt Enable */
+#define UDCOTGICR_IEXF	(1 << 16)	/* Extra Transceiver Interrupt
+					   Falling Edge Interrupt Enable */
+#define UDCOTGICR_IEVV40R (1 << 9)	/* OTG Vbus Valid 4.0V Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV40F (1 << 8)	/* OTG Vbus Valid 4.0V Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV44R (1 << 7)	/* OTG Vbus Valid 4.4V Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEVV44F (1 << 6)	/* OTG Vbus Valid 4.4V Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESVR	(1 << 5)	/* OTG Session Valid Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESVF	(1 << 4)	/* OTG Session Valid Falling Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IESDR	(1 << 3)	/* OTG A-Device SRP Detect Rising
+					   Edge Interrupt Enable */
+#define UDCOTGICR_IESDF	(1 << 2)	/* OTG A-Device SRP Detect Falling
+					   Edge Interrupt Enable */
+#define UDCOTGICR_IEIDR	(1 << 1)	/* OTG ID Change Rising Edge
+					   Interrupt Enable */
+#define UDCOTGICR_IEIDF	(1 << 0)	/* OTG ID Change Falling Edge
+					   Interrupt Enable */
+
+/* Host Port 2 field bits */
+#define UP2OCR_CPVEN	(1 << 0)	/* Charge Pump Vbus Enable */
+#define UP2OCR_CPVPE	(1 << 1)	/* Charge Pump Vbus Pulse Enable */
+					/* Transceiver enablers */
+#define UP2OCR_DPPDE	(1 << 2)	/*   D+ Pull Down Enable */
+#define UP2OCR_DMPDE	(1 << 3)	/*   D- Pull Down Enable */
+#define UP2OCR_DPPUE	(1 << 4)	/*   D+ Pull Up Enable */
+#define UP2OCR_DMPUE	(1 << 5)	/*   D- Pull Up Enable */
+#define UP2OCR_DPPUBE	(1 << 6)	/*   D+ Pull Up Bypass Enable */
+#define UP2OCR_DMPUBE	(1 << 7)	/*   D- Pull Up Bypass Enable */
+#define UP2OCR_EXSP	(1 << 8)	/* External Transceiver Speed Control */
+#define UP2OCR_EXSUS	(1 << 9)	/* External Transceiver Speed Enable */
+#define UP2OCR_IDON	(1 << 10)	/* OTG ID Read Enable */
+#define UP2OCR_HXS	(1 << 16)	/* Transceiver Output Select */
+#define UP2OCR_HXOE	(1 << 17)	/* Transceiver Output Enable */
+#define UP2OCR_SEOS	(1 << 24)	/* Single-Ended Output Select */
+
+#define UDCCSR0_ACM	(1 << 9)	/* Ack Control Mode */
+#define UDCCSR0_AREN	(1 << 8)	/* Ack Response Enable */
+#define UDCCSR0_SA	(1 << 7)	/* Setup Active */
+#define UDCCSR0_RNE	(1 << 6)	/* Receive FIFO Not Empty */
+#define UDCCSR0_FST	(1 << 5)	/* Force Stall */
+#define UDCCSR0_SST	(1 << 4)	/* Sent Stall */
+#define UDCCSR0_DME	(1 << 3)	/* DMA Enable */
+#define UDCCSR0_FTF	(1 << 2)	/* Flush Transmit FIFO */
+#define UDCCSR0_IPR	(1 << 1)	/* IN Packet Ready */
+#define UDCCSR0_OPC	(1 << 0)	/* OUT Packet Complete */
+
+#define UDCCSR_DPE	(1 << 9)	/* Data Packet Error */
+#define UDCCSR_FEF	(1 << 8)	/* Flush Endpoint FIFO */
+#define UDCCSR_SP	(1 << 7)	/* Short Packet Control/Status */
+#define UDCCSR_BNE	(1 << 6)	/* Buffer Not Empty (IN endpoints) */
+#define UDCCSR_BNF	(1 << 6)	/* Buffer Not Full (OUT endpoints) */
+#define UDCCSR_FST	(1 << 5)	/* Force STALL */
+#define UDCCSR_SST	(1 << 4)	/* Sent STALL */
+#define UDCCSR_DME	(1 << 3)	/* DMA Enable */
+#define UDCCSR_TRN	(1 << 2)	/* Tx/Rx NAK */
+#define UDCCSR_PC	(1 << 1)	/* Packet Complete */
+#define UDCCSR_FS	(1 << 0)	/* FIFO needs service */
+
+#define UDCCONR_CN	(0x03 << 25)	/* Configuration Number */
+#define UDCCONR_CN_S	25
+#define UDCCONR_IN	(0x07 << 22)	/* Interface Number */
+#define UDCCONR_IN_S	22
+#define UDCCONR_AISN	(0x07 << 19)	/* Alternate Interface Number */
+#define UDCCONR_AISN_S	19
+#define UDCCONR_EN	(0x0f << 15)	/* Endpoint Number */
+#define UDCCONR_EN_S	15
+#define UDCCONR_ET	(0x03 << 13)	/* Endpoint Type: */
+#define UDCCONR_ET_S	13
+#define UDCCONR_ET_INT	(0x03 << 13)	/*   Interrupt */
+#define UDCCONR_ET_BULK	(0x02 << 13)	/*   Bulk */
+#define UDCCONR_ET_ISO	(0x01 << 13)	/*   Isochronous */
+#define UDCCONR_ET_NU	(0x00 << 13)	/*   Not used */
+#define UDCCONR_ED	(1 << 12)	/* Endpoint Direction */
+#define UDCCONR_MPS	(0x3ff << 2)	/* Maximum Packet Size */
+#define UDCCONR_MPS_S	2
+#define UDCCONR_DE	(1 << 1)	/* Double Buffering Enable */
+#define UDCCONR_EE	(1 << 0)	/* Endpoint Enable */
+
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_SMAC | UDCCR_UDR | UDCCR_UDE)
+#define UDCCSR_WR_MASK	(UDCCSR_DME | UDCCSR_FST)
+#define UDC_FNR_MASK	(0x7ff)
+#define UDC_BCR_MASK	(0x3ff)
+
+/*
+ * UDCCR = UDC Endpoint Configuration Registers
+ * UDCCSR = UDC Control/Status Register for this EP
+ * UDCBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDCDR = UDC Endpoint Data Register (the fifo)
+ */
+#define ofs_UDCCR(ep)	(UDCCRn(ep->idx))
+#define ofs_UDCCSR(ep)	(UDCCSRn(ep->idx))
+#define ofs_UDCBCR(ep)	(UDCBCRn(ep->idx))
+#define ofs_UDCDR(ep)	(UDCDRn(ep->idx))
+
+/* Register access macros */
+#define udc_ep_readl(ep, reg)	\
+	__raw_readl((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writel(ep, reg, value)	\
+	__raw_writel((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_ep_readb(ep, reg)	\
+	__raw_readb((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writeb(ep, reg, value)	\
+	__raw_writeb((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_readl(dev, reg)	\
+	__raw_readl((dev)->regs + (reg))
+#define udc_writel(udc, reg, value)	\
+	__raw_writel((value), (udc)->regs + (reg))
+
+#define UDCCSR_MASK		(UDCCSR_FST | UDCCSR_DME)
+#define UDCCISR0_EP_MASK	~0
+#define UDCCISR1_EP_MASK	0xffff
+#define UDCCSR0_CTRL_REQ_MASK	(UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)
+
+#define EPIDX(ep)	(ep->idx)
+#define EPADDR(ep)	(ep->addr)
+#define EPXFERTYPE(ep)	(ep->type)
+#define EPNAME(ep)	(ep->name)
+#define is_ep0(ep)	(!ep->idx)
+#define EPXFERTYPE_is_ISO(ep) (EPXFERTYPE(ep) == USB_ENDPOINT_XFER_ISOC)
+
+/*
+ * Endpoint definitions
+ *
+ * Once enabled, pxa endpoint configuration is freezed, and cannot change
+ * unless a reset happens or the udc is disabled.
+ * Therefore, we must define all pxa potential endpoint definitions needed for
+ * all gadget and set them up before the udc is enabled.
+ *
+ * As the architecture chosen is fully static, meaning the pxa endpoint
+ * configurations are set up once and for all, we must provide a way to match
+ * one usb endpoint (usb_ep) to several pxa endpoints. The reason is that gadget
+ * layer autoconf doesn't choose the usb_ep endpoint on (config, interface, alt)
+ * criteria, while the pxa architecture requires that.
+ *
+ * The solution is to define several pxa endpoints matching one usb_ep. Ex:
+ *   - "ep1-in" matches pxa endpoint EPA (which is an IN ep at addr 1, when
+ *     the udc talks on (config=3, interface=0, alt=0)
+ *   - "ep1-in" matches pxa endpoint EPB (which is an IN ep at addr 1, when
+ *     the udc talks on (config=3, interface=0, alt=1)
+ *   - "ep1-in" matches pxa endpoint EPC (which is an IN ep at addr 1, when
+ *     the udc talks on (config=2, interface=0, alt=0)
+ *
+ * We'll define the pxa endpoint by its index (EPA => idx=1, EPB => idx=2, ...)
+ */
+
+/*
+ * Endpoint definition helpers
+ */
+#define USB_EP_DEF(addr, bname, dir, type, maxpkt, ctype, cdir) \
+{ .usb_ep = {	.name = bname, .ops = &pxa_ep_ops, .maxpacket = maxpkt, \
+		.caps = USB_EP_CAPS(ctype, cdir), }, \
+  .desc = {	.bEndpointAddress = addr | (dir ? USB_DIR_IN : 0), \
+		.bmAttributes = USB_ENDPOINT_XFER_ ## type, \
+		.wMaxPacketSize = maxpkt, }, \
+  .dev = &memory \
+}
+#define USB_EP_BULK(addr, bname, dir, cdir) \
+	USB_EP_DEF(addr, bname, dir, BULK, BULK_FIFO_SIZE, \
+		USB_EP_CAPS_TYPE_BULK, cdir)
+#define USB_EP_ISO(addr, bname, dir, cdir) \
+	USB_EP_DEF(addr, bname, dir, ISOC, ISO_FIFO_SIZE, \
+		USB_EP_CAPS_TYPE_ISO, cdir)
+#define USB_EP_INT(addr, bname, dir, cdir) \
+	USB_EP_DEF(addr, bname, dir, INT, INT_FIFO_SIZE, \
+		USB_EP_CAPS_TYPE_INT, cdir)
+#define USB_EP_IN_BULK(n)	USB_EP_BULK(n, "ep" #n "in-bulk", 1, \
+					USB_EP_CAPS_DIR_IN)
+#define USB_EP_OUT_BULK(n)	USB_EP_BULK(n, "ep" #n "out-bulk", 0, \
+					USB_EP_CAPS_DIR_OUT)
+#define USB_EP_IN_ISO(n)	USB_EP_ISO(n,  "ep" #n "in-iso", 1, \
+					USB_EP_CAPS_DIR_IN)
+#define USB_EP_OUT_ISO(n)	USB_EP_ISO(n,  "ep" #n "out-iso", 0, \
+					USB_EP_CAPS_DIR_OUT)
+#define USB_EP_IN_INT(n)	USB_EP_INT(n,  "ep" #n "in-int", 1, \
+					USB_EP_CAPS_DIR_IN)
+#define USB_EP_CTRL	USB_EP_DEF(0,  "ep0", 0, CONTROL, EP0_FIFO_SIZE, \
+				USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)
+
+#define PXA_EP_DEF(_idx, _addr, dir, _type, maxpkt, _config, iface, altset) \
+{ \
+	.dev = &memory, \
+	.name = "ep" #_idx, \
+	.idx = _idx, .enabled = 0, \
+	.dir_in = dir, .addr = _addr, \
+	.config = _config, .interface = iface, .alternate = altset, \
+	.type = _type, .fifo_size = maxpkt, \
+}
+#define PXA_EP_BULK(_idx, addr, dir, config, iface, alt) \
+  PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_ISO(_idx, addr, dir, config, iface, alt) \
+  PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_INT(_idx, addr, dir, config, iface, alt) \
+  PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE, \
+		config, iface, alt)
+#define PXA_EP_IN_BULK(i, adr, c, f, a)		PXA_EP_BULK(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_BULK(i, adr, c, f, a)	PXA_EP_BULK(i, adr, 0, c, f, a)
+#define PXA_EP_IN_ISO(i, adr, c, f, a)		PXA_EP_ISO(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_ISO(i, adr, c, f, a)		PXA_EP_ISO(i, adr, 0, c, f, a)
+#define PXA_EP_IN_INT(i, adr, c, f, a)		PXA_EP_INT(i, adr, 1, c, f, a)
+#define PXA_EP_CTRL	PXA_EP_DEF(0, 0, 0, 0, EP0_FIFO_SIZE, 0, 0, 0)
+
+struct pxa27x_udc;
+
+struct stats {
+	unsigned long in_ops;
+	unsigned long out_ops;
+	unsigned long in_bytes;
+	unsigned long out_bytes;
+	unsigned long irqs;
+};
+
+/**
+ * struct udc_usb_ep - container of each usb_ep structure
+ * @usb_ep: usb endpoint
+ * @desc: usb descriptor, especially type and address
+ * @dev: udc managing this endpoint
+ * @pxa_ep: matching pxa_ep (cache of find_pxa_ep() call)
+ */
+struct udc_usb_ep {
+	struct usb_ep usb_ep;
+	struct usb_endpoint_descriptor desc;
+	struct pxa_udc *dev;
+	struct pxa_ep *pxa_ep;
+};
+
+/**
+ * struct pxa_ep - pxa endpoint
+ * @dev: udc device
+ * @queue: requests queue
+ * @lock: lock to pxa_ep data (queues and stats)
+ * @enabled: true when endpoint enabled (not stopped by gadget layer)
+ * @in_handle_ep: number of recursions of handle_ep() function
+ * 	Prevents deadlocks or infinite recursions of types :
+ *	  irq->handle_ep()->req_done()->req.complete()->pxa_ep_queue()->handle_ep()
+ *      or
+ *        pxa_ep_queue()->handle_ep()->req_done()->req.complete()->pxa_ep_queue()
+ * @idx: endpoint index (1 => epA, 2 => epB, ..., 24 => epX)
+ * @name: endpoint name (for trace/debug purpose)
+ * @dir_in: 1 if IN endpoint, 0 if OUT endpoint
+ * @addr: usb endpoint number
+ * @config: configuration in which this endpoint is active
+ * @interface: interface in which this endpoint is active
+ * @alternate: altsetting in which this endpoitn is active
+ * @fifo_size: max packet size in the endpoint fifo
+ * @type: endpoint type (bulk, iso, int, ...)
+ * @udccsr_value: save register of UDCCSR0 for suspend/resume
+ * @udccr_value: save register of UDCCR for suspend/resume
+ * @stats: endpoint statistics
+ *
+ * The *PROBLEM* is that pxa's endpoint configuration scheme is both misdesigned
+ * (cares about config/interface/altsetting, thus placing needless limits on
+ * device capability) and full of implementation bugs forcing it to be set up
+ * for use more or less like a pxa255.
+ *
+ * As we define the pxa_ep statically, we must guess all needed pxa_ep for all
+ * gadget which may work with this udc driver.
+ */
+struct pxa_ep {
+	struct pxa_udc		*dev;
+
+	struct list_head	queue;
+	spinlock_t		lock;		/* Protects this structure */
+						/* (queues, stats) */
+	unsigned		enabled:1;
+	unsigned		in_handle_ep:1;
+
+	unsigned		idx:5;
+	char			*name;
+
+	/*
+	 * Specific pxa endpoint data, needed for hardware initialization
+	 */
+	unsigned		dir_in:1;
+	unsigned		addr:4;
+	unsigned		config:2;
+	unsigned		interface:3;
+	unsigned		alternate:3;
+	unsigned		fifo_size;
+	unsigned		type;
+
+#ifdef CONFIG_PM
+	u32			udccsr_value;
+	u32			udccr_value;
+#endif
+	struct stats		stats;
+};
+
+/**
+ * struct pxa27x_request - container of each usb_request structure
+ * @req: usb request
+ * @udc_usb_ep: usb endpoint the request was submitted on
+ * @in_use: sanity check if request already queued on an pxa_ep
+ * @queue: linked list of requests, linked on pxa_ep->queue
+ */
+struct pxa27x_request {
+	struct usb_request			req;
+	struct udc_usb_ep			*udc_usb_ep;
+	unsigned				in_use:1;
+	struct list_head			queue;
+};
+
+enum ep0_state {
+	WAIT_FOR_SETUP,
+	SETUP_STAGE,
+	IN_DATA_STAGE,
+	OUT_DATA_STAGE,
+	IN_STATUS_STAGE,
+	OUT_STATUS_STAGE,
+	STALL,
+	WAIT_ACK_SET_CONF_INTERF
+};
+
+static char *ep0_state_name[] = {
+	"WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE",
+	"IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL",
+	"WAIT_ACK_SET_CONF_INTERF"
+};
+#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state]
+
+#define EP0_FIFO_SIZE	16U
+#define BULK_FIFO_SIZE	64U
+#define ISO_FIFO_SIZE	256U
+#define INT_FIFO_SIZE	16U
+
+struct udc_stats {
+	unsigned long	irqs_reset;
+	unsigned long	irqs_suspend;
+	unsigned long	irqs_resume;
+	unsigned long	irqs_reconfig;
+};
+
+#define NR_USB_ENDPOINTS (1 + 5)	/* ep0 + ep1in-bulk + .. + ep3in-iso */
+#define NR_PXA_ENDPOINTS (1 + 14)	/* ep0 + epA + epB + .. + epX */
+
+/**
+ * struct pxa_udc - udc structure
+ * @regs: mapped IO space
+ * @irq: udc irq
+ * @clk: udc clock
+ * @usb_gadget: udc gadget structure
+ * @driver: bound gadget (zero, g_ether, g_mass_storage, ...)
+ * @dev: device
+ * @udc_command: machine specific function to activate D+ pullup
+ * @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none)
+ * @transceiver: external transceiver to handle vbus sense and D+ pullup
+ * @ep0state: control endpoint state machine state
+ * @stats: statistics on udc usage
+ * @udc_usb_ep: array of usb endpoints offered by the gadget
+ * @pxa_ep: array of pxa available endpoints
+ * @enabled: UDC was enabled by a previous udc_enable()
+ * @pullup_on: if pullup resistor connected to D+ pin
+ * @pullup_resume: if pullup resistor should be connected to D+ pin on resume
+ * @config: UDC active configuration
+ * @last_interface: UDC interface of the last SET_INTERFACE host request
+ * @last_alternate: UDC altsetting of the last SET_INTERFACE host request
+ * @udccsr0: save of udccsr0 in case of suspend
+ * @debugfs_root: root entry of debug filesystem
+ * @debugfs_state: debugfs entry for "udcstate"
+ * @debugfs_queues: debugfs entry for "queues"
+ * @debugfs_eps: debugfs entry for "epstate"
+ */
+struct pxa_udc {
+	void __iomem				*regs;
+	int					irq;
+	struct clk				*clk;
+
+	struct usb_gadget			gadget;
+	struct usb_gadget_driver		*driver;
+	struct device				*dev;
+	void					(*udc_command)(int);
+	struct gpio_desc			*gpiod;
+	struct usb_phy				*transceiver;
+
+	enum ep0_state				ep0state;
+	struct udc_stats			stats;
+
+	struct udc_usb_ep			udc_usb_ep[NR_USB_ENDPOINTS];
+	struct pxa_ep				pxa_ep[NR_PXA_ENDPOINTS];
+
+	unsigned				enabled:1;
+	unsigned				pullup_on:1;
+	unsigned				pullup_resume:1;
+	unsigned				vbus_sensed:1;
+	unsigned				config:2;
+	unsigned				last_interface:3;
+	unsigned				last_alternate:3;
+
+#ifdef CONFIG_PM
+	unsigned				udccsr0;
+#endif
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+	struct dentry				*debugfs_root;
+#endif
+};
+#define to_pxa(g)	(container_of((g), struct pxa_udc, gadget))
+
+static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct pxa_udc, gadget);
+}
+
+/*
+ * Debugging/message support
+ */
+#define ep_dbg(ep, fmt, arg...) \
+	dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_vdbg(ep, fmt, arg...) \
+	dev_vdbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_err(ep, fmt, arg...) \
+	dev_err(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_info(ep, fmt, arg...) \
+	dev_info(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_warn(ep, fmt, arg...) \
+	dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg)
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
new file mode 100644
index 0000000..11e25a3
--- /dev/null
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -0,0 +1,1981 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R8A66597 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2009 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "r8a66597-udc.h"
+
+#define DRIVER_VERSION	"2011-09-26"
+
+static const char udc_name[] = "r8a66597_udc";
+static const char *r8a66597_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7",
+	"ep8", "ep9",
+};
+
+static void init_controller(struct r8a66597 *r8a66597);
+static void disable_controller(struct r8a66597 *r8a66597);
+static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req);
+static void irq_packet_write(struct r8a66597_ep *ep,
+				struct r8a66597_request *req);
+static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags);
+
+static void transfer_complete(struct r8a66597_ep *ep,
+		struct r8a66597_request *req, int status);
+
+/*-------------------------------------------------------------------------*/
+static inline u16 get_usb_speed(struct r8a66597 *r8a66597)
+{
+	return r8a66597_read(r8a66597, DVSTCTR0) & RHST;
+}
+
+static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+		unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, INTENB0);
+	r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE,
+			INTENB0);
+	r8a66597_bset(r8a66597, (1 << pipenum), reg);
+	r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+		unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, INTENB0);
+	r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE,
+			INTENB0);
+	r8a66597_bclr(r8a66597, (1 << pipenum), reg);
+	r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void r8a66597_usb_connect(struct r8a66597 *r8a66597)
+{
+	r8a66597_bset(r8a66597, CTRE, INTENB0);
+	r8a66597_bset(r8a66597, BEMPE | BRDYE, INTENB0);
+
+	r8a66597_bset(r8a66597, DPRPU, SYSCFG0);
+}
+
+static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+	r8a66597_bclr(r8a66597, CTRE, INTENB0);
+	r8a66597_bclr(r8a66597, BEMPE | BRDYE, INTENB0);
+	r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+
+	r8a66597->gadget.speed = USB_SPEED_UNKNOWN;
+	spin_unlock(&r8a66597->lock);
+	r8a66597->driver->disconnect(&r8a66597->gadget);
+	spin_lock(&r8a66597->lock);
+
+	disable_controller(r8a66597);
+	init_controller(r8a66597);
+	r8a66597_bset(r8a66597, VBSE, INTENB0);
+	INIT_LIST_HEAD(&r8a66597->ep[0].queue);
+}
+
+static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	u16 pid = 0;
+	unsigned long offset;
+
+	if (pipenum == 0) {
+		pid = r8a66597_read(r8a66597, DCPCTR) & PID;
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		pid = r8a66597_read(r8a66597, offset) & PID;
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+			pipenum);
+	}
+
+	return pid;
+}
+
+static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum,
+		u16 pid)
+{
+	unsigned long offset;
+
+	if (pipenum == 0) {
+		r8a66597_mdfy(r8a66597, pid, PID, DCPCTR);
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		r8a66597_mdfy(r8a66597, pid, PID, offset);
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+			pipenum);
+	}
+}
+
+static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	control_reg_set_pid(r8a66597, pipenum, PID_BUF);
+}
+
+static inline void pipe_stop(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	control_reg_set_pid(r8a66597, pipenum, PID_NAK);
+}
+
+static inline void pipe_stall(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	control_reg_set_pid(r8a66597, pipenum, PID_STALL);
+}
+
+static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	u16 ret = 0;
+	unsigned long offset;
+
+	if (pipenum == 0) {
+		ret = r8a66597_read(r8a66597, DCPCTR);
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		ret = r8a66597_read(r8a66597, offset);
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+			pipenum);
+	}
+
+	return ret;
+}
+
+static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	unsigned long offset;
+
+	pipe_stop(r8a66597, pipenum);
+
+	if (pipenum == 0) {
+		r8a66597_bset(r8a66597, SQCLR, DCPCTR);
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		r8a66597_bset(r8a66597, SQCLR, offset);
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n",
+			pipenum);
+	}
+}
+
+static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	unsigned long offset;
+
+	pipe_stop(r8a66597, pipenum);
+
+	if (pipenum == 0) {
+		r8a66597_bset(r8a66597, SQSET, DCPCTR);
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		r8a66597_bset(r8a66597, SQSET, offset);
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597),
+			"unexpect pipe num(%d)\n", pipenum);
+	}
+}
+
+static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	unsigned long offset;
+
+	if (pipenum == 0) {
+		return r8a66597_read(r8a66597, DCPCTR) & SQMON;
+	} else if (pipenum < R8A66597_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		return r8a66597_read(r8a66597, offset) & SQMON;
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597),
+			"unexpect pipe num(%d)\n", pipenum);
+	}
+
+	return 0;
+}
+
+static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	return control_reg_sqmon(r8a66597, pipenum);
+}
+
+static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum,
+			       u16 toggle)
+{
+	if (toggle)
+		control_reg_sqset(r8a66597, pipenum);
+	else
+		control_reg_sqclr(r8a66597, pipenum);
+}
+
+static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	u16 tmp;
+	int size;
+
+	if (pipenum == 0) {
+		tmp = r8a66597_read(r8a66597, DCPCFG);
+		if ((tmp & R8A66597_CNTMD) != 0)
+			size = 256;
+		else {
+			tmp = r8a66597_read(r8a66597, DCPMAXP);
+			size = tmp & MAXP;
+		}
+	} else {
+		r8a66597_write(r8a66597, pipenum, PIPESEL);
+		tmp = r8a66597_read(r8a66597, PIPECFG);
+		if ((tmp & R8A66597_CNTMD) != 0) {
+			tmp = r8a66597_read(r8a66597, PIPEBUF);
+			size = ((tmp >> 10) + 1) * 64;
+		} else {
+			tmp = r8a66597_read(r8a66597, PIPEMAXP);
+			size = tmp & MXPS;
+		}
+	}
+
+	return size;
+}
+
+static inline unsigned short mbw_value(struct r8a66597 *r8a66597)
+{
+	if (r8a66597->pdata->on_chip)
+		return MBW_32;
+	else
+		return MBW_16;
+}
+
+static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum,
+				    u16 isel, u16 fifosel)
+{
+	u16 tmp, mask, loop;
+	int i = 0;
+
+	if (!pipenum) {
+		mask = ISEL | CURPIPE;
+		loop = isel;
+	} else {
+		mask = CURPIPE;
+		loop = pipenum;
+	}
+	r8a66597_mdfy(r8a66597, loop, mask, fifosel);
+
+	do {
+		tmp = r8a66597_read(r8a66597, fifosel);
+		if (i++ > 1000000) {
+			dev_err(r8a66597_to_dev(r8a66597),
+				"r8a66597: register%x, loop %x "
+				"is timeout\n", fifosel, loop);
+			break;
+		}
+		ndelay(1);
+	} while ((tmp & mask) != loop);
+}
+
+static void pipe_change(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
+
+	if (ep->use_dma)
+		r8a66597_bclr(r8a66597, DREQE, ep->fifosel);
+
+	r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel);
+
+	ndelay(450);
+
+	if (r8a66597_is_sudmac(r8a66597) && ep->use_dma)
+		r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel);
+	else
+		r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+
+	if (ep->use_dma)
+		r8a66597_bset(r8a66597, DREQE, ep->fifosel);
+}
+
+static int pipe_buffer_setting(struct r8a66597 *r8a66597,
+		struct r8a66597_pipe_info *info)
+{
+	u16 bufnum = 0, buf_bsize = 0;
+	u16 pipecfg = 0;
+
+	if (info->pipe == 0)
+		return -EINVAL;
+
+	r8a66597_write(r8a66597, info->pipe, PIPESEL);
+
+	if (info->dir_in)
+		pipecfg |= R8A66597_DIR;
+	pipecfg |= info->type;
+	pipecfg |= info->epnum;
+	switch (info->type) {
+	case R8A66597_INT:
+		bufnum = 4 + (info->pipe - R8A66597_BASE_PIPENUM_INT);
+		buf_bsize = 0;
+		break;
+	case R8A66597_BULK:
+		/* isochronous pipes may be used as bulk pipes */
+		if (info->pipe >= R8A66597_BASE_PIPENUM_BULK)
+			bufnum = info->pipe - R8A66597_BASE_PIPENUM_BULK;
+		else
+			bufnum = info->pipe - R8A66597_BASE_PIPENUM_ISOC;
+
+		bufnum = R8A66597_BASE_BUFNUM + (bufnum * 16);
+		buf_bsize = 7;
+		pipecfg |= R8A66597_DBLB;
+		if (!info->dir_in)
+			pipecfg |= R8A66597_SHTNAK;
+		break;
+	case R8A66597_ISO:
+		bufnum = R8A66597_BASE_BUFNUM +
+			 (info->pipe - R8A66597_BASE_PIPENUM_ISOC) * 16;
+		buf_bsize = 7;
+		break;
+	}
+
+	if (buf_bsize && ((bufnum + 16) >= R8A66597_MAX_BUFNUM)) {
+		pr_err("r8a66597 pipe memory is insufficient\n");
+		return -ENOMEM;
+	}
+
+	r8a66597_write(r8a66597, pipecfg, PIPECFG);
+	r8a66597_write(r8a66597, (buf_bsize << 10) | (bufnum), PIPEBUF);
+	r8a66597_write(r8a66597, info->maxpacket, PIPEMAXP);
+	if (info->interval)
+		info->interval--;
+	r8a66597_write(r8a66597, info->interval, PIPEPERI);
+
+	return 0;
+}
+
+static void pipe_buffer_release(struct r8a66597 *r8a66597,
+				struct r8a66597_pipe_info *info)
+{
+	if (info->pipe == 0)
+		return;
+
+	if (is_bulk_pipe(info->pipe)) {
+		r8a66597->bulk--;
+	} else if (is_interrupt_pipe(info->pipe)) {
+		r8a66597->interrupt--;
+	} else if (is_isoc_pipe(info->pipe)) {
+		r8a66597->isochronous--;
+		if (info->type == R8A66597_BULK)
+			r8a66597->bulk--;
+	} else {
+		dev_err(r8a66597_to_dev(r8a66597),
+			"ep_release: unexpect pipenum (%d)\n", info->pipe);
+	}
+}
+
+static void pipe_initialize(struct r8a66597_ep *ep)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+
+	r8a66597_mdfy(r8a66597, 0, CURPIPE, ep->fifosel);
+
+	r8a66597_write(r8a66597, ACLRM, ep->pipectr);
+	r8a66597_write(r8a66597, 0, ep->pipectr);
+	r8a66597_write(r8a66597, SQCLR, ep->pipectr);
+	if (ep->use_dma) {
+		r8a66597_mdfy(r8a66597, ep->pipenum, CURPIPE, ep->fifosel);
+
+		ndelay(450);
+
+		r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel);
+	}
+}
+
+static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
+				struct r8a66597_ep *ep,
+				const struct usb_endpoint_descriptor *desc,
+				u16 pipenum, int dma)
+{
+	ep->use_dma = 0;
+	ep->fifoaddr = CFIFO;
+	ep->fifosel = CFIFOSEL;
+	ep->fifoctr = CFIFOCTR;
+
+	ep->pipectr = get_pipectr_addr(pipenum);
+	if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) {
+		ep->pipetre = get_pipetre_addr(pipenum);
+		ep->pipetrn = get_pipetrn_addr(pipenum);
+	} else {
+		ep->pipetre = 0;
+		ep->pipetrn = 0;
+	}
+	ep->pipenum = pipenum;
+	ep->ep.maxpacket = usb_endpoint_maxp(desc);
+	r8a66597->pipenum2ep[pipenum] = ep;
+	r8a66597->epaddr2ep[usb_endpoint_num(desc)]
+		= ep;
+	INIT_LIST_HEAD(&ep->queue);
+}
+
+static void r8a66597_ep_release(struct r8a66597_ep *ep)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	u16 pipenum = ep->pipenum;
+
+	if (pipenum == 0)
+		return;
+
+	if (ep->use_dma)
+		r8a66597->num_dma--;
+	ep->pipenum = 0;
+	ep->busy = 0;
+	ep->use_dma = 0;
+}
+
+static int alloc_pipe_config(struct r8a66597_ep *ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	struct r8a66597_pipe_info info;
+	int dma = 0;
+	unsigned char *counter;
+	int ret;
+
+	ep->ep.desc = desc;
+
+	if (ep->pipenum)	/* already allocated pipe  */
+		return 0;
+
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) {
+			if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
+				dev_err(r8a66597_to_dev(r8a66597),
+					"bulk pipe is insufficient\n");
+				return -ENODEV;
+			} else {
+				info.pipe = R8A66597_BASE_PIPENUM_ISOC
+						+ r8a66597->isochronous;
+				counter = &r8a66597->isochronous;
+			}
+		} else {
+			info.pipe = R8A66597_BASE_PIPENUM_BULK + r8a66597->bulk;
+			counter = &r8a66597->bulk;
+		}
+		info.type = R8A66597_BULK;
+		dma = 1;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) {
+			dev_err(r8a66597_to_dev(r8a66597),
+				"interrupt pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt;
+		info.type = R8A66597_INT;
+		counter = &r8a66597->interrupt;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
+			dev_err(r8a66597_to_dev(r8a66597),
+				"isochronous pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous;
+		info.type = R8A66597_ISO;
+		counter = &r8a66597->isochronous;
+		break;
+	default:
+		dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n");
+		return -EINVAL;
+	}
+	ep->type = info.type;
+
+	info.epnum = usb_endpoint_num(desc);
+	info.maxpacket = usb_endpoint_maxp(desc);
+	info.interval = desc->bInterval;
+	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		info.dir_in = 1;
+	else
+		info.dir_in = 0;
+
+	ret = pipe_buffer_setting(r8a66597, &info);
+	if (ret < 0) {
+		dev_err(r8a66597_to_dev(r8a66597),
+			"pipe_buffer_setting fail\n");
+		return ret;
+	}
+
+	(*counter)++;
+	if ((counter == &r8a66597->isochronous) && info.type == R8A66597_BULK)
+		r8a66597->bulk++;
+
+	r8a66597_ep_setting(r8a66597, ep, desc, info.pipe, dma);
+	pipe_initialize(ep);
+
+	return 0;
+}
+
+static int free_pipe_config(struct r8a66597_ep *ep)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	struct r8a66597_pipe_info info;
+
+	info.pipe = ep->pipenum;
+	info.type = ep->type;
+	pipe_buffer_release(r8a66597, &info);
+	r8a66597_ep_release(ep);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void pipe_irq_enable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	enable_irq_ready(r8a66597, pipenum);
+	enable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	disable_irq_ready(r8a66597, pipenum);
+	disable_irq_nrdy(r8a66597, pipenum);
+}
+
+/* if complete is true, gadget driver complete function is not call */
+static void control_end(struct r8a66597 *r8a66597, unsigned ccpl)
+{
+	r8a66597->ep[0].internal_ccpl = ccpl;
+	pipe_start(r8a66597, 0);
+	r8a66597_bset(r8a66597, CCPL, DCPCTR);
+}
+
+static void start_ep0_write(struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+
+	pipe_change(r8a66597, ep->pipenum);
+	r8a66597_mdfy(r8a66597, ISEL, (ISEL | CURPIPE), CFIFOSEL);
+	r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+	if (req->req.length == 0) {
+		r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+		pipe_start(r8a66597, 0);
+		transfer_complete(ep, req, 0);
+	} else {
+		r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+		irq_ep0_write(ep, req);
+	}
+}
+
+static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum,
+			    u16 fifosel)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE;
+	if (tmp == pipenum)
+		r8a66597_change_curpipe(r8a66597, 0, 0, fifosel);
+}
+
+static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum,
+			     int enable)
+{
+	struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];
+	u16 tmp, toggle;
+
+	/* check current BFRE bit */
+	r8a66597_write(r8a66597, pipenum, PIPESEL);
+	tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE;
+	if ((enable && tmp) || (!enable && !tmp))
+		return;
+
+	/* change BFRE bit */
+	pipe_stop(r8a66597, pipenum);
+	disable_fifosel(r8a66597, pipenum, CFIFOSEL);
+	disable_fifosel(r8a66597, pipenum, D0FIFOSEL);
+	disable_fifosel(r8a66597, pipenum, D1FIFOSEL);
+
+	toggle = save_usb_toggle(r8a66597, pipenum);
+
+	r8a66597_write(r8a66597, pipenum, PIPESEL);
+	if (enable)
+		r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG);
+	else
+		r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG);
+
+	/* initialize for internal BFRE flag */
+	r8a66597_bset(r8a66597, ACLRM, ep->pipectr);
+	r8a66597_bclr(r8a66597, ACLRM, ep->pipectr);
+
+	restore_usb_toggle(r8a66597, pipenum, toggle);
+}
+
+static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
+				struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	struct r8a66597_dma *dma;
+
+	if (!r8a66597_is_sudmac(r8a66597))
+		return -ENODEV;
+
+	/* Check transfer type */
+	if (!is_bulk_pipe(ep->pipenum))
+		return -EIO;
+
+	if (r8a66597->dma.used)
+		return -EBUSY;
+
+	/* set SUDMAC parameters */
+	dma = &r8a66597->dma;
+	dma->used = 1;
+	if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) {
+		dma->dir = 1;
+	} else {
+		dma->dir = 0;
+		change_bfre_mode(r8a66597, ep->pipenum, 1);
+	}
+
+	/* set r8a66597_ep paramters */
+	ep->use_dma = 1;
+	ep->dma = dma;
+	ep->fifoaddr = D0FIFO;
+	ep->fifosel = D0FIFOSEL;
+	ep->fifoctr = D0FIFOCTR;
+
+	/* dma mapping */
+	return usb_gadget_map_request(&r8a66597->gadget, &req->req, dma->dir);
+}
+
+static void sudmac_free_channel(struct r8a66597 *r8a66597,
+				struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	if (!r8a66597_is_sudmac(r8a66597))
+		return;
+
+	usb_gadget_unmap_request(&r8a66597->gadget, &req->req, ep->dma->dir);
+
+	r8a66597_bclr(r8a66597, DREQE, ep->fifosel);
+	r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel);
+
+	ep->dma->used = 0;
+	ep->use_dma = 0;
+	ep->fifoaddr = CFIFO;
+	ep->fifosel = CFIFOSEL;
+	ep->fifoctr = CFIFOCTR;
+}
+
+static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep,
+			 struct r8a66597_request *req)
+{
+	BUG_ON(req->req.length == 0);
+
+	r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG);
+	r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA);
+	r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC);
+	r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL);
+
+	r8a66597_sudmac_write(r8a66597, DEN, CH0DEN);
+}
+
+static void start_packet_write(struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	u16 tmp;
+
+	pipe_change(r8a66597, ep->pipenum);
+	disable_irq_empty(r8a66597, ep->pipenum);
+	pipe_start(r8a66597, ep->pipenum);
+
+	if (req->req.length == 0) {
+		transfer_complete(ep, req, 0);
+	} else {
+		r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS);
+		if (sudmac_alloc_channel(r8a66597, ep, req) < 0) {
+			/* PIO mode */
+			pipe_change(r8a66597, ep->pipenum);
+			disable_irq_empty(r8a66597, ep->pipenum);
+			pipe_start(r8a66597, ep->pipenum);
+			tmp = r8a66597_read(r8a66597, ep->fifoctr);
+			if (unlikely((tmp & FRDY) == 0))
+				pipe_irq_enable(r8a66597, ep->pipenum);
+			else
+				irq_packet_write(ep, req);
+		} else {
+			/* DMA mode */
+			pipe_change(r8a66597, ep->pipenum);
+			disable_irq_nrdy(r8a66597, ep->pipenum);
+			pipe_start(r8a66597, ep->pipenum);
+			enable_irq_nrdy(r8a66597, ep->pipenum);
+			sudmac_start(r8a66597, ep, req);
+		}
+	}
+}
+
+static void start_packet_read(struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	u16 pipenum = ep->pipenum;
+
+	if (ep->pipenum == 0) {
+		r8a66597_mdfy(r8a66597, 0, (ISEL | CURPIPE), CFIFOSEL);
+		r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+		pipe_start(r8a66597, pipenum);
+		pipe_irq_enable(r8a66597, pipenum);
+	} else {
+		pipe_stop(r8a66597, pipenum);
+		if (ep->pipetre) {
+			enable_irq_nrdy(r8a66597, pipenum);
+			r8a66597_write(r8a66597, TRCLR, ep->pipetre);
+			r8a66597_write(r8a66597,
+				DIV_ROUND_UP(req->req.length, ep->ep.maxpacket),
+				ep->pipetrn);
+			r8a66597_bset(r8a66597, TRENB, ep->pipetre);
+		}
+
+		if (sudmac_alloc_channel(r8a66597, ep, req) < 0) {
+			/* PIO mode */
+			change_bfre_mode(r8a66597, ep->pipenum, 0);
+			pipe_start(r8a66597, pipenum);	/* trigger once */
+			pipe_irq_enable(r8a66597, pipenum);
+		} else {
+			pipe_change(r8a66597, pipenum);
+			sudmac_start(r8a66597, ep, req);
+			pipe_start(r8a66597, pipenum);	/* trigger once */
+		}
+	}
+}
+
+static void start_packet(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+	if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
+		start_packet_write(ep, req);
+	else
+		start_packet_read(ep, req);
+}
+
+static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+	u16 ctsq;
+
+	ctsq = r8a66597_read(ep->r8a66597, INTSTS0) & CTSQ;
+
+	switch (ctsq) {
+	case CS_RDDS:
+		start_ep0_write(ep, req);
+		break;
+	case CS_WRDS:
+		start_packet_read(ep, req);
+		break;
+
+	case CS_WRND:
+		control_end(ep->r8a66597, 0);
+		break;
+	default:
+		dev_err(r8a66597_to_dev(ep->r8a66597),
+			"start_ep0: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static void init_controller(struct r8a66597 *r8a66597)
+{
+	u16 vif = r8a66597->pdata->vif ? LDRV : 0;
+	u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0;
+	u16 endian = r8a66597->pdata->endian ? BIGEND : 0;
+
+	if (r8a66597->pdata->on_chip) {
+		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);
+		r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+		r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+		r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+
+		r8a66597_bset(r8a66597, irq_sense, INTENB1);
+		r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR,
+				DMA0CFG);
+	} else {
+		r8a66597_bset(r8a66597, vif | endian, PINCFG);
+		r8a66597_bset(r8a66597, HSE, SYSCFG0);		/* High spd */
+		r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata),
+				XTAL, SYSCFG0);
+
+		r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+		r8a66597_bclr(r8a66597, DPRPU, SYSCFG0);
+		r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+		r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+
+		mdelay(3);
+
+		r8a66597_bset(r8a66597, PLLC, SYSCFG0);
+
+		mdelay(1);
+
+		r8a66597_bset(r8a66597, SCKE, SYSCFG0);
+
+		r8a66597_bset(r8a66597, irq_sense, INTENB1);
+		r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR,
+			       DMA0CFG);
+	}
+}
+
+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);
+		r8a66597_write(r8a66597, 0, INTENB1);
+		r8a66597_write(r8a66597, 0, BRDYENB);
+		r8a66597_write(r8a66597, 0, BEMPENB);
+		r8a66597_write(r8a66597, 0, NRDYENB);
+
+		/* clear status */
+		r8a66597_write(r8a66597, 0, BRDYSTS);
+		r8a66597_write(r8a66597, 0, NRDYSTS);
+		r8a66597_write(r8a66597, 0, BEMPSTS);
+
+		r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+		r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+
+	} else {
+		r8a66597_bclr(r8a66597, UTST, TESTMODE);
+		r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+		udelay(1);
+		r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+		udelay(1);
+		udelay(1);
+		r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+	}
+}
+
+static void r8a66597_start_xclock(struct r8a66597 *r8a66597)
+{
+	u16 tmp;
+
+	if (!r8a66597->pdata->on_chip) {
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (!(tmp & XCKE))
+			r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+	}
+}
+
+static struct r8a66597_request *get_request_from_ep(struct r8a66597_ep *ep)
+{
+	return list_entry(ep->queue.next, struct r8a66597_request, queue);
+}
+
+/*-------------------------------------------------------------------------*/
+static void transfer_complete(struct r8a66597_ep *ep,
+		struct r8a66597_request *req, int status)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+	int restart = 0;
+
+	if (unlikely(ep->pipenum == 0)) {
+		if (ep->internal_ccpl) {
+			ep->internal_ccpl = 0;
+			return;
+		}
+	}
+
+	list_del_init(&req->queue);
+	if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	if (!list_empty(&ep->queue))
+		restart = 1;
+
+	if (ep->use_dma)
+		sudmac_free_channel(ep->r8a66597, ep, req);
+
+	spin_unlock(&ep->r8a66597->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&ep->r8a66597->lock);
+
+	if (restart) {
+		req = get_request_from_ep(ep);
+		if (ep->ep.desc)
+			start_packet(ep, req);
+	}
+}
+
+static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req)
+{
+	int i;
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+
+	pipe_change(r8a66597, pipenum);
+	r8a66597_bset(r8a66597, ISEL, ep->fifosel);
+
+	i = 0;
+	do {
+		tmp = r8a66597_read(r8a66597, ep->fifoctr);
+		if (i++ > 100000) {
+			dev_err(r8a66597_to_dev(r8a66597),
+				"pipe0 is busy. maybe cpu i/o bus "
+				"conflict. please power off this controller.");
+			return;
+		}
+		ndelay(1);
+	} while ((tmp & FRDY) == 0);
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(r8a66597, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		if (size > 0)
+			r8a66597_write_fifo(r8a66597, ep, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
+			r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		disable_irq_ready(r8a66597, pipenum);
+		disable_irq_empty(r8a66597, pipenum);
+	} else {
+		disable_irq_ready(r8a66597, pipenum);
+		enable_irq_empty(r8a66597, pipenum);
+	}
+	pipe_start(r8a66597, pipenum);
+}
+
+static void irq_packet_write(struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+
+	pipe_change(r8a66597, pipenum);
+	tmp = r8a66597_read(r8a66597, ep->fifoctr);
+	if (unlikely((tmp & FRDY) == 0)) {
+		pipe_stop(r8a66597, pipenum);
+		pipe_irq_disable(r8a66597, pipenum);
+		dev_err(r8a66597_to_dev(r8a66597),
+			"write fifo not ready. pipnum=%d\n", pipenum);
+		return;
+	}
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(r8a66597, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		r8a66597_write_fifo(r8a66597, ep, buf, size);
+		if ((size == 0)
+				|| ((size % ep->ep.maxpacket) != 0)
+				|| ((bufsize != ep->ep.maxpacket)
+					&& (bufsize > size)))
+			r8a66597_bset(r8a66597, BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		disable_irq_ready(r8a66597, pipenum);
+		enable_irq_empty(r8a66597, pipenum);
+	} else {
+		disable_irq_empty(r8a66597, pipenum);
+		pipe_irq_enable(r8a66597, pipenum);
+	}
+}
+
+static void irq_packet_read(struct r8a66597_ep *ep,
+				struct r8a66597_request *req)
+{
+	u16 tmp;
+	int rcv_len, bufsize, req_len;
+	int size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct r8a66597 *r8a66597 = ep->r8a66597;
+	int finish = 0;
+
+	pipe_change(r8a66597, pipenum);
+	tmp = r8a66597_read(r8a66597, ep->fifoctr);
+	if (unlikely((tmp & FRDY) == 0)) {
+		req->req.status = -EPIPE;
+		pipe_stop(r8a66597, pipenum);
+		pipe_irq_disable(r8a66597, pipenum);
+		dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready");
+		return;
+	}
+
+	/* prepare parameters */
+	rcv_len = tmp & DTLN;
+	bufsize = get_buffer_size(r8a66597, pipenum);
+
+	buf = req->req.buf + req->req.actual;
+	req_len = req->req.length - req->req.actual;
+	if (rcv_len < bufsize)
+		size = min(rcv_len, req_len);
+	else
+		size = min(bufsize, req_len);
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (size % ep->ep.maxpacket)
+			|| (size == 0)) {
+		pipe_stop(r8a66597, pipenum);
+		pipe_irq_disable(r8a66597, pipenum);
+		finish = 1;
+	}
+
+	/* read fifo */
+	if (req->req.buf) {
+		if (size == 0)
+			r8a66597_write(r8a66597, BCLR, ep->fifoctr);
+		else
+			r8a66597_read_fifo(r8a66597, ep->fifoaddr, buf, size);
+
+	}
+
+	if ((ep->pipenum != 0) && finish)
+		transfer_complete(ep, req, 0);
+}
+
+static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb)
+{
+	u16 check;
+	u16 pipenum;
+	struct r8a66597_ep *ep;
+	struct r8a66597_request *req;
+
+	if ((status & BRDY0) && (enb & BRDY0)) {
+		r8a66597_write(r8a66597, ~BRDY0, BRDYSTS);
+		r8a66597_mdfy(r8a66597, 0, CURPIPE, CFIFOSEL);
+
+		ep = &r8a66597->ep[0];
+		req = get_request_from_ep(ep);
+		irq_packet_read(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				r8a66597_write(r8a66597, ~check, BRDYSTS);
+				ep = r8a66597->pipenum2ep[pipenum];
+				req = get_request_from_ep(ep);
+				if (ep->ep.desc->bEndpointAddress & USB_DIR_IN)
+					irq_packet_write(ep, req);
+				else
+					irq_packet_read(ep, req);
+			}
+		}
+	}
+}
+
+static void irq_pipe_empty(struct r8a66597 *r8a66597, u16 status, u16 enb)
+{
+	u16 tmp;
+	u16 check;
+	u16 pipenum;
+	struct r8a66597_ep *ep;
+	struct r8a66597_request *req;
+
+	if ((status & BEMP0) && (enb & BEMP0)) {
+		r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
+
+		ep = &r8a66597->ep[0];
+		req = get_request_from_ep(ep);
+		irq_ep0_write(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				r8a66597_write(r8a66597, ~check, BEMPSTS);
+				tmp = control_reg_get(r8a66597, pipenum);
+				if ((tmp & INBUFM) == 0) {
+					disable_irq_empty(r8a66597, pipenum);
+					pipe_irq_disable(r8a66597, pipenum);
+					pipe_stop(r8a66597, pipenum);
+					ep = r8a66597->pipenum2ep[pipenum];
+					req = get_request_from_ep(ep);
+					if (!list_empty(&ep->queue))
+						transfer_complete(ep, req, 0);
+				}
+			}
+		}
+	}
+}
+
+static void get_status(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+	struct r8a66597_ep *ep;
+	u16 pid;
+	u16 status = 0;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		status = r8a66597->device_status;
+		break;
+	case USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		pid = control_reg_get_pid(r8a66597, ep->pipenum);
+		if (pid == PID_STALL)
+			status = 1 << USB_ENDPOINT_HALT;
+		else
+			status = 0;
+		break;
+	default:
+		pipe_stall(r8a66597, 0);
+		return;		/* exit */
+	}
+
+	r8a66597->ep0_data = cpu_to_le16(status);
+	r8a66597->ep0_req->buf = &r8a66597->ep0_data;
+	r8a66597->ep0_req->length = 2;
+	/* AV: what happens if we get called again before that gets through? */
+	spin_unlock(&r8a66597->lock);
+	r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_ATOMIC);
+	spin_lock(&r8a66597->lock);
+}
+
+static void clear_feature(struct r8a66597 *r8a66597,
+				struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(r8a66597, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(r8a66597, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct r8a66597_ep *ep;
+		struct r8a66597_request *req;
+		u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+		ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		if (!ep->wedge) {
+			pipe_stop(r8a66597, ep->pipenum);
+			control_reg_sqclr(r8a66597, ep->pipenum);
+			spin_unlock(&r8a66597->lock);
+			usb_ep_clear_halt(&ep->ep);
+			spin_lock(&r8a66597->lock);
+		}
+
+		control_end(r8a66597, 1);
+
+		req = get_request_from_ep(ep);
+		if (ep->busy) {
+			ep->busy = 0;
+			if (list_empty(&ep->queue))
+				break;
+			start_packet(ep, req);
+		} else if (!list_empty(&ep->queue))
+			pipe_start(r8a66597, ep->pipenum);
+		}
+		break;
+	default:
+		pipe_stall(r8a66597, 0);
+		break;
+	}
+}
+
+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:
+		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);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct r8a66597_ep *ep;
+		u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+		ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK];
+		pipe_stall(r8a66597, ep->pipenum);
+
+		control_end(r8a66597, 1);
+		}
+		break;
+	default:
+		pipe_stall(r8a66597, 0);
+		break;
+	}
+}
+
+/* if return value is true, call class driver's setup() */
+static int setup_packet(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl)
+{
+	u16 *p = (u16 *)ctrl;
+	unsigned long offset = USBREQ;
+	int i, ret = 0;
+
+	/* read fifo */
+	r8a66597_write(r8a66597, ~VALID, INTSTS0);
+
+	for (i = 0; i < 4; i++)
+		p[i] = r8a66597_read(r8a66597, offset + i*2);
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			get_status(r8a66597, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			clear_feature(r8a66597, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			set_feature(r8a66597, ctrl);
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else
+		ret = 1;
+	return ret;
+}
+
+static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597)
+{
+	u16 speed = get_usb_speed(r8a66597);
+
+	switch (speed) {
+	case HSMODE:
+		r8a66597->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case FSMODE:
+		r8a66597->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		r8a66597->gadget.speed = USB_SPEED_UNKNOWN;
+		dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n");
+	}
+}
+
+static void irq_device_state(struct r8a66597 *r8a66597)
+{
+	u16 dvsq;
+
+	dvsq = r8a66597_read(r8a66597, INTSTS0) & DVSQ;
+	r8a66597_write(r8a66597, ~DVST, INTSTS0);
+
+	if (dvsq == DS_DFLT) {
+		/* bus reset */
+		spin_unlock(&r8a66597->lock);
+		usb_gadget_udc_reset(&r8a66597->gadget, r8a66597->driver);
+		spin_lock(&r8a66597->lock);
+		r8a66597_update_usb_speed(r8a66597);
+	}
+	if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG)
+		r8a66597_update_usb_speed(r8a66597);
+	if ((dvsq == DS_CNFG || dvsq == DS_ADDS)
+			&& r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+		r8a66597_update_usb_speed(r8a66597);
+
+	r8a66597->old_dvsq = dvsq;
+}
+
+static void irq_control_stage(struct r8a66597 *r8a66597)
+__releases(r8a66597->lock)
+__acquires(r8a66597->lock)
+{
+	struct usb_ctrlrequest ctrl;
+	u16 ctsq;
+
+	ctsq = r8a66597_read(r8a66597, INTSTS0) & CTSQ;
+	r8a66597_write(r8a66597, ~CTRT, INTSTS0);
+
+	switch (ctsq) {
+	case CS_IDST: {
+		struct r8a66597_ep *ep;
+		struct r8a66597_request *req;
+		ep = &r8a66597->ep[0];
+		req = get_request_from_ep(ep);
+		transfer_complete(ep, req, 0);
+		}
+		break;
+
+	case CS_RDDS:
+	case CS_WRDS:
+	case CS_WRND:
+		if (setup_packet(r8a66597, &ctrl)) {
+			spin_unlock(&r8a66597->lock);
+			if (r8a66597->driver->setup(&r8a66597->gadget, &ctrl)
+				< 0)
+				pipe_stall(r8a66597, 0);
+			spin_lock(&r8a66597->lock);
+		}
+		break;
+	case CS_RDSS:
+	case CS_WRSS:
+		control_end(r8a66597, 0);
+		break;
+	default:
+		dev_err(r8a66597_to_dev(r8a66597),
+			"ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep)
+{
+	u16 pipenum;
+	struct r8a66597_request *req;
+	u32 len;
+	int i = 0;
+
+	pipenum = ep->pipenum;
+	pipe_change(r8a66597, pipenum);
+
+	while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) {
+		udelay(1);
+		if (unlikely(i++ >= 10000)) {	/* timeout = 10 msec */
+			dev_err(r8a66597_to_dev(r8a66597),
+				"%s: FRDY was not set (%d)\n",
+				__func__, pipenum);
+			return;
+		}
+	}
+
+	r8a66597_bset(r8a66597, BCLR, ep->fifoctr);
+	req = get_request_from_ep(ep);
+
+	/* prepare parameters */
+	len = r8a66597_sudmac_read(r8a66597, CH0CBC);
+	req->req.actual += len;
+
+	/* clear */
+	r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR);
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length))
+			|| (len % ep->ep.maxpacket)) {
+		if (ep->dma->dir) {
+			disable_irq_ready(r8a66597, pipenum);
+			enable_irq_empty(r8a66597, pipenum);
+		} else {
+			/* Clear the interrupt flag for next transfer */
+			r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
+			transfer_complete(ep, req, 0);
+		}
+	}
+}
+
+static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597)
+{
+	u32 irqsts;
+	struct r8a66597_ep *ep;
+	u16 pipenum;
+
+	irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS);
+	if (irqsts & CH0ENDS) {
+		r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR);
+		pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE);
+		ep = r8a66597->pipenum2ep[pipenum];
+		sudmac_finish(r8a66597, ep);
+	}
+}
+
+static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
+{
+	struct r8a66597 *r8a66597 = _r8a66597;
+	u16 intsts0;
+	u16 intenb0;
+	u16 savepipe;
+	u16 mask0;
+
+	spin_lock(&r8a66597->lock);
+
+	if (r8a66597_is_sudmac(r8a66597))
+		r8a66597_sudmac_irq(r8a66597);
+
+	intsts0 = r8a66597_read(r8a66597, INTSTS0);
+	intenb0 = r8a66597_read(r8a66597, INTENB0);
+
+	savepipe = r8a66597_read(r8a66597, CFIFOSEL);
+
+	mask0 = intsts0 & intenb0;
+	if (mask0) {
+		u16 brdysts = r8a66597_read(r8a66597, BRDYSTS);
+		u16 bempsts = r8a66597_read(r8a66597, BEMPSTS);
+		u16 brdyenb = r8a66597_read(r8a66597, BRDYENB);
+		u16 bempenb = r8a66597_read(r8a66597, BEMPENB);
+
+		if (mask0 & VBINT) {
+			r8a66597_write(r8a66597,  0xffff & ~VBINT,
+					INTSTS0);
+			r8a66597_start_xclock(r8a66597);
+
+			/* start vbus sampling */
+			r8a66597->old_vbus = r8a66597_read(r8a66597, INTSTS0)
+					& VBSTS;
+			r8a66597->scount = R8A66597_MAX_SAMPLING;
+
+			mod_timer(&r8a66597->timer,
+					jiffies + msecs_to_jiffies(50));
+		}
+		if (intsts0 & DVSQ)
+			irq_device_state(r8a66597);
+
+		if ((intsts0 & BRDY) && (intenb0 & BRDYE)
+				&& (brdysts & brdyenb))
+			irq_pipe_ready(r8a66597, brdysts, brdyenb);
+		if ((intsts0 & BEMP) && (intenb0 & BEMPE)
+				&& (bempsts & bempenb))
+			irq_pipe_empty(r8a66597, bempsts, bempenb);
+
+		if (intsts0 & CTRT)
+			irq_control_stage(r8a66597);
+	}
+
+	r8a66597_write(r8a66597, savepipe, CFIFOSEL);
+
+	spin_unlock(&r8a66597->lock);
+	return IRQ_HANDLED;
+}
+
+static void r8a66597_timer(struct timer_list *t)
+{
+	struct r8a66597 *r8a66597 = from_timer(r8a66597, t, timer);
+	unsigned long flags;
+	u16 tmp;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	tmp = r8a66597_read(r8a66597, SYSCFG0);
+	if (r8a66597->scount > 0) {
+		tmp = r8a66597_read(r8a66597, INTSTS0) & VBSTS;
+		if (tmp == r8a66597->old_vbus) {
+			r8a66597->scount--;
+			if (r8a66597->scount == 0) {
+				if (tmp == VBSTS)
+					r8a66597_usb_connect(r8a66597);
+				else
+					r8a66597_usb_disconnect(r8a66597);
+			} else {
+				mod_timer(&r8a66597->timer,
+					jiffies + msecs_to_jiffies(50));
+			}
+		} else {
+			r8a66597->scount = R8A66597_MAX_SAMPLING;
+			r8a66597->old_vbus = tmp;
+			mod_timer(&r8a66597->timer,
+					jiffies + msecs_to_jiffies(50));
+		}
+	}
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static int r8a66597_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct r8a66597_ep *ep;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+	return alloc_pipe_config(ep, desc);
+}
+
+static int r8a66597_disable(struct usb_ep *_ep)
+{
+	struct r8a66597_ep *ep;
+	struct r8a66597_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+	BUG_ON(!ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = get_request_from_ep(ep);
+		spin_lock_irqsave(&ep->r8a66597->lock, flags);
+		transfer_complete(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+	}
+
+	pipe_irq_disable(ep->r8a66597, ep->pipenum);
+	return free_pipe_config(ep);
+}
+
+static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct r8a66597_request *req;
+
+	req = kzalloc(sizeof(struct r8a66597_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void r8a66597_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct r8a66597_request *req;
+
+	req = container_of(_req, struct r8a66597_request, req);
+	kfree(req);
+}
+
+static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct r8a66597_ep *ep;
+	struct r8a66597_request *req;
+	unsigned long flags;
+	int request = 0;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+	req = container_of(_req, struct r8a66597_request, req);
+
+	if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->r8a66597->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (ep->ep.desc == NULL)	/* control */
+		start_ep0(ep, req);
+	else {
+		if (request && !ep->busy)
+			start_packet(ep, req);
+	}
+
+	spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+	return 0;
+}
+
+static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct r8a66597_ep *ep;
+	struct r8a66597_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+	req = container_of(_req, struct r8a66597_request, req);
+
+	spin_lock_irqsave(&ep->r8a66597->lock, flags);
+	if (!list_empty(&ep->queue))
+		transfer_complete(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+	return 0;
+}
+
+static int r8a66597_set_halt(struct usb_ep *_ep, int value)
+{
+	struct r8a66597_ep *ep = container_of(_ep, struct r8a66597_ep, ep);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ep->r8a66597->lock, flags);
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+	} else if (value) {
+		ep->busy = 1;
+		pipe_stall(ep->r8a66597, ep->pipenum);
+	} else {
+		ep->busy = 0;
+		ep->wedge = 0;
+		pipe_stop(ep->r8a66597, ep->pipenum);
+	}
+	spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+	return ret;
+}
+
+static int r8a66597_set_wedge(struct usb_ep *_ep)
+{
+	struct r8a66597_ep *ep;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+
+	if (!ep || !ep->ep.desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->r8a66597->lock, flags);
+	ep->wedge = 1;
+	spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+
+	return usb_ep_set_halt(_ep);
+}
+
+static void r8a66597_fifo_flush(struct usb_ep *_ep)
+{
+	struct r8a66597_ep *ep;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct r8a66597_ep, ep);
+	spin_lock_irqsave(&ep->r8a66597->lock, flags);
+	if (list_empty(&ep->queue) && !ep->busy) {
+		pipe_stop(ep->r8a66597, ep->pipenum);
+		r8a66597_bclr(ep->r8a66597, BCLR, ep->fifoctr);
+		r8a66597_write(ep->r8a66597, ACLRM, ep->pipectr);
+		r8a66597_write(ep->r8a66597, 0, ep->pipectr);
+	}
+	spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
+}
+
+static const struct usb_ep_ops r8a66597_ep_ops = {
+	.enable		= r8a66597_enable,
+	.disable	= r8a66597_disable,
+
+	.alloc_request	= r8a66597_alloc_request,
+	.free_request	= r8a66597_free_request,
+
+	.queue		= r8a66597_queue,
+	.dequeue	= r8a66597_dequeue,
+
+	.set_halt	= r8a66597_set_halt,
+	.set_wedge	= r8a66597_set_wedge,
+	.fifo_flush	= r8a66597_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+static int r8a66597_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+
+	if (!driver
+			|| driver->max_speed < USB_SPEED_HIGH
+			|| !driver->setup)
+		return -EINVAL;
+	if (!r8a66597)
+		return -ENODEV;
+
+	/* hook up the driver */
+	r8a66597->driver = driver;
+
+	init_controller(r8a66597);
+	r8a66597_bset(r8a66597, VBSE, INTENB0);
+	if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) {
+		r8a66597_start_xclock(r8a66597);
+		/* start vbus sampling */
+		r8a66597->old_vbus = r8a66597_read(r8a66597,
+					 INTSTS0) & VBSTS;
+		r8a66597->scount = R8A66597_MAX_SAMPLING;
+		mod_timer(&r8a66597->timer, jiffies + msecs_to_jiffies(50));
+	}
+
+	return 0;
+}
+
+static int r8a66597_stop(struct usb_gadget *gadget)
+{
+	struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	r8a66597_bclr(r8a66597, VBSE, INTENB0);
+	disable_controller(r8a66597);
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+	r8a66597->driver = NULL;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int r8a66597_get_frame(struct usb_gadget *_gadget)
+{
+	struct r8a66597 *r8a66597 = gadget_to_r8a66597(_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 int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
+{
+	struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+
+	gadget->is_selfpowered = (is_self != 0);
+	if (is_self)
+		r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
+	else
+		r8a66597->device_status &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops r8a66597_gadget_ops = {
+	.get_frame		= r8a66597_get_frame,
+	.udc_start		= r8a66597_start,
+	.udc_stop		= r8a66597_stop,
+	.pullup			= r8a66597_pullup,
+	.set_selfpowered	= r8a66597_set_selfpowered,
+};
+
+static int r8a66597_remove(struct platform_device *pdev)
+{
+	struct r8a66597		*r8a66597 = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&r8a66597->gadget);
+	del_timer_sync(&r8a66597->timer);
+	r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
+
+	if (r8a66597->pdata->on_chip) {
+		clk_disable_unprepare(r8a66597->clk);
+	}
+
+	return 0;
+}
+
+static void nop_completion(struct usb_ep *ep, struct usb_request *r)
+{
+}
+
+static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
+					  struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
+	r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
+	return PTR_ERR_OR_ZERO(r8a66597->sudmac_reg);
+}
+
+static int r8a66597_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	char clk_name[8];
+	struct resource *res, *ires;
+	int irq;
+	void __iomem *reg = NULL;
+	struct r8a66597 *r8a66597 = NULL;
+	int ret = 0;
+	int i;
+	unsigned long irq_trigger;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	irq = ires->start;
+	irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
+
+	if (irq < 0) {
+		dev_err(dev, "platform_get_irq error.\n");
+		return -ENODEV;
+	}
+
+	/* initialize ucd */
+	r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL);
+	if (r8a66597 == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&r8a66597->lock);
+	platform_set_drvdata(pdev, r8a66597);
+	r8a66597->pdata = dev_get_platdata(dev);
+	r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
+
+	r8a66597->gadget.ops = &r8a66597_gadget_ops;
+	r8a66597->gadget.max_speed = USB_SPEED_HIGH;
+	r8a66597->gadget.name = udc_name;
+
+	timer_setup(&r8a66597->timer, r8a66597_timer, 0);
+	r8a66597->reg = reg;
+
+	if (r8a66597->pdata->on_chip) {
+		snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+		r8a66597->clk = devm_clk_get(dev, clk_name);
+		if (IS_ERR(r8a66597->clk)) {
+			dev_err(dev, "cannot get clock \"%s\"\n", clk_name);
+			return PTR_ERR(r8a66597->clk);
+		}
+		clk_prepare_enable(r8a66597->clk);
+	}
+
+	if (r8a66597->pdata->sudmac) {
+		ret = r8a66597_sudmac_ioremap(r8a66597, pdev);
+		if (ret < 0)
+			goto clean_up2;
+	}
+
+	disable_controller(r8a66597); /* make sure controller is disabled */
+
+	ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED,
+			       udc_name, r8a66597);
+	if (ret < 0) {
+		dev_err(dev, "request_irq error (%d)\n", ret);
+		goto clean_up2;
+	}
+
+	INIT_LIST_HEAD(&r8a66597->gadget.ep_list);
+	r8a66597->gadget.ep0 = &r8a66597->ep[0].ep;
+	INIT_LIST_HEAD(&r8a66597->gadget.ep0->ep_list);
+	for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
+		struct r8a66597_ep *ep = &r8a66597->ep[i];
+
+		if (i != 0) {
+			INIT_LIST_HEAD(&r8a66597->ep[i].ep.ep_list);
+			list_add_tail(&r8a66597->ep[i].ep.ep_list,
+					&r8a66597->gadget.ep_list);
+		}
+		ep->r8a66597 = r8a66597;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = r8a66597_ep_name[i];
+		ep->ep.ops = &r8a66597_ep_ops;
+		usb_ep_set_maxpacket_limit(&ep->ep, 512);
+
+		if (i == 0) {
+			ep->ep.caps.type_control = true;
+		} else {
+			ep->ep.caps.type_iso = true;
+			ep->ep.caps.type_bulk = true;
+			ep->ep.caps.type_int = true;
+		}
+		ep->ep.caps.dir_in = true;
+		ep->ep.caps.dir_out = true;
+	}
+	usb_ep_set_maxpacket_limit(&r8a66597->ep[0].ep, 64);
+	r8a66597->ep[0].pipenum = 0;
+	r8a66597->ep[0].fifoaddr = CFIFO;
+	r8a66597->ep[0].fifosel = CFIFOSEL;
+	r8a66597->ep[0].fifoctr = CFIFOCTR;
+	r8a66597->ep[0].pipectr = get_pipectr_addr(0);
+	r8a66597->pipenum2ep[0] = &r8a66597->ep[0];
+	r8a66597->epaddr2ep[0] = &r8a66597->ep[0];
+
+	r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep,
+							GFP_KERNEL);
+	if (r8a66597->ep0_req == NULL) {
+		ret = -ENOMEM;
+		goto clean_up2;
+	}
+	r8a66597->ep0_req->complete = nop_completion;
+
+	ret = usb_add_gadget_udc(dev, &r8a66597->gadget);
+	if (ret)
+		goto err_add_udc;
+
+	dev_info(dev, "version %s\n", DRIVER_VERSION);
+	return 0;
+
+err_add_udc:
+	r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
+clean_up2:
+	if (r8a66597->pdata->on_chip)
+		clk_disable_unprepare(r8a66597->clk);
+
+	if (r8a66597->ep0_req)
+		r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver r8a66597_driver = {
+	.remove =	r8a66597_remove,
+	.driver		= {
+		.name =	(char *) udc_name,
+	},
+};
+
+module_platform_driver_probe(r8a66597_driver, r8a66597_probe);
+
+MODULE_DESCRIPTION("R8A66597 USB gadget driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_ALIAS("platform:r8a66597_udc");
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h
new file mode 100644
index 0000000..9a115ca
--- /dev/null
+++ b/drivers/usb/gadget/udc/r8a66597-udc.h
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R8A66597 UDC
+ *
+ * Copyright (C) 2007-2009 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+ */
+
+#ifndef __R8A66597_H__
+#define __R8A66597_H__
+
+#include <linux/clk.h>
+#include <linux/usb/r8a66597.h>
+
+#define R8A66597_MAX_SAMPLING	10
+
+#define R8A66597_MAX_NUM_PIPE	8
+#define R8A66597_MAX_NUM_BULK	3
+#define R8A66597_MAX_NUM_ISOC	2
+#define R8A66597_MAX_NUM_INT	2
+
+#define R8A66597_BASE_PIPENUM_BULK	3
+#define R8A66597_BASE_PIPENUM_ISOC	1
+#define R8A66597_BASE_PIPENUM_INT	6
+
+#define R8A66597_BASE_BUFNUM	6
+#define R8A66597_MAX_BUFNUM	0x4F
+
+#define is_bulk_pipe(pipenum)	\
+	((pipenum >= R8A66597_BASE_PIPENUM_BULK) && \
+	 (pipenum < (R8A66597_BASE_PIPENUM_BULK + R8A66597_MAX_NUM_BULK)))
+#define is_interrupt_pipe(pipenum)	\
+	((pipenum >= R8A66597_BASE_PIPENUM_INT) && \
+	 (pipenum < (R8A66597_BASE_PIPENUM_INT + R8A66597_MAX_NUM_INT)))
+#define is_isoc_pipe(pipenum)	\
+	((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \
+	 (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC)))
+
+#define r8a66597_is_sudmac(r8a66597)	(r8a66597->pdata->sudmac)
+struct r8a66597_pipe_info {
+	u16	pipe;
+	u16	epnum;
+	u16	maxpacket;
+	u16	type;
+	u16	interval;
+	u16	dir_in;
+};
+
+struct r8a66597_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+struct r8a66597_ep {
+	struct usb_ep		ep;
+	struct r8a66597		*r8a66597;
+	struct r8a66597_dma	*dma;
+
+	struct list_head	queue;
+	unsigned		busy:1;
+	unsigned		wedge:1;
+	unsigned		internal_ccpl:1;	/* use only control */
+
+	/* this member can able to after r8a66597_enable */
+	unsigned		use_dma:1;
+	u16			pipenum;
+	u16			type;
+
+	/* register address */
+	unsigned char		fifoaddr;
+	unsigned char		fifosel;
+	unsigned char		fifoctr;
+	unsigned char		pipectr;
+	unsigned char		pipetre;
+	unsigned char		pipetrn;
+};
+
+struct r8a66597_dma {
+	unsigned		used:1;
+	unsigned		dir:1;	/* 1 = IN(write), 0 = OUT(read) */
+};
+
+struct r8a66597 {
+	spinlock_t		lock;
+	void __iomem		*reg;
+	void __iomem		*sudmac_reg;
+
+	struct clk *clk;
+	struct r8a66597_platdata	*pdata;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct r8a66597_ep	ep[R8A66597_MAX_NUM_PIPE];
+	struct r8a66597_ep	*pipenum2ep[R8A66597_MAX_NUM_PIPE];
+	struct r8a66597_ep	*epaddr2ep[16];
+	struct r8a66597_dma	dma;
+
+	struct timer_list	timer;
+	struct usb_request	*ep0_req;	/* for internal request */
+	u16			ep0_data;	/* for internal request */
+	u16			old_vbus;
+	u16			scount;
+	u16			old_dvsq;
+	u16			device_status;	/* for GET_STATUS */
+
+	/* pipe config */
+	unsigned char bulk;
+	unsigned char interrupt;
+	unsigned char isochronous;
+	unsigned char num_dma;
+
+	unsigned irq_sense_low:1;
+};
+
+#define gadget_to_r8a66597(_gadget)	\
+		container_of(_gadget, struct r8a66597, gadget)
+#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget)
+#define r8a66597_to_dev(r8a66597)	(r8a66597->gadget.dev.parent)
+
+static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
+{
+	return ioread16(r8a66597->reg + offset);
+}
+
+static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
+				      unsigned long offset,
+				      unsigned char *buf,
+				      int len)
+{
+	void __iomem *fifoaddr = r8a66597->reg + offset;
+	unsigned int data = 0;
+	int i;
+
+	if (r8a66597->pdata->on_chip) {
+		/* 32-bit accesses for on_chip controllers */
+
+		/* aligned buf case */
+		if (len >= 4 && !((unsigned long)buf & 0x03)) {
+			ioread32_rep(fifoaddr, buf, len / 4);
+			buf += len & ~0x03;
+			len &= 0x03;
+		}
+
+		/* unaligned buf case */
+		for (i = 0; i < len; i++) {
+			if (!(i & 0x03))
+				data = ioread32(fifoaddr);
+
+			buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+		}
+	} else {
+		/* 16-bit accesses for external controllers */
+
+		/* aligned buf case */
+		if (len >= 2 && !((unsigned long)buf & 0x01)) {
+			ioread16_rep(fifoaddr, buf, len / 2);
+			buf += len & ~0x01;
+			len &= 0x01;
+		}
+
+		/* unaligned buf case */
+		for (i = 0; i < len; i++) {
+			if (!(i & 0x01))
+				data = ioread16(fifoaddr);
+
+			buf[i] = (data >> ((i & 0x01) * 8)) & 0xff;
+		}
+	}
+}
+
+static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
+				  unsigned long offset)
+{
+	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,
+				       struct r8a66597_ep *ep,
+				       unsigned char *buf,
+				       int len)
+{
+	void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr;
+	int adj = 0;
+	int i;
+
+	if (r8a66597->pdata->on_chip) {
+		/* 32-bit access only if buf is 32-bit aligned */
+		if (len >= 4 && !((unsigned long)buf & 0x03)) {
+			iowrite32_rep(fifoaddr, buf, len / 4);
+			buf += len & ~0x03;
+			len &= 0x03;
+		}
+	} else {
+		/* 16-bit access only if buf is 16-bit aligned */
+		if (len >= 2 && !((unsigned long)buf & 0x01)) {
+			iowrite16_rep(fifoaddr, buf, len / 2);
+			buf += len & ~0x01;
+			len &= 0x01;
+		}
+	}
+
+	/* adjust fifo address in the little endian case */
+	if (!(r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)) {
+		if (r8a66597->pdata->on_chip)
+			adj = 0x03; /* 32-bit wide */
+		else
+			adj = 0x01; /* 16-bit wide */
+	}
+
+	if (r8a66597->pdata->wr0_shorted_to_wr1)
+		r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
+	for (i = 0; i < len; i++)
+		iowrite8(buf[i], fifoaddr + adj - (i & adj));
+	if (r8a66597->pdata->wr0_shorted_to_wr1)
+		r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
+}
+
+static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
+{
+	u16 clock = 0;
+
+	switch (pdata->xtal) {
+	case R8A66597_PLATDATA_XTAL_12MHZ:
+		clock = XTAL12;
+		break;
+	case R8A66597_PLATDATA_XTAL_24MHZ:
+		clock = XTAL24;
+		break;
+	case R8A66597_PLATDATA_XTAL_48MHZ:
+		clock = XTAL48;
+		break;
+	default:
+		printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
+		break;
+	}
+
+	return clock;
+}
+
+static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597,
+				       unsigned long offset)
+{
+	return ioread32(r8a66597->sudmac_reg + offset);
+}
+
+static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val,
+					 unsigned long offset)
+{
+	iowrite32(val, r8a66597->sudmac_reg + offset);
+}
+
+#define get_pipectr_addr(pipenum)	(PIPE1CTR + (pipenum - 1) * 2)
+#define get_pipetre_addr(pipenum)	(PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum)	(PIPE1TRN + (pipenum - 1) * 4)
+
+#define enable_irq_ready(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define disable_irq_ready(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define enable_irq_empty(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define disable_irq_empty(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define enable_irq_nrdy(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, NRDYENB)
+#define disable_irq_nrdy(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, NRDYENB)
+
+#endif	/* __R8A66597_H__ */
+
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
new file mode 100644
index 0000000..67d8a50
--- /dev/null
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -0,0 +1,2809 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas USB3.0 Peripheral driver (USB gadget)
+ *
+ * Copyright (C) 2015-2017  Renesas Electronics Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/extcon-provider.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/uaccess.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/of.h>
+#include <linux/usb/role.h>
+
+/* register definitions */
+#define USB3_AXI_INT_STA	0x008
+#define USB3_AXI_INT_ENA	0x00c
+#define USB3_DMA_INT_STA	0x010
+#define USB3_DMA_INT_ENA	0x014
+#define USB3_DMA_CH0_CON(n)	(0x030 + ((n) - 1) * 0x10) /* n = 1 to 4 */
+#define USB3_DMA_CH0_PRD_ADR(n)	(0x034 + ((n) - 1) * 0x10) /* n = 1 to 4 */
+#define USB3_USB_COM_CON	0x200
+#define USB3_USB20_CON		0x204
+#define USB3_USB30_CON		0x208
+#define USB3_USB_STA		0x210
+#define USB3_DRD_CON		0x218
+#define USB3_USB_INT_STA_1	0x220
+#define USB3_USB_INT_STA_2	0x224
+#define USB3_USB_INT_ENA_1	0x228
+#define USB3_USB_INT_ENA_2	0x22c
+#define USB3_STUP_DAT_0		0x230
+#define USB3_STUP_DAT_1		0x234
+#define USB3_USB_OTG_STA	0x268
+#define USB3_USB_OTG_INT_STA	0x26c
+#define USB3_USB_OTG_INT_ENA	0x270
+#define USB3_P0_MOD		0x280
+#define USB3_P0_CON		0x288
+#define USB3_P0_STA		0x28c
+#define USB3_P0_INT_STA		0x290
+#define USB3_P0_INT_ENA		0x294
+#define USB3_P0_LNG		0x2a0
+#define USB3_P0_READ		0x2a4
+#define USB3_P0_WRITE		0x2a8
+#define USB3_PIPE_COM		0x2b0
+#define USB3_PN_MOD		0x2c0
+#define USB3_PN_RAMMAP		0x2c4
+#define USB3_PN_CON		0x2c8
+#define USB3_PN_STA		0x2cc
+#define USB3_PN_INT_STA		0x2d0
+#define USB3_PN_INT_ENA		0x2d4
+#define USB3_PN_LNG		0x2e0
+#define USB3_PN_READ		0x2e4
+#define USB3_PN_WRITE		0x2e8
+#define USB3_SSIFCMD		0x340
+
+/* AXI_INT_ENA and AXI_INT_STA */
+#define AXI_INT_DMAINT		BIT(31)
+#define AXI_INT_EPCINT		BIT(30)
+/* PRD's n = from 1 to 4 */
+#define AXI_INT_PRDEN_CLR_STA_SHIFT(n)	(16 + (n) - 1)
+#define AXI_INT_PRDERR_STA_SHIFT(n)	(0 + (n) - 1)
+#define AXI_INT_PRDEN_CLR_STA(n)	(1 << AXI_INT_PRDEN_CLR_STA_SHIFT(n))
+#define AXI_INT_PRDERR_STA(n)		(1 << AXI_INT_PRDERR_STA_SHIFT(n))
+
+/* DMA_INT_ENA and DMA_INT_STA */
+#define DMA_INT(n)		BIT(n)
+
+/* DMA_CH0_CONn */
+#define DMA_CON_PIPE_DIR	BIT(15)		/* 1: In Transfer */
+#define DMA_CON_PIPE_NO_SHIFT	8
+#define DMA_CON_PIPE_NO_MASK	GENMASK(12, DMA_CON_PIPE_NO_SHIFT)
+#define DMA_COM_PIPE_NO(n)	(((n) << DMA_CON_PIPE_NO_SHIFT) & \
+					 DMA_CON_PIPE_NO_MASK)
+#define DMA_CON_PRD_EN		BIT(0)
+
+/* LCLKSEL */
+#define LCLKSEL_LSEL		BIT(18)
+
+/* USB_COM_CON */
+#define USB_COM_CON_CONF		BIT(24)
+#define USB_COM_CON_PN_WDATAIF_NL	BIT(23)
+#define USB_COM_CON_PN_RDATAIF_NL	BIT(22)
+#define USB_COM_CON_PN_LSTTR_PP		BIT(21)
+#define USB_COM_CON_SPD_MODE		BIT(17)
+#define USB_COM_CON_EP0_EN		BIT(16)
+#define USB_COM_CON_DEV_ADDR_SHIFT	8
+#define USB_COM_CON_DEV_ADDR_MASK	GENMASK(14, USB_COM_CON_DEV_ADDR_SHIFT)
+#define USB_COM_CON_DEV_ADDR(n)		(((n) << USB_COM_CON_DEV_ADDR_SHIFT) & \
+					 USB_COM_CON_DEV_ADDR_MASK)
+#define USB_COM_CON_RX_DETECTION	BIT(1)
+#define USB_COM_CON_PIPE_CLR		BIT(0)
+
+/* USB20_CON */
+#define USB20_CON_B2_PUE		BIT(31)
+#define USB20_CON_B2_SUSPEND		BIT(24)
+#define USB20_CON_B2_CONNECT		BIT(17)
+#define USB20_CON_B2_TSTMOD_SHIFT	8
+#define USB20_CON_B2_TSTMOD_MASK	GENMASK(10, USB20_CON_B2_TSTMOD_SHIFT)
+#define USB20_CON_B2_TSTMOD(n)		(((n) << USB20_CON_B2_TSTMOD_SHIFT) & \
+					 USB20_CON_B2_TSTMOD_MASK)
+#define USB20_CON_B2_TSTMOD_EN		BIT(0)
+
+/* USB30_CON */
+#define USB30_CON_POW_SEL_SHIFT		24
+#define USB30_CON_POW_SEL_MASK		GENMASK(26, USB30_CON_POW_SEL_SHIFT)
+#define USB30_CON_POW_SEL_IN_U3		BIT(26)
+#define USB30_CON_POW_SEL_IN_DISCON	0
+#define USB30_CON_POW_SEL_P2_TO_P0	BIT(25)
+#define USB30_CON_POW_SEL_P0_TO_P3	BIT(24)
+#define USB30_CON_POW_SEL_P0_TO_P2	0
+#define USB30_CON_B3_PLLWAKE		BIT(23)
+#define USB30_CON_B3_CONNECT		BIT(17)
+#define USB30_CON_B3_HOTRST_CMP		BIT(1)
+
+/* USB_STA */
+#define USB_STA_SPEED_MASK	(BIT(2) | BIT(1))
+#define USB_STA_SPEED_HS	BIT(2)
+#define USB_STA_SPEED_FS	BIT(1)
+#define USB_STA_SPEED_SS	0
+#define USB_STA_VBUS_STA	BIT(0)
+
+/* DRD_CON */
+#define DRD_CON_PERI_CON	BIT(24)
+#define DRD_CON_VBOUT		BIT(0)
+
+/* USB_INT_ENA_1 and USB_INT_STA_1 */
+#define USB_INT_1_B3_PLLWKUP	BIT(31)
+#define USB_INT_1_B3_LUPSUCS	BIT(30)
+#define USB_INT_1_B3_DISABLE	BIT(27)
+#define USB_INT_1_B3_WRMRST	BIT(21)
+#define USB_INT_1_B3_HOTRST	BIT(20)
+#define USB_INT_1_B2_USBRST	BIT(12)
+#define USB_INT_1_B2_L1SPND	BIT(11)
+#define USB_INT_1_B2_SPND	BIT(9)
+#define USB_INT_1_B2_RSUM	BIT(8)
+#define USB_INT_1_SPEED		BIT(1)
+#define USB_INT_1_VBUS_CNG	BIT(0)
+
+/* USB_INT_ENA_2 and USB_INT_STA_2 */
+#define USB_INT_2_PIPE(n)	BIT(n)
+
+/* USB_OTG_STA, USB_OTG_INT_STA and USB_OTG_INT_ENA */
+#define USB_OTG_IDMON		BIT(4)
+
+/* P0_MOD */
+#define P0_MOD_DIR		BIT(6)
+
+/* P0_CON and PN_CON */
+#define PX_CON_BYTE_EN_MASK		(BIT(10) | BIT(9))
+#define PX_CON_BYTE_EN_SHIFT		9
+#define PX_CON_BYTE_EN_BYTES(n)		(((n) << PX_CON_BYTE_EN_SHIFT) & \
+					 PX_CON_BYTE_EN_MASK)
+#define PX_CON_SEND			BIT(8)
+
+/* P0_CON */
+#define P0_CON_ST_RES_MASK		(BIT(27) | BIT(26))
+#define P0_CON_ST_RES_FORCE_STALL	BIT(27)
+#define P0_CON_ST_RES_NORMAL		BIT(26)
+#define P0_CON_ST_RES_FORCE_NRDY	0
+#define P0_CON_OT_RES_MASK		(BIT(25) | BIT(24))
+#define P0_CON_OT_RES_FORCE_STALL	BIT(25)
+#define P0_CON_OT_RES_NORMAL		BIT(24)
+#define P0_CON_OT_RES_FORCE_NRDY	0
+#define P0_CON_IN_RES_MASK		(BIT(17) | BIT(16))
+#define P0_CON_IN_RES_FORCE_STALL	BIT(17)
+#define P0_CON_IN_RES_NORMAL		BIT(16)
+#define P0_CON_IN_RES_FORCE_NRDY	0
+#define P0_CON_RES_WEN			BIT(7)
+#define P0_CON_BCLR			BIT(1)
+
+/* P0_STA and PN_STA */
+#define PX_STA_BUFSTS		BIT(0)
+
+/* P0_INT_ENA and P0_INT_STA */
+#define P0_INT_STSED		BIT(18)
+#define P0_INT_STSST		BIT(17)
+#define P0_INT_SETUP		BIT(16)
+#define P0_INT_RCVNL		BIT(8)
+#define P0_INT_ERDY		BIT(7)
+#define P0_INT_FLOW		BIT(6)
+#define P0_INT_STALL		BIT(2)
+#define P0_INT_NRDY		BIT(1)
+#define P0_INT_BFRDY		BIT(0)
+#define P0_INT_ALL_BITS		(P0_INT_STSED | P0_INT_SETUP | P0_INT_BFRDY)
+
+/* PN_MOD */
+#define PN_MOD_DIR		BIT(6)
+#define PN_MOD_TYPE_SHIFT	4
+#define PN_MOD_TYPE_MASK	GENMASK(5, PN_MOD_TYPE_SHIFT)
+#define PN_MOD_TYPE(n)		(((n) << PN_MOD_TYPE_SHIFT) & \
+				 PN_MOD_TYPE_MASK)
+#define PN_MOD_EPNUM_MASK	GENMASK(3, 0)
+#define PN_MOD_EPNUM(n)		((n) & PN_MOD_EPNUM_MASK)
+
+/* PN_RAMMAP */
+#define PN_RAMMAP_RAMAREA_SHIFT	29
+#define PN_RAMMAP_RAMAREA_MASK	GENMASK(31, PN_RAMMAP_RAMAREA_SHIFT)
+#define PN_RAMMAP_RAMAREA_16KB	BIT(31)
+#define PN_RAMMAP_RAMAREA_8KB	(BIT(30) | BIT(29))
+#define PN_RAMMAP_RAMAREA_4KB	BIT(30)
+#define PN_RAMMAP_RAMAREA_2KB	BIT(29)
+#define PN_RAMMAP_RAMAREA_1KB	0
+#define PN_RAMMAP_MPKT_SHIFT	16
+#define PN_RAMMAP_MPKT_MASK	GENMASK(26, PN_RAMMAP_MPKT_SHIFT)
+#define PN_RAMMAP_MPKT(n)	(((n) << PN_RAMMAP_MPKT_SHIFT) & \
+				 PN_RAMMAP_MPKT_MASK)
+#define PN_RAMMAP_RAMIF_SHIFT	14
+#define PN_RAMMAP_RAMIF_MASK	GENMASK(15, PN_RAMMAP_RAMIF_SHIFT)
+#define PN_RAMMAP_RAMIF(n)	(((n) << PN_RAMMAP_RAMIF_SHIFT) & \
+				 PN_RAMMAP_RAMIF_MASK)
+#define PN_RAMMAP_BASEAD_MASK	GENMASK(13, 0)
+#define PN_RAMMAP_BASEAD(offs)	(((offs) >> 3) & PN_RAMMAP_BASEAD_MASK)
+#define PN_RAMMAP_DATA(area, ramif, basead)	((PN_RAMMAP_##area) | \
+						 (PN_RAMMAP_RAMIF(ramif)) | \
+						 (PN_RAMMAP_BASEAD(basead)))
+
+/* PN_CON */
+#define PN_CON_EN		BIT(31)
+#define PN_CON_DATAIF_EN	BIT(30)
+#define PN_CON_RES_MASK		(BIT(17) | BIT(16))
+#define PN_CON_RES_FORCE_STALL	BIT(17)
+#define PN_CON_RES_NORMAL	BIT(16)
+#define PN_CON_RES_FORCE_NRDY	0
+#define PN_CON_LAST		BIT(11)
+#define PN_CON_RES_WEN		BIT(7)
+#define PN_CON_CLR		BIT(0)
+
+/* PN_INT_STA and PN_INT_ENA */
+#define PN_INT_LSTTR	BIT(4)
+#define PN_INT_BFRDY	BIT(0)
+
+/* USB3_SSIFCMD */
+#define SSIFCMD_URES_U2		BIT(9)
+#define SSIFCMD_URES_U1		BIT(8)
+#define SSIFCMD_UDIR_U2		BIT(7)
+#define SSIFCMD_UDIR_U1		BIT(6)
+#define SSIFCMD_UREQ_U2		BIT(5)
+#define SSIFCMD_UREQ_U1		BIT(4)
+
+#define USB3_EP0_SS_MAX_PACKET_SIZE	512
+#define USB3_EP0_HSFS_MAX_PACKET_SIZE	64
+#define USB3_EP0_BUF_SIZE		8
+#define USB3_MAX_NUM_PIPES		6	/* This includes PIPE 0 */
+#define USB3_WAIT_US			3
+#define USB3_DMA_NUM_SETTING_AREA	4
+/*
+ * To avoid double-meaning of "0" (xferred 65536 bytes or received zlp if
+ * buffer size is 65536), this driver uses the maximum size per a entry is
+ * 32768 bytes.
+ */
+#define USB3_DMA_MAX_XFER_SIZE		32768
+#define USB3_DMA_PRD_SIZE		4096
+
+struct renesas_usb3;
+
+/* Physical Region Descriptor Table */
+struct renesas_usb3_prd {
+	u32 word1;
+#define USB3_PRD1_E		BIT(30)		/* the end of chain */
+#define USB3_PRD1_U		BIT(29)		/* completion of transfer */
+#define USB3_PRD1_D		BIT(28)		/* Error occurred */
+#define USB3_PRD1_INT		BIT(27)		/* Interrupt occurred */
+#define USB3_PRD1_LST		BIT(26)		/* Last Packet */
+#define USB3_PRD1_B_INC		BIT(24)
+#define USB3_PRD1_MPS_8		0
+#define USB3_PRD1_MPS_16	BIT(21)
+#define USB3_PRD1_MPS_32	BIT(22)
+#define USB3_PRD1_MPS_64	(BIT(22) | BIT(21))
+#define USB3_PRD1_MPS_512	BIT(23)
+#define USB3_PRD1_MPS_1024	(BIT(23) | BIT(21))
+#define USB3_PRD1_MPS_RESERVED	(BIT(23) | BIT(22) | BIT(21))
+#define USB3_PRD1_SIZE_MASK	GENMASK(15, 0)
+
+	u32 bap;
+};
+#define USB3_DMA_NUM_PRD_ENTRIES	(USB3_DMA_PRD_SIZE / \
+					  sizeof(struct renesas_usb3_prd))
+#define USB3_DMA_MAX_XFER_SIZE_ALL_PRDS	(USB3_DMA_PRD_SIZE / \
+					 sizeof(struct renesas_usb3_prd) * \
+					 USB3_DMA_MAX_XFER_SIZE)
+
+struct renesas_usb3_dma {
+	struct renesas_usb3_prd *prd;
+	dma_addr_t prd_dma;
+	int num;	/* Setting area number (from 1 to 4) */
+	bool used;
+};
+
+struct renesas_usb3_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+#define USB3_EP_NAME_SIZE	8
+struct renesas_usb3_ep {
+	struct usb_ep ep;
+	struct renesas_usb3 *usb3;
+	struct renesas_usb3_dma *dma;
+	int num;
+	char ep_name[USB3_EP_NAME_SIZE];
+	struct list_head queue;
+	u32 rammap_val;
+	bool dir_in;
+	bool halt;
+	bool wedge;
+	bool started;
+};
+
+struct renesas_usb3_priv {
+	int ramsize_per_ramif;		/* unit = bytes */
+	int num_ramif;
+	int ramsize_per_pipe;		/* unit = bytes */
+	bool workaround_for_vbus;	/* if true, don't check vbus signal */
+};
+
+struct renesas_usb3 {
+	void __iomem *reg;
+
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct extcon_dev *extcon;
+	struct work_struct extcon_work;
+	struct phy *phy;
+	struct dentry *dentry;
+
+	struct usb_role_switch *role_sw;
+	struct device *host_dev;
+	struct work_struct role_work;
+	enum usb_role role;
+
+	struct renesas_usb3_ep *usb3_ep;
+	int num_usb3_eps;
+
+	struct renesas_usb3_dma dma[USB3_DMA_NUM_SETTING_AREA];
+
+	spinlock_t lock;
+	int disabled_count;
+
+	struct usb_request *ep0_req;
+	u16 test_mode;
+	u8 ep0_buf[USB3_EP0_BUF_SIZE];
+	bool softconnect;
+	bool workaround_for_vbus;
+	bool extcon_host;		/* check id and set EXTCON_USB_HOST */
+	bool extcon_usb;		/* check vbus and set EXTCON_USB */
+	bool forced_b_device;
+};
+
+#define gadget_to_renesas_usb3(_gadget)	\
+		container_of(_gadget, struct renesas_usb3, gadget)
+#define renesas_usb3_to_gadget(renesas_usb3) (&renesas_usb3->gadget)
+#define usb3_to_dev(_usb3)	(_usb3->gadget.dev.parent)
+
+#define usb_ep_to_usb3_ep(_ep) container_of(_ep, struct renesas_usb3_ep, ep)
+#define usb3_ep_to_usb3(_usb3_ep) (_usb3_ep->usb3)
+#define usb_req_to_usb3_req(_req) container_of(_req, \
+					    struct renesas_usb3_request, req)
+
+#define usb3_get_ep(usb3, n) ((usb3)->usb3_ep + (n))
+#define usb3_for_each_ep(usb3_ep, usb3, i)			\
+		for ((i) = 0, usb3_ep = usb3_get_ep(usb3, (i));	\
+		     (i) < (usb3)->num_usb3_eps;		\
+		     (i)++, usb3_ep = usb3_get_ep(usb3, (i)))
+
+#define usb3_get_dma(usb3, i)	(&(usb3)->dma[i])
+#define usb3_for_each_dma(usb3, dma, i)				\
+		for ((i) = 0, dma = usb3_get_dma((usb3), (i));	\
+		     (i) < USB3_DMA_NUM_SETTING_AREA;		\
+		     (i)++, dma = usb3_get_dma((usb3), (i)))
+
+static const char udc_name[] = "renesas_usb3";
+
+static bool use_dma = 1;
+module_param(use_dma, bool, 0644);
+MODULE_PARM_DESC(use_dma, "use dedicated DMAC");
+
+static void usb3_write(struct renesas_usb3 *usb3, u32 data, u32 offs)
+{
+	iowrite32(data, usb3->reg + offs);
+}
+
+static u32 usb3_read(struct renesas_usb3 *usb3, u32 offs)
+{
+	return ioread32(usb3->reg + offs);
+}
+
+static void usb3_set_bit(struct renesas_usb3 *usb3, u32 bits, u32 offs)
+{
+	u32 val = usb3_read(usb3, offs);
+
+	val |= bits;
+	usb3_write(usb3, val, offs);
+}
+
+static void usb3_clear_bit(struct renesas_usb3 *usb3, u32 bits, u32 offs)
+{
+	u32 val = usb3_read(usb3, offs);
+
+	val &= ~bits;
+	usb3_write(usb3, val, offs);
+}
+
+static int usb3_wait(struct renesas_usb3 *usb3, u32 reg, u32 mask,
+		     u32 expected)
+{
+	int i;
+
+	for (i = 0; i < USB3_WAIT_US; i++) {
+		if ((usb3_read(usb3, reg) & mask) == expected)
+			return 0;
+		udelay(1);
+	}
+
+	dev_dbg(usb3_to_dev(usb3), "%s: timed out (%8x, %08x, %08x)\n",
+		__func__, reg, mask, expected);
+
+	return -EBUSY;
+}
+
+static void renesas_usb3_extcon_work(struct work_struct *work)
+{
+	struct renesas_usb3 *usb3 = container_of(work, struct renesas_usb3,
+						 extcon_work);
+
+	extcon_set_state_sync(usb3->extcon, EXTCON_USB_HOST, usb3->extcon_host);
+	extcon_set_state_sync(usb3->extcon, EXTCON_USB, usb3->extcon_usb);
+}
+
+static void usb3_enable_irq_1(struct renesas_usb3 *usb3, u32 bits)
+{
+	usb3_set_bit(usb3, bits, USB3_USB_INT_ENA_1);
+}
+
+static void usb3_disable_irq_1(struct renesas_usb3 *usb3, u32 bits)
+{
+	usb3_clear_bit(usb3, bits, USB3_USB_INT_ENA_1);
+}
+
+static void usb3_enable_pipe_irq(struct renesas_usb3 *usb3, int num)
+{
+	usb3_set_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
+}
+
+static void usb3_disable_pipe_irq(struct renesas_usb3 *usb3, int num)
+{
+	usb3_clear_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
+}
+
+static bool usb3_is_host(struct renesas_usb3 *usb3)
+{
+	return !(usb3_read(usb3, USB3_DRD_CON) & DRD_CON_PERI_CON);
+}
+
+static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
+{
+	/* Set AXI_INT */
+	usb3_write(usb3, ~0, USB3_DMA_INT_STA);
+	usb3_write(usb3, 0, USB3_DMA_INT_ENA);
+	usb3_set_bit(usb3, AXI_INT_DMAINT | AXI_INT_EPCINT, USB3_AXI_INT_ENA);
+}
+
+static void usb3_init_epc_registers(struct renesas_usb3 *usb3)
+{
+	usb3_write(usb3, ~0, USB3_USB_INT_STA_1);
+	usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
+}
+
+static bool usb3_wakeup_usb2_phy(struct renesas_usb3 *usb3)
+{
+	if (!(usb3_read(usb3, USB3_USB20_CON) & USB20_CON_B2_SUSPEND))
+		return true;	/* already waked it up */
+
+	usb3_clear_bit(usb3, USB20_CON_B2_SUSPEND, USB3_USB20_CON);
+	usb3_enable_irq_1(usb3, USB_INT_1_B2_RSUM);
+
+	return false;
+}
+
+static void usb3_usb2_pullup(struct renesas_usb3 *usb3, int pullup)
+{
+	u32 bits = USB20_CON_B2_PUE | USB20_CON_B2_CONNECT;
+
+	if (usb3->softconnect && pullup)
+		usb3_set_bit(usb3, bits, USB3_USB20_CON);
+	else
+		usb3_clear_bit(usb3, bits, USB3_USB20_CON);
+}
+
+static void usb3_set_test_mode(struct renesas_usb3 *usb3)
+{
+	u32 val = usb3_read(usb3, USB3_USB20_CON);
+
+	val &= ~USB20_CON_B2_TSTMOD_MASK;
+	val |= USB20_CON_B2_TSTMOD(usb3->test_mode);
+	usb3_write(usb3, val | USB20_CON_B2_TSTMOD_EN, USB3_USB20_CON);
+	if (!usb3->test_mode)
+		usb3_clear_bit(usb3, USB20_CON_B2_TSTMOD_EN, USB3_USB20_CON);
+}
+
+static void usb3_start_usb2_connection(struct renesas_usb3 *usb3)
+{
+	usb3->disabled_count++;
+	usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+	usb3_set_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+	usb3_usb2_pullup(usb3, 1);
+}
+
+static int usb3_is_usb3_phy_in_u3(struct renesas_usb3 *usb3)
+{
+	return usb3_read(usb3, USB3_USB30_CON) & USB30_CON_POW_SEL_IN_U3;
+}
+
+static bool usb3_wakeup_usb3_phy(struct renesas_usb3 *usb3)
+{
+	if (!usb3_is_usb3_phy_in_u3(usb3))
+		return true;	/* already waked it up */
+
+	usb3_set_bit(usb3, USB30_CON_B3_PLLWAKE, USB3_USB30_CON);
+	usb3_enable_irq_1(usb3, USB_INT_1_B3_PLLWKUP);
+
+	return false;
+}
+
+static u16 usb3_feature_get_un_enabled(struct renesas_usb3 *usb3)
+{
+	u32 mask_u2 = SSIFCMD_UDIR_U2 | SSIFCMD_UREQ_U2;
+	u32 mask_u1 = SSIFCMD_UDIR_U1 | SSIFCMD_UREQ_U1;
+	u32 val = usb3_read(usb3, USB3_SSIFCMD);
+	u16 ret = 0;
+
+	/* Enables {U2,U1} if the bits of UDIR and UREQ are set to 0 */
+	if (!(val & mask_u2))
+		ret |= 1 << USB_DEV_STAT_U2_ENABLED;
+	if (!(val & mask_u1))
+		ret |= 1 << USB_DEV_STAT_U1_ENABLED;
+
+	return ret;
+}
+
+static void usb3_feature_u2_enable(struct renesas_usb3 *usb3, bool enable)
+{
+	u32 bits = SSIFCMD_UDIR_U2 | SSIFCMD_UREQ_U2;
+
+	/* Enables U2 if the bits of UDIR and UREQ are set to 0 */
+	if (enable)
+		usb3_clear_bit(usb3, bits, USB3_SSIFCMD);
+	else
+		usb3_set_bit(usb3, bits, USB3_SSIFCMD);
+}
+
+static void usb3_feature_u1_enable(struct renesas_usb3 *usb3, bool enable)
+{
+	u32 bits = SSIFCMD_UDIR_U1 | SSIFCMD_UREQ_U1;
+
+	/* Enables U1 if the bits of UDIR and UREQ are set to 0 */
+	if (enable)
+		usb3_clear_bit(usb3, bits, USB3_SSIFCMD);
+	else
+		usb3_set_bit(usb3, bits, USB3_SSIFCMD);
+}
+
+static void usb3_start_operation_for_usb3(struct renesas_usb3 *usb3)
+{
+	usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+	usb3_clear_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+	usb3_set_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+}
+
+static void usb3_start_usb3_connection(struct renesas_usb3 *usb3)
+{
+	usb3_start_operation_for_usb3(usb3);
+	usb3_set_bit(usb3, USB_COM_CON_RX_DETECTION, USB3_USB_COM_CON);
+
+	usb3_enable_irq_1(usb3, USB_INT_1_B3_LUPSUCS | USB_INT_1_B3_DISABLE |
+			  USB_INT_1_SPEED);
+}
+
+static void usb3_stop_usb3_connection(struct renesas_usb3 *usb3)
+{
+	usb3_clear_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+}
+
+static void usb3_transition_to_default_state(struct renesas_usb3 *usb3,
+					     bool is_usb3)
+{
+	usb3_set_bit(usb3, USB_INT_2_PIPE(0), USB3_USB_INT_ENA_2);
+	usb3_write(usb3, P0_INT_ALL_BITS, USB3_P0_INT_STA);
+	usb3_set_bit(usb3, P0_INT_ALL_BITS, USB3_P0_INT_ENA);
+
+	if (is_usb3)
+		usb3_enable_irq_1(usb3, USB_INT_1_B3_WRMRST |
+				  USB_INT_1_B3_HOTRST);
+	else
+		usb3_enable_irq_1(usb3, USB_INT_1_B2_SPND |
+				  USB_INT_1_B2_L1SPND | USB_INT_1_B2_USBRST);
+}
+
+static void usb3_connect(struct renesas_usb3 *usb3)
+{
+	if (usb3_wakeup_usb3_phy(usb3))
+		usb3_start_usb3_connection(usb3);
+}
+
+static void usb3_reset_epc(struct renesas_usb3 *usb3)
+{
+	usb3_clear_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+	usb3_clear_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+	usb3_set_bit(usb3, USB_COM_CON_PIPE_CLR, USB3_USB_COM_CON);
+	usb3->test_mode = 0;
+	usb3_set_test_mode(usb3);
+}
+
+static void usb3_disconnect(struct renesas_usb3 *usb3)
+{
+	usb3->disabled_count = 0;
+	usb3_usb2_pullup(usb3, 0);
+	usb3_clear_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+	usb3_reset_epc(usb3);
+	usb3_disable_irq_1(usb3, USB_INT_1_B2_RSUM | USB_INT_1_B3_PLLWKUP |
+			   USB_INT_1_B3_LUPSUCS | USB_INT_1_B3_DISABLE |
+			   USB_INT_1_SPEED | USB_INT_1_B3_WRMRST |
+			   USB_INT_1_B3_HOTRST | USB_INT_1_B2_SPND |
+			   USB_INT_1_B2_L1SPND | USB_INT_1_B2_USBRST);
+	usb3_clear_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+	usb3_init_epc_registers(usb3);
+
+	if (usb3->driver)
+		usb3->driver->disconnect(&usb3->gadget);
+}
+
+static void usb3_check_vbus(struct renesas_usb3 *usb3)
+{
+	if (usb3->workaround_for_vbus) {
+		usb3_connect(usb3);
+	} else {
+		usb3->extcon_usb = !!(usb3_read(usb3, USB3_USB_STA) &
+							USB_STA_VBUS_STA);
+		if (usb3->extcon_usb)
+			usb3_connect(usb3);
+		else
+			usb3_disconnect(usb3);
+
+		schedule_work(&usb3->extcon_work);
+	}
+}
+
+static void renesas_usb3_role_work(struct work_struct *work)
+{
+	struct renesas_usb3 *usb3 =
+			container_of(work, struct renesas_usb3, role_work);
+
+	usb_role_switch_set_role(usb3->role_sw, usb3->role);
+}
+
+static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
+{
+	if (host)
+		usb3_clear_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
+	else
+		usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
+}
+
+static void usb3_set_mode_by_role_sw(struct renesas_usb3 *usb3, bool host)
+{
+	if (usb3->role_sw) {
+		usb3->role = host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+		schedule_work(&usb3->role_work);
+	} else {
+		usb3_set_mode(usb3, host);
+	}
+}
+
+static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable)
+{
+	if (enable)
+		usb3_set_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
+	else
+		usb3_clear_bit(usb3, DRD_CON_VBOUT, USB3_DRD_CON);
+}
+
+static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	usb3_set_mode_by_role_sw(usb3, host);
+	usb3_vbus_out(usb3, a_dev);
+	/* for A-Peripheral or forced B-device mode */
+	if ((!host && a_dev) ||
+	    (usb3->workaround_for_vbus && usb3->forced_b_device))
+		usb3_connect(usb3);
+	spin_unlock_irqrestore(&usb3->lock, flags);
+}
+
+static bool usb3_is_a_device(struct renesas_usb3 *usb3)
+{
+	return !(usb3_read(usb3, USB3_USB_OTG_STA) & USB_OTG_IDMON);
+}
+
+static void usb3_check_id(struct renesas_usb3 *usb3)
+{
+	usb3->extcon_host = usb3_is_a_device(usb3);
+
+	if (usb3->extcon_host && !usb3->forced_b_device)
+		usb3_mode_config(usb3, true, true);
+	else
+		usb3_mode_config(usb3, false, false);
+
+	schedule_work(&usb3->extcon_work);
+}
+
+static void renesas_usb3_init_controller(struct renesas_usb3 *usb3)
+{
+	usb3_init_axi_bridge(usb3);
+	usb3_init_epc_registers(usb3);
+	usb3_set_bit(usb3, USB_COM_CON_PN_WDATAIF_NL |
+		     USB_COM_CON_PN_RDATAIF_NL | USB_COM_CON_PN_LSTTR_PP,
+		     USB3_USB_COM_CON);
+	usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_STA);
+	usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_ENA);
+
+	usb3_check_id(usb3);
+	usb3_check_vbus(usb3);
+}
+
+static void renesas_usb3_stop_controller(struct renesas_usb3 *usb3)
+{
+	usb3_disconnect(usb3);
+	usb3_write(usb3, 0, USB3_P0_INT_ENA);
+	usb3_write(usb3, 0, USB3_USB_OTG_INT_ENA);
+	usb3_write(usb3, 0, USB3_USB_INT_ENA_1);
+	usb3_write(usb3, 0, USB3_USB_INT_ENA_2);
+	usb3_write(usb3, 0, USB3_AXI_INT_ENA);
+}
+
+static void usb3_irq_epc_int_1_pll_wakeup(struct renesas_usb3 *usb3)
+{
+	usb3_disable_irq_1(usb3, USB_INT_1_B3_PLLWKUP);
+	usb3_clear_bit(usb3, USB30_CON_B3_PLLWAKE, USB3_USB30_CON);
+	usb3_start_usb3_connection(usb3);
+}
+
+static void usb3_irq_epc_int_1_linkup_success(struct renesas_usb3 *usb3)
+{
+	usb3_transition_to_default_state(usb3, true);
+}
+
+static void usb3_irq_epc_int_1_resume(struct renesas_usb3 *usb3)
+{
+	usb3_disable_irq_1(usb3, USB_INT_1_B2_RSUM);
+	usb3_start_usb2_connection(usb3);
+	usb3_transition_to_default_state(usb3, false);
+}
+
+static void usb3_irq_epc_int_1_disable(struct renesas_usb3 *usb3)
+{
+	usb3_stop_usb3_connection(usb3);
+	if (usb3_wakeup_usb2_phy(usb3))
+		usb3_irq_epc_int_1_resume(usb3);
+}
+
+static void usb3_irq_epc_int_1_bus_reset(struct renesas_usb3 *usb3)
+{
+	usb3_reset_epc(usb3);
+	if (usb3->disabled_count < 3)
+		usb3_start_usb3_connection(usb3);
+	else
+		usb3_start_usb2_connection(usb3);
+}
+
+static void usb3_irq_epc_int_1_vbus_change(struct renesas_usb3 *usb3)
+{
+	usb3_check_vbus(usb3);
+}
+
+static void usb3_irq_epc_int_1_hot_reset(struct renesas_usb3 *usb3)
+{
+	usb3_reset_epc(usb3);
+	usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+
+	/* This bit shall be set within 12ms from the start of HotReset */
+	usb3_set_bit(usb3, USB30_CON_B3_HOTRST_CMP, USB3_USB30_CON);
+}
+
+static void usb3_irq_epc_int_1_warm_reset(struct renesas_usb3 *usb3)
+{
+	usb3_reset_epc(usb3);
+	usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+
+	usb3_start_operation_for_usb3(usb3);
+	usb3_enable_irq_1(usb3, USB_INT_1_SPEED);
+}
+
+static void usb3_irq_epc_int_1_speed(struct renesas_usb3 *usb3)
+{
+	u32 speed = usb3_read(usb3, USB3_USB_STA) & USB_STA_SPEED_MASK;
+
+	switch (speed) {
+	case USB_STA_SPEED_SS:
+		usb3->gadget.speed = USB_SPEED_SUPER;
+		usb3->gadget.ep0->maxpacket = USB3_EP0_SS_MAX_PACKET_SIZE;
+		break;
+	case USB_STA_SPEED_HS:
+		usb3->gadget.speed = USB_SPEED_HIGH;
+		usb3->gadget.ep0->maxpacket = USB3_EP0_HSFS_MAX_PACKET_SIZE;
+		break;
+	case USB_STA_SPEED_FS:
+		usb3->gadget.speed = USB_SPEED_FULL;
+		usb3->gadget.ep0->maxpacket = USB3_EP0_HSFS_MAX_PACKET_SIZE;
+		break;
+	default:
+		usb3->gadget.speed = USB_SPEED_UNKNOWN;
+		break;
+	}
+}
+
+static void usb3_irq_epc_int_1(struct renesas_usb3 *usb3, u32 int_sta_1)
+{
+	if (int_sta_1 & USB_INT_1_B3_PLLWKUP)
+		usb3_irq_epc_int_1_pll_wakeup(usb3);
+
+	if (int_sta_1 & USB_INT_1_B3_LUPSUCS)
+		usb3_irq_epc_int_1_linkup_success(usb3);
+
+	if (int_sta_1 & USB_INT_1_B3_HOTRST)
+		usb3_irq_epc_int_1_hot_reset(usb3);
+
+	if (int_sta_1 & USB_INT_1_B3_WRMRST)
+		usb3_irq_epc_int_1_warm_reset(usb3);
+
+	if (int_sta_1 & USB_INT_1_B3_DISABLE)
+		usb3_irq_epc_int_1_disable(usb3);
+
+	if (int_sta_1 & USB_INT_1_B2_USBRST)
+		usb3_irq_epc_int_1_bus_reset(usb3);
+
+	if (int_sta_1 & USB_INT_1_B2_RSUM)
+		usb3_irq_epc_int_1_resume(usb3);
+
+	if (int_sta_1 & USB_INT_1_SPEED)
+		usb3_irq_epc_int_1_speed(usb3);
+
+	if (int_sta_1 & USB_INT_1_VBUS_CNG)
+		usb3_irq_epc_int_1_vbus_change(usb3);
+}
+
+static struct renesas_usb3_request *__usb3_get_request(struct renesas_usb3_ep
+						       *usb3_ep)
+{
+	return list_first_entry_or_null(&usb3_ep->queue,
+					struct renesas_usb3_request, queue);
+}
+
+static struct renesas_usb3_request *usb3_get_request(struct renesas_usb3_ep
+						     *usb3_ep)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	struct renesas_usb3_request *usb3_req;
+	unsigned long flags;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	usb3_req = __usb3_get_request(usb3_ep);
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	return usb3_req;
+}
+
+static void __usb3_request_done(struct renesas_usb3_ep *usb3_ep,
+				struct renesas_usb3_request *usb3_req,
+				int status)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	dev_dbg(usb3_to_dev(usb3), "giveback: ep%2d, %u, %u, %d\n",
+		usb3_ep->num, usb3_req->req.length, usb3_req->req.actual,
+		status);
+	usb3_req->req.status = status;
+	usb3_ep->started = false;
+	list_del_init(&usb3_req->queue);
+	spin_unlock(&usb3->lock);
+	usb_gadget_giveback_request(&usb3_ep->ep, &usb3_req->req);
+	spin_lock(&usb3->lock);
+}
+
+static void usb3_request_done(struct renesas_usb3_ep *usb3_ep,
+			      struct renesas_usb3_request *usb3_req, int status)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	__usb3_request_done(usb3_ep, usb3_req, status);
+	spin_unlock_irqrestore(&usb3->lock, flags);
+}
+
+static void usb3_irq_epc_pipe0_status_end(struct renesas_usb3 *usb3)
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+	struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+	if (usb3_req)
+		usb3_request_done(usb3_ep, usb3_req, 0);
+	if (usb3->test_mode)
+		usb3_set_test_mode(usb3);
+}
+
+static void usb3_get_setup_data(struct renesas_usb3 *usb3,
+				struct usb_ctrlrequest *ctrl)
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+	u32 *data = (u32 *)ctrl;
+
+	*data++ = usb3_read(usb3, USB3_STUP_DAT_0);
+	*data = usb3_read(usb3, USB3_STUP_DAT_1);
+
+	/* update this driver's flag */
+	usb3_ep->dir_in = !!(ctrl->bRequestType & USB_DIR_IN);
+}
+
+static void usb3_set_p0_con_update_res(struct renesas_usb3 *usb3, u32 res)
+{
+	u32 val = usb3_read(usb3, USB3_P0_CON);
+
+	val &= ~(P0_CON_ST_RES_MASK | P0_CON_OT_RES_MASK | P0_CON_IN_RES_MASK);
+	val |= res | P0_CON_RES_WEN;
+	usb3_write(usb3, val, USB3_P0_CON);
+}
+
+static void usb3_set_p0_con_for_ctrl_read_data(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+				   P0_CON_OT_RES_FORCE_STALL |
+				   P0_CON_IN_RES_NORMAL);
+}
+
+static void usb3_set_p0_con_for_ctrl_read_status(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+				   P0_CON_OT_RES_FORCE_STALL |
+				   P0_CON_IN_RES_NORMAL);
+}
+
+static void usb3_set_p0_con_for_ctrl_write_data(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+				   P0_CON_OT_RES_NORMAL |
+				   P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_for_ctrl_write_status(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+				   P0_CON_OT_RES_NORMAL |
+				   P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_for_no_data(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+				   P0_CON_OT_RES_FORCE_STALL |
+				   P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_stall(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_STALL |
+				   P0_CON_OT_RES_FORCE_STALL |
+				   P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_stop(struct renesas_usb3 *usb3)
+{
+	usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+				   P0_CON_OT_RES_FORCE_NRDY |
+				   P0_CON_IN_RES_FORCE_NRDY);
+}
+
+static int usb3_pn_change(struct renesas_usb3 *usb3, int num)
+{
+	if (num == 0 || num > usb3->num_usb3_eps)
+		return -ENXIO;
+
+	usb3_write(usb3, num, USB3_PIPE_COM);
+
+	return 0;
+}
+
+static void usb3_set_pn_con_update_res(struct renesas_usb3 *usb3, u32 res)
+{
+	u32 val = usb3_read(usb3, USB3_PN_CON);
+
+	val &= ~PN_CON_RES_MASK;
+	val |= res & PN_CON_RES_MASK;
+	val |= PN_CON_RES_WEN;
+	usb3_write(usb3, val, USB3_PN_CON);
+}
+
+static void usb3_pn_start(struct renesas_usb3 *usb3)
+{
+	usb3_set_pn_con_update_res(usb3, PN_CON_RES_NORMAL);
+}
+
+static void usb3_pn_stop(struct renesas_usb3 *usb3)
+{
+	usb3_set_pn_con_update_res(usb3, PN_CON_RES_FORCE_NRDY);
+}
+
+static void usb3_pn_stall(struct renesas_usb3 *usb3)
+{
+	usb3_set_pn_con_update_res(usb3, PN_CON_RES_FORCE_STALL);
+}
+
+static int usb3_pn_con_clear(struct renesas_usb3 *usb3)
+{
+	usb3_set_bit(usb3, PN_CON_CLR, USB3_PN_CON);
+
+	return usb3_wait(usb3, USB3_PN_CON, PN_CON_CLR, 0);
+}
+
+static bool usb3_is_transfer_complete(struct renesas_usb3_ep *usb3_ep,
+				      struct renesas_usb3_request *usb3_req)
+{
+	struct usb_request *req = &usb3_req->req;
+
+	if ((!req->zero && req->actual == req->length) ||
+	    (req->actual % usb3_ep->ep.maxpacket) || (req->length == 0))
+		return true;
+	else
+		return false;
+}
+
+static int usb3_wait_pipe_status(struct renesas_usb3_ep *usb3_ep, u32 mask)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	u32 sta_reg = usb3_ep->num ? USB3_PN_STA : USB3_P0_STA;
+
+	return usb3_wait(usb3, sta_reg, mask, mask);
+}
+
+static void usb3_set_px_con_send(struct renesas_usb3_ep *usb3_ep, int bytes,
+				 bool last)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	u32 con_reg = usb3_ep->num ? USB3_PN_CON : USB3_P0_CON;
+	u32 val = usb3_read(usb3, con_reg);
+
+	val |= PX_CON_SEND | PX_CON_BYTE_EN_BYTES(bytes);
+	val |= (usb3_ep->num && last) ? PN_CON_LAST : 0;
+	usb3_write(usb3, val, con_reg);
+}
+
+static int usb3_write_pipe(struct renesas_usb3_ep *usb3_ep,
+			   struct renesas_usb3_request *usb3_req,
+			   u32 fifo_reg)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	int i;
+	int len = min_t(unsigned, usb3_req->req.length - usb3_req->req.actual,
+			usb3_ep->ep.maxpacket);
+	u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
+	u32 tmp = 0;
+	bool is_last = !len ? true : false;
+
+	if (usb3_wait_pipe_status(usb3_ep, PX_STA_BUFSTS) < 0)
+		return -EBUSY;
+
+	/* Update gadget driver parameter */
+	usb3_req->req.actual += len;
+
+	/* Write data to the register */
+	if (len >= 4) {
+		iowrite32_rep(usb3->reg + fifo_reg, buf, len / 4);
+		buf += (len / 4) * 4;
+		len %= 4;	/* update len to use usb3_set_pX_con_send() */
+	}
+
+	if (len) {
+		for (i = 0; i < len; i++)
+			tmp |= buf[i] << (8 * i);
+		usb3_write(usb3, tmp, fifo_reg);
+	}
+
+	if (!is_last)
+		is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
+	/* Send the data */
+	usb3_set_px_con_send(usb3_ep, len, is_last);
+
+	return is_last ? 0 : -EAGAIN;
+}
+
+static u32 usb3_get_received_length(struct renesas_usb3_ep *usb3_ep)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	u32 lng_reg = usb3_ep->num ? USB3_PN_LNG : USB3_P0_LNG;
+
+	return usb3_read(usb3, lng_reg);
+}
+
+static int usb3_read_pipe(struct renesas_usb3_ep *usb3_ep,
+			  struct renesas_usb3_request *usb3_req, u32 fifo_reg)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	int i;
+	int len = min_t(unsigned, usb3_req->req.length - usb3_req->req.actual,
+			usb3_get_received_length(usb3_ep));
+	u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
+	u32 tmp = 0;
+
+	if (!len)
+		return 0;
+
+	/* Update gadget driver parameter */
+	usb3_req->req.actual += len;
+
+	/* Read data from the register */
+	if (len >= 4) {
+		ioread32_rep(usb3->reg + fifo_reg, buf, len / 4);
+		buf += (len / 4) * 4;
+		len %= 4;
+	}
+
+	if (len) {
+		tmp = usb3_read(usb3, fifo_reg);
+		for (i = 0; i < len; i++)
+			buf[i] = (tmp >> (8 * i)) & 0xff;
+	}
+
+	return usb3_is_transfer_complete(usb3_ep, usb3_req) ? 0 : -EAGAIN;
+}
+
+static void usb3_set_status_stage(struct renesas_usb3_ep *usb3_ep,
+				  struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	if (usb3_ep->dir_in) {
+		usb3_set_p0_con_for_ctrl_read_status(usb3);
+	} else {
+		if (!usb3_req->req.length)
+			usb3_set_p0_con_for_no_data(usb3);
+		else
+			usb3_set_p0_con_for_ctrl_write_status(usb3);
+	}
+}
+
+static void usb3_p0_xfer(struct renesas_usb3_ep *usb3_ep,
+			 struct renesas_usb3_request *usb3_req)
+{
+	int ret = -EAGAIN;
+
+	if (usb3_ep->dir_in)
+		ret = usb3_write_pipe(usb3_ep, usb3_req, USB3_P0_WRITE);
+	else
+		ret = usb3_read_pipe(usb3_ep, usb3_req, USB3_P0_READ);
+
+	if (!ret)
+		usb3_set_status_stage(usb3_ep, usb3_req);
+}
+
+static void usb3_start_pipe0(struct renesas_usb3_ep *usb3_ep,
+			     struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	if (usb3_ep->started)
+		return;
+
+	usb3_ep->started = true;
+
+	if (usb3_ep->dir_in) {
+		usb3_set_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
+		usb3_set_p0_con_for_ctrl_read_data(usb3);
+	} else {
+		usb3_clear_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
+		if (usb3_req->req.length)
+			usb3_set_p0_con_for_ctrl_write_data(usb3);
+	}
+
+	usb3_p0_xfer(usb3_ep, usb3_req);
+}
+
+static void usb3_enable_dma_pipen(struct renesas_usb3 *usb3)
+{
+	usb3_set_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON);
+}
+
+static void usb3_disable_dma_pipen(struct renesas_usb3 *usb3)
+{
+	usb3_clear_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON);
+}
+
+static void usb3_enable_dma_irq(struct renesas_usb3 *usb3, int num)
+{
+	usb3_set_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA);
+}
+
+static void usb3_disable_dma_irq(struct renesas_usb3 *usb3, int num)
+{
+	usb3_clear_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA);
+}
+
+static u32 usb3_dma_mps_to_prd_word1(struct renesas_usb3_ep *usb3_ep)
+{
+	switch (usb3_ep->ep.maxpacket) {
+	case 8:
+		return USB3_PRD1_MPS_8;
+	case 16:
+		return USB3_PRD1_MPS_16;
+	case 32:
+		return USB3_PRD1_MPS_32;
+	case 64:
+		return USB3_PRD1_MPS_64;
+	case 512:
+		return USB3_PRD1_MPS_512;
+	case 1024:
+		return USB3_PRD1_MPS_1024;
+	default:
+		return USB3_PRD1_MPS_RESERVED;
+	}
+}
+
+static bool usb3_dma_get_setting_area(struct renesas_usb3_ep *usb3_ep,
+				      struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	struct renesas_usb3_dma *dma;
+	int i;
+	bool ret = false;
+
+	if (usb3_req->req.length > USB3_DMA_MAX_XFER_SIZE_ALL_PRDS) {
+		dev_dbg(usb3_to_dev(usb3), "%s: the length is too big (%d)\n",
+			__func__, usb3_req->req.length);
+		return false;
+	}
+
+	/* The driver doesn't handle zero-length packet via dmac */
+	if (!usb3_req->req.length)
+		return false;
+
+	if (usb3_dma_mps_to_prd_word1(usb3_ep) == USB3_PRD1_MPS_RESERVED)
+		return false;
+
+	usb3_for_each_dma(usb3, dma, i) {
+		if (dma->used)
+			continue;
+
+		if (usb_gadget_map_request(&usb3->gadget, &usb3_req->req,
+					   usb3_ep->dir_in) < 0)
+			break;
+
+		dma->used = true;
+		usb3_ep->dma = dma;
+		ret = true;
+		break;
+	}
+
+	return ret;
+}
+
+static void usb3_dma_put_setting_area(struct renesas_usb3_ep *usb3_ep,
+				      struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	int i;
+	struct renesas_usb3_dma *dma;
+
+	usb3_for_each_dma(usb3, dma, i) {
+		if (usb3_ep->dma == dma) {
+			usb_gadget_unmap_request(&usb3->gadget, &usb3_req->req,
+						 usb3_ep->dir_in);
+			dma->used = false;
+			usb3_ep->dma = NULL;
+			break;
+		}
+	}
+}
+
+static void usb3_dma_fill_prd(struct renesas_usb3_ep *usb3_ep,
+			      struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd;
+	u32 remain = usb3_req->req.length;
+	u32 dma = usb3_req->req.dma;
+	u32 len;
+	int i = 0;
+
+	do {
+		len = min_t(u32, remain, USB3_DMA_MAX_XFER_SIZE) &
+			    USB3_PRD1_SIZE_MASK;
+		cur_prd->word1 = usb3_dma_mps_to_prd_word1(usb3_ep) |
+				 USB3_PRD1_B_INC | len;
+		cur_prd->bap = dma;
+		remain -= len;
+		dma += len;
+		if (!remain || (i + 1) < USB3_DMA_NUM_PRD_ENTRIES)
+			break;
+
+		cur_prd++;
+		i++;
+	} while (1);
+
+	cur_prd->word1 |= USB3_PRD1_E | USB3_PRD1_INT;
+	if (usb3_ep->dir_in)
+		cur_prd->word1 |= USB3_PRD1_LST;
+}
+
+static void usb3_dma_kick_prd(struct renesas_usb3_ep *usb3_ep)
+{
+	struct renesas_usb3_dma *dma = usb3_ep->dma;
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	u32 dma_con = DMA_COM_PIPE_NO(usb3_ep->num) | DMA_CON_PRD_EN;
+
+	if (usb3_ep->dir_in)
+		dma_con |= DMA_CON_PIPE_DIR;
+
+	wmb();	/* prd entries should be in system memory here */
+
+	usb3_write(usb3, 1 << usb3_ep->num, USB3_DMA_INT_STA);
+	usb3_write(usb3, AXI_INT_PRDEN_CLR_STA(dma->num) |
+		   AXI_INT_PRDERR_STA(dma->num), USB3_AXI_INT_STA);
+
+	usb3_write(usb3, dma->prd_dma, USB3_DMA_CH0_PRD_ADR(dma->num));
+	usb3_write(usb3, dma_con, USB3_DMA_CH0_CON(dma->num));
+	usb3_enable_dma_irq(usb3, usb3_ep->num);
+}
+
+static void usb3_dma_stop_prd(struct renesas_usb3_ep *usb3_ep)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	struct renesas_usb3_dma *dma = usb3_ep->dma;
+
+	usb3_disable_dma_irq(usb3, usb3_ep->num);
+	usb3_write(usb3, 0, USB3_DMA_CH0_CON(dma->num));
+}
+
+static int usb3_dma_update_status(struct renesas_usb3_ep *usb3_ep,
+				  struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd;
+	struct usb_request *req = &usb3_req->req;
+	u32 remain, len;
+	int i = 0;
+	int status = 0;
+
+	rmb();	/* The controller updated prd entries */
+
+	do {
+		if (cur_prd->word1 & USB3_PRD1_D)
+			status = -EIO;
+		if (cur_prd->word1 & USB3_PRD1_E)
+			len = req->length % USB3_DMA_MAX_XFER_SIZE;
+		else
+			len = USB3_DMA_MAX_XFER_SIZE;
+		remain = cur_prd->word1 & USB3_PRD1_SIZE_MASK;
+		req->actual += len - remain;
+
+		if (cur_prd->word1 & USB3_PRD1_E ||
+		    (i + 1) < USB3_DMA_NUM_PRD_ENTRIES)
+			break;
+
+		cur_prd++;
+		i++;
+	} while (1);
+
+	return status;
+}
+
+static bool usb3_dma_try_start(struct renesas_usb3_ep *usb3_ep,
+			       struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	if (!use_dma)
+		return false;
+
+	if (usb3_dma_get_setting_area(usb3_ep, usb3_req)) {
+		usb3_pn_stop(usb3);
+		usb3_enable_dma_pipen(usb3);
+		usb3_dma_fill_prd(usb3_ep, usb3_req);
+		usb3_dma_kick_prd(usb3_ep);
+		usb3_pn_start(usb3);
+		return true;
+	}
+
+	return false;
+}
+
+static int usb3_dma_try_stop(struct renesas_usb3_ep *usb3_ep,
+			     struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+	int status = 0;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (!usb3_ep->dma)
+		goto out;
+
+	if (!usb3_pn_change(usb3, usb3_ep->num))
+		usb3_disable_dma_pipen(usb3);
+	usb3_dma_stop_prd(usb3_ep);
+	status = usb3_dma_update_status(usb3_ep, usb3_req);
+	usb3_dma_put_setting_area(usb3_ep, usb3_req);
+
+out:
+	spin_unlock_irqrestore(&usb3->lock, flags);
+	return status;
+}
+
+static int renesas_usb3_dma_free_prd(struct renesas_usb3 *usb3,
+				     struct device *dev)
+{
+	int i;
+	struct renesas_usb3_dma *dma;
+
+	usb3_for_each_dma(usb3, dma, i) {
+		if (dma->prd) {
+			dma_free_coherent(dev, USB3_DMA_PRD_SIZE,
+					  dma->prd, dma->prd_dma);
+			dma->prd = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int renesas_usb3_dma_alloc_prd(struct renesas_usb3 *usb3,
+				      struct device *dev)
+{
+	int i;
+	struct renesas_usb3_dma *dma;
+
+	if (!use_dma)
+		return 0;
+
+	usb3_for_each_dma(usb3, dma, i) {
+		dma->prd = dma_alloc_coherent(dev, USB3_DMA_PRD_SIZE,
+					      &dma->prd_dma, GFP_KERNEL);
+		if (!dma->prd) {
+			renesas_usb3_dma_free_prd(usb3, dev);
+			return -ENOMEM;
+		}
+		dma->num = i + 1;
+	}
+
+	return 0;
+}
+
+static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep,
+			     struct renesas_usb3_request *usb3_req)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	struct renesas_usb3_request *usb3_req_first = usb3_get_request(usb3_ep);
+	unsigned long flags;
+	int ret = -EAGAIN;
+	u32 enable_bits = 0;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (usb3_ep->halt || usb3_ep->started)
+		goto out;
+	if (usb3_req != usb3_req_first)
+		goto out;
+
+	if (usb3_pn_change(usb3, usb3_ep->num) < 0)
+		goto out;
+
+	usb3_ep->started = true;
+
+	if (usb3_dma_try_start(usb3_ep, usb3_req))
+		goto out;
+
+	usb3_pn_start(usb3);
+
+	if (usb3_ep->dir_in) {
+		ret = usb3_write_pipe(usb3_ep, usb3_req, USB3_PN_WRITE);
+		enable_bits |= PN_INT_LSTTR;
+	}
+
+	if (ret < 0)
+		enable_bits |= PN_INT_BFRDY;
+
+	if (enable_bits) {
+		usb3_set_bit(usb3, enable_bits, USB3_PN_INT_ENA);
+		usb3_enable_pipe_irq(usb3, usb3_ep->num);
+	}
+out:
+	spin_unlock_irqrestore(&usb3->lock, flags);
+}
+
+static int renesas_usb3_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				 gfp_t gfp_flags)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+	struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	dev_dbg(usb3_to_dev(usb3), "ep_queue: ep%2d, %u\n", usb3_ep->num,
+		_req->length);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+	spin_lock_irqsave(&usb3->lock, flags);
+	list_add_tail(&usb3_req->queue, &usb3_ep->queue);
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	if (!usb3_ep->num)
+		usb3_start_pipe0(usb3_ep, usb3_req);
+	else
+		usb3_start_pipen(usb3_ep, usb3_req);
+
+	return 0;
+}
+
+static void usb3_set_device_address(struct renesas_usb3 *usb3, u16 addr)
+{
+	/* DEV_ADDR bit field is cleared by WarmReset, HotReset and BusReset */
+	usb3_set_bit(usb3, USB_COM_CON_DEV_ADDR(addr), USB3_USB_COM_CON);
+}
+
+static bool usb3_std_req_set_address(struct renesas_usb3 *usb3,
+				     struct usb_ctrlrequest *ctrl)
+{
+	if (ctrl->wValue >= 128)
+		return true;	/* stall */
+
+	usb3_set_device_address(usb3, ctrl->wValue);
+	usb3_set_p0_con_for_no_data(usb3);
+
+	return false;
+}
+
+static void usb3_pipe0_internal_xfer(struct renesas_usb3 *usb3,
+				     void *tx_data, size_t len,
+				     void (*complete)(struct usb_ep *ep,
+						      struct usb_request *req))
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+
+	if (tx_data)
+		memcpy(usb3->ep0_buf, tx_data,
+		       min_t(size_t, len, USB3_EP0_BUF_SIZE));
+
+	usb3->ep0_req->buf = &usb3->ep0_buf;
+	usb3->ep0_req->length = len;
+	usb3->ep0_req->complete = complete;
+	renesas_usb3_ep_queue(&usb3_ep->ep, usb3->ep0_req, GFP_ATOMIC);
+}
+
+static void usb3_pipe0_get_status_completion(struct usb_ep *ep,
+					     struct usb_request *req)
+{
+}
+
+static bool usb3_std_req_get_status(struct renesas_usb3 *usb3,
+				    struct usb_ctrlrequest *ctrl)
+{
+	bool stall = false;
+	struct renesas_usb3_ep *usb3_ep;
+	int num;
+	u16 status = 0;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		if (usb3->gadget.is_selfpowered)
+			status |= 1 << USB_DEVICE_SELF_POWERED;
+		if (usb3->gadget.speed == USB_SPEED_SUPER)
+			status |= usb3_feature_get_un_enabled(usb3);
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		usb3_ep = usb3_get_ep(usb3, num);
+		if (usb3_ep->halt)
+			status |= 1 << USB_ENDPOINT_HALT;
+		break;
+	default:
+		stall = true;
+		break;
+	}
+
+	if (!stall) {
+		status = cpu_to_le16(status);
+		dev_dbg(usb3_to_dev(usb3), "get_status: req = %p\n",
+			usb_req_to_usb3_req(usb3->ep0_req));
+		usb3_pipe0_internal_xfer(usb3, &status, sizeof(status),
+					 usb3_pipe0_get_status_completion);
+	}
+
+	return stall;
+}
+
+static bool usb3_std_req_feature_device(struct renesas_usb3 *usb3,
+					struct usb_ctrlrequest *ctrl, bool set)
+{
+	bool stall = true;
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+
+	switch (w_value) {
+	case USB_DEVICE_TEST_MODE:
+		if (!set)
+			break;
+		usb3->test_mode = le16_to_cpu(ctrl->wIndex) >> 8;
+		stall = false;
+		break;
+	case USB_DEVICE_U1_ENABLE:
+	case USB_DEVICE_U2_ENABLE:
+		if (usb3->gadget.speed != USB_SPEED_SUPER)
+			break;
+		if (w_value == USB_DEVICE_U1_ENABLE)
+			usb3_feature_u1_enable(usb3, set);
+		if (w_value == USB_DEVICE_U2_ENABLE)
+			usb3_feature_u2_enable(usb3, set);
+		stall = false;
+		break;
+	default:
+		break;
+	}
+
+	return stall;
+}
+
+static int usb3_set_halt_p0(struct renesas_usb3_ep *usb3_ep, bool halt)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	if (unlikely(usb3_ep->num))
+		return -EINVAL;
+
+	usb3_ep->halt = halt;
+	if (halt)
+		usb3_set_p0_con_stall(usb3);
+	else
+		usb3_set_p0_con_stop(usb3);
+
+	return 0;
+}
+
+static int usb3_set_halt_pn(struct renesas_usb3_ep *usb3_ep, bool halt,
+			    bool is_clear_feature)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (!usb3_pn_change(usb3, usb3_ep->num)) {
+		usb3_ep->halt = halt;
+		if (halt) {
+			usb3_pn_stall(usb3);
+		} else if (!is_clear_feature || !usb3_ep->wedge) {
+			usb3_pn_con_clear(usb3);
+			usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+			usb3_pn_stop(usb3);
+		}
+	}
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	return 0;
+}
+
+static int usb3_set_halt(struct renesas_usb3_ep *usb3_ep, bool halt,
+			 bool is_clear_feature)
+{
+	int ret = 0;
+
+	if (halt && usb3_ep->started)
+		return -EAGAIN;
+
+	if (usb3_ep->num)
+		ret = usb3_set_halt_pn(usb3_ep, halt, is_clear_feature);
+	else
+		ret = usb3_set_halt_p0(usb3_ep, halt);
+
+	return ret;
+}
+
+static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3,
+					  struct usb_ctrlrequest *ctrl,
+					  bool set)
+{
+	int num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+	struct renesas_usb3_ep *usb3_ep;
+	struct renesas_usb3_request *usb3_req;
+
+	if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+		return true;	/* stall */
+
+	usb3_ep = usb3_get_ep(usb3, num);
+	usb3_set_halt(usb3_ep, set, true);
+
+	/* Restarts a queue if clear feature */
+	if (!set) {
+		usb3_ep->started = false;
+		usb3_req = usb3_get_request(usb3_ep);
+		if (usb3_req)
+			usb3_start_pipen(usb3_ep, usb3_req);
+	}
+
+	return false;
+}
+
+static bool usb3_std_req_feature(struct renesas_usb3 *usb3,
+				 struct usb_ctrlrequest *ctrl, bool set)
+{
+	bool stall = false;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		stall = usb3_std_req_feature_device(usb3, ctrl, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		stall = usb3_std_req_feature_endpoint(usb3, ctrl, set);
+		break;
+	default:
+		stall = true;
+		break;
+	}
+
+	if (!stall)
+		usb3_set_p0_con_for_no_data(usb3);
+
+	return stall;
+}
+
+static void usb3_pipe0_set_sel_completion(struct usb_ep *ep,
+					  struct usb_request *req)
+{
+	/* TODO */
+}
+
+static bool usb3_std_req_set_sel(struct renesas_usb3 *usb3,
+				 struct usb_ctrlrequest *ctrl)
+{
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	if (w_length != 6)
+		return true;	/* stall */
+
+	dev_dbg(usb3_to_dev(usb3), "set_sel: req = %p\n",
+		usb_req_to_usb3_req(usb3->ep0_req));
+	usb3_pipe0_internal_xfer(usb3, NULL, 6, usb3_pipe0_set_sel_completion);
+
+	return false;
+}
+
+static bool usb3_std_req_set_configuration(struct renesas_usb3 *usb3,
+					   struct usb_ctrlrequest *ctrl)
+{
+	if (ctrl->wValue > 0)
+		usb3_set_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+	else
+		usb3_clear_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+
+	return false;
+}
+
+/**
+ * usb3_handle_standard_request - handle some standard requests
+ * @usb3: the renesas_usb3 pointer
+ * @ctrl: a pointer of setup data
+ *
+ * Returns true if this function handled a standard request
+ */
+static bool usb3_handle_standard_request(struct renesas_usb3 *usb3,
+					 struct usb_ctrlrequest *ctrl)
+{
+	bool ret = false;
+	bool stall = false;
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_SET_ADDRESS:
+			stall = usb3_std_req_set_address(usb3, ctrl);
+			ret = true;
+			break;
+		case USB_REQ_GET_STATUS:
+			stall = usb3_std_req_get_status(usb3, ctrl);
+			ret = true;
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			stall = usb3_std_req_feature(usb3, ctrl, false);
+			ret = true;
+			break;
+		case USB_REQ_SET_FEATURE:
+			stall = usb3_std_req_feature(usb3, ctrl, true);
+			ret = true;
+			break;
+		case USB_REQ_SET_SEL:
+			stall = usb3_std_req_set_sel(usb3, ctrl);
+			ret = true;
+			break;
+		case USB_REQ_SET_ISOCH_DELAY:
+			/* This hardware doesn't support Isochronous xfer */
+			stall = true;
+			ret = true;
+			break;
+		case USB_REQ_SET_CONFIGURATION:
+			usb3_std_req_set_configuration(usb3, ctrl);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (stall)
+		usb3_set_p0_con_stall(usb3);
+
+	return ret;
+}
+
+static int usb3_p0_con_clear_buffer(struct renesas_usb3 *usb3)
+{
+	usb3_set_bit(usb3, P0_CON_BCLR, USB3_P0_CON);
+
+	return usb3_wait(usb3, USB3_P0_CON, P0_CON_BCLR, 0);
+}
+
+static void usb3_irq_epc_pipe0_setup(struct renesas_usb3 *usb3)
+{
+	struct usb_ctrlrequest ctrl;
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+
+	/* Call giveback function if previous transfer is not completed */
+	if (usb3_ep->started)
+		usb3_request_done(usb3_ep, usb3_get_request(usb3_ep),
+				  -ECONNRESET);
+
+	usb3_p0_con_clear_buffer(usb3);
+	usb3_get_setup_data(usb3, &ctrl);
+	if (!usb3_handle_standard_request(usb3, &ctrl))
+		if (usb3->driver->setup(&usb3->gadget, &ctrl) < 0)
+			usb3_set_p0_con_stall(usb3);
+}
+
+static void usb3_irq_epc_pipe0_bfrdy(struct renesas_usb3 *usb3)
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+	struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+	if (!usb3_req)
+		return;
+
+	usb3_p0_xfer(usb3_ep, usb3_req);
+}
+
+static void usb3_irq_epc_pipe0(struct renesas_usb3 *usb3)
+{
+	u32 p0_int_sta = usb3_read(usb3, USB3_P0_INT_STA);
+
+	p0_int_sta &= usb3_read(usb3, USB3_P0_INT_ENA);
+	usb3_write(usb3, p0_int_sta, USB3_P0_INT_STA);
+	if (p0_int_sta & P0_INT_STSED)
+		usb3_irq_epc_pipe0_status_end(usb3);
+	if (p0_int_sta & P0_INT_SETUP)
+		usb3_irq_epc_pipe0_setup(usb3);
+	if (p0_int_sta & P0_INT_BFRDY)
+		usb3_irq_epc_pipe0_bfrdy(usb3);
+}
+
+static void usb3_request_done_pipen(struct renesas_usb3 *usb3,
+				    struct renesas_usb3_ep *usb3_ep,
+				    struct renesas_usb3_request *usb3_req,
+				    int status)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (usb3_pn_change(usb3, usb3_ep->num))
+		usb3_pn_stop(usb3);
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	usb3_disable_pipe_irq(usb3, usb3_ep->num);
+	usb3_request_done(usb3_ep, usb3_req, status);
+
+	/* get next usb3_req */
+	usb3_req = usb3_get_request(usb3_ep);
+	if (usb3_req)
+		usb3_start_pipen(usb3_ep, usb3_req);
+}
+
+static void usb3_irq_epc_pipen_lsttr(struct renesas_usb3 *usb3, int num)
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, num);
+	struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+	if (!usb3_req)
+		return;
+
+	if (usb3_ep->dir_in) {
+		dev_dbg(usb3_to_dev(usb3), "%s: len = %u, actual = %u\n",
+			__func__, usb3_req->req.length, usb3_req->req.actual);
+		usb3_request_done_pipen(usb3, usb3_ep, usb3_req, 0);
+	}
+}
+
+static void usb3_irq_epc_pipen_bfrdy(struct renesas_usb3 *usb3, int num)
+{
+	struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, num);
+	struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+	bool done = false;
+
+	if (!usb3_req)
+		return;
+
+	spin_lock(&usb3->lock);
+	if (usb3_pn_change(usb3, num))
+		goto out;
+
+	if (usb3_ep->dir_in) {
+		/* Do not stop the IN pipe here to detect LSTTR interrupt */
+		if (!usb3_write_pipe(usb3_ep, usb3_req, USB3_PN_WRITE))
+			usb3_clear_bit(usb3, PN_INT_BFRDY, USB3_PN_INT_ENA);
+	} else {
+		if (!usb3_read_pipe(usb3_ep, usb3_req, USB3_PN_READ))
+			done = true;
+	}
+
+out:
+	/* need to unlock because usb3_request_done_pipen() locks it */
+	spin_unlock(&usb3->lock);
+
+	if (done)
+		usb3_request_done_pipen(usb3, usb3_ep, usb3_req, 0);
+}
+
+static void usb3_irq_epc_pipen(struct renesas_usb3 *usb3, int num)
+{
+	u32 pn_int_sta;
+
+	spin_lock(&usb3->lock);
+	if (usb3_pn_change(usb3, num) < 0) {
+		spin_unlock(&usb3->lock);
+		return;
+	}
+
+	pn_int_sta = usb3_read(usb3, USB3_PN_INT_STA);
+	pn_int_sta &= usb3_read(usb3, USB3_PN_INT_ENA);
+	usb3_write(usb3, pn_int_sta, USB3_PN_INT_STA);
+	spin_unlock(&usb3->lock);
+	if (pn_int_sta & PN_INT_LSTTR)
+		usb3_irq_epc_pipen_lsttr(usb3, num);
+	if (pn_int_sta & PN_INT_BFRDY)
+		usb3_irq_epc_pipen_bfrdy(usb3, num);
+}
+
+static void usb3_irq_epc_int_2(struct renesas_usb3 *usb3, u32 int_sta_2)
+{
+	int i;
+
+	for (i = 0; i < usb3->num_usb3_eps; i++) {
+		if (int_sta_2 & USB_INT_2_PIPE(i)) {
+			if (!i)
+				usb3_irq_epc_pipe0(usb3);
+			else
+				usb3_irq_epc_pipen(usb3, i);
+		}
+	}
+}
+
+static void usb3_irq_idmon_change(struct renesas_usb3 *usb3)
+{
+	usb3_check_id(usb3);
+}
+
+static void usb3_irq_otg_int(struct renesas_usb3 *usb3, u32 otg_int_sta)
+{
+	if (otg_int_sta & USB_OTG_IDMON)
+		usb3_irq_idmon_change(usb3);
+}
+
+static void usb3_irq_epc(struct renesas_usb3 *usb3)
+{
+	u32 int_sta_1 = usb3_read(usb3, USB3_USB_INT_STA_1);
+	u32 int_sta_2 = usb3_read(usb3, USB3_USB_INT_STA_2);
+	u32 otg_int_sta = usb3_read(usb3, USB3_USB_OTG_INT_STA);
+
+	int_sta_1 &= usb3_read(usb3, USB3_USB_INT_ENA_1);
+	if (int_sta_1) {
+		usb3_write(usb3, int_sta_1, USB3_USB_INT_STA_1);
+		usb3_irq_epc_int_1(usb3, int_sta_1);
+	}
+
+	int_sta_2 &= usb3_read(usb3, USB3_USB_INT_ENA_2);
+	if (int_sta_2)
+		usb3_irq_epc_int_2(usb3, int_sta_2);
+
+	otg_int_sta &= usb3_read(usb3, USB3_USB_OTG_INT_ENA);
+	if (otg_int_sta) {
+		usb3_write(usb3, otg_int_sta, USB3_USB_OTG_INT_STA);
+		usb3_irq_otg_int(usb3, otg_int_sta);
+	}
+}
+
+static void usb3_irq_dma_int(struct renesas_usb3 *usb3, u32 dma_sta)
+{
+	struct renesas_usb3_ep *usb3_ep;
+	struct renesas_usb3_request *usb3_req;
+	int i, status;
+
+	for (i = 0; i < usb3->num_usb3_eps; i++) {
+		if (!(dma_sta & DMA_INT(i)))
+			continue;
+
+		usb3_ep = usb3_get_ep(usb3, i);
+		if (!(usb3_read(usb3, USB3_AXI_INT_STA) &
+		    AXI_INT_PRDEN_CLR_STA(usb3_ep->dma->num)))
+			continue;
+
+		usb3_req = usb3_get_request(usb3_ep);
+		status = usb3_dma_try_stop(usb3_ep, usb3_req);
+		usb3_request_done_pipen(usb3, usb3_ep, usb3_req, status);
+	}
+}
+
+static void usb3_irq_dma(struct renesas_usb3 *usb3)
+{
+	u32 dma_sta = usb3_read(usb3, USB3_DMA_INT_STA);
+
+	dma_sta &= usb3_read(usb3, USB3_DMA_INT_ENA);
+	if (dma_sta) {
+		usb3_write(usb3, dma_sta, USB3_DMA_INT_STA);
+		usb3_irq_dma_int(usb3, dma_sta);
+	}
+}
+
+static irqreturn_t renesas_usb3_irq(int irq, void *_usb3)
+{
+	struct renesas_usb3 *usb3 = _usb3;
+	irqreturn_t ret = IRQ_NONE;
+	u32 axi_int_sta = usb3_read(usb3, USB3_AXI_INT_STA);
+
+	if (axi_int_sta & AXI_INT_DMAINT) {
+		usb3_irq_dma(usb3);
+		ret = IRQ_HANDLED;
+	}
+
+	if (axi_int_sta & AXI_INT_EPCINT) {
+		usb3_irq_epc(usb3);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static void usb3_write_pn_mod(struct renesas_usb3_ep *usb3_ep,
+			      const struct usb_endpoint_descriptor *desc)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	u32 val = 0;
+
+	val |= usb3_ep->dir_in ? PN_MOD_DIR : 0;
+	val |= PN_MOD_TYPE(usb_endpoint_type(desc));
+	val |= PN_MOD_EPNUM(usb_endpoint_num(desc));
+	usb3_write(usb3, val, USB3_PN_MOD);
+}
+
+static u32 usb3_calc_ramarea(int ram_size)
+{
+	WARN_ON(ram_size > SZ_16K);
+
+	if (ram_size <= SZ_1K)
+		return PN_RAMMAP_RAMAREA_1KB;
+	else if (ram_size <= SZ_2K)
+		return PN_RAMMAP_RAMAREA_2KB;
+	else if (ram_size <= SZ_4K)
+		return PN_RAMMAP_RAMAREA_4KB;
+	else if (ram_size <= SZ_8K)
+		return PN_RAMMAP_RAMAREA_8KB;
+	else
+		return PN_RAMMAP_RAMAREA_16KB;
+}
+
+static u32 usb3_calc_rammap_val(struct renesas_usb3_ep *usb3_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	int i;
+	static const u32 max_packet_array[] = {8, 16, 32, 64, 512};
+	u32 mpkt = PN_RAMMAP_MPKT(1024);
+
+	for (i = 0; i < ARRAY_SIZE(max_packet_array); i++) {
+		if (usb_endpoint_maxp(desc) <= max_packet_array[i])
+			mpkt = PN_RAMMAP_MPKT(max_packet_array[i]);
+	}
+
+	return usb3_ep->rammap_val | mpkt;
+}
+
+static int usb3_enable_pipe_n(struct renesas_usb3_ep *usb3_ep,
+			      const struct usb_endpoint_descriptor *desc)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	usb3_ep->dir_in = usb_endpoint_dir_in(desc);
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (!usb3_pn_change(usb3, usb3_ep->num)) {
+		usb3_write_pn_mod(usb3_ep, desc);
+		usb3_write(usb3, usb3_calc_rammap_val(usb3_ep, desc),
+			   USB3_PN_RAMMAP);
+		usb3_pn_con_clear(usb3);
+		usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+	}
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	return 0;
+}
+
+static int usb3_disable_pipe_n(struct renesas_usb3_ep *usb3_ep)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	usb3_ep->halt = false;
+
+	spin_lock_irqsave(&usb3->lock, flags);
+	if (!usb3_pn_change(usb3, usb3_ep->num)) {
+		usb3_write(usb3, 0, USB3_PN_INT_ENA);
+		usb3_write(usb3, 0, USB3_PN_RAMMAP);
+		usb3_clear_bit(usb3, PN_CON_EN, USB3_PN_CON);
+	}
+	spin_unlock_irqrestore(&usb3->lock, flags);
+
+	return 0;
+}
+
+/*------- usb_ep_ops -----------------------------------------------------*/
+static int renesas_usb3_ep_enable(struct usb_ep *_ep,
+				  const struct usb_endpoint_descriptor *desc)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+
+	return usb3_enable_pipe_n(usb3_ep, desc);
+}
+
+static int renesas_usb3_ep_disable(struct usb_ep *_ep)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+	struct renesas_usb3_request *usb3_req;
+
+	do {
+		usb3_req = usb3_get_request(usb3_ep);
+		if (!usb3_req)
+			break;
+		usb3_dma_try_stop(usb3_ep, usb3_req);
+		usb3_request_done(usb3_ep, usb3_req, -ESHUTDOWN);
+	} while (1);
+
+	return usb3_disable_pipe_n(usb3_ep);
+}
+
+static struct usb_request *__renesas_usb3_ep_alloc_request(gfp_t gfp_flags)
+{
+	struct renesas_usb3_request *usb3_req;
+
+	usb3_req = kzalloc(sizeof(struct renesas_usb3_request), gfp_flags);
+	if (!usb3_req)
+		return NULL;
+
+	INIT_LIST_HEAD(&usb3_req->queue);
+
+	return &usb3_req->req;
+}
+
+static void __renesas_usb3_ep_free_request(struct usb_request *_req)
+{
+	struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+
+	kfree(usb3_req);
+}
+
+static struct usb_request *renesas_usb3_ep_alloc_request(struct usb_ep *_ep,
+							 gfp_t gfp_flags)
+{
+	return __renesas_usb3_ep_alloc_request(gfp_flags);
+}
+
+static void renesas_usb3_ep_free_request(struct usb_ep *_ep,
+					 struct usb_request *_req)
+{
+	__renesas_usb3_ep_free_request(_req);
+}
+
+static int renesas_usb3_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+	struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	dev_dbg(usb3_to_dev(usb3), "ep_dequeue: ep%2d, %u\n", usb3_ep->num,
+		_req->length);
+
+	usb3_dma_try_stop(usb3_ep, usb3_req);
+	usb3_request_done_pipen(usb3, usb3_ep, usb3_req, -ECONNRESET);
+
+	return 0;
+}
+
+static int renesas_usb3_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	return usb3_set_halt(usb_ep_to_usb3_ep(_ep), !!value, false);
+}
+
+static int renesas_usb3_ep_set_wedge(struct usb_ep *_ep)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+
+	usb3_ep->wedge = true;
+	return usb3_set_halt(usb3_ep, true, false);
+}
+
+static void renesas_usb3_ep_fifo_flush(struct usb_ep *_ep)
+{
+	struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+	unsigned long flags;
+
+	if (usb3_ep->num) {
+		spin_lock_irqsave(&usb3->lock, flags);
+		if (!usb3_pn_change(usb3, usb3_ep->num)) {
+			usb3_pn_con_clear(usb3);
+			usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+		}
+		spin_unlock_irqrestore(&usb3->lock, flags);
+	} else {
+		usb3_p0_con_clear_buffer(usb3);
+	}
+}
+
+static const struct usb_ep_ops renesas_usb3_ep_ops = {
+	.enable		= renesas_usb3_ep_enable,
+	.disable	= renesas_usb3_ep_disable,
+
+	.alloc_request	= renesas_usb3_ep_alloc_request,
+	.free_request	= renesas_usb3_ep_free_request,
+
+	.queue		= renesas_usb3_ep_queue,
+	.dequeue	= renesas_usb3_ep_dequeue,
+
+	.set_halt	= renesas_usb3_ep_set_halt,
+	.set_wedge	= renesas_usb3_ep_set_wedge,
+	.fifo_flush	= renesas_usb3_ep_fifo_flush,
+};
+
+/*------- usb_gadget_ops -------------------------------------------------*/
+static int renesas_usb3_start(struct usb_gadget *gadget,
+			      struct usb_gadget_driver *driver)
+{
+	struct renesas_usb3 *usb3;
+
+	if (!driver || driver->max_speed < USB_SPEED_FULL ||
+	    !driver->setup)
+		return -EINVAL;
+
+	usb3 = gadget_to_renesas_usb3(gadget);
+
+	/* hook up the driver */
+	usb3->driver = driver;
+
+	if (usb3->phy)
+		phy_init(usb3->phy);
+
+	pm_runtime_get_sync(usb3_to_dev(usb3));
+
+	renesas_usb3_init_controller(usb3);
+
+	return 0;
+}
+
+static int renesas_usb3_stop(struct usb_gadget *gadget)
+{
+	struct renesas_usb3 *usb3 = gadget_to_renesas_usb3(gadget);
+
+	usb3->softconnect = false;
+	usb3->gadget.speed = USB_SPEED_UNKNOWN;
+	usb3->driver = NULL;
+	renesas_usb3_stop_controller(usb3);
+
+	if (usb3->phy)
+		phy_exit(usb3->phy);
+
+	pm_runtime_put(usb3_to_dev(usb3));
+
+	return 0;
+}
+
+static int renesas_usb3_get_frame(struct usb_gadget *_gadget)
+{
+	return -EOPNOTSUPP;
+}
+
+static int renesas_usb3_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct renesas_usb3 *usb3 = gadget_to_renesas_usb3(gadget);
+
+	usb3->softconnect = !!is_on;
+
+	return 0;
+}
+
+static int renesas_usb3_set_selfpowered(struct usb_gadget *gadget, int is_self)
+{
+	gadget->is_selfpowered = !!is_self;
+
+	return 0;
+}
+
+static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
+	.get_frame		= renesas_usb3_get_frame,
+	.udc_start		= renesas_usb3_start,
+	.udc_stop		= renesas_usb3_stop,
+	.pullup			= renesas_usb3_pullup,
+	.set_selfpowered	= renesas_usb3_set_selfpowered,
+};
+
+static enum usb_role renesas_usb3_role_switch_get(struct device *dev)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+	enum usb_role cur_role;
+
+	pm_runtime_get_sync(dev);
+	cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+	pm_runtime_put(dev);
+
+	return cur_role;
+}
+
+static int renesas_usb3_role_switch_set(struct device *dev,
+					enum usb_role role)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+	struct device *host = usb3->host_dev;
+	enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+
+	pm_runtime_get_sync(dev);
+	if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) {
+		device_release_driver(host);
+		usb3_set_mode(usb3, false);
+	} else if (cur_role == USB_ROLE_DEVICE && role == USB_ROLE_HOST) {
+		/* Must set the mode before device_attach of the host */
+		usb3_set_mode(usb3, true);
+		/* This device_attach() might sleep */
+		if (device_attach(host) < 0)
+			dev_err(dev, "device_attach(host) failed\n");
+	}
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+static ssize_t role_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+	bool new_mode_is_host;
+
+	if (!usb3->driver)
+		return -ENODEV;
+
+	if (usb3->forced_b_device)
+		return -EBUSY;
+
+	if (!strncmp(buf, "host", strlen("host")))
+		new_mode_is_host = true;
+	else if (!strncmp(buf, "peripheral", strlen("peripheral")))
+		new_mode_is_host = false;
+	else
+		return -EINVAL;
+
+	if (new_mode_is_host == usb3_is_host(usb3))
+		return -EINVAL;
+
+	usb3_mode_config(usb3, new_mode_is_host, usb3_is_a_device(usb3));
+
+	return count;
+}
+
+static ssize_t role_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+
+	if (!usb3->driver)
+		return -ENODEV;
+
+	return sprintf(buf, "%s\n", usb3_is_host(usb3) ? "host" : "peripheral");
+}
+static DEVICE_ATTR_RW(role);
+
+static int renesas_usb3_b_device_show(struct seq_file *s, void *unused)
+{
+	struct renesas_usb3 *usb3 = s->private;
+
+	seq_printf(s, "%d\n", usb3->forced_b_device);
+
+	return 0;
+}
+
+static int renesas_usb3_b_device_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, renesas_usb3_b_device_show, inode->i_private);
+}
+
+static ssize_t renesas_usb3_b_device_write(struct file *file,
+					   const char __user *ubuf,
+					   size_t count, loff_t *ppos)
+{
+	struct seq_file *s = file->private_data;
+	struct renesas_usb3 *usb3 = s->private;
+	char buf[32];
+
+	if (!usb3->driver)
+		return -ENODEV;
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1))
+		usb3->forced_b_device = true;
+	else
+		usb3->forced_b_device = false;
+
+	if (usb3->workaround_for_vbus)
+		usb3_disconnect(usb3);
+
+	/* Let this driver call usb3_connect() anyway */
+	usb3_check_id(usb3);
+
+	return count;
+}
+
+static const struct file_operations renesas_usb3_b_device_fops = {
+	.open = renesas_usb3_b_device_open,
+	.write = renesas_usb3_b_device_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3,
+				      struct device *dev)
+{
+	usb3->dentry = debugfs_create_dir(dev_name(dev), NULL);
+
+	debugfs_create_file("b_device", 0644, usb3->dentry, usb3,
+			    &renesas_usb3_b_device_fops);
+}
+
+/*------- platform_driver ------------------------------------------------*/
+static int renesas_usb3_remove(struct platform_device *pdev)
+{
+	struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
+
+	debugfs_remove_recursive(usb3->dentry);
+	device_remove_file(&pdev->dev, &dev_attr_role);
+
+	usb_role_switch_unregister(usb3->role_sw);
+
+	usb_del_gadget_udc(&usb3->gadget);
+	renesas_usb3_dma_free_prd(usb3, &pdev->dev);
+
+	__renesas_usb3_ep_free_request(usb3->ep0_req);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
+				const struct renesas_usb3_priv *priv)
+{
+	struct renesas_usb3_ep *usb3_ep;
+	int i;
+
+	/* calculate num_usb3_eps from renesas_usb3_priv */
+	usb3->num_usb3_eps = priv->ramsize_per_ramif * priv->num_ramif * 2 /
+			     priv->ramsize_per_pipe + 1;
+
+	if (usb3->num_usb3_eps > USB3_MAX_NUM_PIPES)
+		usb3->num_usb3_eps = USB3_MAX_NUM_PIPES;
+
+	usb3->usb3_ep = devm_kcalloc(dev,
+				     usb3->num_usb3_eps, sizeof(*usb3_ep),
+				     GFP_KERNEL);
+	if (!usb3->usb3_ep)
+		return -ENOMEM;
+
+	dev_dbg(dev, "%s: num_usb3_eps = %d\n", __func__, usb3->num_usb3_eps);
+	/*
+	 * This driver prepares pipes as follows:
+	 *  - odd pipes = IN pipe
+	 *  - even pipes = OUT pipe (except pipe 0)
+	 */
+	usb3_for_each_ep(usb3_ep, usb3, i) {
+		snprintf(usb3_ep->ep_name, sizeof(usb3_ep->ep_name), "ep%d", i);
+		usb3_ep->usb3 = usb3;
+		usb3_ep->num = i;
+		usb3_ep->ep.name = usb3_ep->ep_name;
+		usb3_ep->ep.ops = &renesas_usb3_ep_ops;
+		INIT_LIST_HEAD(&usb3_ep->queue);
+		INIT_LIST_HEAD(&usb3_ep->ep.ep_list);
+		if (!i) {
+			/* for control pipe */
+			usb3->gadget.ep0 = &usb3_ep->ep;
+			usb_ep_set_maxpacket_limit(&usb3_ep->ep,
+						USB3_EP0_SS_MAX_PACKET_SIZE);
+			usb3_ep->ep.caps.type_control = true;
+			usb3_ep->ep.caps.dir_in = true;
+			usb3_ep->ep.caps.dir_out = true;
+			continue;
+		}
+
+		/* for bulk or interrupt pipe */
+		usb_ep_set_maxpacket_limit(&usb3_ep->ep, ~0);
+		list_add_tail(&usb3_ep->ep.ep_list, &usb3->gadget.ep_list);
+		usb3_ep->ep.caps.type_bulk = true;
+		usb3_ep->ep.caps.type_int = true;
+		if (i & 1)
+			usb3_ep->ep.caps.dir_in = true;
+		else
+			usb3_ep->ep.caps.dir_out = true;
+	}
+
+	return 0;
+}
+
+static void renesas_usb3_init_ram(struct renesas_usb3 *usb3, struct device *dev,
+				  const struct renesas_usb3_priv *priv)
+{
+	struct renesas_usb3_ep *usb3_ep;
+	int i;
+	u32 ramif[2], basead[2];	/* index 0 = for IN pipes */
+	u32 *cur_ramif, *cur_basead;
+	u32 val;
+
+	memset(ramif, 0, sizeof(ramif));
+	memset(basead, 0, sizeof(basead));
+
+	/*
+	 * This driver prepares pipes as follows:
+	 *  - all pipes = the same size as "ramsize_per_pipe"
+	 * Please refer to the "Method of Specifying RAM Mapping"
+	 */
+	usb3_for_each_ep(usb3_ep, usb3, i) {
+		if (!i)
+			continue;	/* out of scope if ep num = 0 */
+		if (usb3_ep->ep.caps.dir_in) {
+			cur_ramif = &ramif[0];
+			cur_basead = &basead[0];
+		} else {
+			cur_ramif = &ramif[1];
+			cur_basead = &basead[1];
+		}
+
+		if (*cur_basead > priv->ramsize_per_ramif)
+			continue;	/* out of memory for IN or OUT pipe */
+
+		/* calculate rammap_val */
+		val = PN_RAMMAP_RAMIF(*cur_ramif);
+		val |= usb3_calc_ramarea(priv->ramsize_per_pipe);
+		val |= PN_RAMMAP_BASEAD(*cur_basead);
+		usb3_ep->rammap_val = val;
+
+		dev_dbg(dev, "ep%2d: val = %08x, ramif = %d, base = %x\n",
+			i, val, *cur_ramif, *cur_basead);
+
+		/* update current ramif */
+		if (*cur_ramif + 1 == priv->num_ramif) {
+			*cur_ramif = 0;
+			*cur_basead += priv->ramsize_per_pipe;
+		} else {
+			(*cur_ramif)++;
+		}
+	}
+}
+
+static const struct renesas_usb3_priv renesas_usb3_priv_r8a7795_es1 = {
+	.ramsize_per_ramif = SZ_16K,
+	.num_ramif = 2,
+	.ramsize_per_pipe = SZ_4K,
+	.workaround_for_vbus = true,
+};
+
+static const struct renesas_usb3_priv renesas_usb3_priv_gen3 = {
+	.ramsize_per_ramif = SZ_16K,
+	.num_ramif = 4,
+	.ramsize_per_pipe = SZ_4K,
+};
+
+static const struct of_device_id usb3_of_match[] = {
+	{
+		.compatible = "renesas,r8a7795-usb3-peri",
+		.data = &renesas_usb3_priv_gen3,
+	},
+	{
+		.compatible = "renesas,rcar-gen3-usb3-peri",
+		.data = &renesas_usb3_priv_gen3,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, usb3_of_match);
+
+static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
+	{
+		.soc_id = "r8a7795", .revision = "ES1.*",
+		.data = &renesas_usb3_priv_r8a7795_es1,
+	},
+	{ /* sentinel */ },
+};
+
+static const unsigned int renesas_usb3_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_NONE,
+};
+
+static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = {
+	.set = renesas_usb3_role_switch_set,
+	.get = renesas_usb3_role_switch_get,
+	.allow_userspace_control = true,
+};
+
+static int renesas_usb3_probe(struct platform_device *pdev)
+{
+	struct renesas_usb3 *usb3;
+	struct resource *res;
+	int irq, ret;
+	const struct renesas_usb3_priv *priv;
+	const struct soc_device_attribute *attr;
+
+	attr = soc_device_match(renesas_usb3_quirks_match);
+	if (attr)
+		priv = attr->data;
+	else
+		priv = of_device_get_match_data(&pdev->dev);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+		return irq;
+	}
+
+	usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
+	if (!usb3)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	usb3->reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(usb3->reg))
+		return PTR_ERR(usb3->reg);
+
+	platform_set_drvdata(pdev, usb3);
+	spin_lock_init(&usb3->lock);
+
+	usb3->gadget.ops = &renesas_usb3_gadget_ops;
+	usb3->gadget.name = udc_name;
+	usb3->gadget.max_speed = USB_SPEED_SUPER;
+	INIT_LIST_HEAD(&usb3->gadget.ep_list);
+	ret = renesas_usb3_init_ep(usb3, &pdev->dev, priv);
+	if (ret < 0)
+		return ret;
+	renesas_usb3_init_ram(usb3, &pdev->dev, priv);
+
+	ret = devm_request_irq(&pdev->dev, irq, renesas_usb3_irq, 0,
+			       dev_name(&pdev->dev), usb3);
+	if (ret < 0)
+		return ret;
+
+	INIT_WORK(&usb3->extcon_work, renesas_usb3_extcon_work);
+	usb3->extcon = devm_extcon_dev_allocate(&pdev->dev, renesas_usb3_cable);
+	if (IS_ERR(usb3->extcon))
+		return PTR_ERR(usb3->extcon);
+
+	ret = devm_extcon_dev_register(&pdev->dev, usb3->extcon);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register extcon\n");
+		return ret;
+	}
+
+	/* for ep0 handling */
+	usb3->ep0_req = __renesas_usb3_ep_alloc_request(GFP_KERNEL);
+	if (!usb3->ep0_req)
+		return -ENOMEM;
+
+	ret = renesas_usb3_dma_alloc_prd(usb3, &pdev->dev);
+	if (ret < 0)
+		goto err_alloc_prd;
+
+	/*
+	 * This is optional. So, if this driver cannot get a phy,
+	 * this driver will not handle a phy anymore.
+	 */
+	usb3->phy = devm_phy_optional_get(&pdev->dev, "usb");
+	if (IS_ERR(usb3->phy)) {
+		ret = PTR_ERR(usb3->phy);
+		goto err_add_udc;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget);
+	if (ret < 0)
+		goto err_add_udc;
+
+	ret = device_create_file(&pdev->dev, &dev_attr_role);
+	if (ret < 0)
+		goto err_dev_create;
+
+	INIT_WORK(&usb3->role_work, renesas_usb3_role_work);
+	usb3->role_sw = usb_role_switch_register(&pdev->dev,
+					&renesas_usb3_role_switch_desc);
+	if (!IS_ERR(usb3->role_sw)) {
+		usb3->host_dev = usb_of_get_companion_dev(&pdev->dev);
+		if (!usb3->host_dev) {
+			/* If not found, this driver will not use a role sw */
+			usb_role_switch_unregister(usb3->role_sw);
+			usb3->role_sw = NULL;
+		}
+	} else {
+		usb3->role_sw = NULL;
+	}
+
+	usb3->workaround_for_vbus = priv->workaround_for_vbus;
+
+	renesas_usb3_debugfs_init(usb3, &pdev->dev);
+
+	dev_info(&pdev->dev, "probed%s\n", usb3->phy ? " with phy" : "");
+
+	return 0;
+
+err_dev_create:
+	usb_del_gadget_udc(&usb3->gadget);
+
+err_add_udc:
+	renesas_usb3_dma_free_prd(usb3, &pdev->dev);
+
+err_alloc_prd:
+	__renesas_usb3_ep_free_request(usb3->ep0_req);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int renesas_usb3_suspend(struct device *dev)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!usb3->driver)
+		return 0;
+
+	renesas_usb3_stop_controller(usb3);
+	if (usb3->phy)
+		phy_exit(usb3->phy);
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+static int renesas_usb3_resume(struct device *dev)
+{
+	struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!usb3->driver)
+		return 0;
+
+	if (usb3->phy)
+		phy_init(usb3->phy);
+	pm_runtime_get_sync(dev);
+	renesas_usb3_init_controller(usb3);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend,
+			renesas_usb3_resume);
+
+static struct platform_driver renesas_usb3_driver = {
+	.probe		= renesas_usb3_probe,
+	.remove		= renesas_usb3_remove,
+	.driver		= {
+		.name =	(char *)udc_name,
+		.pm		= &renesas_usb3_pm_ops,
+		.of_match_table = of_match_ptr(usb3_of_match),
+	},
+};
+module_platform_driver(renesas_usb3_driver);
+
+MODULE_DESCRIPTION("Renesas USB3.0 Peripheral driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
+MODULE_ALIAS("platform:renesas_usb3");
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
new file mode 100644
index 0000000..31c7c55
--- /dev/null
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -0,0 +1,1369 @@
+// SPDX-License-Identifier: GPL-2.0
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/prefetch.h>
+#include <linux/platform_data/s3c-hsudc.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/regs-s3c2443-clock.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+static const char * const s3c_hsudc_supply_names[] = {
+	"vdda",		/* analog phy supply, 3.3V */
+	"vddi",		/* digital phy supply, 1.2V */
+	"vddosc",	/* oscillator supply, 1.8V - 3.3V */
+};
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	struct list_head queue;
+	u8 stopped;
+	u8 wedge;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	struct usb_phy *transceiver;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
+	spinlock_t lock;
+	void __iomem *regs;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	usb_gadget_giveback_request(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl(hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl(hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = hsep->wedge = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored
+ * @_ep: Endpoint on which wedge has to be set.
+ *
+ * Sets the halt feature with the clear requests ignored.
+ */
+static int s3c_hsudc_set_wedge(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+
+	if (!hsep)
+		return -EINVAL;
+
+	hsep->wedge = 1;
+	return usb_ep_set_halt(_ep);
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			if (set || !hsep->wedge)
+				s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = our_ep(_ep);
+	if (!_ep || !desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < usb_endpoint_maxp(desc))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& usb_endpoint_maxp(desc) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = hsep->wedge = 0;
+	hsep->ep.desc = desc;
+	hsep->ep.maxpacket = usb_endpoint_maxp(desc);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->ep.desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->ep.desc = NULL;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof(*hsreq), gfp_flags);
+	if (!hsreq)
+		return NULL;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = our_req(_req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = our_req(_req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = our_ep(_ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl(hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = NULL;
+		} else {
+			csr = readl(hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = NULL;
+		}
+	}
+
+	if (hsreq)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = our_ep(_ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static const struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+	.set_wedge = s3c_hsudc_set_wedge,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64);
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->ep.desc = NULL;
+	hsep->stopped = 0;
+	hsep->wedge = 0;
+
+	if (epnum == 0) {
+		hsep->ep.caps.type_control = true;
+		hsep->ep.caps.dir_in = true;
+		hsep->ep.caps.dir_out = true;
+	} else {
+		hsep->ep.caps.type_iso = true;
+		hsep->ep.caps.type_bulk = true;
+		hsep->ep.caps.type_int = true;
+	}
+
+	if (epnum & 1)
+		hsep->ep.caps.dir_in = true;
+	else
+		hsep->ep.caps.dir_out = true;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+static int s3c_hsudc_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = to_hsudc(gadget);
+	int ret;
+
+	if (!driver
+		|| driver->max_speed < USB_SPEED_FULL
+		|| !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),
+				    hsudc->supplies);
+	if (ret != 0) {
+		dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret);
+		goto err_supplies;
+	}
+
+	/* connect to bus through transceiver */
+	if (!IS_ERR_OR_NULL(hsudc->transceiver)) {
+		ret = otg_set_peripheral(hsudc->transceiver->otg,
+					&hsudc->gadget);
+		if (ret) {
+			dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
+					hsudc->gadget.name);
+			goto err_otg;
+		}
+	}
+
+	enable_irq(hsudc->irq);
+	s3c_hsudc_reconfig(hsudc);
+
+	pm_runtime_get_sync(hsudc->dev);
+
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+err_otg:
+	regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
+err_supplies:
+	hsudc->driver = NULL;
+	return ret;
+}
+
+static int s3c_hsudc_stop(struct usb_gadget *gadget)
+{
+	struct s3c_hsudc *hsudc = to_hsudc(gadget);
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	s3c_hsudc_uninit_phy();
+
+	pm_runtime_put(hsudc->dev);
+
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	if (!IS_ERR_OR_NULL(hsudc->transceiver))
+		(void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
+
+	disable_irq(hsudc->irq);
+
+	regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
+	hsudc->driver = NULL;
+
+	return 0;
+}
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	struct s3c_hsudc *hsudc = to_hsudc(gadget);
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!IS_ERR_OR_NULL(hsudc->transceiver))
+		return usb_phy_set_power(hsudc->transceiver, mA);
+
+	return -EOPNOTSUPP;
+}
+
+static const struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+	.udc_start	= s3c_hsudc_start,
+	.udc_stop	= s3c_hsudc_stop,
+	.vbus_draw	= s3c_hsudc_vbus_draw,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev);
+	int ret, i;
+
+	hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = dev_get_platdata(&pdev->dev);
+
+	hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+
+	for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
+		hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
+				 hsudc->supplies);
+	if (ret != 0) {
+		dev_err(dev, "failed to request supplies: %d\n", ret);
+		goto err_supplies;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	hsudc->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(hsudc->regs)) {
+		ret = PTR_ERR(hsudc->regs);
+		goto err_res;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	hsudc->gadget.max_speed = USB_SPEED_HIGH;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_res;
+	}
+	hsudc->irq = ret;
+
+	ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,
+				driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_res;
+	}
+
+	hsudc->uclk = devm_clk_get(&pdev->dev, "usb-device");
+	if (IS_ERR(hsudc->uclk)) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		ret = PTR_ERR(hsudc->uclk);
+		goto err_res;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+
+	ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
+	if (ret)
+		goto err_add_udc;
+
+	pm_runtime_enable(dev);
+
+	return 0;
+err_add_udc:
+	clk_disable(hsudc->uclk);
+err_res:
+	if (!IS_ERR_OR_NULL(hsudc->transceiver))
+		usb_put_phy(hsudc->transceiver);
+
+err_supplies:
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+module_platform_driver(s3c_hsudc_driver);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c-hsudc");
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
new file mode 100644
index 0000000..8bf5ad7
--- /dev/null
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -0,0 +1,2016 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.c
+ *
+ * Samsung S3C24xx series on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ */
+
+#define pr_fmt(fmt) "s3c2410_udc: " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/prefetch.h>
+#include <linux/io.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+#include <mach/irqs.h>
+
+#include <mach/hardware.h>
+
+#include <plat/regs-udc.h>
+#include <linux/platform_data/usb-s3c2410_udc.h>
+
+
+#include "s3c2410_udc.h"
+
+#define DRIVER_DESC	"S3C2410 USB Device Controller Gadget"
+#define DRIVER_AUTHOR	"Herbert Pötzl <herbert@13thfloor.at>, " \
+			"Arnaud Patard <arnaud.patard@rtp-net.org>"
+
+static const char		gadget_name[] = "s3c2410_udc";
+static const char		driver_desc[] = DRIVER_DESC;
+
+static struct s3c2410_udc	*the_controller;
+static struct clk		*udc_clock;
+static struct clk		*usb_bus_clock;
+static void __iomem		*base_addr;
+static u64			rsrc_start;
+static u64			rsrc_len;
+static struct dentry		*s3c2410_udc_debugfs_root;
+
+static inline u32 udc_read(u32 reg)
+{
+	return readb(base_addr + reg);
+}
+
+static inline void udc_write(u32 value, u32 reg)
+{
+	writeb(value, base_addr + reg);
+}
+
+static inline void udc_writeb(void __iomem *base, u32 value, u32 reg)
+{
+	writeb(value, base + reg);
+}
+
+static struct s3c2410_udc_mach_info *udc_info;
+
+/*************************** DEBUG FUNCTION ***************************/
+#define DEBUG_NORMAL	1
+#define DEBUG_VERBOSE	2
+
+#ifdef CONFIG_USB_S3C2410_DEBUG
+#define USB_S3C2410_DEBUG_LEVEL 0
+
+static uint32_t s3c2410_ticks = 0;
+
+__printf(2, 3)
+static void dprintk(int level, const char *fmt, ...)
+{
+	static long prevticks;
+	static int invocation;
+	struct va_format vaf;
+	va_list args;
+
+	if (level > USB_S3C2410_DEBUG_LEVEL)
+		return;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	if (s3c2410_ticks != prevticks) {
+		prevticks = s3c2410_ticks;
+		invocation = 0;
+	}
+
+	pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf);
+
+	va_end(args);
+}
+#else
+__printf(2, 3)
+static void dprintk(int level, const char *fmt, ...)
+{
+}
+#endif
+
+static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
+{
+	u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
+	u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
+	u32 ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2;
+	u32 ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2;
+
+	addr_reg       = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
+	pwr_reg        = udc_read(S3C2410_UDC_PWR_REG);
+	ep_int_reg     = udc_read(S3C2410_UDC_EP_INT_REG);
+	usb_int_reg    = udc_read(S3C2410_UDC_USB_INT_REG);
+	ep_int_en_reg  = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0_csr        = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	udc_write(1, S3C2410_UDC_INDEX_REG);
+	ep1_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep1_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	udc_write(2, S3C2410_UDC_INDEX_REG);
+	ep2_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep2_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+
+	seq_printf(m, "FUNC_ADDR_REG  : 0x%04X\n"
+		 "PWR_REG        : 0x%04X\n"
+		 "EP_INT_REG     : 0x%04X\n"
+		 "USB_INT_REG    : 0x%04X\n"
+		 "EP_INT_EN_REG  : 0x%04X\n"
+		 "USB_INT_EN_REG : 0x%04X\n"
+		 "EP0_CSR        : 0x%04X\n"
+		 "EP1_I_CSR1     : 0x%04X\n"
+		 "EP1_I_CSR2     : 0x%04X\n"
+		 "EP1_O_CSR1     : 0x%04X\n"
+		 "EP1_O_CSR2     : 0x%04X\n"
+		 "EP2_I_CSR1     : 0x%04X\n"
+		 "EP2_I_CSR2     : 0x%04X\n"
+		 "EP2_O_CSR1     : 0x%04X\n"
+		 "EP2_O_CSR2     : 0x%04X\n",
+			addr_reg, pwr_reg, ep_int_reg, usb_int_reg,
+			ep_int_en_reg, usb_int_en_reg, ep0_csr,
+			ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2,
+			ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2
+		);
+
+	return 0;
+}
+
+static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
+					 struct file *file)
+{
+	return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
+}
+
+static const struct file_operations s3c2410_udc_debugfs_fops = {
+	.open		= s3c2410_udc_debugfs_fops_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.owner		= THIS_MODULE,
+};
+
+/* io macros */
+
+static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,
+			S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_se(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
+{
+	udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
+				| S3C2410_UDC_EP0_CSR_DE),
+			S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
+			| S3C2410_UDC_EP0_CSR_DE),
+		S3C2410_UDC_EP0_CSR_REG);
+}
+
+/*------------------------- I/O ----------------------------------*/
+
+/*
+ *	s3c2410_udc_done
+ */
+static void s3c2410_udc_done(struct s3c2410_ep *ep,
+		struct s3c2410_request *req, int status)
+{
+	unsigned halted = ep->halted;
+
+	list_del_init(&req->queue);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	ep->halted = 1;
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	ep->halted = halted;
+}
+
+static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
+		struct s3c2410_ep *ep, int status)
+{
+	/* Sanity check */
+	if (&ep->queue == NULL)
+		return;
+
+	while (!list_empty(&ep->queue)) {
+		struct s3c2410_request *req;
+		req = list_entry(ep->queue.next, struct s3c2410_request,
+				queue);
+		s3c2410_udc_done(ep, req, status);
+	}
+}
+
+static inline int s3c2410_udc_fifo_count_out(void)
+{
+	int tmp;
+
+	tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);
+	return tmp;
+}
+
+/*
+ *	s3c2410_udc_write_packet
+ */
+static inline int s3c2410_udc_write_packet(int fifo,
+		struct s3c2410_request *req,
+		unsigned max)
+{
+	unsigned len = min(req->req.length - req->req.actual, max);
+	u8 *buf = req->req.buf + req->req.actual;
+
+	prefetch(buf);
+
+	dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__,
+		req->req.actual, req->req.length, len, req->req.actual + len);
+
+	req->req.actual += len;
+
+	udelay(5);
+	writesb(base_addr + fifo, buf, len);
+	return len;
+}
+
+/*
+ *	s3c2410_udc_write_fifo
+ *
+ * return:  0 = still running, 1 = completed, negative = errno
+ */
+static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
+		struct s3c2410_request *req)
+{
+	unsigned	count;
+	int		is_last;
+	u32		idx;
+	int		fifo_reg;
+	u32		ep_csr;
+
+	idx = ep->bEndpointAddress & 0x7F;
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);
+
+	/* last packet is often short (sometimes a zlp) */
+	if (count != ep->ep.maxpacket)
+		is_last = 1;
+	else if (req->req.length != req->req.actual || req->req.zero)
+		is_last = 0;
+	else
+		is_last = 2;
+
+	/* Only ep0 debug messages are interesting */
+	if (idx == 0)
+		dprintk(DEBUG_NORMAL,
+			"Written ep%d %d.%d of %d b [last %d,z %d]\n",
+			idx, count, req->req.actual, req->req.length,
+			is_last, req->req.zero);
+
+	if (is_last) {
+		/* The order is important. It prevents sending 2 packets
+		 * at the same time */
+
+		if (idx == 0) {
+			/* Reset signal => no need to say 'data sent' */
+			if (!(udc_read(S3C2410_UDC_USB_INT_REG)
+					& S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_de_in(base_addr);
+			ep->dev->ep0state = EP0_IDLE;
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+					S3C2410_UDC_IN_CSR1_REG);
+		}
+
+		s3c2410_udc_done(ep, req, 0);
+		is_last = 1;
+	} else {
+		if (idx == 0) {
+			/* Reset signal => no need to say 'data sent' */
+			if (!(udc_read(S3C2410_UDC_USB_INT_REG)
+					& S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_ipr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+					S3C2410_UDC_IN_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static inline int s3c2410_udc_read_packet(int fifo, u8 *buf,
+		struct s3c2410_request *req, unsigned avail)
+{
+	unsigned len;
+
+	len = min(req->req.length - req->req.actual, avail);
+	req->req.actual += len;
+
+	readsb(fifo + base_addr, buf, len);
+	return len;
+}
+
+/*
+ * return:  0 = still running, 1 = queue empty, negative = errno
+ */
+static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
+				 struct s3c2410_request *req)
+{
+	u8		*buf;
+	u32		ep_csr;
+	unsigned	bufferspace;
+	int		is_last = 1;
+	unsigned	avail;
+	int		fifo_count = 0;
+	u32		idx;
+	int		fifo_reg;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	if (!req->req.length)
+		return 1;
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+	if (!bufferspace) {
+		dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__);
+		return -1;
+	}
+
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+
+	fifo_count = s3c2410_udc_fifo_count_out();
+	dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);
+
+	if (fifo_count > ep->ep.maxpacket)
+		avail = ep->ep.maxpacket;
+	else
+		avail = fifo_count;
+
+	fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);
+
+	/* checking this with ep0 is not accurate as we already
+	 * read a control request
+	 **/
+	if (idx != 0 && fifo_count < ep->ep.maxpacket) {
+		is_last = 1;
+		/* overflowed this request?  flush extra data */
+		if (fifo_count != avail)
+			req->req.status = -EOVERFLOW;
+	} else {
+		is_last = (req->req.length <= req->req.actual) ? 1 : 0;
+	}
+
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+	fifo_count = s3c2410_udc_fifo_count_out();
+
+	/* Only ep0 debug messages are interesting */
+	if (idx == 0)
+		dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
+			__func__, fifo_count, is_last);
+
+	if (is_last) {
+		if (idx == 0) {
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			ep->dev->ep0state = EP0_IDLE;
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+					S3C2410_UDC_OUT_CSR1_REG);
+		}
+
+		s3c2410_udc_done(ep, req, 0);
+	} else {
+		if (idx == 0) {
+			s3c2410_udc_clear_ep0_opr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+					S3C2410_UDC_OUT_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
+{
+	unsigned char *outbuf = (unsigned char *)crq;
+	int bytes_read = 0;
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+
+	bytes_read = s3c2410_udc_fifo_count_out();
+
+	dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read);
+
+	if (bytes_read > sizeof(struct usb_ctrlrequest))
+		bytes_read = sizeof(struct usb_ctrlrequest);
+
+	readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read);
+
+	dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__,
+		bytes_read, crq->bRequest, crq->bRequestType,
+		crq->wValue, crq->wIndex, crq->wLength);
+
+	return bytes_read;
+}
+
+static int s3c2410_udc_get_status(struct s3c2410_udc *dev,
+		struct usb_ctrlrequest *crq)
+{
+	u16 status = 0;
+	u8 ep_num = crq->wIndex & 0x7F;
+	u8 is_in = crq->wIndex & USB_DIR_IN;
+
+	switch (crq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		break;
+
+	case USB_RECIP_DEVICE:
+		status = dev->devstatus;
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		if (ep_num > 4 || crq->wLength > 2)
+			return 1;
+
+		if (ep_num == 0) {
+			udc_write(0, S3C2410_UDC_INDEX_REG);
+			status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			status = status & S3C2410_UDC_EP0_CSR_SENDSTL;
+		} else {
+			udc_write(ep_num, S3C2410_UDC_INDEX_REG);
+			if (is_in) {
+				status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+				status = status & S3C2410_UDC_ICSR1_SENDSTL;
+			} else {
+				status = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+				status = status & S3C2410_UDC_OCSR1_SENDSTL;
+			}
+		}
+
+		status = status ? 1 : 0;
+		break;
+
+	default:
+		return 1;
+	}
+
+	/* Seems to be needed to get it working. ouch :( */
+	udelay(5);
+	udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG);
+	udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG);
+	s3c2410_udc_set_ep0_de_in(base_addr);
+
+	return 0;
+}
+/*------------------------- usb state machine -------------------------------*/
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value);
+
+static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
+					struct s3c2410_ep *ep,
+					struct usb_ctrlrequest *crq,
+					u32 ep0csr)
+{
+	int len, ret, tmp;
+
+	/* start control request? */
+	if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY))
+		return;
+
+	s3c2410_udc_nuke(dev, ep, -EPROTO);
+
+	len = s3c2410_udc_read_fifo_crq(crq);
+	if (len != sizeof(*crq)) {
+		dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
+			" wanted %d bytes got %d. Stalling out...\n",
+			sizeof(*crq), len);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		return;
+	}
+
+	dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",
+		crq->bRequest, crq->bRequestType, crq->wLength);
+
+	/* cope with automagic for some standard requests. */
+	dev->req_std = (crq->bRequestType & USB_TYPE_MASK)
+		== USB_TYPE_STANDARD;
+	dev->req_config = 0;
+	dev->req_pending = 1;
+
+	switch (crq->bRequest) {
+	case USB_REQ_SET_CONFIGURATION:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ...\n");
+
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			dev->req_config = 1;
+			s3c2410_udc_set_ep0_de_out(base_addr);
+		}
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ...\n");
+
+		if (crq->bRequestType == USB_RECIP_INTERFACE) {
+			dev->req_config = 1;
+			s3c2410_udc_set_ep0_de_out(base_addr);
+		}
+		break;
+
+	case USB_REQ_SET_ADDRESS:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ...\n");
+
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			tmp = crq->wValue & 0x7F;
+			dev->address = tmp;
+			udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),
+					S3C2410_UDC_FUNC_ADDR_REG);
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			return;
+		}
+		break;
+
+	case USB_REQ_GET_STATUS:
+		dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ...\n");
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (dev->req_std) {
+			if (!s3c2410_udc_get_status(dev, crq))
+				return;
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+
+	default:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+		break;
+	}
+
+	if (crq->bRequestType & USB_DIR_IN)
+		dev->ep0state = EP0_IN_DATA_PHASE;
+	else
+		dev->ep0state = EP0_OUT_DATA_PHASE;
+
+	if (!dev->driver)
+		return;
+
+	/* deliver the request to the gadget driver */
+	ret = dev->driver->setup(&dev->gadget, crq);
+	if (ret < 0) {
+		if (dev->req_config) {
+			dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
+				crq->bRequest, ret);
+			return;
+		}
+
+		if (ret == -EOPNOTSUPP)
+			dprintk(DEBUG_NORMAL, "Operation not supported\n");
+		else
+			dprintk(DEBUG_NORMAL,
+				"dev->driver->setup failed. (%d)\n", ret);
+
+		udelay(5);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		dev->ep0state = EP0_IDLE;
+		/* deferred i/o == no response yet */
+	} else if (dev->req_pending) {
+		dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
+		dev->req_pending = 0;
+	}
+
+	dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
+}
+
+static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
+{
+	u32			ep0csr;
+	struct s3c2410_ep	*ep = &dev->ep[0];
+	struct s3c2410_request	*req;
+	struct usb_ctrlrequest	crq;
+
+	if (list_empty(&ep->queue))
+		req = NULL;
+	else
+		req = list_entry(ep->queue.next, struct s3c2410_request, queue);
+
+	/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
+	 * S3C2410_UDC_EP0_CSR_REG when index is zero */
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
+		ep0csr, ep0states[dev->ep0state]);
+
+	/* clear stall status */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
+		s3c2410_udc_nuke(dev, ep, -EPIPE);
+		dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
+		s3c2410_udc_clear_ep0_sst(base_addr);
+		dev->ep0state = EP0_IDLE;
+		return;
+	}
+
+	/* clear setup end */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
+		dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
+		s3c2410_udc_nuke(dev, ep, 0);
+		s3c2410_udc_clear_ep0_se(base_addr);
+		dev->ep0state = EP0_IDLE;
+	}
+
+	switch (dev->ep0state) {
+	case EP0_IDLE:
+		s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
+		break;
+
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+		dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
+		if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
+			s3c2410_udc_write_fifo(ep, req);
+		break;
+
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+		dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
+		if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
+			s3c2410_udc_read_fifo(ep, req);
+		break;
+
+	case EP0_END_XFER:
+		dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
+		dev->ep0state = EP0_IDLE;
+		break;
+
+	case EP0_STALL:
+		dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
+		dev->ep0state = EP0_IDLE;
+		break;
+	}
+}
+
+/*
+ *	handle_ep - Manage I/O endpoints
+ */
+
+static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
+{
+	struct s3c2410_request	*req;
+	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
+	u32			ep_csr1;
+	u32			idx;
+
+	if (likely(!list_empty(&ep->queue)))
+		req = list_entry(ep->queue.next,
+				struct s3c2410_request, queue);
+	else
+		req = NULL;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	if (is_in) {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
+			idx, ep_csr1, req ? 1 : 0);
+
+		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
+			dprintk(DEBUG_VERBOSE, "st\n");
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
+					S3C2410_UDC_IN_CSR1_REG);
+			return;
+		}
+
+		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
+			s3c2410_udc_write_fifo(ep, req);
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
+
+		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
+					S3C2410_UDC_OUT_CSR1_REG);
+			return;
+		}
+
+		if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
+			s3c2410_udc_read_fifo(ep, req);
+	}
+}
+
+#include <mach/regs-irq.h>
+
+/*
+ *	s3c2410_udc_irq - interrupt handler
+ */
+static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
+{
+	struct s3c2410_udc *dev = _dev;
+	int usb_status;
+	int usbd_status;
+	int pwr_reg;
+	int ep0csr;
+	int i;
+	u32 idx, idx2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* Driver connected ? */
+	if (!dev->driver) {
+		/* Clear interrupts */
+		udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
+				S3C2410_UDC_USB_INT_REG);
+		udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
+				S3C2410_UDC_EP_INT_REG);
+	}
+
+	/* Save index */
+	idx = udc_read(S3C2410_UDC_INDEX_REG);
+
+	/* Read status registers */
+	usb_status = udc_read(S3C2410_UDC_USB_INT_REG);
+	usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);
+	pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
+
+	udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
+		usb_status, usbd_status, pwr_reg, ep0csr);
+
+	/*
+	 * Now, handle interrupts. There's two types :
+	 * - Reset, Resume, Suspend coming -> usb_int_reg
+	 * - EP -> ep_int_reg
+	 */
+
+	/* RESET */
+	if (usb_status & S3C2410_UDC_USBINT_RESET) {
+		/* two kind of reset :
+		 * - reset start -> pwr reg = 8
+		 * - reset end   -> pwr reg = 0
+		 **/
+		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
+			ep0csr, pwr_reg);
+
+		dev->gadget.speed = USB_SPEED_UNKNOWN;
+		udc_write(0x00, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
+				S3C2410_UDC_MAXP_REG);
+		dev->address = 0;
+
+		dev->ep0state = EP0_IDLE;
+		dev->gadget.speed = USB_SPEED_FULL;
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_RESET,
+				S3C2410_UDC_USB_INT_REG);
+
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return IRQ_HANDLED;
+	}
+
+	/* RESUME */
+	if (usb_status & S3C2410_UDC_USBINT_RESUME) {
+		dprintk(DEBUG_NORMAL, "USB resume\n");
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_RESUME,
+				S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->resume)
+			dev->driver->resume(&dev->gadget);
+	}
+
+	/* SUSPEND */
+	if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
+		dprintk(DEBUG_NORMAL, "USB suspend\n");
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_SUSPEND,
+				S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->suspend)
+			dev->driver->suspend(&dev->gadget);
+
+		dev->ep0state = EP0_IDLE;
+	}
+
+	/* EP */
+	/* control traffic */
+	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
+	 * generate an interrupt
+	 */
+	if (usbd_status & S3C2410_UDC_INT_EP0) {
+		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
+		/* Clear the interrupt bit by setting it to 1 */
+		udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
+		s3c2410_udc_handle_ep0(dev);
+	}
+
+	/* endpoint data transfers */
+	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+		u32 tmp = 1 << i;
+		if (usbd_status & tmp) {
+			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
+
+			/* Clear the interrupt bit by setting it to 1 */
+			udc_write(tmp, S3C2410_UDC_EP_INT_REG);
+			s3c2410_udc_handle_ep(&dev->ep[i]);
+		}
+	}
+
+	/* what else causes this interrupt? a receive! who is it? */
+	if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
+		for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+			idx2 = udc_read(S3C2410_UDC_INDEX_REG);
+			udc_write(i, S3C2410_UDC_INDEX_REG);
+
+			if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
+				s3c2410_udc_handle_ep(&dev->ep[i]);
+
+			/* restore index */
+			udc_write(idx2, S3C2410_UDC_INDEX_REG);
+		}
+	}
+
+	dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
+
+	/* Restore old index */
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return IRQ_HANDLED;
+}
+/*------------------------- s3c2410_ep_ops ----------------------------------*/
+
+static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c2410_ep, ep);
+}
+
+static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c2410_udc, gadget);
+}
+
+static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c2410_request, req);
+}
+
+/*
+ *	s3c2410_udc_ep_enable
+ */
+static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
+				 const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c2410_udc	*dev;
+	struct s3c2410_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+	u32			csr1, csr2;
+	u32			int_en_reg;
+
+	ep = to_s3c2410_ep(_ep);
+
+	if (!_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 = usb_endpoint_maxp(desc);
+
+	local_irq_save(flags);
+	_ep->maxpacket = max;
+	ep->ep.desc = desc;
+	ep->halted = 0;
+	ep->bEndpointAddress = desc->bEndpointAddress;
+
+	/* set max packet */
+	udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+	udc_write(max >> 3, S3C2410_UDC_MAXP_REG);
+
+	/* set type, direction, address; reset fifo counters */
+	if (desc->bEndpointAddress & USB_DIR_IN) {
+		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
+	} else {
+		/* don't flush in fifo or it will cause endpoint interrupt */
+		csr1 = S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
+
+		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
+		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
+	}
+
+	/* enable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);
+
+	/* print some debug message */
+	tmp = desc->bEndpointAddress;
+	dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
+		 _ep->name, ep->num, tmp,
+		 desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
+
+	local_irq_restore(flags);
+	s3c2410_udc_set_halt(_ep, 0);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_ep_disable
+ */
+static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
+	unsigned long flags;
+	u32 int_en_reg;
+
+	if (!_ep || !ep->ep.desc) {
+		dprintk(DEBUG_NORMAL, "%s not enabled\n",
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
+
+	ep->ep.desc = NULL;
+	ep->halted = 1;
+
+	s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN);
+
+	/* disable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_alloc_request
+ */
+static struct usb_request *
+s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
+{
+	struct s3c2410_request *req;
+
+	dprintk(DEBUG_VERBOSE, "%s(%p,%d)\n", __func__, _ep, mem_flags);
+
+	if (!_ep)
+		return NULL;
+
+	req = kzalloc(sizeof(struct s3c2410_request), mem_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+	return &req->req;
+}
+
+/*
+ * s3c2410_udc_free_request
+ */
+static void
+s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_request	*req = to_s3c2410_req(_req);
+
+	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
+
+	if (!ep || !_req || (!ep->ep.desc && _ep->name != ep0name))
+		return;
+
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+/*
+ *	s3c2410_udc_queue
+ */
+static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
+		gfp_t gfp_flags)
+{
+	struct s3c2410_request	*req = to_s3c2410_req(_req);
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_udc	*dev;
+	u32			ep_csr = 0;
+	int			fifo_count = 0;
+	unsigned long		flags;
+
+	if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely(!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		return -ESHUTDOWN;
+	}
+
+	local_irq_save(flags);
+
+	if (unlikely(!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue))) {
+		if (!_req)
+			dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
+		else {
+			dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
+				__func__, !_req->complete, !_req->buf,
+				!list_empty(&req->queue));
+		}
+
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
+		 __func__, ep->bEndpointAddress, _req->length);
+
+	if (ep->bEndpointAddress) {
+		udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
+
+		ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
+				? S3C2410_UDC_IN_CSR1_REG
+				: S3C2410_UDC_OUT_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	} else {
+		udc_write(0, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	}
+
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue) && !ep->halted) {
+		if (ep->bEndpointAddress == 0 /* ep0 */) {
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
+						&& s3c2410_udc_write_fifo(ep,
+							req)) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				if ((!_req->length)
+					|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
+						&& s3c2410_udc_read_fifo(ep,
+							req))) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			default:
+				local_irq_restore(flags);
+				return -EL2HLT;
+			}
+		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
+				&& s3c2410_udc_write_fifo(ep, req)) {
+			req = NULL;
+		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
+				&& fifo_count
+				&& s3c2410_udc_read_fifo(ep, req)) {
+			req = NULL;
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely(req))
+		list_add_tail(&req->queue, &ep->queue);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
+	return 0;
+}
+
+/*
+ *	s3c2410_udc_dequeue
+ */
+static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_udc	*udc;
+	int			retval = -EINVAL;
+	unsigned long		flags;
+	struct s3c2410_request	*req = NULL;
+
+	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
+
+	if (!the_controller->driver)
+		return -ESHUTDOWN;
+
+	if (!_ep || !_req)
+		return retval;
+
+	udc = to_s3c2410_udc(ep->gadget);
+
+	local_irq_save(flags);
+
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init(&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+
+	if (retval == 0) {
+		dprintk(DEBUG_VERBOSE,
+			"dequeued req %p from %s, len %d buf %p\n",
+			req, _ep->name, _req->length, _req->buf);
+
+		s3c2410_udc_done(ep, req, -ECONNRESET);
+	}
+
+	local_irq_restore(flags);
+	return retval;
+}
+
+/*
+ * s3c2410_udc_set_halt
+ */
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	u32			ep_csr = 0;
+	unsigned long		flags;
+	u32			idx;
+
+	if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	if (idx == 0) {
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
+				? S3C2410_UDC_IN_CSR1_REG
+				: S3C2410_UDC_OUT_CSR1_REG);
+
+		if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
+					S3C2410_UDC_IN_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+				ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+			}
+		} else {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
+					S3C2410_UDC_OUT_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+				ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+			}
+		}
+	}
+
+	ep->halted = value ? 1 : 0;
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable		= s3c2410_udc_ep_enable,
+	.disable	= s3c2410_udc_ep_disable,
+
+	.alloc_request	= s3c2410_udc_alloc_request,
+	.free_request	= s3c2410_udc_free_request,
+
+	.queue		= s3c2410_udc_queue,
+	.dequeue	= s3c2410_udc_dequeue,
+
+	.set_halt	= s3c2410_udc_set_halt,
+};
+
+/*------------------------- usb_gadget_ops ----------------------------------*/
+
+/*
+ *	s3c2410_udc_get_frame
+ */
+static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
+{
+	int tmp;
+
+	dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
+
+	tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
+	return tmp;
+}
+
+/*
+ *	s3c2410_udc_wakeup
+ */
+static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+	return 0;
+}
+
+/*
+ *	s3c2410_udc_set_selfpowered
+ */
+static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	gadget->is_selfpowered = (value != 0);
+	if (value)
+		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+static void s3c2410_udc_disable(struct s3c2410_udc *dev);
+static void s3c2410_udc_enable(struct s3c2410_udc *dev);
+
+static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && (udc_info->udc_command ||
+		gpio_is_valid(udc_info->pullup_pin))) {
+
+		if (is_on)
+			s3c2410_udc_enable(udc);
+		else {
+			if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+				if (udc->driver && udc->driver->disconnect)
+					udc->driver->disconnect(&udc->gadget);
+
+			}
+			s3c2410_udc_disable(udc);
+		}
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	udc->vbus = (is_active != 0);
+	s3c2410_udc_set_pullup(udc, is_active);
+	return 0;
+}
+
+static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	s3c2410_udc_set_pullup(udc, is_on);
+	return 0;
+}
+
+static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
+{
+	struct s3c2410_udc	*dev = _dev;
+	unsigned int		value;
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	value = gpio_get_value(udc_info->vbus_pin) ? 1 : 0;
+	if (udc_info->vbus_pin_inverted)
+		value = !value;
+
+	if (value != dev->vbus)
+		s3c2410_udc_vbus_session(&dev->gadget, value);
+
+	return IRQ_HANDLED;
+}
+
+static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && udc_info->vbus_draw) {
+		udc_info->vbus_draw(ma);
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
+static int s3c2410_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int s3c2410_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops s3c2410_ops = {
+	.get_frame		= s3c2410_udc_get_frame,
+	.wakeup			= s3c2410_udc_wakeup,
+	.set_selfpowered	= s3c2410_udc_set_selfpowered,
+	.pullup			= s3c2410_udc_pullup,
+	.vbus_session		= s3c2410_udc_vbus_session,
+	.vbus_draw		= s3c2410_vbus_draw,
+	.udc_start		= s3c2410_udc_start,
+	.udc_stop		= s3c2410_udc_stop,
+};
+
+static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
+{
+	if (!udc_info)
+		return;
+
+	if (udc_info->udc_command) {
+		udc_info->udc_command(cmd);
+	} else if (gpio_is_valid(udc_info->pullup_pin)) {
+		int value;
+
+		switch (cmd) {
+		case S3C2410_UDC_P_ENABLE:
+			value = 1;
+			break;
+		case S3C2410_UDC_P_DISABLE:
+			value = 0;
+			break;
+		default:
+			return;
+		}
+		value ^= udc_info->pullup_pin_inverted;
+
+		gpio_set_value(udc_info->pullup_pin, value);
+	}
+}
+
+/*------------------------- gadget driver handling---------------------------*/
+/*
+ * s3c2410_udc_disable
+ */
+static void s3c2410_udc_disable(struct s3c2410_udc *dev)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	/* Disable all interrupts */
+	udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* Clear the interrupt registers */
+	udc_write(S3C2410_UDC_USBINT_RESET
+				| S3C2410_UDC_USBINT_RESUME
+				| S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_REG);
+
+	udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
+
+	/* Good bye, cruel world */
+	s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
+
+	/* Set speed to unknown */
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+/*
+ * s3c2410_udc_reinit
+ */
+static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
+{
+	u32 i;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->ep0state = EP0_IDLE;
+
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		struct s3c2410_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->dev = dev;
+		ep->ep.desc = NULL;
+		ep->halted = 0;
+		INIT_LIST_HEAD(&ep->queue);
+		usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket);
+	}
+}
+
+/*
+ * s3c2410_udc_enable
+ */
+static void s3c2410_udc_enable(struct s3c2410_udc *dev)
+{
+	int i;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n");
+
+	/* dev->gadget.speed = USB_SPEED_UNKNOWN; */
+	dev->gadget.speed = USB_SPEED_FULL;
+
+	/* Set MAXP for all endpoints */
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		udc_write(i, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3,
+				S3C2410_UDC_MAXP_REG);
+	}
+
+	/* Set default power state */
+	udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
+
+	/* Enable reset and suspend interrupt interrupts */
+	udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_EN_REG);
+
+	/* Enable ep0 interrupt */
+	udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* time to say "hello, world" */
+	s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
+}
+
+static int s3c2410_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = to_s3c2410(g);
+
+	dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name);
+
+	/* Hook the driver */
+	udc->driver = driver;
+
+	/* Enable udc */
+	s3c2410_udc_enable(udc);
+
+	return 0;
+}
+
+static int s3c2410_udc_stop(struct usb_gadget *g)
+{
+	struct s3c2410_udc *udc = to_s3c2410(g);
+
+	udc->driver = NULL;
+
+	/* Disable udc */
+	s3c2410_udc_disable(udc);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+static struct s3c2410_udc memory = {
+	.gadget = {
+		.ops		= &s3c2410_ops,
+		.ep0		= &memory.ep[0].ep,
+		.name		= gadget_name,
+		.dev = {
+			.init_name	= "gadget",
+		},
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.num		= 0,
+		.ep = {
+			.name		= ep0name,
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+	},
+
+	/* first group of endpoints */
+	.ep[1] = {
+		.num		= 1,
+		.ep = {
+			.name		= "ep1-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 1,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[2] = {
+		.num		= 2,
+		.ep = {
+			.name		= "ep2-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 2,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[3] = {
+		.num		= 3,
+		.ep = {
+			.name		= "ep3-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 3,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[4] = {
+		.num		= 4,
+		.ep = {
+			.name		= "ep4-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+			.caps		= USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
+						USB_EP_CAPS_DIR_ALL),
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 4,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	}
+
+};
+
+/*
+ *	probe - binds to the platform device
+ */
+static int s3c2410_udc_probe(struct platform_device *pdev)
+{
+	struct s3c2410_udc *udc = &memory;
+	struct device *dev = &pdev->dev;
+	int retval;
+	int irq;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
+	if (IS_ERR(usb_bus_clock)) {
+		dev_err(dev, "failed to get usb bus clock source\n");
+		return PTR_ERR(usb_bus_clock);
+	}
+
+	clk_prepare_enable(usb_bus_clock);
+
+	udc_clock = clk_get(NULL, "usb-device");
+	if (IS_ERR(udc_clock)) {
+		dev_err(dev, "failed to get udc clock source\n");
+		return PTR_ERR(udc_clock);
+	}
+
+	clk_prepare_enable(udc_clock);
+
+	mdelay(10);
+
+	dev_dbg(dev, "got and enabled clocks\n");
+
+	if (strncmp(pdev->name, "s3c2440", 7) == 0) {
+		dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
+		memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
+	}
+
+	spin_lock_init(&udc->lock);
+	udc_info = dev_get_platdata(&pdev->dev);
+
+	rsrc_start = S3C2410_PA_USBDEV;
+	rsrc_len   = S3C24XX_SZ_USBDEV;
+
+	if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
+		return -EBUSY;
+
+	base_addr = ioremap(rsrc_start, rsrc_len);
+	if (!base_addr) {
+		retval = -ENOMEM;
+		goto err_mem;
+	}
+
+	the_controller = udc;
+	platform_set_drvdata(pdev, udc);
+
+	s3c2410_udc_disable(udc);
+	s3c2410_udc_reinit(udc);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+			     0, gadget_name, udc);
+
+	if (retval != 0) {
+		dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
+		retval = -EBUSY;
+		goto err_map;
+	}
+
+	dev_dbg(dev, "got irq %i\n", IRQ_USBD);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		retval = gpio_request(udc_info->vbus_pin, "udc vbus");
+		if (retval < 0) {
+			dev_err(dev, "cannot claim vbus pin\n");
+			goto err_int;
+		}
+
+		irq = gpio_to_irq(udc_info->vbus_pin);
+		if (irq < 0) {
+			dev_err(dev, "no irq for gpio vbus pin\n");
+			retval = irq;
+			goto err_gpio_claim;
+		}
+
+		retval = request_irq(irq, s3c2410_udc_vbus_irq,
+				     IRQF_TRIGGER_RISING
+				     | IRQF_TRIGGER_FALLING | IRQF_SHARED,
+				     gadget_name, udc);
+
+		if (retval != 0) {
+			dev_err(dev, "can't get vbus irq %d, err %d\n",
+				irq, retval);
+			retval = -EBUSY;
+			goto err_gpio_claim;
+		}
+
+		dev_dbg(dev, "got irq %i\n", irq);
+	} else {
+		udc->vbus = 1;
+	}
+
+	if (udc_info && !udc_info->udc_command &&
+		gpio_is_valid(udc_info->pullup_pin)) {
+
+		retval = gpio_request_one(udc_info->pullup_pin,
+				udc_info->vbus_pin_inverted ?
+				GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
+				"udc pullup");
+		if (retval)
+			goto err_vbus_irq;
+	}
+
+	retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+	if (retval)
+		goto err_add_udc;
+
+	udc->regs_info = debugfs_create_file("registers", S_IRUGO,
+					     s3c2410_udc_debugfs_root, udc,
+					     &s3c2410_udc_debugfs_fops);
+
+	dev_dbg(dev, "probe ok\n");
+
+	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);
+err_gpio_claim:
+	if (udc_info && udc_info->vbus_pin > 0)
+		gpio_free(udc_info->vbus_pin);
+err_int:
+	free_irq(IRQ_USBD, udc);
+err_map:
+	iounmap(base_addr);
+err_mem:
+	release_mem_region(rsrc_start, rsrc_len);
+
+	return retval;
+}
+
+/*
+ *	s3c2410_udc_remove
+ */
+static int s3c2410_udc_remove(struct platform_device *pdev)
+{
+	struct s3c2410_udc *udc = platform_get_drvdata(pdev);
+	unsigned int irq;
+
+	dev_dbg(&pdev->dev, "%s()\n", __func__);
+
+	if (udc->driver)
+		return -EBUSY;
+
+	usb_del_gadget_udc(&udc->gadget);
+	debugfs_remove(udc->regs_info);
+
+	if (udc_info && !udc_info->udc_command &&
+		gpio_is_valid(udc_info->pullup_pin))
+		gpio_free(udc_info->pullup_pin);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		irq = gpio_to_irq(udc_info->vbus_pin);
+		free_irq(irq, udc);
+	}
+
+	free_irq(IRQ_USBD, udc);
+
+	iounmap(base_addr);
+	release_mem_region(rsrc_start, rsrc_len);
+
+	if (!IS_ERR(udc_clock) && udc_clock != NULL) {
+		clk_disable_unprepare(udc_clock);
+		clk_put(udc_clock);
+		udc_clock = NULL;
+	}
+
+	if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
+		clk_disable_unprepare(usb_bus_clock);
+		clk_put(usb_bus_clock);
+		usb_bus_clock = NULL;
+	}
+
+	dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
+
+	return 0;
+}
+
+static int s3c2410_udc_resume(struct platform_device *pdev)
+{
+	s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
+
+	return 0;
+}
+#else
+#define s3c2410_udc_suspend	NULL
+#define s3c2410_udc_resume	NULL
+#endif
+
+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_24x0 = {
+	.driver		= {
+		.name	= "s3c24x0-usbgadget",
+	},
+	.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)
+{
+	int retval;
+
+	dprintk(DEBUG_NORMAL, "%s\n", gadget_name);
+
+	s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
+
+	retval = platform_driver_register(&udc_driver_24x0);
+	if (retval)
+		goto err;
+
+	return 0;
+
+err:
+	debugfs_remove(s3c2410_udc_debugfs_root);
+	return retval;
+}
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver_24x0);
+	debugfs_remove_recursive(s3c2410_udc_debugfs_root);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h
new file mode 100644
index 0000000..bdcaa8d
--- /dev/null
+++ b/drivers/usb/gadget/udc/s3c2410_udc.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.h
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ */
+
+#ifndef _S3C2410_UDC_H
+#define _S3C2410_UDC_H
+
+struct s3c2410_ep {
+	struct list_head		queue;
+	unsigned long			last_io;	/* jiffies timestamp */
+	struct usb_gadget		*gadget;
+	struct s3c2410_udc		*dev;
+	struct usb_ep			ep;
+	u8				num;
+
+	unsigned short			fifo_size;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+
+	unsigned			halted : 1;
+	unsigned			already_seen : 1;
+	unsigned			setup_stage : 1;
+};
+
+
+/* Warning : ep0 has a fifo of 16 bytes */
+/* Don't try to set 32 or 64            */
+/* also testusb 14 fails  wit 16 but is */
+/* fine with 8                          */
+#define EP0_FIFO_SIZE		 8
+#define EP_FIFO_SIZE		64
+#define DEFAULT_POWER_STATE	0x00
+
+#define S3C2440_EP_FIFO_SIZE	128
+
+static const char ep0name [] = "ep0";
+
+static const char *const ep_name[] = {
+	ep0name,                                /* everyone has ep0 */
+	/* s3c2410 four bidirectional bulk endpoints */
+	"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
+};
+
+#define S3C2410_ENDPOINTS       ARRAY_SIZE(ep_name)
+
+struct s3c2410_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+enum ep0_state {
+        EP0_IDLE,
+        EP0_IN_DATA_PHASE,
+        EP0_OUT_DATA_PHASE,
+        EP0_END_XFER,
+        EP0_STALL,
+};
+
+static const char *ep0states[]= {
+        "EP0_IDLE",
+        "EP0_IN_DATA_PHASE",
+        "EP0_OUT_DATA_PHASE",
+        "EP0_END_XFER",
+        "EP0_STALL",
+};
+
+struct s3c2410_udc {
+	spinlock_t			lock;
+
+	struct s3c2410_ep		ep[S3C2410_ENDPOINTS];
+	int				address;
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct s3c2410_request		fifo_req;
+	u8				fifo_buf[EP_FIFO_SIZE];
+	u16				devstatus;
+
+	u32				port_status;
+	int				ep0state;
+
+	unsigned			got_irq : 1;
+
+	unsigned			req_std : 1;
+	unsigned			req_config : 1;
+	unsigned			req_pending : 1;
+	u8				vbus;
+	struct dentry			*regs_info;
+};
+#define to_s3c2410(g)	(container_of((g), struct s3c2410_udc, gadget))
+
+#endif
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
new file mode 100644
index 0000000..d4da47f
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -0,0 +1,3220 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * amd5536.c -- AMD 5536 UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ */
+
+/*
+ * This file does the core driver implementation for the UDC that is based
+ * on Synopsys device controller IP (different than HS OTG IP) that is either
+ * connected through PCI bus or integrated to SoC platforms.
+ */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION		"Synopsys USB Device Controller"
+#define UDC_DRIVER_VERSION_STRING	"01.00.0206"
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/dmapool.h>
+#include <linux/prefetch.h>
+#include <linux/moduleparam.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include "amd5536udc.h"
+
+static void udc_tasklet_disconnect(unsigned long);
+static void udc_setup_endpoints(struct udc *dev);
+static void udc_soft_reset(struct udc *dev);
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
+static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
+
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = "udc";
+
+/* structure to hold endpoint function pointers */
+static const struct usb_ep_ops udc_ep_ops;
+
+/* received setup data */
+static union udc_setup_data setup_data;
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* irq spin lock for soft reset */
+static DEFINE_SPINLOCK(udc_irq_spinlock);
+/* stall spin lock */
+static DEFINE_SPINLOCK(udc_stall_spinlock);
+
+/*
+* slave mode: pending bytes in rx fifo after nyet,
+* used if EPIN irq came but no req was available
+*/
+static unsigned int udc_rxfifo_pending;
+
+/* count soft resets after suspend to avoid loop */
+static int soft_reset_occured;
+static int soft_reset_after_usbreset_occured;
+
+/* timer */
+static struct timer_list udc_timer;
+static int stop_timer;
+
+/* set_rde -- Is used to control enabling of RX DMA. Problem is
+ * that UDC has only one bit (RDE) to enable/disable RX DMA for
+ * all OUT endpoints. So we have to handle race conditions like
+ * when OUT data reaches the fifo but no request was queued yet.
+ * This cannot be solved by letting the RX DMA disabled until a
+ * request gets queued because there may be other OUT packets
+ * in the FIFO (important for not blocking control traffic).
+ * The value of set_rde controls the correspondig timer.
+ *
+ * set_rde -1 == not used, means it is alloed to be set to 0 or 1
+ * set_rde  0 == do not touch RDE, do no start the RDE timer
+ * set_rde  1 == timer function will look whether FIFO has data
+ * set_rde  2 == set by timer function to enable RX DMA on next call
+ */
+static int set_rde = -1;
+
+static DECLARE_COMPLETION(on_exit);
+static struct timer_list udc_pollstall_timer;
+static int stop_pollstall_timer;
+static DECLARE_COMPLETION(on_pollstall_exit);
+
+/* tasklet for usb disconnect */
+static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
+		(unsigned long) &udc);
+
+
+/* endpoint names used for print */
+static const char ep0_string[] = "ep0in";
+static const struct {
+	const char *name;
+	const struct usb_ep_caps caps;
+} ep_info[] = {
+#define EP_INFO(_name, _caps) \
+	{ \
+		.name = _name, \
+		.caps = _caps, \
+	}
+
+	EP_INFO(ep0_string,
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep1in-int",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep2in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep3in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep4in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep5in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep6in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep7in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep8in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep9in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep10in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep11in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep12in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep13in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep14in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep15in-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
+	EP_INFO("ep0out",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep1out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep2out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep3out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep4out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep5out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep6out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep7out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep8out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep9out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep10out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep11out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep12out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep13out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep14out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+	EP_INFO("ep15out-bulk",
+		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
+
+#undef EP_INFO
+};
+
+/* buffer fill mode */
+static int use_dma_bufferfill_mode;
+/* tx buffer size for high speed */
+static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
+
+/*---------------------------------------------------------------------------*/
+/* Prints UDC device registers and endpoint irq registers */
+static void print_regs(struct udc *dev)
+{
+	DBG(dev, "------- Device registers -------\n");
+	DBG(dev, "dev config     = %08x\n", readl(&dev->regs->cfg));
+	DBG(dev, "dev control    = %08x\n", readl(&dev->regs->ctl));
+	DBG(dev, "dev status     = %08x\n", readl(&dev->regs->sts));
+	DBG(dev, "\n");
+	DBG(dev, "dev int's      = %08x\n", readl(&dev->regs->irqsts));
+	DBG(dev, "dev intmask    = %08x\n", readl(&dev->regs->irqmsk));
+	DBG(dev, "\n");
+	DBG(dev, "dev ep int's   = %08x\n", readl(&dev->regs->ep_irqsts));
+	DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));
+	DBG(dev, "\n");
+	DBG(dev, "USE DMA        = %d\n", use_dma);
+	if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
+		DBG(dev, "DMA mode       = PPBNDU (packet per buffer "
+			"WITHOUT desc. update)\n");
+		dev_info(dev->dev, "DMA mode (%s)\n", "PPBNDU");
+	} else if (use_dma && use_dma_ppb && use_dma_ppb_du) {
+		DBG(dev, "DMA mode       = PPBDU (packet per buffer "
+			"WITH desc. update)\n");
+		dev_info(dev->dev, "DMA mode (%s)\n", "PPBDU");
+	}
+	if (use_dma && use_dma_bufferfill_mode) {
+		DBG(dev, "DMA mode       = BF (buffer fill mode)\n");
+		dev_info(dev->dev, "DMA mode (%s)\n", "BF");
+	}
+	if (!use_dma)
+		dev_info(dev->dev, "FIFO mode\n");
+	DBG(dev, "-------------------------------------------------------\n");
+}
+
+/* Masks unused interrupts */
+int udc_mask_unused_interrupts(struct udc *dev)
+{
+	u32 tmp;
+
+	/* mask all dev interrupts */
+	tmp =	AMD_BIT(UDC_DEVINT_SVC) |
+		AMD_BIT(UDC_DEVINT_ENUM) |
+		AMD_BIT(UDC_DEVINT_US) |
+		AMD_BIT(UDC_DEVINT_UR) |
+		AMD_BIT(UDC_DEVINT_ES) |
+		AMD_BIT(UDC_DEVINT_SI) |
+		AMD_BIT(UDC_DEVINT_SOF)|
+		AMD_BIT(UDC_DEVINT_SC);
+	writel(tmp, &dev->regs->irqmsk);
+
+	/* mask all ep interrupts */
+	writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
+
+/* Enables endpoint 0 interrupts */
+static int udc_enable_ep0_interrupts(struct udc *dev)
+{
+	u32 tmp;
+
+	DBG(dev, "udc_enable_ep0_interrupts()\n");
+
+	/* read irq mask */
+	tmp = readl(&dev->regs->ep_irqmsk);
+	/* enable ep0 irq's */
+	tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
+		& AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);
+	writel(tmp, &dev->regs->ep_irqmsk);
+
+	return 0;
+}
+
+/* Enables device interrupts for SET_INTF and SET_CONFIG */
+int udc_enable_dev_setup_interrupts(struct udc *dev)
+{
+	u32 tmp;
+
+	DBG(dev, "enable device interrupts for setup data\n");
+
+	/* read irq mask */
+	tmp = readl(&dev->regs->irqmsk);
+
+	/* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
+	tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
+		& AMD_UNMASK_BIT(UDC_DEVINT_SC)
+		& AMD_UNMASK_BIT(UDC_DEVINT_UR)
+		& AMD_UNMASK_BIT(UDC_DEVINT_SVC)
+		& AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
+	writel(tmp, &dev->regs->irqmsk);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
+
+/* Calculates fifo start of endpoint based on preceding endpoints */
+static int udc_set_txfifo_addr(struct udc_ep *ep)
+{
+	struct udc	*dev;
+	u32 tmp;
+	int i;
+
+	if (!ep || !(ep->in))
+		return -EINVAL;
+
+	dev = ep->dev;
+	ep->txfifo = dev->txfifo;
+
+	/* traverse ep's */
+	for (i = 0; i < ep->num; i++) {
+		if (dev->ep[i].regs) {
+			/* read fifo size */
+			tmp = readl(&dev->ep[i].regs->bufin_framenum);
+			tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
+			ep->txfifo += tmp;
+		}
+	}
+	return 0;
+}
+
+/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
+static u32 cnak_pending;
+
+static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num)
+{
+	if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {
+		DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);
+		cnak_pending |= 1 << (num);
+		ep->naking = 1;
+	} else
+		cnak_pending = cnak_pending & (~(1 << (num)));
+}
+
+
+/* Enables endpoint, is called by gadget driver */
+static int
+udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
+{
+	struct udc_ep		*ep;
+	struct udc		*dev;
+	u32			tmp;
+	unsigned long		iflags;
+	u8 udc_csr_epix;
+	unsigned		maxpacket;
+
+	if (!usbep
+			|| usbep->name == ep0_string
+			|| !desc
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	dev = ep->dev;
+
+	DBG(dev, "udc_ep_enable() ep %d\n", ep->num);
+
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&dev->lock, iflags);
+	ep->ep.desc = desc;
+
+	ep->halted = 0;
+
+	/* set traffic type */
+	tmp = readl(&dev->ep[ep->num].regs->ctl);
+	tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
+	writel(tmp, &dev->ep[ep->num].regs->ctl);
+
+	/* set max packet size */
+	maxpacket = usb_endpoint_maxp(desc);
+	tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
+	tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
+	ep->ep.maxpacket = maxpacket;
+	writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
+
+	/* IN ep */
+	if (ep->in) {
+
+		/* ep ix in UDC CSR register space */
+		udc_csr_epix = ep->num;
+
+		/* set buffer size (tx fifo entries) */
+		tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
+		/* double buffering: fifo size = 2 x max packet size */
+		tmp = AMD_ADDBITS(
+				tmp,
+				maxpacket * UDC_EPIN_BUFF_SIZE_MULT
+					  / UDC_DWORD_BYTES,
+				UDC_EPIN_BUFF_SIZE);
+		writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
+
+		/* calc. tx fifo base addr */
+		udc_set_txfifo_addr(ep);
+
+		/* flush fifo */
+		tmp = readl(&ep->regs->ctl);
+		tmp |= AMD_BIT(UDC_EPCTL_F);
+		writel(tmp, &ep->regs->ctl);
+
+	/* OUT ep */
+	} else {
+		/* ep ix in UDC CSR register space */
+		udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+
+		/* set max packet size UDC CSR	*/
+		tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+		tmp = AMD_ADDBITS(tmp, maxpacket,
+					UDC_CSR_NE_MAX_PKT);
+		writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+
+		if (use_dma && !ep->in) {
+			/* alloc and init BNA dummy request */
+			ep->bna_dummy_req = udc_alloc_bna_dummy(ep);
+			ep->bna_occurred = 0;
+		}
+
+		if (ep->num != UDC_EP0OUT_IX)
+			dev->data_ep_enabled = 1;
+	}
+
+	/* set ep values */
+	tmp = readl(&dev->csr->ne[udc_csr_epix]);
+	/* max packet */
+	tmp = AMD_ADDBITS(tmp, maxpacket, UDC_CSR_NE_MAX_PKT);
+	/* ep number */
+	tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
+	/* ep direction */
+	tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
+	/* ep type */
+	tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
+	/* ep config */
+	tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
+	/* ep interface */
+	tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
+	/* ep alt */
+	tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
+	/* write reg */
+	writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+	/* enable ep irq */
+	tmp = readl(&dev->regs->ep_irqmsk);
+	tmp &= AMD_UNMASK_BIT(ep->num);
+	writel(tmp, &dev->regs->ep_irqmsk);
+
+	/*
+	 * clear NAK by writing CNAK
+	 * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written
+	 */
+	if (!use_dma || ep->in) {
+		tmp = readl(&ep->regs->ctl);
+		tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+		writel(tmp, &ep->regs->ctl);
+		ep->naking = 0;
+		UDC_QUEUE_CNAK(ep, ep->num);
+	}
+	tmp = desc->bEndpointAddress;
+	DBG(dev, "%s enabled\n", usbep->name);
+
+	spin_unlock_irqrestore(&dev->lock, iflags);
+	return 0;
+}
+
+/* Resets endpoint */
+static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
+{
+	u32		tmp;
+
+	VDBG(ep->dev, "ep-%d reset\n", ep->num);
+	ep->ep.desc = NULL;
+	ep->ep.ops = &udc_ep_ops;
+	INIT_LIST_HEAD(&ep->queue);
+
+	usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
+	/* set NAK */
+	tmp = readl(&ep->regs->ctl);
+	tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+	writel(tmp, &ep->regs->ctl);
+	ep->naking = 1;
+
+	/* disable interrupt */
+	tmp = readl(&regs->ep_irqmsk);
+	tmp |= AMD_BIT(ep->num);
+	writel(tmp, &regs->ep_irqmsk);
+
+	if (ep->in) {
+		/* unset P and IN bit of potential former DMA */
+		tmp = readl(&ep->regs->ctl);
+		tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
+		writel(tmp, &ep->regs->ctl);
+
+		tmp = readl(&ep->regs->sts);
+		tmp |= AMD_BIT(UDC_EPSTS_IN);
+		writel(tmp, &ep->regs->sts);
+
+		/* flush the fifo */
+		tmp = readl(&ep->regs->ctl);
+		tmp |= AMD_BIT(UDC_EPCTL_F);
+		writel(tmp, &ep->regs->ctl);
+
+	}
+	/* reset desc pointer */
+	writel(0, &ep->regs->desptr);
+}
+
+/* Disables endpoint, is called by gadget driver */
+static int udc_ep_disable(struct usb_ep *usbep)
+{
+	struct udc_ep	*ep = NULL;
+	unsigned long	iflags;
+
+	if (!usbep)
+		return -EINVAL;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	if (usbep->name == ep0_string || !ep->ep.desc)
+		return -EINVAL;
+
+	DBG(ep->dev, "Disable ep-%d\n", ep->num);
+
+	spin_lock_irqsave(&ep->dev->lock, iflags);
+	udc_free_request(&ep->ep, &ep->bna_dummy_req->req);
+	empty_req_queue(ep);
+	ep_init(ep->dev->regs, ep);
+	spin_unlock_irqrestore(&ep->dev->lock, iflags);
+
+	return 0;
+}
+
+/* Allocates request packet, called by gadget driver */
+static struct usb_request *
+udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
+{
+	struct udc_request	*req;
+	struct udc_data_dma	*dma_desc;
+	struct udc_ep	*ep;
+
+	if (!usbep)
+		return NULL;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+
+	VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
+	req = kzalloc(sizeof(struct udc_request), gfp);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_DONT_USE;
+	INIT_LIST_HEAD(&req->queue);
+
+	if (ep->dma) {
+		/* ep0 in requests are allocated from data pool here */
+		dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp,
+						&req->td_phys);
+		if (!dma_desc) {
+			kfree(req);
+			return NULL;
+		}
+
+		VDBG(ep->dev, "udc_alloc_req: req = %p dma_desc = %p, "
+				"td_phys = %lx\n",
+				req, dma_desc,
+				(unsigned long)req->td_phys);
+		/* prevent from using desc. - set HOST BUSY */
+		dma_desc->status = AMD_ADDBITS(dma_desc->status,
+						UDC_DMA_STP_STS_BS_HOST_BUSY,
+						UDC_DMA_STP_STS_BS);
+		dma_desc->bufptr = cpu_to_le32(DMA_DONT_USE);
+		req->td_data = dma_desc;
+		req->td_data_last = NULL;
+		req->chain_len = 1;
+	}
+
+	return &req->req;
+}
+
+/* frees pci pool descriptors of a DMA chain */
+static void udc_free_dma_chain(struct udc *dev, struct udc_request *req)
+{
+	struct udc_data_dma *td = req->td_data;
+	unsigned int i;
+
+	dma_addr_t addr_next = 0x00;
+	dma_addr_t addr = (dma_addr_t)td->next;
+
+	DBG(dev, "free chain req = %p\n", req);
+
+	/* do not free first desc., will be done by free for request */
+	for (i = 1; i < req->chain_len; i++) {
+		td = phys_to_virt(addr);
+		addr_next = (dma_addr_t)td->next;
+		dma_pool_free(dev->data_requests, td, addr);
+		addr = addr_next;
+	}
+}
+
+/* Frees request packet, called by gadget driver */
+static void
+udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+	struct udc_ep	*ep;
+	struct udc_request	*req;
+
+	if (!usbep || !usbreq)
+		return;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	req = container_of(usbreq, struct udc_request, req);
+	VDBG(ep->dev, "free_req req=%p\n", req);
+	BUG_ON(!list_empty(&req->queue));
+	if (req->td_data) {
+		VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
+
+		/* free dma chain if created */
+		if (req->chain_len > 1)
+			udc_free_dma_chain(ep->dev, req);
+
+		dma_pool_free(ep->dev->data_requests, req->td_data,
+							req->td_phys);
+	}
+	kfree(req);
+}
+
+/* Init BNA dummy descriptor for HOST BUSY and pointing to itself */
+static void udc_init_bna_dummy(struct udc_request *req)
+{
+	if (req) {
+		/* set last bit */
+		req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+		/* set next pointer to itself */
+		req->td_data->next = req->td_phys;
+		/* set HOST BUSY */
+		req->td_data->status
+			= AMD_ADDBITS(req->td_data->status,
+					UDC_DMA_STP_STS_BS_DMA_DONE,
+					UDC_DMA_STP_STS_BS);
+#ifdef UDC_VERBOSE
+		pr_debug("bna desc = %p, sts = %08x\n",
+			req->td_data, req->td_data->status);
+#endif
+	}
+}
+
+/* Allocate BNA dummy descriptor */
+static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep)
+{
+	struct udc_request *req = NULL;
+	struct usb_request *_req = NULL;
+
+	/* alloc the dummy request */
+	_req = udc_alloc_request(&ep->ep, GFP_ATOMIC);
+	if (_req) {
+		req = container_of(_req, struct udc_request, req);
+		ep->bna_dummy_req = req;
+		udc_init_bna_dummy(req);
+	}
+	return req;
+}
+
+/* Write data to TX fifo for IN packets */
+static void
+udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
+{
+	u8			*req_buf;
+	u32			*buf;
+	int			i, j;
+	unsigned		bytes = 0;
+	unsigned		remaining = 0;
+
+	if (!req || !ep)
+		return;
+
+	req_buf = req->buf + req->actual;
+	prefetch(req_buf);
+	remaining = req->length - req->actual;
+
+	buf = (u32 *) req_buf;
+
+	bytes = ep->ep.maxpacket;
+	if (bytes > remaining)
+		bytes = remaining;
+
+	/* dwords first */
+	for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+		writel(*(buf + i), ep->txfifo);
+
+	/* remaining bytes must be written by byte access */
+	for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+		writeb((u8)(*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
+							ep->txfifo);
+	}
+
+	/* dummy write confirm */
+	writel(0, &ep->regs->confirm);
+}
+
+/* Read dwords from RX fifo for OUT transfers */
+static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
+{
+	int i;
+
+	VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
+
+	for (i = 0; i < dwords; i++)
+		*(buf + i) = readl(dev->rxfifo);
+	return 0;
+}
+
+/* Read bytes from RX fifo for OUT transfers */
+static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
+{
+	int i, j;
+	u32 tmp;
+
+	VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
+
+	/* dwords first */
+	for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
+		*((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
+
+	/* remaining bytes must be read by byte access */
+	if (bytes % UDC_DWORD_BYTES) {
+		tmp = readl(dev->rxfifo);
+		for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+			*(buf + (i<<2) + j) = (u8)(tmp & UDC_BYTE_MASK);
+			tmp = tmp >> UDC_BITS_PER_BYTE;
+		}
+	}
+
+	return 0;
+}
+
+/* Read data from RX fifo for OUT transfers */
+static int
+udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req)
+{
+	u8 *buf;
+	unsigned buf_space;
+	unsigned bytes = 0;
+	unsigned finished = 0;
+
+	/* received number bytes */
+	bytes = readl(&ep->regs->sts);
+	bytes = AMD_GETBITS(bytes, UDC_EPSTS_RX_PKT_SIZE);
+
+	buf_space = req->req.length - req->req.actual;
+	buf = req->req.buf + req->req.actual;
+	if (bytes > buf_space) {
+		if ((buf_space % ep->ep.maxpacket) != 0) {
+			DBG(ep->dev,
+				"%s: rx %d bytes, rx-buf space = %d bytesn\n",
+				ep->ep.name, bytes, buf_space);
+			req->req.status = -EOVERFLOW;
+		}
+		bytes = buf_space;
+	}
+	req->req.actual += bytes;
+
+	/* last packet ? */
+	if (((bytes % ep->ep.maxpacket) != 0) || (!bytes)
+		|| ((req->req.actual == req->req.length) && !req->req.zero))
+		finished = 1;
+
+	/* read rx fifo bytes */
+	VDBG(ep->dev, "ep %s: rxfifo read %d bytes\n", ep->ep.name, bytes);
+	udc_rxfifo_read_bytes(ep->dev, buf, bytes);
+
+	return finished;
+}
+
+/* Creates or re-inits a DMA chain */
+static int udc_create_dma_chain(
+	struct udc_ep *ep,
+	struct udc_request *req,
+	unsigned long buf_len, gfp_t gfp_flags
+)
+{
+	unsigned long bytes = req->req.length;
+	unsigned int i;
+	dma_addr_t dma_addr;
+	struct udc_data_dma	*td = NULL;
+	struct udc_data_dma	*last = NULL;
+	unsigned long txbytes;
+	unsigned create_new_chain = 0;
+	unsigned len;
+
+	VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n",
+	     bytes, buf_len);
+	dma_addr = DMA_DONT_USE;
+
+	/* unset L bit in first desc for OUT */
+	if (!ep->in)
+		req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
+
+	/* alloc only new desc's if not already available */
+	len = req->req.length / ep->ep.maxpacket;
+	if (req->req.length % ep->ep.maxpacket)
+		len++;
+
+	if (len > req->chain_len) {
+		/* shorter chain already allocated before */
+		if (req->chain_len > 1)
+			udc_free_dma_chain(ep->dev, req);
+		req->chain_len = len;
+		create_new_chain = 1;
+	}
+
+	td = req->td_data;
+	/* gen. required number of descriptors and buffers */
+	for (i = buf_len; i < bytes; i += buf_len) {
+		/* create or determine next desc. */
+		if (create_new_chain) {
+			td = dma_pool_alloc(ep->dev->data_requests,
+					    gfp_flags, &dma_addr);
+			if (!td)
+				return -ENOMEM;
+
+			td->status = 0;
+		} else if (i == buf_len) {
+			/* first td */
+			td = (struct udc_data_dma *)phys_to_virt(
+						req->td_data->next);
+			td->status = 0;
+		} else {
+			td = (struct udc_data_dma *)phys_to_virt(last->next);
+			td->status = 0;
+		}
+
+		if (td)
+			td->bufptr = req->req.dma + i; /* assign buffer */
+		else
+			break;
+
+		/* short packet ? */
+		if ((bytes - i) >= buf_len) {
+			txbytes = buf_len;
+		} else {
+			/* short packet */
+			txbytes = bytes - i;
+		}
+
+		/* link td and assign tx bytes */
+		if (i == buf_len) {
+			if (create_new_chain)
+				req->td_data->next = dma_addr;
+			/*
+			 * else
+			 *	req->td_data->next = virt_to_phys(td);
+			 */
+			/* write tx bytes */
+			if (ep->in) {
+				/* first desc */
+				req->td_data->status =
+					AMD_ADDBITS(req->td_data->status,
+						    ep->ep.maxpacket,
+						    UDC_DMA_IN_STS_TXBYTES);
+				/* second desc */
+				td->status = AMD_ADDBITS(td->status,
+							txbytes,
+							UDC_DMA_IN_STS_TXBYTES);
+			}
+		} else {
+			if (create_new_chain)
+				last->next = dma_addr;
+			/*
+			 * else
+			 *	last->next = virt_to_phys(td);
+			 */
+			if (ep->in) {
+				/* write tx bytes */
+				td->status = AMD_ADDBITS(td->status,
+							txbytes,
+							UDC_DMA_IN_STS_TXBYTES);
+			}
+		}
+		last = td;
+	}
+	/* set last bit */
+	if (td) {
+		td->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+		/* last desc. points to itself */
+		req->td_data_last = td;
+	}
+
+	return 0;
+}
+
+/* create/re-init a DMA descriptor or a DMA descriptor chain */
+static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp)
+{
+	int	retval = 0;
+	u32	tmp;
+
+	VDBG(ep->dev, "prep_dma\n");
+	VDBG(ep->dev, "prep_dma ep%d req->td_data=%p\n",
+			ep->num, req->td_data);
+
+	/* set buffer pointer */
+	req->td_data->bufptr = req->req.dma;
+
+	/* set last bit */
+	req->td_data->status |= AMD_BIT(UDC_DMA_IN_STS_L);
+
+	/* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
+	if (use_dma_ppb) {
+
+		retval = udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+		if (retval != 0) {
+			if (retval == -ENOMEM)
+				DBG(ep->dev, "Out of DMA memory\n");
+			return retval;
+		}
+		if (ep->in) {
+			if (req->req.length == ep->ep.maxpacket) {
+				/* write tx bytes */
+				req->td_data->status =
+					AMD_ADDBITS(req->td_data->status,
+						ep->ep.maxpacket,
+						UDC_DMA_IN_STS_TXBYTES);
+
+			}
+		}
+
+	}
+
+	if (ep->in) {
+		VDBG(ep->dev, "IN: use_dma_ppb=%d req->req.len=%d "
+				"maxpacket=%d ep%d\n",
+				use_dma_ppb, req->req.length,
+				ep->ep.maxpacket, ep->num);
+		/*
+		 * if bytes < max packet then tx bytes must
+		 * be written in packet per buffer mode
+		 */
+		if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
+				|| ep->num == UDC_EP0OUT_IX
+				|| ep->num == UDC_EP0IN_IX) {
+			/* write tx bytes */
+			req->td_data->status =
+				AMD_ADDBITS(req->td_data->status,
+						req->req.length,
+						UDC_DMA_IN_STS_TXBYTES);
+			/* reset frame num */
+			req->td_data->status =
+				AMD_ADDBITS(req->td_data->status,
+						0,
+						UDC_DMA_IN_STS_FRAMENUM);
+		}
+		/* set HOST BUSY */
+		req->td_data->status =
+			AMD_ADDBITS(req->td_data->status,
+				UDC_DMA_STP_STS_BS_HOST_BUSY,
+				UDC_DMA_STP_STS_BS);
+	} else {
+		VDBG(ep->dev, "OUT set host ready\n");
+		/* set HOST READY */
+		req->td_data->status =
+			AMD_ADDBITS(req->td_data->status,
+				UDC_DMA_STP_STS_BS_HOST_READY,
+				UDC_DMA_STP_STS_BS);
+
+
+			/* clear NAK by writing CNAK */
+			if (ep->naking) {
+				tmp = readl(&ep->regs->ctl);
+				tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+				writel(tmp, &ep->regs->ctl);
+				ep->naking = 0;
+				UDC_QUEUE_CNAK(ep, ep->num);
+			}
+
+	}
+
+	return retval;
+}
+
+/* Completes request packet ... caller MUST hold lock */
+static void
+complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
+__releases(ep->dev->lock)
+__acquires(ep->dev->lock)
+{
+	struct udc		*dev;
+	unsigned		halted;
+
+	VDBG(ep->dev, "complete_req(): ep%d\n", ep->num);
+
+	dev = ep->dev;
+	/* unmap DMA */
+	if (ep->dma)
+		usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
+
+	halted = ep->halted;
+	ep->halted = 1;
+
+	/* set new status if pending */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = sts;
+
+	/* remove from ep queue */
+	list_del_init(&req->queue);
+
+	VDBG(ep->dev, "req %p => complete %d bytes at %s with sts %d\n",
+		&req->req, req->req.length, ep->ep.name, sts);
+
+	spin_unlock(&dev->lock);
+	usb_gadget_giveback_request(&ep->ep, &req->req);
+	spin_lock(&dev->lock);
+	ep->halted = halted;
+}
+
+/* Iterates to the end of a DMA chain and returns last descriptor */
+static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
+{
+	struct udc_data_dma	*td;
+
+	td = req->td_data;
+	while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
+		td = phys_to_virt(td->next);
+
+	return td;
+
+}
+
+/* Iterates to the end of a DMA chain and counts bytes received */
+static u32 udc_get_ppbdu_rxbytes(struct udc_request *req)
+{
+	struct udc_data_dma	*td;
+	u32 count;
+
+	td = req->td_data;
+	/* received number bytes */
+	count = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_RXBYTES);
+
+	while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
+		td = phys_to_virt(td->next);
+		/* received number bytes */
+		if (td) {
+			count += AMD_GETBITS(td->status,
+				UDC_DMA_OUT_STS_RXBYTES);
+		}
+	}
+
+	return count;
+
+}
+
+/* Enabling RX DMA */
+static void udc_set_rde(struct udc *dev)
+{
+	u32 tmp;
+
+	VDBG(dev, "udc_set_rde()\n");
+	/* stop RDE timer */
+	if (timer_pending(&udc_timer)) {
+		set_rde = 0;
+		mod_timer(&udc_timer, jiffies - 1);
+	}
+	/* set RDE */
+	tmp = readl(&dev->regs->ctl);
+	tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+	writel(tmp, &dev->regs->ctl);
+}
+
+/* Queues a request packet, called by gadget driver */
+static int
+udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
+{
+	int			retval = 0;
+	u8			open_rxfifo = 0;
+	unsigned long		iflags;
+	struct udc_ep		*ep;
+	struct udc_request	*req;
+	struct udc		*dev;
+	u32			tmp;
+
+	/* check the inputs */
+	req = container_of(usbreq, struct udc_request, req);
+
+	if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf
+			|| !list_empty(&req->queue))
+		return -EINVAL;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+		return -EINVAL;
+
+	VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in);
+	dev = ep->dev;
+
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	/* map dma (usually done before) */
+	if (ep->dma) {
+		VDBG(dev, "DMA map req %p\n", req);
+		retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
+		if (retval)
+			return retval;
+	}
+
+	VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
+			usbep->name, usbreq, usbreq->length,
+			req->td_data, usbreq->buf);
+
+	spin_lock_irqsave(&dev->lock, iflags);
+	usbreq->actual = 0;
+	usbreq->status = -EINPROGRESS;
+	req->dma_done = 0;
+
+	/* on empty queue just do first transfer */
+	if (list_empty(&ep->queue)) {
+		/* zlp */
+		if (usbreq->length == 0) {
+			/* IN zlp's are handled by hardware */
+			complete_req(ep, req, 0);
+			VDBG(dev, "%s: zlp\n", ep->ep.name);
+			/*
+			 * if set_config or set_intf is waiting for ack by zlp
+			 * then set CSR_DONE
+			 */
+			if (dev->set_cfg_not_acked) {
+				tmp = readl(&dev->regs->ctl);
+				tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
+				writel(tmp, &dev->regs->ctl);
+				dev->set_cfg_not_acked = 0;
+			}
+			/* setup command is ACK'ed now by zlp */
+			if (dev->waiting_zlp_ack_ep0in) {
+				/* clear NAK by writing CNAK in EP0_IN */
+				tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+				tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+				writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+				dev->ep[UDC_EP0IN_IX].naking = 0;
+				UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX],
+							UDC_EP0IN_IX);
+				dev->waiting_zlp_ack_ep0in = 0;
+			}
+			goto finished;
+		}
+		if (ep->dma) {
+			retval = prep_dma(ep, req, GFP_ATOMIC);
+			if (retval != 0)
+				goto finished;
+			/* write desc pointer to enable DMA */
+			if (ep->in) {
+				/* set HOST READY */
+				req->td_data->status =
+					AMD_ADDBITS(req->td_data->status,
+						UDC_DMA_IN_STS_BS_HOST_READY,
+						UDC_DMA_IN_STS_BS);
+			}
+
+			/* disabled rx dma while descriptor update */
+			if (!ep->in) {
+				/* stop RDE timer */
+				if (timer_pending(&udc_timer)) {
+					set_rde = 0;
+					mod_timer(&udc_timer, jiffies - 1);
+				}
+				/* clear RDE */
+				tmp = readl(&dev->regs->ctl);
+				tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+				writel(tmp, &dev->regs->ctl);
+				open_rxfifo = 1;
+
+				/*
+				 * if BNA occurred then let BNA dummy desc.
+				 * point to current desc.
+				 */
+				if (ep->bna_occurred) {
+					VDBG(dev, "copy to BNA dummy desc.\n");
+					memcpy(ep->bna_dummy_req->td_data,
+						req->td_data,
+						sizeof(struct udc_data_dma));
+				}
+			}
+			/* write desc pointer */
+			writel(req->td_phys, &ep->regs->desptr);
+
+			/* clear NAK by writing CNAK */
+			if (ep->naking) {
+				tmp = readl(&ep->regs->ctl);
+				tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+				writel(tmp, &ep->regs->ctl);
+				ep->naking = 0;
+				UDC_QUEUE_CNAK(ep, ep->num);
+			}
+
+			if (ep->in) {
+				/* enable ep irq */
+				tmp = readl(&dev->regs->ep_irqmsk);
+				tmp &= AMD_UNMASK_BIT(ep->num);
+				writel(tmp, &dev->regs->ep_irqmsk);
+			}
+		} else if (ep->in) {
+				/* enable ep irq */
+				tmp = readl(&dev->regs->ep_irqmsk);
+				tmp &= AMD_UNMASK_BIT(ep->num);
+				writel(tmp, &dev->regs->ep_irqmsk);
+			}
+
+	} else if (ep->dma) {
+
+		/*
+		 * prep_dma not used for OUT ep's, this is not possible
+		 * for PPB modes, because of chain creation reasons
+		 */
+		if (ep->in) {
+			retval = prep_dma(ep, req, GFP_ATOMIC);
+			if (retval != 0)
+				goto finished;
+		}
+	}
+	VDBG(dev, "list_add\n");
+	/* add request to ep queue */
+	if (req) {
+
+		list_add_tail(&req->queue, &ep->queue);
+
+		/* open rxfifo if out data queued */
+		if (open_rxfifo) {
+			/* enable DMA */
+			req->dma_going = 1;
+			udc_set_rde(dev);
+			if (ep->num != UDC_EP0OUT_IX)
+				dev->data_ep_queued = 1;
+		}
+		/* stop OUT naking */
+		if (!ep->in) {
+			if (!use_dma && udc_rxfifo_pending) {
+				DBG(dev, "udc_queue(): pending bytes in "
+					"rxfifo after nyet\n");
+				/*
+				 * read pending bytes afer nyet:
+				 * referring to isr
+				 */
+				if (udc_rxfifo_read(ep, req)) {
+					/* finish */
+					complete_req(ep, req, 0);
+				}
+				udc_rxfifo_pending = 0;
+
+			}
+		}
+	}
+
+finished:
+	spin_unlock_irqrestore(&dev->lock, iflags);
+	return retval;
+}
+
+/* Empty request queue of an endpoint; caller holds spinlock */
+void empty_req_queue(struct udc_ep *ep)
+{
+	struct udc_request	*req;
+
+	ep->halted = 1;
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next,
+			struct udc_request,
+			queue);
+		complete_req(ep, req, -ESHUTDOWN);
+	}
+}
+EXPORT_SYMBOL_GPL(empty_req_queue);
+
+/* Dequeues a request packet, called by gadget driver */
+static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+	struct udc_ep		*ep;
+	struct udc_request	*req;
+	unsigned		halted;
+	unsigned long		iflags;
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0
+				&& ep->num != UDC_EP0OUT_IX)))
+		return -EINVAL;
+
+	req = container_of(usbreq, struct udc_request, req);
+
+	spin_lock_irqsave(&ep->dev->lock, iflags);
+	halted = ep->halted;
+	ep->halted = 1;
+	/* request in processing or next one */
+	if (ep->queue.next == &req->queue) {
+		if (ep->dma && req->dma_going) {
+			if (ep->in)
+				ep->cancel_transfer = 1;
+			else {
+				u32 tmp;
+				u32 dma_sts;
+				/* stop potential receive DMA */
+				tmp = readl(&udc->regs->ctl);
+				writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_RDE),
+							&udc->regs->ctl);
+				/*
+				 * Cancel transfer later in ISR
+				 * if descriptor was touched.
+				 */
+				dma_sts = AMD_GETBITS(req->td_data->status,
+							UDC_DMA_OUT_STS_BS);
+				if (dma_sts != UDC_DMA_OUT_STS_BS_HOST_READY)
+					ep->cancel_transfer = 1;
+				else {
+					udc_init_bna_dummy(ep->req);
+					writel(ep->bna_dummy_req->td_phys,
+						&ep->regs->desptr);
+				}
+				writel(tmp, &udc->regs->ctl);
+			}
+		}
+	}
+	complete_req(ep, req, -ECONNRESET);
+	ep->halted = halted;
+
+	spin_unlock_irqrestore(&ep->dev->lock, iflags);
+	return 0;
+}
+
+/* Halt or clear halt of endpoint */
+static int
+udc_set_halt(struct usb_ep *usbep, int halt)
+{
+	struct udc_ep	*ep;
+	u32 tmp;
+	unsigned long iflags;
+	int retval = 0;
+
+	if (!usbep)
+		return -EINVAL;
+
+	pr_debug("set_halt %s: halt=%d\n", usbep->name, halt);
+
+	ep = container_of(usbep, struct udc_ep, ep);
+	if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+		return -EINVAL;
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&udc_stall_spinlock, iflags);
+	/* halt or clear halt */
+	if (halt) {
+		if (ep->num == 0)
+			ep->dev->stall_ep0in = 1;
+		else {
+			/*
+			 * set STALL
+			 * rxfifo empty not taken into acount
+			 */
+			tmp = readl(&ep->regs->ctl);
+			tmp |= AMD_BIT(UDC_EPCTL_S);
+			writel(tmp, &ep->regs->ctl);
+			ep->halted = 1;
+
+			/* setup poll timer */
+			if (!timer_pending(&udc_pollstall_timer)) {
+				udc_pollstall_timer.expires = jiffies +
+					HZ * UDC_POLLSTALL_TIMER_USECONDS
+					/ (1000 * 1000);
+				if (!stop_pollstall_timer) {
+					DBG(ep->dev, "start polltimer\n");
+					add_timer(&udc_pollstall_timer);
+				}
+			}
+		}
+	} else {
+		/* ep is halted by set_halt() before */
+		if (ep->halted) {
+			tmp = readl(&ep->regs->ctl);
+			/* clear stall bit */
+			tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+			/* clear NAK by writing CNAK */
+			tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+			writel(tmp, &ep->regs->ctl);
+			ep->halted = 0;
+			UDC_QUEUE_CNAK(ep, ep->num);
+		}
+	}
+	spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+	return retval;
+}
+
+/* gadget interface */
+static const struct usb_ep_ops udc_ep_ops = {
+	.enable		= udc_ep_enable,
+	.disable	= udc_ep_disable,
+
+	.alloc_request	= udc_alloc_request,
+	.free_request	= udc_free_request,
+
+	.queue		= udc_queue,
+	.dequeue	= udc_dequeue,
+
+	.set_halt	= udc_set_halt,
+	/* fifo ops not implemented */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Get frame counter (not implemented) */
+static int udc_get_frame(struct usb_gadget *gadget)
+{
+	return -EOPNOTSUPP;
+}
+
+/* Initiates a remote wakeup */
+static int udc_remote_wakeup(struct udc *dev)
+{
+	unsigned long flags;
+	u32 tmp;
+
+	DBG(dev, "UDC initiates remote wakeup\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	tmp = readl(&dev->regs->ctl);
+	tmp |= AMD_BIT(UDC_DEVCTL_RES);
+	writel(tmp, &dev->regs->ctl);
+	tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES);
+	writel(tmp, &dev->regs->ctl);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 0;
+}
+
+/* Remote wakeup gadget interface */
+static int udc_wakeup(struct usb_gadget *gadget)
+{
+	struct udc		*dev;
+
+	if (!gadget)
+		return -EINVAL;
+	dev = container_of(gadget, struct udc, gadget);
+	udc_remote_wakeup(dev);
+
+	return 0;
+}
+
+static int amd5536_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver);
+static int amd5536_udc_stop(struct usb_gadget *g);
+
+static const struct usb_gadget_ops udc_ops = {
+	.wakeup		= udc_wakeup,
+	.get_frame	= udc_get_frame,
+	.udc_start	= amd5536_udc_start,
+	.udc_stop	= amd5536_udc_stop,
+};
+
+/* Setups endpoint parameters, adds endpoints to linked list */
+static void make_ep_lists(struct udc *dev)
+{
+	/* make gadget ep lists */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
+						&dev->gadget.ep_list);
+	list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list,
+						&dev->gadget.ep_list);
+	list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
+						&dev->gadget.ep_list);
+
+	/* fifo config */
+	dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
+	if (dev->gadget.speed == USB_SPEED_FULL)
+		dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
+	else if (dev->gadget.speed == USB_SPEED_HIGH)
+		dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
+	dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
+}
+
+/* Inits UDC context */
+void udc_basic_init(struct udc *dev)
+{
+	u32	tmp;
+
+	DBG(dev, "udc_basic_init()\n");
+
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* stop RDE timer */
+	if (timer_pending(&udc_timer)) {
+		set_rde = 0;
+		mod_timer(&udc_timer, jiffies - 1);
+	}
+	/* stop poll stall timer */
+	if (timer_pending(&udc_pollstall_timer))
+		mod_timer(&udc_pollstall_timer, jiffies - 1);
+	/* disable DMA */
+	tmp = readl(&dev->regs->ctl);
+	tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+	tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
+	writel(tmp, &dev->regs->ctl);
+
+	/* enable dynamic CSR programming */
+	tmp = readl(&dev->regs->cfg);
+	tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
+	/* set self powered */
+	tmp |= AMD_BIT(UDC_DEVCFG_SP);
+	/* set remote wakeupable */
+	tmp |= AMD_BIT(UDC_DEVCFG_RWKP);
+	writel(tmp, &dev->regs->cfg);
+
+	make_ep_lists(dev);
+
+	dev->data_ep_enabled = 0;
+	dev->data_ep_queued = 0;
+}
+EXPORT_SYMBOL_GPL(udc_basic_init);
+
+/* init registers at driver load time */
+static int startup_registers(struct udc *dev)
+{
+	u32 tmp;
+
+	/* init controller by soft reset */
+	udc_soft_reset(dev);
+
+	/* mask not needed interrupts */
+	udc_mask_unused_interrupts(dev);
+
+	/* put into initial config */
+	udc_basic_init(dev);
+	/* link up all endpoints */
+	udc_setup_endpoints(dev);
+
+	/* program speed */
+	tmp = readl(&dev->regs->cfg);
+	if (use_fullspeed)
+		tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+	else
+		tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
+	writel(tmp, &dev->regs->cfg);
+
+	return 0;
+}
+
+/* Sets initial endpoint parameters */
+static void udc_setup_endpoints(struct udc *dev)
+{
+	struct udc_ep	*ep;
+	u32	tmp;
+	u32	reg;
+
+	DBG(dev, "udc_setup_endpoints()\n");
+
+	/* read enum speed */
+	tmp = readl(&dev->regs->sts);
+	tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
+	if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
+		dev->gadget.speed = USB_SPEED_HIGH;
+	else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
+		dev->gadget.speed = USB_SPEED_FULL;
+
+	/* set basic ep parameters */
+	for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+		ep = &dev->ep[tmp];
+		ep->dev = dev;
+		ep->ep.name = ep_info[tmp].name;
+		ep->ep.caps = ep_info[tmp].caps;
+		ep->num = tmp;
+		/* txfifo size is calculated at enable time */
+		ep->txfifo = dev->txfifo;
+
+		/* fifo size */
+		if (tmp < UDC_EPIN_NUM) {
+			ep->fifo_depth = UDC_TXFIFO_SIZE;
+			ep->in = 1;
+		} else {
+			ep->fifo_depth = UDC_RXFIFO_SIZE;
+			ep->in = 0;
+
+		}
+		ep->regs = &dev->ep_regs[tmp];
+		/*
+		 * ep will be reset only if ep was not enabled before to avoid
+		 * disabling ep interrupts when ENUM interrupt occurs but ep is
+		 * not enabled by gadget driver
+		 */
+		if (!ep->ep.desc)
+			ep_init(dev->regs, ep);
+
+		if (use_dma) {
+			/*
+			 * ep->dma is not really used, just to indicate that
+			 * DMA is active: remove this
+			 * dma regs = dev control regs
+			 */
+			ep->dma = &dev->regs->ctl;
+
+			/* nak OUT endpoints until enable - not for ep0 */
+			if (tmp != UDC_EP0IN_IX && tmp != UDC_EP0OUT_IX
+						&& tmp > UDC_EPIN_NUM) {
+				/* set NAK */
+				reg = readl(&dev->ep[tmp].regs->ctl);
+				reg |= AMD_BIT(UDC_EPCTL_SNAK);
+				writel(reg, &dev->ep[tmp].regs->ctl);
+				dev->ep[tmp].naking = 1;
+
+			}
+		}
+	}
+	/* EP0 max packet */
+	if (dev->gadget.speed == USB_SPEED_FULL) {
+		usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+					   UDC_FS_EP0IN_MAX_PKT_SIZE);
+		usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+					   UDC_FS_EP0OUT_MAX_PKT_SIZE);
+	} else if (dev->gadget.speed == USB_SPEED_HIGH) {
+		usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+					   UDC_EP0IN_MAX_PKT_SIZE);
+		usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+					   UDC_EP0OUT_MAX_PKT_SIZE);
+	}
+
+	/*
+	 * with suspend bug workaround, ep0 params for gadget driver
+	 * are set at gadget driver bind() call
+	 */
+	dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+	dev->ep[UDC_EP0IN_IX].halted = 0;
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+	/* init cfg/alt/int */
+	dev->cur_config = 0;
+	dev->cur_intf = 0;
+	dev->cur_alt = 0;
+}
+
+/* Bringup after Connect event, initial bringup to be ready for ep0 events */
+static void usb_connect(struct udc *dev)
+{
+	/* Return if already connected */
+	if (dev->connected)
+		return;
+
+	dev_info(dev->dev, "USB Connect\n");
+
+	dev->connected = 1;
+
+	/* put into initial config */
+	udc_basic_init(dev);
+
+	/* enable device setup interrupts */
+	udc_enable_dev_setup_interrupts(dev);
+}
+
+/*
+ * Calls gadget with disconnect event and resets the UDC and makes
+ * initial bringup to be ready for ep0 events
+ */
+static void usb_disconnect(struct udc *dev)
+{
+	/* Return if already disconnected */
+	if (!dev->connected)
+		return;
+
+	dev_info(dev->dev, "USB Disconnect\n");
+
+	dev->connected = 0;
+
+	/* mask interrupts */
+	udc_mask_unused_interrupts(dev);
+
+	/* REVISIT there doesn't seem to be a point to having this
+	 * talk to a tasklet ... do it directly, we already hold
+	 * the spinlock needed to process the disconnect.
+	 */
+
+	tasklet_schedule(&disconnect_tasklet);
+}
+
+/* Tasklet for disconnect to be outside of interrupt context */
+static void udc_tasklet_disconnect(unsigned long par)
+{
+	struct udc *dev = (struct udc *)(*((struct udc **) par));
+	u32 tmp;
+
+	DBG(dev, "Tasklet disconnect\n");
+	spin_lock_irq(&dev->lock);
+
+	if (dev->driver) {
+		spin_unlock(&dev->lock);
+		dev->driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+
+		/* empty queues */
+		for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+			empty_req_queue(&dev->ep[tmp]);
+
+	}
+
+	/* disable ep0 */
+	ep_init(dev->regs,
+			&dev->ep[UDC_EP0IN_IX]);
+
+
+	if (!soft_reset_occured) {
+		/* init controller by soft reset */
+		udc_soft_reset(dev);
+		soft_reset_occured++;
+	}
+
+	/* re-enable dev interrupts */
+	udc_enable_dev_setup_interrupts(dev);
+	/* back to full speed ? */
+	if (use_fullspeed) {
+		tmp = readl(&dev->regs->cfg);
+		tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+		writel(tmp, &dev->regs->cfg);
+	}
+
+	spin_unlock_irq(&dev->lock);
+}
+
+/* Reset the UDC core */
+static void udc_soft_reset(struct udc *dev)
+{
+	unsigned long	flags;
+
+	DBG(dev, "Soft reset\n");
+	/*
+	 * reset possible waiting interrupts, because int.
+	 * status is lost after soft reset,
+	 * ep int. status reset
+	 */
+	writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
+	/* device int. status reset */
+	writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
+
+	/* Don't do this for Broadcom UDC since this is a reserved
+	 * bit.
+	 */
+	if (dev->chiprev != UDC_BCM_REV) {
+		spin_lock_irqsave(&udc_irq_spinlock, flags);
+		writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+		readl(&dev->regs->cfg);
+		spin_unlock_irqrestore(&udc_irq_spinlock, flags);
+	}
+}
+
+/* RDE timer callback to set RDE bit */
+static void udc_timer_function(struct timer_list *unused)
+{
+	u32 tmp;
+
+	spin_lock_irq(&udc_irq_spinlock);
+
+	if (set_rde > 0) {
+		/*
+		 * open the fifo if fifo was filled on last timer call
+		 * conditionally
+		 */
+		if (set_rde > 1) {
+			/* set RDE to receive setup data */
+			tmp = readl(&udc->regs->ctl);
+			tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+			writel(tmp, &udc->regs->ctl);
+			set_rde = -1;
+		} else if (readl(&udc->regs->sts)
+				& AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
+			/*
+			 * if fifo empty setup polling, do not just
+			 * open the fifo
+			 */
+			udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
+			if (!stop_timer)
+				add_timer(&udc_timer);
+		} else {
+			/*
+			 * fifo contains data now, setup timer for opening
+			 * the fifo when timer expires to be able to receive
+			 * setup packets, when data packets gets queued by
+			 * gadget layer then timer will forced to expire with
+			 * set_rde=0 (RDE is set in udc_queue())
+			 */
+			set_rde++;
+			/* debug: lhadmot_timer_start = 221070 */
+			udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
+			if (!stop_timer)
+				add_timer(&udc_timer);
+		}
+
+	} else
+		set_rde = -1; /* RDE was set by udc_queue() */
+	spin_unlock_irq(&udc_irq_spinlock);
+	if (stop_timer)
+		complete(&on_exit);
+
+}
+
+/* Handle halt state, used in stall poll timer */
+static void udc_handle_halt_state(struct udc_ep *ep)
+{
+	u32 tmp;
+	/* set stall as long not halted */
+	if (ep->halted == 1) {
+		tmp = readl(&ep->regs->ctl);
+		/* STALL cleared ? */
+		if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
+			/*
+			 * FIXME: MSC spec requires that stall remains
+			 * even on receivng of CLEAR_FEATURE HALT. So
+			 * we would set STALL again here to be compliant.
+			 * But with current mass storage drivers this does
+			 * not work (would produce endless host retries).
+			 * So we clear halt on CLEAR_FEATURE.
+			 *
+			DBG(ep->dev, "ep %d: set STALL again\n", ep->num);
+			tmp |= AMD_BIT(UDC_EPCTL_S);
+			writel(tmp, &ep->regs->ctl);*/
+
+			/* clear NAK by writing CNAK */
+			tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+			writel(tmp, &ep->regs->ctl);
+			ep->halted = 0;
+			UDC_QUEUE_CNAK(ep, ep->num);
+		}
+	}
+}
+
+/* Stall timer callback to poll S bit and set it again after */
+static void udc_pollstall_timer_function(struct timer_list *unused)
+{
+	struct udc_ep *ep;
+	int halted = 0;
+
+	spin_lock_irq(&udc_stall_spinlock);
+	/*
+	 * only one IN and OUT endpoints are handled
+	 * IN poll stall
+	 */
+	ep = &udc->ep[UDC_EPIN_IX];
+	udc_handle_halt_state(ep);
+	if (ep->halted)
+		halted = 1;
+	/* OUT poll stall */
+	ep = &udc->ep[UDC_EPOUT_IX];
+	udc_handle_halt_state(ep);
+	if (ep->halted)
+		halted = 1;
+
+	/* setup timer again when still halted */
+	if (!stop_pollstall_timer && halted) {
+		udc_pollstall_timer.expires = jiffies +
+					HZ * UDC_POLLSTALL_TIMER_USECONDS
+					/ (1000 * 1000);
+		add_timer(&udc_pollstall_timer);
+	}
+	spin_unlock_irq(&udc_stall_spinlock);
+
+	if (stop_pollstall_timer)
+		complete(&on_pollstall_exit);
+}
+
+/* Inits endpoint 0 so that SETUP packets are processed */
+static void activate_control_endpoints(struct udc *dev)
+{
+	u32 tmp;
+
+	DBG(dev, "activate_control_endpoints\n");
+
+	/* flush fifo */
+	tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+	tmp |= AMD_BIT(UDC_EPCTL_F);
+	writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+	/* set ep0 directions */
+	dev->ep[UDC_EP0IN_IX].in = 1;
+	dev->ep[UDC_EP0OUT_IX].in = 0;
+
+	/* set buffer size (tx fifo entries) of EP0_IN */
+	tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+	if (dev->gadget.speed == USB_SPEED_FULL)
+		tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
+					UDC_EPIN_BUFF_SIZE);
+	else if (dev->gadget.speed == USB_SPEED_HIGH)
+		tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE,
+					UDC_EPIN_BUFF_SIZE);
+	writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+
+	/* set max packet size of EP0_IN */
+	tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+	if (dev->gadget.speed == USB_SPEED_FULL)
+		tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
+					UDC_EP_MAX_PKT_SIZE);
+	else if (dev->gadget.speed == USB_SPEED_HIGH)
+		tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
+				UDC_EP_MAX_PKT_SIZE);
+	writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+
+	/* set max packet size of EP0_OUT */
+	tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+	if (dev->gadget.speed == USB_SPEED_FULL)
+		tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+					UDC_EP_MAX_PKT_SIZE);
+	else if (dev->gadget.speed == USB_SPEED_HIGH)
+		tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+					UDC_EP_MAX_PKT_SIZE);
+	writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+
+	/* set max packet size of EP0 in UDC CSR */
+	tmp = readl(&dev->csr->ne[0]);
+	if (dev->gadget.speed == USB_SPEED_FULL)
+		tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+					UDC_CSR_NE_MAX_PKT);
+	else if (dev->gadget.speed == USB_SPEED_HIGH)
+		tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+					UDC_CSR_NE_MAX_PKT);
+	writel(tmp, &dev->csr->ne[0]);
+
+	if (use_dma) {
+		dev->ep[UDC_EP0OUT_IX].td->status |=
+			AMD_BIT(UDC_DMA_OUT_STS_L);
+		/* write dma desc address */
+		writel(dev->ep[UDC_EP0OUT_IX].td_stp_dma,
+			&dev->ep[UDC_EP0OUT_IX].regs->subptr);
+		writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+			&dev->ep[UDC_EP0OUT_IX].regs->desptr);
+		/* stop RDE timer */
+		if (timer_pending(&udc_timer)) {
+			set_rde = 0;
+			mod_timer(&udc_timer, jiffies - 1);
+		}
+		/* stop pollstall timer */
+		if (timer_pending(&udc_pollstall_timer))
+			mod_timer(&udc_pollstall_timer, jiffies - 1);
+		/* enable DMA */
+		tmp = readl(&dev->regs->ctl);
+		tmp |= AMD_BIT(UDC_DEVCTL_MODE)
+				| AMD_BIT(UDC_DEVCTL_RDE)
+				| AMD_BIT(UDC_DEVCTL_TDE);
+		if (use_dma_bufferfill_mode)
+			tmp |= AMD_BIT(UDC_DEVCTL_BF);
+		else if (use_dma_ppb_du)
+			tmp |= AMD_BIT(UDC_DEVCTL_DU);
+		writel(tmp, &dev->regs->ctl);
+	}
+
+	/* clear NAK by writing CNAK for EP0IN */
+	tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+	tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+	writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+	dev->ep[UDC_EP0IN_IX].naking = 0;
+	UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+	/* clear NAK by writing CNAK for EP0OUT */
+	tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+	tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+	writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+	dev->ep[UDC_EP0OUT_IX].naking = 0;
+	UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+}
+
+/* Make endpoint 0 ready for control traffic */
+static int setup_ep0(struct udc *dev)
+{
+	activate_control_endpoints(dev);
+	/* enable ep0 interrupts */
+	udc_enable_ep0_interrupts(dev);
+	/* enable device setup interrupts */
+	udc_enable_dev_setup_interrupts(dev);
+
+	return 0;
+}
+
+/* Called by gadget driver to register itself */
+static int amd5536_udc_start(struct usb_gadget *g,
+		struct usb_gadget_driver *driver)
+{
+	struct udc *dev = to_amd5536_udc(g);
+	u32 tmp;
+
+	driver->driver.bus = NULL;
+	dev->driver = driver;
+
+	/* Some gadget drivers use both ep0 directions.
+	 * NOTE: to gadget driver, ep0 is just one endpoint...
+	 */
+	dev->ep[UDC_EP0OUT_IX].ep.driver_data =
+		dev->ep[UDC_EP0IN_IX].ep.driver_data;
+
+	/* get ready for ep0 traffic */
+	setup_ep0(dev);
+
+	/* clear SD */
+	tmp = readl(&dev->regs->ctl);
+	tmp = tmp & AMD_CLEAR_BIT(UDC_DEVCTL_SD);
+	writel(tmp, &dev->regs->ctl);
+
+	usb_connect(dev);
+
+	return 0;
+}
+
+/* shutdown requests and disconnect from gadget */
+static void
+shutdown(struct udc *dev, struct usb_gadget_driver *driver)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	int tmp;
+
+	/* empty queues and init hardware */
+	udc_basic_init(dev);
+
+	for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+		empty_req_queue(&dev->ep[tmp]);
+
+	udc_setup_endpoints(dev);
+}
+
+/* Called by gadget driver to unregister itself */
+static int amd5536_udc_stop(struct usb_gadget *g)
+{
+	struct udc *dev = to_amd5536_udc(g);
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	udc_mask_unused_interrupts(dev);
+	shutdown(dev, NULL);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	dev->driver = NULL;
+
+	/* set SD */
+	tmp = readl(&dev->regs->ctl);
+	tmp |= AMD_BIT(UDC_DEVCTL_SD);
+	writel(tmp, &dev->regs->ctl);
+
+	return 0;
+}
+
+/* Clear pending NAK bits */
+static void udc_process_cnak_queue(struct udc *dev)
+{
+	u32 tmp;
+	u32 reg;
+
+	/* check epin's */
+	DBG(dev, "CNAK pending queue processing\n");
+	for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
+		if (cnak_pending & (1 << tmp)) {
+			DBG(dev, "CNAK pending for ep%d\n", tmp);
+			/* clear NAK by writing CNAK */
+			reg = readl(&dev->ep[tmp].regs->ctl);
+			reg |= AMD_BIT(UDC_EPCTL_CNAK);
+			writel(reg, &dev->ep[tmp].regs->ctl);
+			dev->ep[tmp].naking = 0;
+			UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
+		}
+	}
+	/* ...	and ep0out */
+	if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
+		DBG(dev, "CNAK pending for ep%d\n", UDC_EP0OUT_IX);
+		/* clear NAK by writing CNAK */
+		reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+		reg |= AMD_BIT(UDC_EPCTL_CNAK);
+		writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+		dev->ep[UDC_EP0OUT_IX].naking = 0;
+		UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX],
+				dev->ep[UDC_EP0OUT_IX].num);
+	}
+}
+
+/* Enabling RX DMA after setup packet */
+static void udc_ep0_set_rde(struct udc *dev)
+{
+	if (use_dma) {
+		/*
+		 * only enable RXDMA when no data endpoint enabled
+		 * or data is queued
+		 */
+		if (!dev->data_ep_enabled || dev->data_ep_queued) {
+			udc_set_rde(dev);
+		} else {
+			/*
+			 * setup timer for enabling RDE (to not enable
+			 * RXFIFO DMA for data endpoints to early)
+			 */
+			if (set_rde != 0 && !timer_pending(&udc_timer)) {
+				udc_timer.expires =
+					jiffies + HZ/UDC_RDE_TIMER_DIV;
+				set_rde = 1;
+				if (!stop_timer)
+					add_timer(&udc_timer);
+			}
+		}
+	}
+}
+
+
+/* Interrupt handler for data OUT traffic */
+static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
+{
+	irqreturn_t		ret_val = IRQ_NONE;
+	u32			tmp;
+	struct udc_ep		*ep;
+	struct udc_request	*req;
+	unsigned int		count;
+	struct udc_data_dma	*td = NULL;
+	unsigned		dma_done;
+
+	VDBG(dev, "ep%d irq\n", ep_ix);
+	ep = &dev->ep[ep_ix];
+
+	tmp = readl(&ep->regs->sts);
+	if (use_dma) {
+		/* BNA event ? */
+		if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+			DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
+					ep->num, readl(&ep->regs->desptr));
+			/* clear BNA */
+			writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
+			if (!ep->cancel_transfer)
+				ep->bna_occurred = 1;
+			else
+				ep->cancel_transfer = 0;
+			ret_val = IRQ_HANDLED;
+			goto finished;
+		}
+	}
+	/* HE event ? */
+	if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
+		dev_err(dev->dev, "HE ep%dout occurred\n", ep->num);
+
+		/* clear HE */
+		writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+		ret_val = IRQ_HANDLED;
+		goto finished;
+	}
+
+	if (!list_empty(&ep->queue)) {
+
+		/* next request */
+		req = list_entry(ep->queue.next,
+			struct udc_request, queue);
+	} else {
+		req = NULL;
+		udc_rxfifo_pending = 1;
+	}
+	VDBG(dev, "req = %p\n", req);
+	/* fifo mode */
+	if (!use_dma) {
+
+		/* read fifo */
+		if (req && udc_rxfifo_read(ep, req)) {
+			ret_val = IRQ_HANDLED;
+
+			/* finish */
+			complete_req(ep, req, 0);
+			/* next request */
+			if (!list_empty(&ep->queue) && !ep->halted) {
+				req = list_entry(ep->queue.next,
+					struct udc_request, queue);
+			} else
+				req = NULL;
+		}
+
+	/* DMA */
+	} else if (!ep->cancel_transfer && req) {
+		ret_val = IRQ_HANDLED;
+
+		/* check for DMA done */
+		if (!use_dma_ppb) {
+			dma_done = AMD_GETBITS(req->td_data->status,
+						UDC_DMA_OUT_STS_BS);
+		/* packet per buffer mode - rx bytes */
+		} else {
+			/*
+			 * if BNA occurred then recover desc. from
+			 * BNA dummy desc.
+			 */
+			if (ep->bna_occurred) {
+				VDBG(dev, "Recover desc. from BNA dummy\n");
+				memcpy(req->td_data, ep->bna_dummy_req->td_data,
+						sizeof(struct udc_data_dma));
+				ep->bna_occurred = 0;
+				udc_init_bna_dummy(ep->req);
+			}
+			td = udc_get_last_dma_desc(req);
+			dma_done = AMD_GETBITS(td->status, UDC_DMA_OUT_STS_BS);
+		}
+		if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
+			/* buffer fill mode - rx bytes */
+			if (!use_dma_ppb) {
+				/* received number bytes */
+				count = AMD_GETBITS(req->td_data->status,
+						UDC_DMA_OUT_STS_RXBYTES);
+				VDBG(dev, "rx bytes=%u\n", count);
+			/* packet per buffer mode - rx bytes */
+			} else {
+				VDBG(dev, "req->td_data=%p\n", req->td_data);
+				VDBG(dev, "last desc = %p\n", td);
+				/* received number bytes */
+				if (use_dma_ppb_du) {
+					/* every desc. counts bytes */
+					count = udc_get_ppbdu_rxbytes(req);
+				} else {
+					/* last desc. counts bytes */
+					count = AMD_GETBITS(td->status,
+						UDC_DMA_OUT_STS_RXBYTES);
+					if (!count && req->req.length
+						== UDC_DMA_MAXPACKET) {
+						/*
+						 * on 64k packets the RXBYTES
+						 * field is zero
+						 */
+						count = UDC_DMA_MAXPACKET;
+					}
+				}
+				VDBG(dev, "last desc rx bytes=%u\n", count);
+			}
+
+			tmp = req->req.length - req->req.actual;
+			if (count > tmp) {
+				if ((tmp % ep->ep.maxpacket) != 0) {
+					DBG(dev, "%s: rx %db, space=%db\n",
+						ep->ep.name, count, tmp);
+					req->req.status = -EOVERFLOW;
+				}
+				count = tmp;
+			}
+			req->req.actual += count;
+			req->dma_going = 0;
+			/* complete request */
+			complete_req(ep, req, 0);
+
+			/* next request */
+			if (!list_empty(&ep->queue) && !ep->halted) {
+				req = list_entry(ep->queue.next,
+					struct udc_request,
+					queue);
+				/*
+				 * DMA may be already started by udc_queue()
+				 * called by gadget drivers completion
+				 * routine. This happens when queue
+				 * holds one request only.
+				 */
+				if (req->dma_going == 0) {
+					/* next dma */
+					if (prep_dma(ep, req, GFP_ATOMIC) != 0)
+						goto finished;
+					/* write desc pointer */
+					writel(req->td_phys,
+						&ep->regs->desptr);
+					req->dma_going = 1;
+					/* enable DMA */
+					udc_set_rde(dev);
+				}
+			} else {
+				/*
+				 * implant BNA dummy descriptor to allow
+				 * RXFIFO opening by RDE
+				 */
+				if (ep->bna_dummy_req) {
+					/* write desc pointer */
+					writel(ep->bna_dummy_req->td_phys,
+						&ep->regs->desptr);
+					ep->bna_occurred = 0;
+				}
+
+				/*
+				 * schedule timer for setting RDE if queue
+				 * remains empty to allow ep0 packets pass
+				 * through
+				 */
+				if (set_rde != 0
+						&& !timer_pending(&udc_timer)) {
+					udc_timer.expires =
+						jiffies
+						+ HZ*UDC_RDE_TIMER_SECONDS;
+					set_rde = 1;
+					if (!stop_timer)
+						add_timer(&udc_timer);
+				}
+				if (ep->num != UDC_EP0OUT_IX)
+					dev->data_ep_queued = 0;
+			}
+
+		} else {
+			/*
+			* RX DMA must be reenabled for each desc in PPBDU mode
+			* and must be enabled for PPBNDU mode in case of BNA
+			*/
+			udc_set_rde(dev);
+		}
+
+	} else if (ep->cancel_transfer) {
+		ret_val = IRQ_HANDLED;
+		ep->cancel_transfer = 0;
+	}
+
+	/* check pending CNAKS */
+	if (cnak_pending) {
+		/* CNAk processing when rxfifo empty only */
+		if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+			udc_process_cnak_queue(dev);
+	}
+
+	/* clear OUT bits in ep status */
+	writel(UDC_EPSTS_OUT_CLEAR, &ep->regs->sts);
+finished:
+	return ret_val;
+}
+
+/* Interrupt handler for data IN traffic */
+static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
+{
+	irqreturn_t ret_val = IRQ_NONE;
+	u32 tmp;
+	u32 epsts;
+	struct udc_ep *ep;
+	struct udc_request *req;
+	struct udc_data_dma *td;
+	unsigned len;
+
+	ep = &dev->ep[ep_ix];
+
+	epsts = readl(&ep->regs->sts);
+	if (use_dma) {
+		/* BNA ? */
+		if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
+			dev_err(dev->dev,
+				"BNA ep%din occurred - DESPTR = %08lx\n",
+				ep->num,
+				(unsigned long) readl(&ep->regs->desptr));
+
+			/* clear BNA */
+			writel(epsts, &ep->regs->sts);
+			ret_val = IRQ_HANDLED;
+			goto finished;
+		}
+	}
+	/* HE event ? */
+	if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
+		dev_err(dev->dev,
+			"HE ep%dn occurred - DESPTR = %08lx\n",
+			ep->num, (unsigned long) readl(&ep->regs->desptr));
+
+		/* clear HE */
+		writel(epsts | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts);
+		ret_val = IRQ_HANDLED;
+		goto finished;
+	}
+
+	/* DMA completion */
+	if (epsts & AMD_BIT(UDC_EPSTS_TDC)) {
+		VDBG(dev, "TDC set- completion\n");
+		ret_val = IRQ_HANDLED;
+		if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
+			req = list_entry(ep->queue.next,
+					struct udc_request, queue);
+			/*
+			 * length bytes transferred
+			 * check dma done of last desc. in PPBDU mode
+			 */
+			if (use_dma_ppb_du) {
+				td = udc_get_last_dma_desc(req);
+				if (td)
+					req->req.actual = req->req.length;
+			} else {
+				/* assume all bytes transferred */
+				req->req.actual = req->req.length;
+			}
+
+			if (req->req.actual == req->req.length) {
+				/* complete req */
+				complete_req(ep, req, 0);
+				req->dma_going = 0;
+				/* further request available ? */
+				if (list_empty(&ep->queue)) {
+					/* disable interrupt */
+					tmp = readl(&dev->regs->ep_irqmsk);
+					tmp |= AMD_BIT(ep->num);
+					writel(tmp, &dev->regs->ep_irqmsk);
+				}
+			}
+		}
+		ep->cancel_transfer = 0;
+
+	}
+	/*
+	 * status reg has IN bit set and TDC not set (if TDC was handled,
+	 * IN must not be handled (UDC defect) ?
+	 */
+	if ((epsts & AMD_BIT(UDC_EPSTS_IN))
+			&& !(epsts & AMD_BIT(UDC_EPSTS_TDC))) {
+		ret_val = IRQ_HANDLED;
+		if (!list_empty(&ep->queue)) {
+			/* next request */
+			req = list_entry(ep->queue.next,
+					struct udc_request, queue);
+			/* FIFO mode */
+			if (!use_dma) {
+				/* write fifo */
+				udc_txfifo_write(ep, &req->req);
+				len = req->req.length - req->req.actual;
+				if (len > ep->ep.maxpacket)
+					len = ep->ep.maxpacket;
+				req->req.actual += len;
+				if (req->req.actual == req->req.length
+					|| (len != ep->ep.maxpacket)) {
+					/* complete req */
+					complete_req(ep, req, 0);
+				}
+			/* DMA */
+			} else if (req && !req->dma_going) {
+				VDBG(dev, "IN DMA : req=%p req->td_data=%p\n",
+					req, req->td_data);
+				if (req->td_data) {
+
+					req->dma_going = 1;
+
+					/*
+					 * unset L bit of first desc.
+					 * for chain
+					 */
+					if (use_dma_ppb && req->req.length >
+							ep->ep.maxpacket) {
+						req->td_data->status &=
+							AMD_CLEAR_BIT(
+							UDC_DMA_IN_STS_L);
+					}
+
+					/* write desc pointer */
+					writel(req->td_phys, &ep->regs->desptr);
+
+					/* set HOST READY */
+					req->td_data->status =
+						AMD_ADDBITS(
+						req->td_data->status,
+						UDC_DMA_IN_STS_BS_HOST_READY,
+						UDC_DMA_IN_STS_BS);
+
+					/* set poll demand bit */
+					tmp = readl(&ep->regs->ctl);
+					tmp |= AMD_BIT(UDC_EPCTL_P);
+					writel(tmp, &ep->regs->ctl);
+				}
+			}
+
+		} else if (!use_dma && ep->in) {
+			/* disable interrupt */
+			tmp = readl(
+				&dev->regs->ep_irqmsk);
+			tmp |= AMD_BIT(ep->num);
+			writel(tmp,
+				&dev->regs->ep_irqmsk);
+		}
+	}
+	/* clear status bits */
+	writel(epsts, &ep->regs->sts);
+
+finished:
+	return ret_val;
+
+}
+
+/* Interrupt handler for Control OUT traffic */
+static irqreturn_t udc_control_out_isr(struct udc *dev)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	irqreturn_t ret_val = IRQ_NONE;
+	u32 tmp;
+	int setup_supported;
+	u32 count;
+	int set = 0;
+	struct udc_ep	*ep;
+	struct udc_ep	*ep_tmp;
+
+	ep = &dev->ep[UDC_EP0OUT_IX];
+
+	/* clear irq */
+	writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
+
+	tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+	/* check BNA and clear if set */
+	if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+		VDBG(dev, "ep0: BNA set\n");
+		writel(AMD_BIT(UDC_EPSTS_BNA),
+			&dev->ep[UDC_EP0OUT_IX].regs->sts);
+		ep->bna_occurred = 1;
+		ret_val = IRQ_HANDLED;
+		goto finished;
+	}
+
+	/* type of data: SETUP or DATA 0 bytes */
+	tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
+	VDBG(dev, "data_typ = %x\n", tmp);
+
+	/* setup data */
+	if (tmp == UDC_EPSTS_OUT_SETUP) {
+		ret_val = IRQ_HANDLED;
+
+		ep->dev->stall_ep0in = 0;
+		dev->waiting_zlp_ack_ep0in = 0;
+
+		/* set NAK for EP0_IN */
+		tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+		tmp |= AMD_BIT(UDC_EPCTL_SNAK);
+		writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+		dev->ep[UDC_EP0IN_IX].naking = 1;
+		/* get setup data */
+		if (use_dma) {
+
+			/* clear OUT bits in ep status */
+			writel(UDC_EPSTS_OUT_CLEAR,
+				&dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+			setup_data.data[0] =
+				dev->ep[UDC_EP0OUT_IX].td_stp->data12;
+			setup_data.data[1] =
+				dev->ep[UDC_EP0OUT_IX].td_stp->data34;
+			/* set HOST READY */
+			dev->ep[UDC_EP0OUT_IX].td_stp->status =
+					UDC_DMA_STP_STS_BS_HOST_READY;
+		} else {
+			/* read fifo */
+			udc_rxfifo_read_dwords(dev, setup_data.data, 2);
+		}
+
+		/* determine direction of control data */
+		if ((setup_data.request.bRequestType & USB_DIR_IN) != 0) {
+			dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+			/* enable RDE */
+			udc_ep0_set_rde(dev);
+			set = 0;
+		} else {
+			dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
+			/*
+			 * implant BNA dummy descriptor to allow RXFIFO opening
+			 * by RDE
+			 */
+			if (ep->bna_dummy_req) {
+				/* write desc pointer */
+				writel(ep->bna_dummy_req->td_phys,
+					&dev->ep[UDC_EP0OUT_IX].regs->desptr);
+				ep->bna_occurred = 0;
+			}
+
+			set = 1;
+			dev->ep[UDC_EP0OUT_IX].naking = 1;
+			/*
+			 * setup timer for enabling RDE (to not enable
+			 * RXFIFO DMA for data to early)
+			 */
+			set_rde = 1;
+			if (!timer_pending(&udc_timer)) {
+				udc_timer.expires = jiffies +
+							HZ/UDC_RDE_TIMER_DIV;
+				if (!stop_timer)
+					add_timer(&udc_timer);
+			}
+		}
+
+		/*
+		 * mass storage reset must be processed here because
+		 * next packet may be a CLEAR_FEATURE HALT which would not
+		 * clear the stall bit when no STALL handshake was received
+		 * before (autostall can cause this)
+		 */
+		if (setup_data.data[0] == UDC_MSCRES_DWORD0
+				&& setup_data.data[1] == UDC_MSCRES_DWORD1) {
+			DBG(dev, "MSC Reset\n");
+			/*
+			 * clear stall bits
+			 * only one IN and OUT endpoints are handled
+			 */
+			ep_tmp = &udc->ep[UDC_EPIN_IX];
+			udc_set_halt(&ep_tmp->ep, 0);
+			ep_tmp = &udc->ep[UDC_EPOUT_IX];
+			udc_set_halt(&ep_tmp->ep, 0);
+		}
+
+		/* call gadget with setup data received */
+		spin_unlock(&dev->lock);
+		setup_supported = dev->driver->setup(&dev->gadget,
+						&setup_data.request);
+		spin_lock(&dev->lock);
+
+		tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+		/* ep0 in returns data (not zlp) on IN phase */
+		if (setup_supported >= 0 && setup_supported <
+				UDC_EP0IN_MAXPACKET) {
+			/* clear NAK by writing CNAK in EP0_IN */
+			tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+			writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+			dev->ep[UDC_EP0IN_IX].naking = 0;
+			UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+
+		/* if unsupported request then stall */
+		} else if (setup_supported < 0) {
+			tmp |= AMD_BIT(UDC_EPCTL_S);
+			writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+		} else
+			dev->waiting_zlp_ack_ep0in = 1;
+
+
+		/* clear NAK by writing CNAK in EP0_OUT */
+		if (!set) {
+			tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+			tmp |= AMD_BIT(UDC_EPCTL_CNAK);
+			writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+			dev->ep[UDC_EP0OUT_IX].naking = 0;
+			UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+		}
+
+		if (!use_dma) {
+			/* clear OUT bits in ep status */
+			writel(UDC_EPSTS_OUT_CLEAR,
+				&dev->ep[UDC_EP0OUT_IX].regs->sts);
+		}
+
+	/* data packet 0 bytes */
+	} else if (tmp == UDC_EPSTS_OUT_DATA) {
+		/* clear OUT bits in ep status */
+		writel(UDC_EPSTS_OUT_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+		/* get setup data: only 0 packet */
+		if (use_dma) {
+			/* no req if 0 packet, just reactivate */
+			if (list_empty(&dev->ep[UDC_EP0OUT_IX].queue)) {
+				VDBG(dev, "ZLP\n");
+
+				/* set HOST READY */
+				dev->ep[UDC_EP0OUT_IX].td->status =
+					AMD_ADDBITS(
+					dev->ep[UDC_EP0OUT_IX].td->status,
+					UDC_DMA_OUT_STS_BS_HOST_READY,
+					UDC_DMA_OUT_STS_BS);
+				/* enable RDE */
+				udc_ep0_set_rde(dev);
+				ret_val = IRQ_HANDLED;
+
+			} else {
+				/* control write */
+				ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+				/* re-program desc. pointer for possible ZLPs */
+				writel(dev->ep[UDC_EP0OUT_IX].td_phys,
+					&dev->ep[UDC_EP0OUT_IX].regs->desptr);
+				/* enable RDE */
+				udc_ep0_set_rde(dev);
+			}
+		} else {
+
+			/* received number bytes */
+			count = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+			count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
+			/* out data for fifo mode not working */
+			count = 0;
+
+			/* 0 packet or real data ? */
+			if (count != 0) {
+				ret_val |= udc_data_out_isr(dev, UDC_EP0OUT_IX);
+			} else {
+				/* dummy read confirm */
+				readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
+				ret_val = IRQ_HANDLED;
+			}
+		}
+	}
+
+	/* check pending CNAKS */
+	if (cnak_pending) {
+		/* CNAk processing when rxfifo empty only */
+		if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+			udc_process_cnak_queue(dev);
+	}
+
+finished:
+	return ret_val;
+}
+
+/* Interrupt handler for Control IN traffic */
+static irqreturn_t udc_control_in_isr(struct udc *dev)
+{
+	irqreturn_t ret_val = IRQ_NONE;
+	u32 tmp;
+	struct udc_ep *ep;
+	struct udc_request *req;
+	unsigned len;
+
+	ep = &dev->ep[UDC_EP0IN_IX];
+
+	/* clear irq */
+	writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
+
+	tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
+	/* DMA completion */
+	if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
+		VDBG(dev, "isr: TDC clear\n");
+		ret_val = IRQ_HANDLED;
+
+		/* clear TDC bit */
+		writel(AMD_BIT(UDC_EPSTS_TDC),
+				&dev->ep[UDC_EP0IN_IX].regs->sts);
+
+	/* status reg has IN bit set ? */
+	} else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
+		ret_val = IRQ_HANDLED;
+
+		if (ep->dma) {
+			/* clear IN bit */
+			writel(AMD_BIT(UDC_EPSTS_IN),
+				&dev->ep[UDC_EP0IN_IX].regs->sts);
+		}
+		if (dev->stall_ep0in) {
+			DBG(dev, "stall ep0in\n");
+			/* halt ep0in */
+			tmp = readl(&ep->regs->ctl);
+			tmp |= AMD_BIT(UDC_EPCTL_S);
+			writel(tmp, &ep->regs->ctl);
+		} else {
+			if (!list_empty(&ep->queue)) {
+				/* next request */
+				req = list_entry(ep->queue.next,
+						struct udc_request, queue);
+
+				if (ep->dma) {
+					/* write desc pointer */
+					writel(req->td_phys, &ep->regs->desptr);
+					/* set HOST READY */
+					req->td_data->status =
+						AMD_ADDBITS(
+						req->td_data->status,
+						UDC_DMA_STP_STS_BS_HOST_READY,
+						UDC_DMA_STP_STS_BS);
+
+					/* set poll demand bit */
+					tmp =
+					readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+					tmp |= AMD_BIT(UDC_EPCTL_P);
+					writel(tmp,
+					&dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+					/* all bytes will be transferred */
+					req->req.actual = req->req.length;
+
+					/* complete req */
+					complete_req(ep, req, 0);
+
+				} else {
+					/* write fifo */
+					udc_txfifo_write(ep, &req->req);
+
+					/* lengh bytes transferred */
+					len = req->req.length - req->req.actual;
+					if (len > ep->ep.maxpacket)
+						len = ep->ep.maxpacket;
+
+					req->req.actual += len;
+					if (req->req.actual == req->req.length
+						|| (len != ep->ep.maxpacket)) {
+						/* complete req */
+						complete_req(ep, req, 0);
+					}
+				}
+
+			}
+		}
+		ep->halted = 0;
+		dev->stall_ep0in = 0;
+		if (!ep->dma) {
+			/* clear IN bit */
+			writel(AMD_BIT(UDC_EPSTS_IN),
+				&dev->ep[UDC_EP0IN_IX].regs->sts);
+		}
+	}
+
+	return ret_val;
+}
+
+
+/* Interrupt handler for global device events */
+static irqreturn_t udc_dev_isr(struct udc *dev, u32 dev_irq)
+__releases(dev->lock)
+__acquires(dev->lock)
+{
+	irqreturn_t ret_val = IRQ_NONE;
+	u32 tmp;
+	u32 cfg;
+	struct udc_ep *ep;
+	u16 i;
+	u8 udc_csr_epix;
+
+	/* SET_CONFIG irq ? */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
+		ret_val = IRQ_HANDLED;
+
+		/* read config value */
+		tmp = readl(&dev->regs->sts);
+		cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
+		DBG(dev, "SET_CONFIG interrupt: config=%d\n", cfg);
+		dev->cur_config = cfg;
+		dev->set_cfg_not_acked = 1;
+
+		/* make usb request for gadget driver */
+		memset(&setup_data, 0 , sizeof(union udc_setup_data));
+		setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
+		setup_data.request.wValue = cpu_to_le16(dev->cur_config);
+
+		/* programm the NE registers */
+		for (i = 0; i < UDC_EP_NUM; i++) {
+			ep = &dev->ep[i];
+			if (ep->in) {
+
+				/* ep ix in UDC CSR register space */
+				udc_csr_epix = ep->num;
+
+
+			/* OUT ep */
+			} else {
+				/* ep ix in UDC CSR register space */
+				udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+			}
+
+			tmp = readl(&dev->csr->ne[udc_csr_epix]);
+			/* ep cfg */
+			tmp = AMD_ADDBITS(tmp, ep->dev->cur_config,
+						UDC_CSR_NE_CFG);
+			/* write reg */
+			writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+			/* clear stall bits */
+			ep->halted = 0;
+			tmp = readl(&ep->regs->ctl);
+			tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+			writel(tmp, &ep->regs->ctl);
+		}
+		/* call gadget zero with setup data received */
+		spin_unlock(&dev->lock);
+		tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+		spin_lock(&dev->lock);
+
+	} /* SET_INTERFACE ? */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
+		ret_val = IRQ_HANDLED;
+
+		dev->set_cfg_not_acked = 1;
+		/* read interface and alt setting values */
+		tmp = readl(&dev->regs->sts);
+		dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
+		dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
+
+		/* make usb request for gadget driver */
+		memset(&setup_data, 0 , sizeof(union udc_setup_data));
+		setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
+		setup_data.request.bRequestType = USB_RECIP_INTERFACE;
+		setup_data.request.wValue = cpu_to_le16(dev->cur_alt);
+		setup_data.request.wIndex = cpu_to_le16(dev->cur_intf);
+
+		DBG(dev, "SET_INTERFACE interrupt: alt=%d intf=%d\n",
+				dev->cur_alt, dev->cur_intf);
+
+		/* programm the NE registers */
+		for (i = 0; i < UDC_EP_NUM; i++) {
+			ep = &dev->ep[i];
+			if (ep->in) {
+
+				/* ep ix in UDC CSR register space */
+				udc_csr_epix = ep->num;
+
+
+			/* OUT ep */
+			} else {
+				/* ep ix in UDC CSR register space */
+				udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+			}
+
+			/* UDC CSR reg */
+			/* set ep values */
+			tmp = readl(&dev->csr->ne[udc_csr_epix]);
+			/* ep interface */
+			tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf,
+						UDC_CSR_NE_INTF);
+			/* tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF); */
+			/* ep alt */
+			tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt,
+						UDC_CSR_NE_ALT);
+			/* write reg */
+			writel(tmp, &dev->csr->ne[udc_csr_epix]);
+
+			/* clear stall bits */
+			ep->halted = 0;
+			tmp = readl(&ep->regs->ctl);
+			tmp = tmp & AMD_CLEAR_BIT(UDC_EPCTL_S);
+			writel(tmp, &ep->regs->ctl);
+		}
+
+		/* call gadget zero with setup data received */
+		spin_unlock(&dev->lock);
+		tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+		spin_lock(&dev->lock);
+
+	} /* USB reset */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
+		DBG(dev, "USB Reset interrupt\n");
+		ret_val = IRQ_HANDLED;
+
+		/* allow soft reset when suspend occurs */
+		soft_reset_occured = 0;
+
+		dev->waiting_zlp_ack_ep0in = 0;
+		dev->set_cfg_not_acked = 0;
+
+		/* mask not needed interrupts */
+		udc_mask_unused_interrupts(dev);
+
+		/* call gadget to resume and reset configs etc. */
+		spin_unlock(&dev->lock);
+		if (dev->sys_suspended && dev->driver->resume) {
+			dev->driver->resume(&dev->gadget);
+			dev->sys_suspended = 0;
+		}
+		usb_gadget_udc_reset(&dev->gadget, dev->driver);
+		spin_lock(&dev->lock);
+
+		/* disable ep0 to empty req queue */
+		empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+		ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+		/* soft reset when rxfifo not empty */
+		tmp = readl(&dev->regs->sts);
+		if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
+				&& !soft_reset_after_usbreset_occured) {
+			udc_soft_reset(dev);
+			soft_reset_after_usbreset_occured++;
+		}
+
+		/*
+		 * DMA reset to kill potential old DMA hw hang,
+		 * POLL bit is already reset by ep_init() through
+		 * disconnect()
+		 */
+		DBG(dev, "DMA machine reset\n");
+		tmp = readl(&dev->regs->cfg);
+		writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg);
+		writel(tmp, &dev->regs->cfg);
+
+		/* put into initial config */
+		udc_basic_init(dev);
+
+		/* enable device setup interrupts */
+		udc_enable_dev_setup_interrupts(dev);
+
+		/* enable suspend interrupt */
+		tmp = readl(&dev->regs->irqmsk);
+		tmp &= AMD_UNMASK_BIT(UDC_DEVINT_US);
+		writel(tmp, &dev->regs->irqmsk);
+
+	} /* USB suspend */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
+		DBG(dev, "USB Suspend interrupt\n");
+		ret_val = IRQ_HANDLED;
+		if (dev->driver->suspend) {
+			spin_unlock(&dev->lock);
+			dev->sys_suspended = 1;
+			dev->driver->suspend(&dev->gadget);
+			spin_lock(&dev->lock);
+		}
+	} /* new speed ? */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
+		DBG(dev, "ENUM interrupt\n");
+		ret_val = IRQ_HANDLED;
+		soft_reset_after_usbreset_occured = 0;
+
+		/* disable ep0 to empty req queue */
+		empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+		ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+		/* link up all endpoints */
+		udc_setup_endpoints(dev);
+		dev_info(dev->dev, "Connect: %s\n",
+			 usb_speed_string(dev->gadget.speed));
+
+		/* init ep 0 */
+		activate_control_endpoints(dev);
+
+		/* enable ep0 interrupts */
+		udc_enable_ep0_interrupts(dev);
+	}
+	/* session valid change interrupt */
+	if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
+		DBG(dev, "USB SVC interrupt\n");
+		ret_val = IRQ_HANDLED;
+
+		/* check that session is not valid to detect disconnect */
+		tmp = readl(&dev->regs->sts);
+		if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
+			/* disable suspend interrupt */
+			tmp = readl(&dev->regs->irqmsk);
+			tmp |= AMD_BIT(UDC_DEVINT_US);
+			writel(tmp, &dev->regs->irqmsk);
+			DBG(dev, "USB Disconnect (session valid low)\n");
+			/* cleanup on disconnect */
+			usb_disconnect(udc);
+		}
+
+	}
+
+	return ret_val;
+}
+
+/* Interrupt Service Routine, see Linux Kernel Doc for parameters */
+irqreturn_t udc_irq(int irq, void *pdev)
+{
+	struct udc *dev = pdev;
+	u32 reg;
+	u16 i;
+	u32 ep_irq;
+	irqreturn_t ret_val = IRQ_NONE;
+
+	spin_lock(&dev->lock);
+
+	/* check for ep irq */
+	reg = readl(&dev->regs->ep_irqsts);
+	if (reg) {
+		if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
+			ret_val |= udc_control_out_isr(dev);
+		if (reg & AMD_BIT(UDC_EPINT_IN_EP0))
+			ret_val |= udc_control_in_isr(dev);
+
+		/*
+		 * data endpoint
+		 * iterate ep's
+		 */
+		for (i = 1; i < UDC_EP_NUM; i++) {
+			ep_irq = 1 << i;
+			if (!(reg & ep_irq) || i == UDC_EPINT_OUT_EP0)
+				continue;
+
+			/* clear irq status */
+			writel(ep_irq, &dev->regs->ep_irqsts);
+
+			/* irq for out ep ? */
+			if (i > UDC_EPIN_NUM)
+				ret_val |= udc_data_out_isr(dev, i);
+			else
+				ret_val |= udc_data_in_isr(dev, i);
+		}
+
+	}
+
+
+	/* check for dev irq */
+	reg = readl(&dev->regs->irqsts);
+	if (reg) {
+		/* clear irq */
+		writel(reg, &dev->regs->irqsts);
+		ret_val |= udc_dev_isr(dev, reg);
+	}
+
+
+	spin_unlock(&dev->lock);
+	return ret_val;
+}
+EXPORT_SYMBOL_GPL(udc_irq);
+
+/* Tears down device */
+void gadget_release(struct device *pdev)
+{
+	struct amd5536udc *dev = dev_get_drvdata(pdev);
+	kfree(dev);
+}
+EXPORT_SYMBOL_GPL(gadget_release);
+
+/* Cleanup on device remove */
+void udc_remove(struct udc *dev)
+{
+	/* remove timer */
+	stop_timer++;
+	if (timer_pending(&udc_timer))
+		wait_for_completion(&on_exit);
+	del_timer_sync(&udc_timer);
+	/* remove pollstall timer */
+	stop_pollstall_timer++;
+	if (timer_pending(&udc_pollstall_timer))
+		wait_for_completion(&on_pollstall_exit);
+	del_timer_sync(&udc_pollstall_timer);
+	udc = NULL;
+}
+EXPORT_SYMBOL_GPL(udc_remove);
+
+/* free all the dma pools */
+void free_dma_pools(struct udc *dev)
+{
+	dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
+		      dev->ep[UDC_EP0OUT_IX].td_phys);
+	dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+		      dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+	dma_pool_destroy(dev->stp_requests);
+	dma_pool_destroy(dev->data_requests);
+}
+EXPORT_SYMBOL_GPL(free_dma_pools);
+
+/* create dma pools on init */
+int init_dma_pools(struct udc *dev)
+{
+	struct udc_stp_dma	*td_stp;
+	struct udc_data_dma	*td_data;
+	int retval;
+
+	/* consistent DMA mode setting ? */
+	if (use_dma_ppb) {
+		use_dma_bufferfill_mode = 0;
+	} else {
+		use_dma_ppb_du = 0;
+		use_dma_bufferfill_mode = 1;
+	}
+
+	/* DMA setup */
+	dev->data_requests = dma_pool_create("data_requests", dev->dev,
+		sizeof(struct udc_data_dma), 0, 0);
+	if (!dev->data_requests) {
+		DBG(dev, "can't get request data pool\n");
+		return -ENOMEM;
+	}
+
+	/* EP0 in dma regs = dev control regs */
+	dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
+
+	/* dma desc for setup data */
+	dev->stp_requests = dma_pool_create("setup requests", dev->dev,
+		sizeof(struct udc_stp_dma), 0, 0);
+	if (!dev->stp_requests) {
+		DBG(dev, "can't get stp request pool\n");
+		retval = -ENOMEM;
+		goto err_create_dma_pool;
+	}
+	/* setup */
+	td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+				&dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+	if (!td_stp) {
+		retval = -ENOMEM;
+		goto err_alloc_dma;
+	}
+	dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
+
+	/* data: 0 packets !? */
+	td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL,
+				&dev->ep[UDC_EP0OUT_IX].td_phys);
+	if (!td_data) {
+		retval = -ENOMEM;
+		goto err_alloc_phys;
+	}
+	dev->ep[UDC_EP0OUT_IX].td = td_data;
+	return 0;
+
+err_alloc_phys:
+	dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp,
+		      dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+err_alloc_dma:
+	dma_pool_destroy(dev->stp_requests);
+	dev->stp_requests = NULL;
+err_create_dma_pool:
+	dma_pool_destroy(dev->data_requests);
+	dev->data_requests = NULL;
+	return retval;
+}
+EXPORT_SYMBOL_GPL(init_dma_pools);
+
+/* general probe */
+int udc_probe(struct udc *dev)
+{
+	char		tmp[128];
+	u32		reg;
+	int		retval;
+
+	/* device struct setup */
+	dev->gadget.ops = &udc_ops;
+
+	dev_set_name(&dev->gadget.dev, "gadget");
+	dev->gadget.name = name;
+	dev->gadget.max_speed = USB_SPEED_HIGH;
+
+	/* init registers, interrupts, ... */
+	startup_registers(dev);
+
+	dev_info(dev->dev, "%s\n", mod_desc);
+
+	snprintf(tmp, sizeof(tmp), "%d", dev->irq);
+
+	/* Print this device info for AMD chips only*/
+	if (dev->chiprev == UDC_HSA0_REV ||
+	    dev->chiprev == UDC_HSB1_REV) {
+		dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
+			 tmp, dev->phys_addr, dev->chiprev,
+			 (dev->chiprev == UDC_HSA0_REV) ?
+			 "A0" : "B1");
+		strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+		if (dev->chiprev == UDC_HSA0_REV) {
+			dev_err(dev->dev, "chip revision is A0; too old\n");
+			retval = -ENODEV;
+			goto finished;
+		}
+		dev_info(dev->dev,
+			 "driver version: %s(for Geode5536 B1)\n", tmp);
+	}
+
+	udc = dev;
+
+	retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
+					    gadget_release);
+	if (retval)
+		goto finished;
+
+	/* timer init */
+	timer_setup(&udc_timer, udc_timer_function, 0);
+	timer_setup(&udc_pollstall_timer, udc_pollstall_timer_function, 0);
+
+	/* set SD */
+	reg = readl(&dev->regs->ctl);
+	reg |= AMD_BIT(UDC_DEVCTL_SD);
+	writel(reg, &dev->regs->ctl);
+
+	/* print dev register info */
+	print_regs(dev);
+
+	return 0;
+
+finished:
+	return retval;
+}
+EXPORT_SYMBOL_GPL(udc_probe);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
new file mode 100644
index 0000000..32f1d3e
--- /dev/null
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * snps_udc_plat.c - Synopsys UDC Platform Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ */
+
+#include <linux/extcon.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/module.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include "amd5536udc.h"
+
+/* description */
+#define UDC_MOD_DESCRIPTION     "Synopsys UDC platform driver"
+
+static void start_udc(struct udc *udc)
+{
+	if (udc->driver) {
+		dev_info(udc->dev, "Connecting...\n");
+		udc_enable_dev_setup_interrupts(udc);
+		udc_basic_init(udc);
+		udc->connected = 1;
+	}
+}
+
+static void stop_udc(struct udc *udc)
+{
+	int tmp;
+	u32 reg;
+
+	spin_lock(&udc->lock);
+
+	/* Flush the receieve fifo */
+	reg = readl(&udc->regs->ctl);
+	reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
+	writel(reg, &udc->regs->ctl);
+
+	reg = readl(&udc->regs->ctl);
+	reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
+	writel(reg, &udc->regs->ctl);
+	dev_dbg(udc->dev, "ep rx queue flushed\n");
+
+	/* Mask interrupts. Required more so when the
+	 * UDC is connected to a DRD phy.
+	 */
+	udc_mask_unused_interrupts(udc);
+
+	/* Disconnect gadget driver */
+	if (udc->driver) {
+		spin_unlock(&udc->lock);
+		udc->driver->disconnect(&udc->gadget);
+		spin_lock(&udc->lock);
+
+		/* empty queues */
+		for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+			empty_req_queue(&udc->ep[tmp]);
+	}
+	udc->connected = 0;
+
+	spin_unlock(&udc->lock);
+	dev_info(udc->dev, "Device disconnected\n");
+}
+
+static void udc_drd_work(struct work_struct *work)
+{
+	struct udc *udc;
+
+	udc = container_of(to_delayed_work(work),
+			   struct udc, drd_work);
+
+	if (udc->conn_type) {
+		dev_dbg(udc->dev, "idle -> device\n");
+		start_udc(udc);
+	} else {
+		dev_dbg(udc->dev, "device -> idle\n");
+		stop_udc(udc);
+	}
+}
+
+static int usbd_connect_notify(struct notifier_block *self,
+			       unsigned long event, void *ptr)
+{
+	struct udc *udc = container_of(self, struct udc, nb);
+
+	dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
+
+	udc->conn_type = event;
+
+	schedule_delayed_work(&udc->drd_work, 0);
+
+	return NOTIFY_OK;
+}
+
+static int udc_plat_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct udc *udc;
+	int ret;
+
+	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	spin_lock_init(&udc->lock);
+	udc->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->virt_addr = devm_ioremap_resource(dev, res);
+	if (IS_ERR(udc->regs))
+		return PTR_ERR(udc->regs);
+
+	/* udc csr registers base */
+	udc->csr = udc->virt_addr + UDC_CSR_ADDR;
+
+	/* dev registers base */
+	udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
+
+	/* ep registers base */
+	udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
+
+	/* fifo's base */
+	udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
+	udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
+
+	udc->phys_addr = (unsigned long)res->start;
+
+	udc->irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (udc->irq <= 0) {
+		dev_err(dev, "Can't parse and map interrupt\n");
+		return -EINVAL;
+	}
+
+	udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+	if (IS_ERR(udc->udc_phy)) {
+		dev_err(dev, "Failed to obtain phy from device tree\n");
+		return PTR_ERR(udc->udc_phy);
+	}
+
+	ret = phy_init(udc->udc_phy);
+	if (ret) {
+		dev_err(dev, "UDC phy init failed");
+		return ret;
+	}
+
+	ret = phy_power_on(udc->udc_phy);
+	if (ret) {
+		dev_err(dev, "UDC phy power on failed");
+		phy_exit(udc->udc_phy);
+		return ret;
+	}
+
+	/* Register for extcon if supported */
+	if (of_get_property(dev->of_node, "extcon", NULL)) {
+		udc->edev = extcon_get_edev_by_phandle(dev, 0);
+		if (IS_ERR(udc->edev)) {
+			if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
+				return -EPROBE_DEFER;
+			dev_err(dev, "Invalid or missing extcon\n");
+			ret = PTR_ERR(udc->edev);
+			goto exit_phy;
+		}
+
+		udc->nb.notifier_call = usbd_connect_notify;
+		ret = extcon_register_notifier(udc->edev, EXTCON_USB,
+					       &udc->nb);
+		if (ret < 0) {
+			dev_err(dev, "Can't register extcon device\n");
+			goto exit_phy;
+		}
+
+		ret = extcon_get_state(udc->edev, EXTCON_USB);
+		if (ret < 0) {
+			dev_err(dev, "Can't get cable state\n");
+			goto exit_extcon;
+		} else if (ret) {
+			udc->conn_type = ret;
+		}
+		INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
+	}
+
+	/* init dma pools */
+	if (use_dma) {
+		ret = init_dma_pools(udc);
+		if (ret != 0)
+			goto exit_extcon;
+	}
+
+	ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
+			       "snps-udc", udc);
+	if (ret < 0) {
+		dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
+		goto exit_dma;
+	}
+
+	platform_set_drvdata(pdev, udc);
+	udc->chiprev = UDC_BCM_REV;
+
+	if (udc_probe(udc)) {
+		ret = -ENODEV;
+		goto exit_dma;
+	}
+	dev_info(dev, "Synopsys UDC platform driver probe successful\n");
+
+	return 0;
+
+exit_dma:
+	if (use_dma)
+		free_dma_pools(udc);
+exit_extcon:
+	if (udc->edev)
+		extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
+exit_phy:
+	if (udc->udc_phy) {
+		phy_power_off(udc->udc_phy);
+		phy_exit(udc->udc_phy);
+	}
+	return ret;
+}
+
+static int udc_plat_remove(struct platform_device *pdev)
+{
+	struct udc *dev;
+
+	dev = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&dev->gadget);
+	/* gadget driver must not be registered */
+	if (WARN_ON(dev->driver))
+		return 0;
+
+	/* dma pool cleanup */
+	free_dma_pools(dev);
+
+	udc_remove(dev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (dev->drd_wq) {
+		flush_workqueue(dev->drd_wq);
+		destroy_workqueue(dev->drd_wq);
+	}
+
+	phy_power_off(dev->udc_phy);
+	phy_exit(dev->udc_phy);
+	extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
+
+	dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int udc_plat_suspend(struct device *dev)
+{
+	struct udc *udc;
+
+	udc = dev_get_drvdata(dev);
+	stop_udc(udc);
+
+	if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
+		dev_dbg(udc->dev, "device -> idle\n");
+		stop_udc(udc);
+	}
+	phy_power_off(udc->udc_phy);
+	phy_exit(udc->udc_phy);
+
+	return 0;
+}
+
+static int udc_plat_resume(struct device *dev)
+{
+	struct udc *udc;
+	int ret;
+
+	udc = dev_get_drvdata(dev);
+
+	ret = phy_init(udc->udc_phy);
+	if (ret) {
+		dev_err(udc->dev, "UDC phy init failure");
+		return ret;
+	}
+
+	ret = phy_power_on(udc->udc_phy);
+	if (ret) {
+		dev_err(udc->dev, "UDC phy power on failure");
+		phy_exit(udc->udc_phy);
+		return ret;
+	}
+
+	if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
+		dev_dbg(udc->dev, "idle -> device\n");
+		start_udc(udc);
+	}
+
+	return 0;
+}
+static const struct dev_pm_ops udc_plat_pm_ops = {
+	.suspend	= udc_plat_suspend,
+	.resume		= udc_plat_resume,
+};
+#endif
+
+#if defined(CONFIG_OF)
+static const struct of_device_id of_udc_match[] = {
+	{ .compatible = "brcm,ns2-udc", },
+	{ .compatible = "brcm,cygnus-udc", },
+	{ .compatible = "brcm,iproc-udc", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_udc_match);
+#endif
+
+static struct platform_driver udc_plat_driver = {
+	.probe		= udc_plat_probe,
+	.remove		= udc_plat_remove,
+	.driver		= {
+		.name	= "snps-udc-plat",
+		.of_match_table = of_match_ptr(of_udc_match),
+#ifdef CONFIG_PM_SLEEP
+		.pm	= &udc_plat_pm_ops,
+#endif
+	},
+};
+module_platform_driver(udc_plat_driver);
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c
new file mode 100644
index 0000000..7430624
--- /dev/null
+++ b/drivers/usb/gadget/udc/trace.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * trace.c - USB Gadget Framework Trace Support
+ *
+ * Copyright (C) 2016 Intel Corporation
+ * Author: Felipe Balbi <felipe.balbi@linux.intel.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h
new file mode 100644
index 0000000..2d1f68b
--- /dev/null
+++ b/drivers/usb/gadget/udc/trace.h
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * udc.c - Core UDC Framework
+ *
+ * Copyright (C) 2016 Intel Corporation
+ * Author: Felipe Balbi <felipe.balbi@linux.intel.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM gadget
+
+#if !defined(__UDC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __UDC_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/gadget.h>
+
+DECLARE_EVENT_CLASS(udc_log_gadget,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret),
+	TP_STRUCT__entry(
+		__field(enum usb_device_speed, speed)
+		__field(enum usb_device_speed, max_speed)
+		__field(enum usb_device_state, state)
+		__field(unsigned, mA)
+		__field(unsigned, sg_supported)
+		__field(unsigned, is_otg)
+		__field(unsigned, is_a_peripheral)
+		__field(unsigned, b_hnp_enable)
+		__field(unsigned, a_hnp_support)
+		__field(unsigned, hnp_polling_support)
+		__field(unsigned, host_request_flag)
+		__field(unsigned, quirk_ep_out_aligned_size)
+		__field(unsigned, quirk_altset_not_supp)
+		__field(unsigned, quirk_stall_not_supp)
+		__field(unsigned, quirk_zlp_not_supp)
+		__field(unsigned, is_selfpowered)
+		__field(unsigned, deactivated)
+		__field(unsigned, connected)
+		__field(int, ret)
+	),
+	TP_fast_assign(
+		__entry->speed = g->speed;
+		__entry->max_speed = g->max_speed;
+		__entry->state = g->state;
+		__entry->mA = g->mA;
+		__entry->sg_supported = g->sg_supported;
+		__entry->is_otg = g->is_otg;
+		__entry->is_a_peripheral = g->is_a_peripheral;
+		__entry->b_hnp_enable = g->b_hnp_enable;
+		__entry->a_hnp_support = g->a_hnp_support;
+		__entry->hnp_polling_support = g->hnp_polling_support;
+		__entry->host_request_flag = g->host_request_flag;
+		__entry->quirk_ep_out_aligned_size = g->quirk_ep_out_aligned_size;
+		__entry->quirk_altset_not_supp = g->quirk_altset_not_supp;
+		__entry->quirk_stall_not_supp = g->quirk_stall_not_supp;
+		__entry->quirk_zlp_not_supp = g->quirk_zlp_not_supp;
+		__entry->is_selfpowered = g->is_selfpowered;
+		__entry->deactivated = g->deactivated;
+		__entry->connected = g->connected;
+		__entry->ret = ret;
+	),
+	TP_printk("speed %d/%d state %d %dmA [%s%s%s%s%s%s%s%s%s%s%s%s%s%s] --> %d",
+		__entry->speed, __entry->max_speed, __entry->state, __entry->mA,
+		__entry->sg_supported ? "sg:" : "",
+		__entry->is_otg ? "OTG:" : "",
+		__entry->is_a_peripheral ? "a_peripheral:" : "",
+		__entry->b_hnp_enable ? "b_hnp:" : "",
+		__entry->a_hnp_support ? "a_hnp:" : "",
+		__entry->hnp_polling_support ? "hnp_poll:" : "",
+		__entry->host_request_flag ? "hostreq:" : "",
+		__entry->quirk_ep_out_aligned_size ? "out_aligned:" : "",
+		__entry->quirk_altset_not_supp ? "no_altset:" : "",
+		__entry->quirk_stall_not_supp ? "no_stall:" : "",
+		__entry->quirk_zlp_not_supp ? "no_zlp" : "",
+		__entry->is_selfpowered ? "self-powered:" : "bus-powered:",
+		__entry->deactivated ? "deactivated:" : "activated:",
+		__entry->connected ? "connected" : "disconnected",
+		__entry->ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_wakeup,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_set_selfpowered,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_clear_selfpowered,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_connect,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_draw,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_disconnect,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_connect,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_disconnect,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_deactivate,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DEFINE_EVENT(udc_log_gadget, usb_gadget_activate,
+	TP_PROTO(struct usb_gadget *g, int ret),
+	TP_ARGS(g, ret)
+);
+
+DECLARE_EVENT_CLASS(udc_log_ep,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret),
+	TP_STRUCT__entry(
+		__dynamic_array(char, name, UDC_TRACE_STR_MAX)
+		__field(unsigned, maxpacket)
+		__field(unsigned, maxpacket_limit)
+		__field(unsigned, max_streams)
+		__field(unsigned, mult)
+		__field(unsigned, maxburst)
+		__field(u8, address)
+		__field(bool, claimed)
+		__field(bool, enabled)
+		__field(int, ret)
+	),
+	TP_fast_assign(
+		snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
+		__entry->maxpacket = ep->maxpacket;
+		__entry->maxpacket_limit = ep->maxpacket_limit;
+		__entry->max_streams = ep->max_streams;
+		__entry->mult = ep->mult;
+		__entry->maxburst = ep->maxburst;
+		__entry->address = ep->address,
+		__entry->claimed = ep->claimed;
+		__entry->enabled = ep->enabled;
+		__entry->ret = ret;
+	),
+	TP_printk("%s: mps %d/%d streams %d mult %d burst %d addr %02x %s%s --> %d",
+		__get_str(name), __entry->maxpacket, __entry->maxpacket_limit,
+		__entry->max_streams, __entry->mult, __entry->maxburst,
+		__entry->address, __entry->claimed ? "claimed:" : "released:",
+		__entry->enabled ? "enabled" : "disabled", ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_set_maxpacket_limit,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_enable,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_disable,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_set_halt,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_clear_halt,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_set_wedge,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_fifo_status,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DEFINE_EVENT(udc_log_ep, usb_ep_fifo_flush,
+	TP_PROTO(struct usb_ep *ep, int ret),
+	TP_ARGS(ep, ret)
+);
+
+DECLARE_EVENT_CLASS(udc_log_req,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret),
+	TP_STRUCT__entry(
+		__dynamic_array(char, name, UDC_TRACE_STR_MAX)
+		__field(unsigned, length)
+		__field(unsigned, actual)
+		__field(unsigned, num_sgs)
+		__field(unsigned, num_mapped_sgs)
+		__field(unsigned, stream_id)
+		__field(unsigned, no_interrupt)
+		__field(unsigned, zero)
+		__field(unsigned, short_not_ok)
+		__field(int, status)
+		__field(int, ret)
+		__field(struct usb_request *, req)
+	),
+	TP_fast_assign(
+		snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
+		__entry->length = req->length;
+		__entry->actual = req->actual;
+		__entry->num_sgs = req->num_sgs;
+		__entry->num_mapped_sgs = req->num_mapped_sgs;
+		__entry->stream_id = req->stream_id;
+		__entry->no_interrupt = req->no_interrupt;
+		__entry->zero = req->zero;
+		__entry->short_not_ok = req->short_not_ok;
+		__entry->status = req->status;
+		__entry->ret = ret;
+		__entry->req = req;
+	),
+	TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
+		__get_str(name),__entry->req,  __entry->actual, __entry->length,
+		__entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id,
+		__entry->zero ? "Z" : "z",
+		__entry->short_not_ok ? "S" : "s",
+		__entry->no_interrupt ? "i" : "I",
+		__entry->status, __entry->ret
+	)
+);
+
+DEFINE_EVENT(udc_log_req, usb_ep_alloc_request,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret)
+);
+
+DEFINE_EVENT(udc_log_req, usb_ep_free_request,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret)
+);
+
+DEFINE_EVENT(udc_log_req, usb_ep_queue,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret)
+);
+
+DEFINE_EVENT(udc_log_req, usb_ep_dequeue,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret)
+);
+
+DEFINE_EVENT(udc_log_req, usb_gadget_giveback_request,
+	TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
+	TP_ARGS(ep, req, ret)
+);
+
+#endif /* __UDC_TRACE_H */
+
+/* this part has to be here */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
new file mode 100644
index 0000000..6407e43
--- /dev/null
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -0,0 +1,2176 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Xilinx USB peripheral controller driver
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ * Copyright (C) 2010 - 2014 Xilinx, Inc.
+ *
+ * Some parts of this driver code is based on the driver for at91-series
+ * USB peripheral controller (at91_udc.c).
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/prefetch.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Register offsets for the USB device.*/
+#define XUSB_EP0_CONFIG_OFFSET		0x0000  /* EP0 Config Reg Offset */
+#define XUSB_SETUP_PKT_ADDR_OFFSET	0x0080  /* Setup Packet Address */
+#define XUSB_ADDRESS_OFFSET		0x0100  /* Address Register */
+#define XUSB_CONTROL_OFFSET		0x0104  /* Control Register */
+#define XUSB_STATUS_OFFSET		0x0108  /* Status Register */
+#define XUSB_FRAMENUM_OFFSET		0x010C	/* Frame Number Register */
+#define XUSB_IER_OFFSET			0x0110	/* Interrupt Enable Register */
+#define XUSB_BUFFREADY_OFFSET		0x0114	/* Buffer Ready Register */
+#define XUSB_TESTMODE_OFFSET		0x0118	/* Test Mode Register */
+#define XUSB_DMA_RESET_OFFSET		0x0200  /* DMA Soft Reset Register */
+#define XUSB_DMA_CONTROL_OFFSET		0x0204	/* DMA Control Register */
+#define XUSB_DMA_DSAR_ADDR_OFFSET	0x0208	/* DMA source Address Reg */
+#define XUSB_DMA_DDAR_ADDR_OFFSET	0x020C	/* DMA destination Addr Reg */
+#define XUSB_DMA_LENGTH_OFFSET		0x0210	/* DMA Length Register */
+#define XUSB_DMA_STATUS_OFFSET		0x0214	/* DMA Status Register */
+
+/* Endpoint Configuration Space offsets */
+#define XUSB_EP_CFGSTATUS_OFFSET	0x00	/* Endpoint Config Status  */
+#define XUSB_EP_BUF0COUNT_OFFSET	0x08	/* Buffer 0 Count */
+#define XUSB_EP_BUF1COUNT_OFFSET	0x0C	/* Buffer 1 Count */
+
+#define XUSB_CONTROL_USB_READY_MASK	0x80000000 /* USB ready Mask */
+#define XUSB_CONTROL_USB_RMTWAKE_MASK	0x40000000 /* Remote wake up mask */
+
+/* Interrupt register related masks.*/
+#define XUSB_STATUS_GLOBAL_INTR_MASK	0x80000000 /* Global Intr Enable */
+#define XUSB_STATUS_DMADONE_MASK	0x04000000 /* DMA done Mask */
+#define XUSB_STATUS_DMAERR_MASK		0x02000000 /* DMA Error Mask */
+#define XUSB_STATUS_DMABUSY_MASK	0x80000000 /* DMA Error Mask */
+#define XUSB_STATUS_RESUME_MASK		0x01000000 /* USB Resume Mask */
+#define XUSB_STATUS_RESET_MASK		0x00800000 /* USB Reset Mask */
+#define XUSB_STATUS_SUSPEND_MASK	0x00400000 /* USB Suspend Mask */
+#define XUSB_STATUS_DISCONNECT_MASK	0x00200000 /* USB Disconnect Mask */
+#define XUSB_STATUS_FIFO_BUFF_RDY_MASK	0x00100000 /* FIFO Buff Ready Mask */
+#define XUSB_STATUS_FIFO_BUFF_FREE_MASK	0x00080000 /* FIFO Buff Free Mask */
+#define XUSB_STATUS_SETUP_PACKET_MASK	0x00040000 /* Setup packet received */
+#define XUSB_STATUS_EP1_BUFF2_COMP_MASK	0x00000200 /* EP 1 Buff 2 Processed */
+#define XUSB_STATUS_EP1_BUFF1_COMP_MASK	0x00000002 /* EP 1 Buff 1 Processed */
+#define XUSB_STATUS_EP0_BUFF2_COMP_MASK	0x00000100 /* EP 0 Buff 2 Processed */
+#define XUSB_STATUS_EP0_BUFF1_COMP_MASK	0x00000001 /* EP 0 Buff 1 Processed */
+#define XUSB_STATUS_HIGH_SPEED_MASK	0x00010000 /* USB Speed Mask */
+/* Suspend,Reset,Suspend and Disconnect Mask */
+#define XUSB_STATUS_INTR_EVENT_MASK	0x01E00000
+/* Buffers  completion Mask */
+#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK	0x0000FEFF
+/* Mask for buffer 0 and buffer 1 completion for all Endpoints */
+#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK	0x00000101
+#define XUSB_STATUS_EP_BUFF2_SHIFT	8	   /* EP buffer offset */
+
+/* Endpoint Configuration Status Register */
+#define XUSB_EP_CFG_VALID_MASK		0x80000000 /* Endpoint Valid bit */
+#define XUSB_EP_CFG_STALL_MASK		0x40000000 /* Endpoint Stall bit */
+#define XUSB_EP_CFG_DATA_TOGGLE_MASK	0x08000000 /* Endpoint Data toggle */
+
+/* USB device specific global configuration constants.*/
+#define XUSB_MAX_ENDPOINTS		8	/* Maximum End Points */
+#define XUSB_EP_NUMBER_ZERO		0	/* End point Zero */
+/* DPRAM is the source address for DMA transfer */
+#define XUSB_DMA_READ_FROM_DPRAM	0x80000000
+#define XUSB_DMA_DMASR_BUSY		0x80000000 /* DMA busy */
+#define XUSB_DMA_DMASR_ERROR		0x40000000 /* DMA Error */
+/*
+ * When this bit is set, the DMA buffer ready bit is set by hardware upon
+ * DMA transfer completion.
+ */
+#define XUSB_DMA_BRR_CTRL		0x40000000 /* DMA bufready ctrl bit */
+/* Phase States */
+#define SETUP_PHASE			0x0000	/* Setup Phase */
+#define DATA_PHASE			0x0001  /* Data Phase */
+#define STATUS_PHASE			0x0002  /* Status Phase */
+
+#define EP0_MAX_PACKET		64 /* Endpoint 0 maximum packet length */
+#define STATUSBUFF_SIZE		2  /* Buffer size for GET_STATUS command */
+#define EPNAME_SIZE		4  /* Buffer size for endpoint name */
+
+/* container_of helper macros */
+#define to_udc(g)	 container_of((g), struct xusb_udc, gadget)
+#define to_xusb_ep(ep)	 container_of((ep), struct xusb_ep, ep_usb)
+#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req)
+
+/**
+ * struct xusb_req - Xilinx USB device request structure
+ * @usb_req: Linux usb request structure
+ * @queue: usb device request queue
+ * @ep: pointer to xusb_endpoint structure
+ */
+struct xusb_req {
+	struct usb_request usb_req;
+	struct list_head queue;
+	struct xusb_ep *ep;
+};
+
+/**
+ * struct xusb_ep - USB end point structure.
+ * @ep_usb: usb endpoint instance
+ * @queue: endpoint message queue
+ * @udc: xilinx usb peripheral driver instance pointer
+ * @desc: pointer to the usb endpoint descriptor
+ * @rambase: the endpoint buffer address
+ * @offset: the endpoint register offset value
+ * @name: name of the endpoint
+ * @epnumber: endpoint number
+ * @maxpacket: maximum packet size the endpoint can store
+ * @buffer0count: the size of the packet recieved in the first buffer
+ * @buffer1count: the size of the packet received in the second buffer
+ * @curbufnum: current buffer of endpoint that will be processed next
+ * @buffer0ready: the busy state of first buffer
+ * @buffer1ready: the busy state of second buffer
+ * @is_in: endpoint direction (IN or OUT)
+ * @is_iso: endpoint type(isochronous or non isochronous)
+ */
+struct xusb_ep {
+	struct usb_ep ep_usb;
+	struct list_head queue;
+	struct xusb_udc *udc;
+	const struct usb_endpoint_descriptor *desc;
+	u32  rambase;
+	u32  offset;
+	char name[4];
+	u16  epnumber;
+	u16  maxpacket;
+	u16  buffer0count;
+	u16  buffer1count;
+	u8   curbufnum;
+	bool buffer0ready;
+	bool buffer1ready;
+	bool is_in;
+	bool is_iso;
+};
+
+/**
+ * struct xusb_udc -  USB peripheral driver structure
+ * @gadget: USB gadget driver instance
+ * @ep: an array of endpoint structures
+ * @driver: pointer to the usb gadget driver instance
+ * @setup: usb_ctrlrequest structure for control requests
+ * @req: pointer to dummy request for get status command
+ * @dev: pointer to device structure in gadget
+ * @usb_state: device in suspended state or not
+ * @remote_wkp: remote wakeup enabled by host
+ * @setupseqtx: tx status
+ * @setupseqrx: rx status
+ * @addr: the usb device base address
+ * @lock: instance of spinlock
+ * @dma_enabled: flag indicating whether the dma is included in the system
+ * @read_fn: function pointer to read device registers
+ * @write_fn: function pointer to write to device registers
+ */
+struct xusb_udc {
+	struct usb_gadget gadget;
+	struct xusb_ep ep[8];
+	struct usb_gadget_driver *driver;
+	struct usb_ctrlrequest setup;
+	struct xusb_req *req;
+	struct device *dev;
+	u32 usb_state;
+	u32 remote_wkp;
+	u32 setupseqtx;
+	u32 setupseqrx;
+	void __iomem *addr;
+	spinlock_t lock;
+	bool dma_enabled;
+
+	unsigned int (*read_fn)(void __iomem *);
+	void (*write_fn)(void __iomem *, u32, u32);
+};
+
+/* Endpoint buffer start addresses in the core */
+static u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500,
+			  0x1600 };
+
+static const char driver_name[] = "xilinx-udc";
+static const char ep0name[] = "ep0";
+
+/* Control endpoint configuration.*/
+static const struct usb_endpoint_descriptor config_bulk_out_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_OUT,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(EP0_MAX_PACKET),
+};
+
+/**
+ * xudc_write32 - little endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32(void __iomem *addr, u32 offset, u32 val)
+{
+	iowrite32(val, addr + offset);
+}
+
+/**
+ * xudc_read32 - little endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32(void __iomem *addr)
+{
+	return ioread32(addr);
+}
+
+/**
+ * xudc_write32_be - big endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32_be(void __iomem *addr, u32 offset, u32 val)
+{
+	iowrite32be(val, addr + offset);
+}
+
+/**
+ * xudc_read32_be - big endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32_be(void __iomem *addr)
+{
+	return ioread32be(addr);
+}
+
+/**
+ * xudc_wrstatus - Sets up the usb device status stages.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_wrstatus(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+	u32 epcfgreg;
+
+	epcfgreg = udc->read_fn(udc->addr + ep0->offset)|
+				XUSB_EP_CFG_DATA_TOGGLE_MASK;
+	udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+	udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0);
+	udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+}
+
+/**
+ * xudc_epconfig - Configures the given endpoint.
+ * @ep: pointer to the usb device endpoint structure.
+ * @udc: pointer to the usb peripheral controller structure.
+ *
+ * This function configures a specific endpoint with the given configuration
+ * data.
+ */
+static void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc)
+{
+	u32 epcfgreg;
+
+	/*
+	 * Configure the end point direction, type, Max Packet Size and the
+	 * EP buffer location.
+	 */
+	epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) |
+		   (ep->ep_usb.maxpacket << 15) | (ep->rambase));
+	udc->write_fn(udc->addr, ep->offset, epcfgreg);
+
+	/* Set the Buffer count and the Buffer ready bits.*/
+	udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET,
+		      ep->buffer0count);
+	udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET,
+		      ep->buffer1count);
+	if (ep->buffer0ready)
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			      1 << ep->epnumber);
+	if (ep->buffer1ready)
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			      1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+}
+
+/**
+ * xudc_start_dma - Starts DMA transfer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @src: DMA source address.
+ * @dst: DMA destination address.
+ * @length: number of bytes to transfer.
+ *
+ * Return: 0 on success, error code on failure
+ *
+ * This function starts DMA transfer by writing to DMA source,
+ * destination and lenth registers.
+ */
+static int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src,
+			  dma_addr_t dst, u32 length)
+{
+	struct xusb_udc *udc = ep->udc;
+	int rc = 0;
+	u32 timeout = 500;
+	u32 reg;
+
+	/*
+	 * Set the addresses in the DMA source and
+	 * destination registers and then set the length
+	 * into the DMA length register.
+	 */
+	udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src);
+	udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst);
+	udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length);
+
+	/*
+	 * Wait till DMA transaction is complete and
+	 * check whether the DMA transaction was
+	 * successful.
+	 */
+	do {
+		reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET);
+		if (!(reg &  XUSB_DMA_DMASR_BUSY))
+			break;
+
+		/*
+		 * We can't sleep here, because it's also called from
+		 * interrupt context.
+		 */
+		timeout--;
+		if (!timeout) {
+			dev_err(udc->dev, "DMA timeout\n");
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	} while (1);
+
+	if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) &
+			  XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){
+		dev_err(udc->dev, "DMA Error\n");
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+/**
+ * xudc_dma_send - Sends IN data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to data to be sent.
+ * @length: number of bytes to send.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ *	   code on failure.
+ *
+ * This function sends data using DMA.
+ */
+static int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req,
+			 u8 *buffer, u32 length)
+{
+	u32 *eprambase;
+	dma_addr_t src;
+	dma_addr_t dst;
+	struct xusb_udc *udc = ep->udc;
+
+	src = req->usb_req.dma + req->usb_req.actual;
+	if (req->usb_req.length)
+		dma_sync_single_for_device(udc->dev, src,
+					   length, DMA_TO_DEVICE);
+	if (!ep->curbufnum && !ep->buffer0ready) {
+		/* Get the Buffer address and copy the transmit data.*/
+		eprambase = (u32 __force *)(udc->addr + ep->rambase);
+		dst = virt_to_phys(eprambase);
+		udc->write_fn(udc->addr, ep->offset +
+			      XUSB_EP_BUF0COUNT_OFFSET, length);
+		udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+			      XUSB_DMA_BRR_CTRL | (1 << ep->epnumber));
+		ep->buffer0ready = 1;
+		ep->curbufnum = 1;
+	} else if (ep->curbufnum && !ep->buffer1ready) {
+		/* Get the Buffer address and copy the transmit data.*/
+		eprambase = (u32 __force *)(udc->addr + ep->rambase +
+			     ep->ep_usb.maxpacket);
+		dst = virt_to_phys(eprambase);
+		udc->write_fn(udc->addr, ep->offset +
+			      XUSB_EP_BUF1COUNT_OFFSET, length);
+		udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+			      XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber +
+			      XUSB_STATUS_EP_BUFF2_SHIFT)));
+		ep->buffer1ready = 1;
+		ep->curbufnum = 0;
+	} else {
+		/* None of ping pong buffers are ready currently .*/
+		return -EAGAIN;
+	}
+
+	return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_dma_receive - Receives OUT data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to storage buffer of received data.
+ * @length: number of bytes to receive.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ *	   code on failure.
+ *
+ * This function receives data using DMA.
+ */
+static int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req,
+			    u8 *buffer, u32 length)
+{
+	u32 *eprambase;
+	dma_addr_t src;
+	dma_addr_t dst;
+	struct xusb_udc *udc = ep->udc;
+
+	dst = req->usb_req.dma + req->usb_req.actual;
+	if (!ep->curbufnum && !ep->buffer0ready) {
+		/* Get the Buffer address and copy the transmit data */
+		eprambase = (u32 __force *)(udc->addr + ep->rambase);
+		src = virt_to_phys(eprambase);
+		udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+			      XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+			      (1 << ep->epnumber));
+		ep->buffer0ready = 1;
+		ep->curbufnum = 1;
+	} else if (ep->curbufnum && !ep->buffer1ready) {
+		/* Get the Buffer address and copy the transmit data */
+		eprambase = (u32 __force *)(udc->addr +
+			     ep->rambase + ep->ep_usb.maxpacket);
+		src = virt_to_phys(eprambase);
+		udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+			      XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+			      (1 << (ep->epnumber +
+			      XUSB_STATUS_EP_BUFF2_SHIFT)));
+		ep->buffer1ready = 1;
+		ep->curbufnum = 0;
+	} else {
+		/* None of the ping-pong buffers are ready currently */
+		return -EAGAIN;
+	}
+
+	return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_eptxrx - Transmits or receives data to or from an endpoint.
+ * @ep: pointer to the usb endpoint configuration structure.
+ * @req: pointer to the usb request structure.
+ * @bufferptr: pointer to buffer containing the data to be sent.
+ * @bufferlen: The number of data bytes to be sent.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free.
+ *
+ * This function copies the transmit/receive data to/from the end point buffer
+ * and enables the buffer for transmission/reception.
+ */
+static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+		       u8 *bufferptr, u32 bufferlen)
+{
+	u32 *eprambase;
+	u32 bytestosend;
+	int rc = 0;
+	struct xusb_udc *udc = ep->udc;
+
+	bytestosend = bufferlen;
+	if (udc->dma_enabled) {
+		if (ep->is_in)
+			rc = xudc_dma_send(ep, req, bufferptr, bufferlen);
+		else
+			rc = xudc_dma_receive(ep, req, bufferptr, bufferlen);
+		return rc;
+	}
+	/* Put the transmit buffer into the correct ping-pong buffer.*/
+	if (!ep->curbufnum && !ep->buffer0ready) {
+		/* Get the Buffer address and copy the transmit data.*/
+		eprambase = (u32 __force *)(udc->addr + ep->rambase);
+		if (ep->is_in) {
+			memcpy(eprambase, bufferptr, bytestosend);
+			udc->write_fn(udc->addr, ep->offset +
+				      XUSB_EP_BUF0COUNT_OFFSET, bufferlen);
+		} else {
+			memcpy(bufferptr, eprambase, bytestosend);
+		}
+		/*
+		 * Enable the buffer for transmission.
+		 */
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			      1 << ep->epnumber);
+		ep->buffer0ready = 1;
+		ep->curbufnum = 1;
+	} else if (ep->curbufnum && !ep->buffer1ready) {
+		/* Get the Buffer address and copy the transmit data.*/
+		eprambase = (u32 __force *)(udc->addr + ep->rambase +
+			     ep->ep_usb.maxpacket);
+		if (ep->is_in) {
+			memcpy(eprambase, bufferptr, bytestosend);
+			udc->write_fn(udc->addr, ep->offset +
+				      XUSB_EP_BUF1COUNT_OFFSET, bufferlen);
+		} else {
+			memcpy(bufferptr, eprambase, bytestosend);
+		}
+		/*
+		 * Enable the buffer for transmission.
+		 */
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			      1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+		ep->buffer1ready = 1;
+		ep->curbufnum = 0;
+	} else {
+		/* None of the ping-pong buffers are ready currently */
+		return -EAGAIN;
+	}
+	return rc;
+}
+
+/**
+ * xudc_done - Exeutes the endpoint data transfer completion tasks.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @status: Status of the data transfer.
+ *
+ * Deletes the message from the queue and updates data transfer completion
+ * status.
+ */
+static void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status)
+{
+	struct xusb_udc *udc = ep->udc;
+
+	list_del_init(&req->queue);
+
+	if (req->usb_req.status == -EINPROGRESS)
+		req->usb_req.status = status;
+	else
+		status = req->usb_req.status;
+
+	if (status && status != -ESHUTDOWN)
+		dev_dbg(udc->dev, "%s done %p, status %d\n",
+			ep->ep_usb.name, req, status);
+	/* unmap request if DMA is present*/
+	if (udc->dma_enabled && ep->epnumber && req->usb_req.length)
+		usb_gadget_unmap_request(&udc->gadget, &req->usb_req,
+					 ep->is_in);
+
+	if (req->usb_req.complete) {
+		spin_unlock(&udc->lock);
+		req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+		spin_lock(&udc->lock);
+	}
+}
+
+/**
+ * xudc_read_fifo - Reads the data from the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Pulls OUT packet data from the endpoint buffer.
+ */
+static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+	u8 *buf;
+	u32 is_short, count, bufferspace;
+	u8 bufoffset;
+	u8 two_pkts = 0;
+	int ret;
+	int retval = -EAGAIN;
+	struct xusb_udc *udc = ep->udc;
+
+	if (ep->buffer0ready && ep->buffer1ready) {
+		dev_dbg(udc->dev, "Packet NOT ready!\n");
+		return retval;
+	}
+top:
+	if (ep->curbufnum)
+		bufoffset = XUSB_EP_BUF1COUNT_OFFSET;
+	else
+		bufoffset = XUSB_EP_BUF0COUNT_OFFSET;
+
+	count = udc->read_fn(udc->addr + ep->offset + bufoffset);
+
+	if (!ep->buffer0ready && !ep->buffer1ready)
+		two_pkts = 1;
+
+	buf = req->usb_req.buf + req->usb_req.actual;
+	prefetchw(buf);
+	bufferspace = req->usb_req.length - req->usb_req.actual;
+	is_short = count < ep->ep_usb.maxpacket;
+
+	if (unlikely(!bufferspace)) {
+		/*
+		 * This happens when the driver's buffer
+		 * is smaller than what the host sent.
+		 * discard the extra data.
+		 */
+		if (req->usb_req.status != -EOVERFLOW)
+			dev_dbg(udc->dev, "%s overflow %d\n",
+				ep->ep_usb.name, count);
+		req->usb_req.status = -EOVERFLOW;
+		xudc_done(ep, req, -EOVERFLOW);
+		return 0;
+	}
+
+	ret = xudc_eptxrx(ep, req, buf, count);
+	switch (ret) {
+	case 0:
+		req->usb_req.actual += min(count, bufferspace);
+		dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n",
+			ep->ep_usb.name, count, is_short ? "/S" : "", req,
+			req->usb_req.actual, req->usb_req.length);
+		bufferspace -= count;
+		/* Completion */
+		if ((req->usb_req.actual == req->usb_req.length) || is_short) {
+			if (udc->dma_enabled && req->usb_req.length)
+				dma_sync_single_for_cpu(udc->dev,
+							req->usb_req.dma,
+							req->usb_req.actual,
+							DMA_FROM_DEVICE);
+			xudc_done(ep, req, 0);
+			return 0;
+		}
+		if (two_pkts) {
+			two_pkts = 0;
+			goto top;
+		}
+		break;
+	case -EAGAIN:
+		dev_dbg(udc->dev, "receive busy\n");
+		break;
+	case -EINVAL:
+	case -ETIMEDOUT:
+		/* DMA error, dequeue the request */
+		xudc_done(ep, req, -ECONNRESET);
+		retval = 0;
+		break;
+	}
+
+	return retval;
+}
+
+/**
+ * xudc_write_fifo - Writes data into the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Loads endpoint buffer for an IN packet.
+ */
+static int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+	u32 max;
+	u32 length;
+	int ret;
+	int retval = -EAGAIN;
+	struct xusb_udc *udc = ep->udc;
+	int is_last, is_short = 0;
+	u8 *buf;
+
+	max = le16_to_cpu(ep->desc->wMaxPacketSize);
+	buf = req->usb_req.buf + req->usb_req.actual;
+	prefetch(buf);
+	length = req->usb_req.length - req->usb_req.actual;
+	length = min(length, max);
+
+	ret = xudc_eptxrx(ep, req, buf, length);
+	switch (ret) {
+	case 0:
+		req->usb_req.actual += length;
+		if (unlikely(length != max)) {
+			is_last = is_short = 1;
+		} else {
+			if (likely(req->usb_req.length !=
+				   req->usb_req.actual) || req->usb_req.zero)
+				is_last = 0;
+			else
+				is_last = 1;
+		}
+		dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n",
+			__func__, ep->ep_usb.name, length, is_last ? "/L" : "",
+			is_short ? "/S" : "",
+			req->usb_req.length - req->usb_req.actual, req);
+		/* completion */
+		if (is_last) {
+			xudc_done(ep, req, 0);
+			retval = 0;
+		}
+		break;
+	case -EAGAIN:
+		dev_dbg(udc->dev, "Send busy\n");
+		break;
+	case -EINVAL:
+	case -ETIMEDOUT:
+		/* DMA error, dequeue the request */
+		xudc_done(ep, req, -ECONNRESET);
+		retval = 0;
+		break;
+	}
+
+	return retval;
+}
+
+/**
+ * xudc_nuke - Cleans up the data transfer message list.
+ * @ep: pointer to the usb device endpoint structure.
+ * @status: Status of the data transfer.
+ */
+static void xudc_nuke(struct xusb_ep *ep, int status)
+{
+	struct xusb_req *req;
+
+	while (!list_empty(&ep->queue)) {
+		req = list_first_entry(&ep->queue, struct xusb_req, queue);
+		xudc_done(ep, req, status);
+	}
+}
+
+/**
+ * xudc_ep_set_halt - Stalls/unstalls the given endpoint.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @value: value to indicate stall/unstall.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct xusb_ep *ep = to_xusb_ep(_ep);
+	struct xusb_udc *udc;
+	unsigned long flags;
+	u32 epcfgreg;
+
+	if (!_ep || (!ep->desc && ep->epnumber)) {
+		pr_debug("%s: bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+	udc = ep->udc;
+
+	if (ep->is_in && (!list_empty(&ep->queue)) && value) {
+		dev_dbg(udc->dev, "requests pending can't halt\n");
+		return -EAGAIN;
+	}
+
+	if (ep->buffer0ready || ep->buffer1ready) {
+		dev_dbg(udc->dev, "HW buffers busy can't halt\n");
+		return -EAGAIN;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (value) {
+		/* Stall the device.*/
+		epcfgreg = udc->read_fn(udc->addr + ep->offset);
+		epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+		udc->write_fn(udc->addr, ep->offset, epcfgreg);
+	} else {
+		/* Unstall the device.*/
+		epcfgreg = udc->read_fn(udc->addr + ep->offset);
+		epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+		udc->write_fn(udc->addr, ep->offset, epcfgreg);
+		if (ep->epnumber) {
+			/* Reset the toggle bit.*/
+			epcfgreg = udc->read_fn(ep->udc->addr + ep->offset);
+			epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+			udc->write_fn(udc->addr, ep->offset, epcfgreg);
+		}
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @ep: pointer to the xusb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep_enable(struct xusb_ep *ep,
+			    const struct usb_endpoint_descriptor *desc)
+{
+	struct xusb_udc *udc = ep->udc;
+	u32 tmp;
+	u32 epcfg;
+	u32 ier;
+	u16 maxpacket;
+
+	ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0);
+	/* Bit 3...0:endpoint number */
+	ep->epnumber = (desc->bEndpointAddress & 0x0f);
+	ep->desc = desc;
+	ep->ep_usb.desc = desc;
+	tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	switch (tmp) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		dev_dbg(udc->dev, "only one control endpoint\n");
+		/* NON- ISO */
+		ep->is_iso = 0;
+		return -EINVAL;
+	case USB_ENDPOINT_XFER_INT:
+		/* NON- ISO */
+		ep->is_iso = 0;
+		if (maxpacket > 64) {
+			dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+			return -EINVAL;
+		}
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		/* NON- ISO */
+		ep->is_iso = 0;
+		if (!(is_power_of_2(maxpacket) && maxpacket >= 8 &&
+				maxpacket <= 512)) {
+			dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+			return -EINVAL;
+		}
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		/* ISO */
+		ep->is_iso = 1;
+		break;
+	}
+
+	ep->buffer0ready = 0;
+	ep->buffer1ready = 0;
+	ep->curbufnum = 0;
+	ep->rambase = rambase[ep->epnumber];
+	xudc_epconfig(ep, udc);
+
+	dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n",
+		ep->epnumber, maxpacket);
+
+	/* Enable the End point.*/
+	epcfg = udc->read_fn(udc->addr + ep->offset);
+	epcfg |= XUSB_EP_CFG_VALID_MASK;
+	udc->write_fn(udc->addr, ep->offset, epcfg);
+	if (ep->epnumber)
+		ep->rambase <<= 2;
+
+	/* Enable buffer completion interrupts for endpoint */
+	ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+	ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber);
+	udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+	/* for OUT endpoint set buffers ready to receive */
+	if (ep->epnumber && !ep->is_in) {
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			      1 << ep->epnumber);
+		ep->buffer0ready = 1;
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+			     (1 << (ep->epnumber +
+			      XUSB_STATUS_EP_BUFF2_SHIFT)));
+		ep->buffer1ready = 1;
+	}
+
+	return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_enable(struct usb_ep *_ep,
+			  const struct usb_endpoint_descriptor *desc)
+{
+	struct xusb_ep *ep;
+	struct xusb_udc *udc;
+	unsigned long flags;
+	int ret;
+
+	if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_debug("%s: bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+
+	ep = to_xusb_ep(_ep);
+	udc = ep->udc;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_dbg(udc->dev, "bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+	ret = __xudc_ep_enable(ep, desc);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+/**
+ * xudc_ep_disable - Disables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_disable(struct usb_ep *_ep)
+{
+	struct xusb_ep *ep;
+	unsigned long flags;
+	u32 epcfg;
+	struct xusb_udc *udc;
+
+	if (!_ep) {
+		pr_debug("%s: invalid ep\n", __func__);
+		return -EINVAL;
+	}
+
+	ep = to_xusb_ep(_ep);
+	udc = ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	xudc_nuke(ep, -ESHUTDOWN);
+
+	/* Restore the endpoint's pristine config */
+	ep->desc = NULL;
+	ep->ep_usb.desc = NULL;
+
+	dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
+	/* Disable the endpoint.*/
+	epcfg = udc->read_fn(udc->addr + ep->offset);
+	epcfg &= ~XUSB_EP_CFG_VALID_MASK;
+	udc->write_fn(udc->addr, ep->offset, epcfg);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/**
+ * xudc_ep_alloc_request - Initializes the request queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: pointer to request structure on success and a NULL on failure.
+ */
+static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
+						 gfp_t gfp_flags)
+{
+	struct xusb_ep *ep = to_xusb_ep(_ep);
+	struct xusb_req *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->ep = ep;
+	INIT_LIST_HEAD(&req->queue);
+	return &req->usb_req;
+}
+
+/**
+ * xudc_free_request - Releases the request from queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ */
+static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct xusb_req *req = to_xusb_req(_req);
+
+	kfree(req);
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @ep0: pointer to the xusb endpoint 0 structure.
+ * @req: pointer to the xusb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req)
+{
+	struct xusb_udc *udc = ep0->udc;
+	u32 length;
+	u8 *corebuf;
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+		return -EINVAL;
+	}
+	if (!list_empty(&ep0->queue)) {
+		dev_dbg(udc->dev, "%s:ep0 busy\n", __func__);
+		return -EBUSY;
+	}
+
+	req->usb_req.status = -EINPROGRESS;
+	req->usb_req.actual = 0;
+
+	list_add_tail(&req->queue, &ep0->queue);
+
+	if (udc->setup.bRequestType & USB_DIR_IN) {
+		prefetch(req->usb_req.buf);
+		length = req->usb_req.length;
+		corebuf = (void __force *) ((ep0->rambase << 2) +
+			   udc->addr);
+		length = req->usb_req.actual = min_t(u32, length,
+						     EP0_MAX_PACKET);
+		memcpy(corebuf, req->usb_req.buf, length);
+		udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length);
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+	} else {
+		if (udc->setup.wLength) {
+			/* Enable EP0 buffer to receive data */
+			udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+			udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+		} else {
+			xudc_wrstatus(udc);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @_ep: pointer to the usb endpoint 0 structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req,
+			  gfp_t gfp_flags)
+{
+	struct xusb_req *req	= to_xusb_req(_req);
+	struct xusb_ep	*ep0	= to_xusb_ep(_ep);
+	struct xusb_udc *udc	= ep0->udc;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	ret = __xudc_ep0_queue(ep0, req);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return ret;
+}
+
+/**
+ * xudc_ep_queue - Adds the request to endpoint queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+			 gfp_t gfp_flags)
+{
+	struct xusb_req *req = to_xusb_req(_req);
+	struct xusb_ep	*ep  = to_xusb_ep(_ep);
+	struct xusb_udc *udc = ep->udc;
+	int  ret;
+	unsigned long flags;
+
+	if (!ep->desc) {
+		dev_dbg(udc->dev, "%s:queing request to disabled %s\n",
+			__func__, ep->name);
+		return -ESHUTDOWN;
+	}
+
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (udc->dma_enabled) {
+		ret = usb_gadget_map_request(&udc->gadget, &req->usb_req,
+					     ep->is_in);
+		if (ret) {
+			dev_dbg(udc->dev, "gadget_map failed ep%d\n",
+				ep->epnumber);
+			spin_unlock_irqrestore(&udc->lock, flags);
+			return -EAGAIN;
+		}
+	}
+
+	if (list_empty(&ep->queue)) {
+		if (ep->is_in) {
+			dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n");
+			if (!xudc_write_fifo(ep, req))
+				req = NULL;
+		} else {
+			dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n");
+			if (!xudc_read_fifo(ep, req))
+				req = NULL;
+		}
+	}
+
+	if (req != NULL)
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+}
+
+/**
+ * xudc_ep_dequeue - Removes the request from the queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct xusb_ep *ep	= to_xusb_ep(_ep);
+	struct xusb_req *req	= to_xusb_req(_req);
+	struct xusb_udc *udc	= ep->udc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->usb_req == _req)
+			break;
+	}
+	if (&req->usb_req != _req) {
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+	xudc_done(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * xudc_ep0_enable - Enables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 enable should not be called by gadget layer.
+ */
+static int xudc_ep0_enable(struct usb_ep *ep,
+			   const struct usb_endpoint_descriptor *desc)
+{
+	return -EINVAL;
+}
+
+/**
+ * xudc_ep0_disable - Disables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 disable should not be called by gadget layer.
+ */
+static int xudc_ep0_disable(struct usb_ep *ep)
+{
+	return -EINVAL;
+}
+
+static const struct usb_ep_ops xusb_ep0_ops = {
+	.enable		= xudc_ep0_enable,
+	.disable	= xudc_ep0_disable,
+	.alloc_request	= xudc_ep_alloc_request,
+	.free_request	= xudc_free_request,
+	.queue		= xudc_ep0_queue,
+	.dequeue	= xudc_ep_dequeue,
+	.set_halt	= xudc_ep_set_halt,
+};
+
+static const struct usb_ep_ops xusb_ep_ops = {
+	.enable		= xudc_ep_enable,
+	.disable	= xudc_ep_disable,
+	.alloc_request	= xudc_ep_alloc_request,
+	.free_request	= xudc_free_request,
+	.queue		= xudc_ep_queue,
+	.dequeue	= xudc_ep_dequeue,
+	.set_halt	= xudc_ep_set_halt,
+};
+
+/**
+ * xudc_get_frame - Reads the current usb frame number.
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: current frame number for success and error value on failure.
+ */
+static int xudc_get_frame(struct usb_gadget *gadget)
+{
+	struct xusb_udc *udc;
+	int frame;
+
+	if (!gadget)
+		return -ENODEV;
+
+	udc = to_udc(gadget);
+	frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET);
+	return frame;
+}
+
+/**
+ * xudc_wakeup - Send remote wakeup signal to host
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: 0 on success and error on failure
+ */
+static int xudc_wakeup(struct usb_gadget *gadget)
+{
+	struct xusb_udc *udc = to_udc(gadget);
+	u32 crtlreg;
+	int status = -EINVAL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* Remote wake up not enabled by host */
+	if (!udc->remote_wkp)
+		goto done;
+
+	crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+	crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK;
+	/* set remote wake up bit */
+	udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+	/*
+	 * wait for a while and reset remote wake up bit since this bit
+	 * is not cleared by HW after sending remote wakeup to host.
+	 */
+	mdelay(2);
+
+	crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK;
+	udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+	status = 0;
+done:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return status;
+}
+
+/**
+ * xudc_pullup - start/stop USB traffic
+ * @gadget: pointer to the usb gadget structure.
+ * @is_on: flag to start or stop
+ *
+ * Return: 0 always
+ *
+ * This function starts/stops SIE engine of IP based on is_on.
+ */
+static int xudc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct xusb_udc *udc = to_udc(gadget);
+	unsigned long flags;
+	u32 crtlreg;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+	if (is_on)
+		crtlreg |= XUSB_CONTROL_USB_READY_MASK;
+	else
+		crtlreg &= ~XUSB_CONTROL_USB_READY_MASK;
+
+	udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * xudc_eps_init - initialize endpoints.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_eps_init(struct xusb_udc *udc)
+{
+	u32 ep_number;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+	for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) {
+		struct xusb_ep *ep = &udc->ep[ep_number];
+
+		if (ep_number) {
+			list_add_tail(&ep->ep_usb.ep_list,
+				      &udc->gadget.ep_list);
+			usb_ep_set_maxpacket_limit(&ep->ep_usb,
+						  (unsigned short) ~0);
+			snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number);
+			ep->ep_usb.name = ep->name;
+			ep->ep_usb.ops = &xusb_ep_ops;
+
+			ep->ep_usb.caps.type_iso = true;
+			ep->ep_usb.caps.type_bulk = true;
+			ep->ep_usb.caps.type_int = true;
+		} else {
+			ep->ep_usb.name = ep0name;
+			usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET);
+			ep->ep_usb.ops = &xusb_ep0_ops;
+
+			ep->ep_usb.caps.type_control = true;
+		}
+
+		ep->ep_usb.caps.dir_in = true;
+		ep->ep_usb.caps.dir_out = true;
+
+		ep->udc = udc;
+		ep->epnumber = ep_number;
+		ep->desc = NULL;
+		/*
+		 * The configuration register address offset between
+		 * each endpoint is 0x10.
+		 */
+		ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10);
+		ep->is_in = 0;
+		ep->is_iso = 0;
+		ep->maxpacket = 0;
+		xudc_epconfig(ep, udc);
+
+		/* Initialize one queue per endpoint */
+		INIT_LIST_HEAD(&ep->queue);
+	}
+}
+
+/**
+ * xudc_stop_activity - Stops any further activity on the device.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_stop_activity(struct xusb_udc *udc)
+{
+	int i;
+	struct xusb_ep *ep;
+
+	for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+		xudc_nuke(ep, -ESHUTDOWN);
+	}
+}
+
+/**
+ * xudc_start - Starts the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to gadget driver structure
+ *
+ * Return: zero on success and error on failure
+ */
+static int xudc_start(struct usb_gadget *gadget,
+		      struct usb_gadget_driver *driver)
+{
+	struct xusb_udc *udc	= to_udc(gadget);
+	struct xusb_ep *ep0	= &udc->ep[XUSB_EP_NUMBER_ZERO];
+	const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (udc->driver) {
+		dev_err(udc->dev, "%s is already bound to %s\n",
+			udc->gadget.name, udc->driver->driver.name);
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* hook up the driver */
+	udc->driver = driver;
+	udc->gadget.speed = driver->max_speed;
+
+	/* Enable the control endpoint. */
+	ret = __xudc_ep_enable(ep0, desc);
+
+	/* Set device address and remote wakeup to 0 */
+	udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+	udc->remote_wkp = 0;
+err:
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return ret;
+}
+
+/**
+ * xudc_stop - stops the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to usb gadget driver structure
+ *
+ * Return: zero always
+ */
+static int xudc_stop(struct usb_gadget *gadget)
+{
+	struct xusb_udc *udc = to_udc(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->driver = NULL;
+
+	/* Set device address and remote wakeup to 0 */
+	udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+	udc->remote_wkp = 0;
+
+	xudc_stop_activity(udc);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops xusb_udc_ops = {
+	.get_frame	= xudc_get_frame,
+	.wakeup		= xudc_wakeup,
+	.pullup		= xudc_pullup,
+	.udc_start	= xudc_start,
+	.udc_stop	= xudc_stop,
+};
+
+/**
+ * xudc_clear_stall_all_ep - clears stall of every endpoint.
+ * @udc: pointer to the udc structure.
+ */
+static void xudc_clear_stall_all_ep(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep;
+	u32 epcfgreg;
+	int i;
+
+	for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+		ep = &udc->ep[i];
+		epcfgreg = udc->read_fn(udc->addr + ep->offset);
+		epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+		udc->write_fn(udc->addr, ep->offset, epcfgreg);
+		if (ep->epnumber) {
+			/* Reset the toggle bit.*/
+			epcfgreg = udc->read_fn(udc->addr + ep->offset);
+			epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+			udc->write_fn(udc->addr, ep->offset, epcfgreg);
+		}
+	}
+}
+
+/**
+ * xudc_startup_handler - The usb device controller interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus: The mask value containing the interrupt sources.
+ *
+ * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts.
+ */
+static void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+	u32 intrreg;
+
+	if (intrstatus & XUSB_STATUS_RESET_MASK) {
+
+		dev_dbg(udc->dev, "Reset\n");
+
+		if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK)
+			udc->gadget.speed = USB_SPEED_HIGH;
+		else
+			udc->gadget.speed = USB_SPEED_FULL;
+
+		xudc_stop_activity(udc);
+		xudc_clear_stall_all_ep(udc);
+		udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+		/* Set device address and remote wakeup to 0 */
+		udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+		udc->remote_wkp = 0;
+
+		/* Enable the suspend, resume and disconnect */
+		intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+		intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK |
+			   XUSB_STATUS_DISCONNECT_MASK;
+		udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+	}
+	if (intrstatus & XUSB_STATUS_SUSPEND_MASK) {
+
+		dev_dbg(udc->dev, "Suspend\n");
+
+		/* Enable the reset, resume and disconnect */
+		intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+		intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+			   XUSB_STATUS_DISCONNECT_MASK;
+		udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+		udc->usb_state = USB_STATE_SUSPENDED;
+
+		if (udc->driver->suspend) {
+			spin_unlock(&udc->lock);
+			udc->driver->suspend(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+	if (intrstatus & XUSB_STATUS_RESUME_MASK) {
+		bool condition = (udc->usb_state != USB_STATE_SUSPENDED);
+
+		dev_WARN_ONCE(udc->dev, condition,
+				"Resume IRQ while not suspended\n");
+
+		dev_dbg(udc->dev, "Resume\n");
+
+		/* Enable the reset, suspend and disconnect */
+		intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+		intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK |
+			   XUSB_STATUS_DISCONNECT_MASK;
+		udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+		udc->usb_state = 0;
+
+		if (udc->driver->resume) {
+			spin_unlock(&udc->lock);
+			udc->driver->resume(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+	if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) {
+
+		dev_dbg(udc->dev, "Disconnect\n");
+
+		/* Enable the reset, resume and suspend */
+		intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+		intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+			   XUSB_STATUS_SUSPEND_MASK;
+		udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+		if (udc->driver && udc->driver->disconnect) {
+			spin_unlock(&udc->lock);
+			udc->driver->disconnect(&udc->gadget);
+			spin_lock(&udc->lock);
+		}
+	}
+}
+
+/**
+ * xudc_ep0_stall - Stall endpoint zero.
+ * @udc: pointer to the udc structure.
+ *
+ * This function stalls endpoint zero.
+ */
+static void xudc_ep0_stall(struct xusb_udc *udc)
+{
+	u32 epcfgreg;
+	struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+
+	epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+	epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+	udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+}
+
+/**
+ * xudc_setaddress - executes SET_ADDRESS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB SET_ADDRESS command
+ */
+static void xudc_setaddress(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0	= &udc->ep[0];
+	struct xusb_req *req	= udc->req;
+	int ret;
+
+	req->usb_req.length = 0;
+	ret = __xudc_ep0_queue(ep0, req);
+	if (ret == 0)
+		return;
+
+	dev_err(udc->dev, "Can't respond to SET ADDRESS request\n");
+	xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_getstatus - executes GET_STATUS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB GET_STATUS command
+ */
+static void xudc_getstatus(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0	= &udc->ep[0];
+	struct xusb_req *req	= udc->req;
+	struct xusb_ep *target_ep;
+	u16 status = 0;
+	u32 epcfgreg;
+	int epnum;
+	u32 halt;
+	int ret;
+
+	switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		/* Get device status */
+		status = 1 << USB_DEVICE_SELF_POWERED;
+		if (udc->remote_wkp)
+			status |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+		target_ep = &udc->ep[epnum];
+		epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+		halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
+		if (udc->setup.wIndex & USB_DIR_IN) {
+			if (!target_ep->is_in)
+				goto stall;
+		} else {
+			if (target_ep->is_in)
+				goto stall;
+		}
+		if (halt)
+			status = 1 << USB_ENDPOINT_HALT;
+		break;
+	default:
+		goto stall;
+	}
+
+	req->usb_req.length = 2;
+	*(u16 *)req->usb_req.buf = cpu_to_le16(status);
+	ret = __xudc_ep0_queue(ep0, req);
+	if (ret == 0)
+		return;
+stall:
+	dev_err(udc->dev, "Can't respond to getstatus request\n");
+	xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_set_clear_feature - Executes the set feature and clear feature commands.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Processes the SET_FEATURE and CLEAR_FEATURE commands.
+ */
+static void xudc_set_clear_feature(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0	= &udc->ep[0];
+	struct xusb_req *req	= udc->req;
+	struct xusb_ep *target_ep;
+	u8 endpoint;
+	u8 outinbit;
+	u32 epcfgreg;
+	int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0);
+	int ret;
+
+	switch (udc->setup.bRequestType) {
+	case USB_RECIP_DEVICE:
+		switch (udc->setup.wValue) {
+		case USB_DEVICE_TEST_MODE:
+			/*
+			 * The Test Mode will be executed
+			 * after the status phase.
+			 */
+			break;
+		case USB_DEVICE_REMOTE_WAKEUP:
+			if (flag)
+				udc->remote_wkp = 1;
+			else
+				udc->remote_wkp = 0;
+			break;
+		default:
+			xudc_ep0_stall(udc);
+			break;
+		}
+		break;
+	case USB_RECIP_ENDPOINT:
+		if (!udc->setup.wValue) {
+			endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+			target_ep = &udc->ep[endpoint];
+			outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
+			outinbit = outinbit >> 7;
+
+			/* Make sure direction matches.*/
+			if (outinbit != target_ep->is_in) {
+				xudc_ep0_stall(udc);
+				return;
+			}
+			epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+			if (!endpoint) {
+				/* Clear the stall.*/
+				epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+				udc->write_fn(udc->addr,
+					      target_ep->offset, epcfgreg);
+			} else {
+				if (flag) {
+					epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+					udc->write_fn(udc->addr,
+						      target_ep->offset,
+						      epcfgreg);
+				} else {
+					/* Unstall the endpoint.*/
+					epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK |
+						XUSB_EP_CFG_DATA_TOGGLE_MASK);
+					udc->write_fn(udc->addr,
+						      target_ep->offset,
+						      epcfgreg);
+				}
+			}
+		}
+		break;
+	default:
+		xudc_ep0_stall(udc);
+		return;
+	}
+
+	req->usb_req.length = 0;
+	ret = __xudc_ep0_queue(ep0, req);
+	if (ret == 0)
+		return;
+
+	dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+	xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_handle_setup - Processes the setup packet.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Process setup packet and delegate to gadget layer.
+ */
+static void xudc_handle_setup(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0 = &udc->ep[0];
+	struct usb_ctrlrequest setup;
+	u32 *ep0rambase;
+
+	/* Load up the chapter 9 command buffer.*/
+	ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET);
+	memcpy(&setup, ep0rambase, 8);
+
+	udc->setup = setup;
+	udc->setup.wValue = cpu_to_le16(setup.wValue);
+	udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+	udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+	/* Clear previous requests */
+	xudc_nuke(ep0, -ECONNRESET);
+
+	if (udc->setup.bRequestType & USB_DIR_IN) {
+		/* Execute the get command.*/
+		udc->setupseqrx = STATUS_PHASE;
+		udc->setupseqtx = DATA_PHASE;
+	} else {
+		/* Execute the put command.*/
+		udc->setupseqrx = DATA_PHASE;
+		udc->setupseqtx = STATUS_PHASE;
+	}
+
+	switch (udc->setup.bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase form udc */
+		if ((udc->setup.bRequestType &
+				(USB_DIR_IN | USB_TYPE_MASK)) !=
+				(USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		xudc_getstatus(udc);
+		return;
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (udc->setup.bRequestType != (USB_DIR_OUT |
+				USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		xudc_setaddress(udc);
+		return;
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Requests with no data phase, status phase from udc */
+		if ((udc->setup.bRequestType & USB_TYPE_MASK)
+				!= USB_TYPE_STANDARD)
+			break;
+		xudc_set_clear_feature(udc);
+		return;
+	default:
+		break;
+	}
+
+	spin_unlock(&udc->lock);
+	if (udc->driver->setup(&udc->gadget, &setup) < 0)
+		xudc_ep0_stall(udc);
+	spin_lock(&udc->lock);
+}
+
+/**
+ * xudc_ep0_out - Processes the endpoint 0 OUT token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_out(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0 = &udc->ep[0];
+	struct xusb_req *req;
+	u8 *ep0rambase;
+	unsigned int bytes_to_rx;
+	void *buffer;
+
+	req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+
+	switch (udc->setupseqrx) {
+	case STATUS_PHASE:
+		/*
+		 * This resets both state machines for the next
+		 * Setup packet.
+		 */
+		udc->setupseqrx = SETUP_PHASE;
+		udc->setupseqtx = SETUP_PHASE;
+		req->usb_req.actual = req->usb_req.length;
+		xudc_done(ep0, req, 0);
+		break;
+	case DATA_PHASE:
+		bytes_to_rx = udc->read_fn(udc->addr +
+					   XUSB_EP_BUF0COUNT_OFFSET);
+		/* Copy the data to be received from the DPRAM. */
+		ep0rambase = (u8 __force *) (udc->addr +
+			     (ep0->rambase << 2));
+		buffer = req->usb_req.buf + req->usb_req.actual;
+		req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
+		memcpy(buffer, ep0rambase, bytes_to_rx);
+
+		if (req->usb_req.length == req->usb_req.actual) {
+			/* Data transfer completed get ready for Status stage */
+			xudc_wrstatus(udc);
+		} else {
+			/* Enable EP0 buffer to receive data */
+			udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+			udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * xudc_ep0_in - Processes the endpoint 0 IN token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_in(struct xusb_udc *udc)
+{
+	struct xusb_ep *ep0 = &udc->ep[0];
+	struct xusb_req *req;
+	unsigned int bytes_to_tx;
+	void *buffer;
+	u32 epcfgreg;
+	u16 count = 0;
+	u16 length;
+	u8 *ep0rambase;
+	u8 test_mode = udc->setup.wIndex >> 8;
+
+	req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+	bytes_to_tx = req->usb_req.length - req->usb_req.actual;
+
+	switch (udc->setupseqtx) {
+	case STATUS_PHASE:
+		switch (udc->setup.bRequest) {
+		case USB_REQ_SET_ADDRESS:
+			/* Set the address of the device.*/
+			udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET,
+				      udc->setup.wValue);
+			break;
+		case USB_REQ_SET_FEATURE:
+			if (udc->setup.bRequestType ==
+					USB_RECIP_DEVICE) {
+				if (udc->setup.wValue ==
+						USB_DEVICE_TEST_MODE)
+					udc->write_fn(udc->addr,
+						      XUSB_TESTMODE_OFFSET,
+						      test_mode);
+			}
+			break;
+		}
+		req->usb_req.actual = req->usb_req.length;
+		xudc_done(ep0, req, 0);
+		break;
+	case DATA_PHASE:
+		if (!bytes_to_tx) {
+			/*
+			 * We're done with data transfer, next
+			 * will be zero length OUT with data toggle of
+			 * 1. Setup data_toggle.
+			 */
+			epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+			epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK;
+			udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+			udc->setupseqtx = STATUS_PHASE;
+		} else {
+			length = count = min_t(u32, bytes_to_tx,
+					       EP0_MAX_PACKET);
+			/* Copy the data to be transmitted into the DPRAM. */
+			ep0rambase = (u8 __force *) (udc->addr +
+				     (ep0->rambase << 2));
+			buffer = req->usb_req.buf + req->usb_req.actual;
+			req->usb_req.actual = req->usb_req.actual + length;
+			memcpy(ep0rambase, buffer, length);
+		}
+		udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count);
+		udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus:	It's the mask value for the interrupt sources on endpoint 0.
+ *
+ * Processes the commands received during enumeration phase.
+ */
+static void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+
+	if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) {
+		xudc_handle_setup(udc);
+	} else {
+		if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK)
+			xudc_ep0_out(udc);
+		else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK)
+			xudc_ep0_in(udc);
+	}
+}
+
+/**
+ * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @epnum: End point number for which the interrupt is to be processed
+ * @intrstatus:	mask value for interrupt sources of endpoints other
+ *		than endpoint 0.
+ *
+ * Processes the buffer completion interrupts.
+ */
+static void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum,
+				    u32 intrstatus)
+{
+
+	struct xusb_req *req;
+	struct xusb_ep *ep;
+
+	ep = &udc->ep[epnum];
+	/* Process the End point interrupts.*/
+	if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum))
+		ep->buffer0ready = 0;
+	if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum))
+		ep->buffer1ready = 0;
+
+	if (list_empty(&ep->queue))
+		return;
+
+	req = list_first_entry(&ep->queue, struct xusb_req, queue);
+
+	if (ep->is_in)
+		xudc_write_fifo(ep, req);
+	else
+		xudc_read_fifo(ep, req);
+}
+
+/**
+ * xudc_irq - The main interrupt handler.
+ * @irq: The interrupt number.
+ * @_udc: pointer to the usb device controller structure.
+ *
+ * Return: IRQ_HANDLED after the interrupt is handled.
+ */
+static irqreturn_t xudc_irq(int irq, void *_udc)
+{
+	struct xusb_udc *udc = _udc;
+	u32 intrstatus;
+	u32 ier;
+	u8 index;
+	u32 bufintr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/*
+	 * Event interrupts are level sensitive hence first disable
+	 * IER, read ISR and figure out active interrupts.
+	 */
+	ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+	ier &= ~XUSB_STATUS_INTR_EVENT_MASK;
+	udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+	/* Read the Interrupt Status Register.*/
+	intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET);
+
+	/* Call the handler for the event interrupt.*/
+	if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) {
+		/*
+		 * Check if there is any action to be done for :
+		 * - USB Reset received {XUSB_STATUS_RESET_MASK}
+		 * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK}
+		 * - USB Resume received {XUSB_STATUS_RESUME_MASK}
+		 * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK}
+		 */
+		xudc_startup_handler(udc, intrstatus);
+	}
+
+	/* Check the buffer completion interrupts */
+	if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) {
+		/* Enable Reset, Suspend, Resume and Disconnect  */
+		ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+		ier |= XUSB_STATUS_INTR_EVENT_MASK;
+		udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+		if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK)
+			xudc_ctrl_ep_handler(udc, intrstatus);
+
+		for (index = 1; index < 8; index++) {
+			bufintr = ((intrstatus &
+				  (XUSB_STATUS_EP1_BUFF1_COMP_MASK <<
+				  (index - 1))) || (intrstatus &
+				  (XUSB_STATUS_EP1_BUFF2_COMP_MASK <<
+				  (index - 1))));
+			if (bufintr) {
+				xudc_nonctrl_ep_handler(udc, index,
+							intrstatus);
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/**
+ * xudc_probe - The device probe function for driver initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	struct xusb_udc *udc;
+	int irq;
+	int ret;
+	u32 ier;
+	u8 *buff;
+
+	udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	/* Create a dummy request for GET_STATUS, SET_ADDRESS */
+	udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req),
+				GFP_KERNEL);
+	if (!udc->req)
+		return -ENOMEM;
+
+	buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL);
+	if (!buff)
+		return -ENOMEM;
+
+	udc->req->usb_req.buf = buff;
+
+	/* Map the registers */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	udc->addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(udc->addr))
+		return PTR_ERR(udc->addr);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "unable to get irq\n");
+		return irq;
+	}
+	ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0,
+			       dev_name(&pdev->dev), udc);
+	if (ret < 0) {
+		dev_dbg(&pdev->dev, "unable to request irq %d", irq);
+		goto fail;
+	}
+
+	udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma");
+
+	/* Setup gadget structure */
+	udc->gadget.ops = &xusb_udc_ops;
+	udc->gadget.max_speed = USB_SPEED_HIGH;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb;
+	udc->gadget.name = driver_name;
+
+	spin_lock_init(&udc->lock);
+
+	/* Check for IP endianness */
+	udc->write_fn = xudc_write32_be;
+	udc->read_fn = xudc_read32_be;
+	udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J);
+	if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET))
+			!= TEST_J) {
+		udc->write_fn = xudc_write32;
+		udc->read_fn = xudc_read32;
+	}
+	udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+	xudc_eps_init(udc);
+
+	/* Set device address to 0.*/
+	udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+
+	ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+	if (ret)
+		goto fail;
+
+	udc->dev = &udc->gadget.dev;
+
+	/* Enable the interrupts.*/
+	ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK |
+	      XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK |
+	      XUSB_STATUS_SETUP_PACKET_MASK |
+	      XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK;
+
+	udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+	platform_set_drvdata(pdev, udc);
+
+	dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to %p %s\n",
+		 driver_name, (u32)res->start, udc->addr,
+		 udc->dma_enabled ? "with DMA" : "without DMA");
+
+	return 0;
+fail:
+	dev_err(&pdev->dev, "probe failed, %d\n", ret);
+	return ret;
+}
+
+/**
+ * xudc_remove - Releases the resources allocated during the initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 always
+ */
+static int xudc_remove(struct platform_device *pdev)
+{
+	struct xusb_udc *udc = platform_get_drvdata(pdev);
+
+	usb_del_gadget_udc(&udc->gadget);
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id usb_of_match[] = {
+	{ .compatible = "xlnx,usb2-device-4.00.a", },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, usb_of_match);
+
+static struct platform_driver xudc_driver = {
+	.driver = {
+		.name = driver_name,
+		.of_match_table = usb_of_match,
+	},
+	.probe = xudc_probe,
+	.remove = xudc_remove,
+};
+
+module_platform_driver(xudc_driver);
+
+MODULE_DESCRIPTION("Xilinx udc driver");
+MODULE_AUTHOR("Xilinx, Inc");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
new file mode 100644
index 0000000..7c24d1c
--- /dev/null
+++ b/drivers/usb/gadget/usbstring.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2003 David Brownell
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/nls.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor 
+ * @table: of c strings encoded using UTF-8
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes, must be 16-bit aligned
+ *
+ * Finds the UTF-8 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll probably
+ * "switch (wIndex) { ... }"  in your ep0 string descriptor logic,
+ * using this routine after choosing which set of UTF-8 strings to use.
+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
+ * characters (which are also widely used in C strings).
+ */
+int
+usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf)
+{
+	struct usb_string	*s;
+	int			len;
+
+	/* descriptor 0 has the language id */
+	if (id == 0) {
+		buf [0] = 4;
+		buf [1] = USB_DT_STRING;
+		buf [2] = (u8) table->language;
+		buf [3] = (u8) (table->language >> 8);
+		return 4;
+	}
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == id)
+			break;
+
+	/* unrecognized: stall. */
+	if (!s || !s->s)
+		return -EINVAL;
+
+	/* string descriptors have length, tag, then UTF16-LE text */
+	len = min ((size_t) 126, strlen (s->s));
+	len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN,
+			(wchar_t *) &buf[2], 126);
+	if (len < 0)
+		return -EINVAL;
+	buf [0] = (len + 1) * 2;
+	buf [1] = USB_DT_STRING;
+	return buf [0];
+}
+EXPORT_SYMBOL_GPL(usb_gadget_get_string);