Merge tag 'v3.17-rc4' into next
authorFelipe Balbi <balbi@ti.com>
Mon, 8 Sep 2014 19:39:01 +0000 (14:39 -0500)
committerFelipe Balbi <balbi@ti.com>
Mon, 8 Sep 2014 19:39:01 +0000 (14:39 -0500)
Merge Linux 3.17-rc4 here so we have all the latest
fixes on next too. This also cleans up a few conflicts
when applying patches.

Signed-off-by: Felipe Balbi <balbi@ti.com>
Conflicts:
drivers/usb/gadget/Makefile
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/legacy/Makefile
drivers/usb/phy/phy-samsung-usb.h

68 files changed:
Documentation/ABI/testing/configfs-usb-gadget-uac1 [new file with mode: 0644]
Documentation/ABI/testing/configfs-usb-gadget-uac2 [new file with mode: 0644]
Documentation/devicetree/bindings/usb/dwc3-st.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/mxs-phy.txt
Documentation/devicetree/bindings/usb/renesas_usbhs.txt [new file with mode: 0644]
MAINTAINERS
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/debug.c [new file with mode: 0644]
drivers/usb/dwc3/debug.h
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/dwc3-omap.c
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/dwc3-st.c [new file with mode: 0644]
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/dwc3/io.h
drivers/usb/dwc3/trace.c [new file with mode: 0644]
drivers/usb/dwc3/trace.h [new file with mode: 0644]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/composite.c
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_acm.c
drivers/usb/gadget/function/f_loopback.c
drivers/usb/gadget/function/f_mass_storage.c
drivers/usb/gadget/function/f_obex.c
drivers/usb/gadget/function/f_serial.c
drivers/usb/gadget/function/f_sourcesink.c
drivers/usb/gadget/function/f_uac1.c
drivers/usb/gadget/function/f_uac2.c
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/f_uvc.h
drivers/usb/gadget/function/g_zero.h
drivers/usb/gadget/function/u_serial.c
drivers/usb/gadget/function/u_uac1.c
drivers/usb/gadget/function/u_uac1.h
drivers/usb/gadget/function/u_uac2.h [new file with mode: 0644]
drivers/usb/gadget/function/uvc.h
drivers/usb/gadget/function/uvc_queue.c
drivers/usb/gadget/function/uvc_v4l2.c
drivers/usb/gadget/function/uvc_video.c
drivers/usb/gadget/legacy/Kconfig
drivers/usb/gadget/legacy/Makefile
drivers/usb/gadget/legacy/audio.c
drivers/usb/gadget/legacy/webcam.c
drivers/usb/gadget/legacy/zero.c
drivers/usb/gadget/udc/Kconfig
drivers/usb/gadget/udc/r8a66597-udc.c
drivers/usb/misc/usbtest.c
drivers/usb/musb/musb_cppi41.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-msm-usb.c
drivers/usb/phy/phy-mxs-usb.c
drivers/usb/phy/phy-samsung-usb.c [deleted file]
drivers/usb/phy/phy-samsung-usb.h [deleted file]
drivers/usb/phy/phy-samsung-usb2.c [deleted file]
drivers/usb/phy/phy-samsung-usb3.c [deleted file]
drivers/usb/phy/phy-twl6030-usb.c
drivers/usb/renesas_usbhs/common.c
include/linux/platform_data/samsung-usbphy.h [deleted file]
include/linux/usb/gadget.h
include/uapi/linux/usb/functionfs.h
tools/usb/ffs-test.c

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
new file mode 100644 (file)
index 0000000..8ba9a12
--- /dev/null
@@ -0,0 +1,12 @@
+What:          /config/usb-gadget/gadget/functions/uac1.name
+Date:          Sep 2014
+KernelVersion: 3.18
+Description:
+               The attributes:
+
+               audio_buf_size - audio buffer size
+               fn_cap - capture pcm device file name
+               fn_cntl - control device file name
+               fn_play - playback pcm device file name
+               req_buf_size - ISO OUT endpoint request buffer size
+               req_count - ISO OUT endpoint request count
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
new file mode 100644 (file)
index 0000000..2bfdd4e
--- /dev/null
@@ -0,0 +1,12 @@
+What:          /config/usb-gadget/gadget/functions/uac2.name
+Date:          Sep 2014
+KernelVersion: 3.18
+Description:
+               The attributes:
+
+               c_chmask - capture channel mask
+               c_srate - capture sampling rate
+               c_ssize - capture sample size (bytes)
+               p_chmask - playback channel mask
+               p_srate - playback sampling rate
+               p_ssize - playback sample size (bytes)
diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt
new file mode 100644 (file)
index 0000000..f9d7025
--- /dev/null
@@ -0,0 +1,68 @@
+ST DWC3 glue logic
+
+This file documents the parameters for the dwc3-st driver.
+This driver controls the glue logic used to configure the dwc3 core on
+STiH407 based platforms.
+
+Required properties:
+ - compatible  : must be "st,stih407-dwc3"
+ - reg         : glue logic base address and USB syscfg ctrl register offset
+ - reg-names   : should be "reg-glue" and "syscfg-reg"
+ - st,syscon   : should be phandle to system configuration node which
+                 encompasses the glue registers
+ - resets      : list of phandle and reset specifier pairs. There should be two entries, one
+                 for the powerdown and softreset lines of the usb3 IP
+ - reset-names : list of reset signal names. Names should be "powerdown" and "softreset"
+See: Documentation/devicetree/bindings/reset/st,sti-powerdown.txt
+See: Documentation/devicetree/bindings/reset/reset.txt
+
+ - #address-cells, #size-cells : should be '1' if the device has sub-nodes
+   with 'reg' property
+
+ - pinctl-names        : A pinctrl state named "default" must be defined
+See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+ - pinctrl-0   : Pin control group
+See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+ - ranges      : allows valid 1:1 translation between child's address space and
+                 parent's address space
+
+Sub-nodes:
+The dwc3 core should be added as subnode to ST DWC3 glue as shown in the
+example below. The DT binding details of dwc3 can be found in:
+Documentation/devicetree/bindings/usb/dwc3.txt
+
+NB: The dr_mode property described in [1] is NOT optional for this driver, as the default value
+is "otg", which isn't supported by this SoC. Valid dr_mode values for dwc3-st are either "host"
+or "device".
+
+[1] Documentation/devicetree/bindings/usb/generic.txt
+
+Example:
+
+st_dwc3: dwc3@8f94000 {
+       status          = "disabled";
+       compatible      = "st,stih407-dwc3";
+       reg             = <0x08f94000 0x1000>, <0x110 0x4>;
+       reg-names       = "reg-glue", "syscfg-reg";
+       st,syscfg       = <&syscfg_core>;
+       resets          = <&powerdown STIH407_USB3_POWERDOWN>,
+                         <&softreset STIH407_MIPHY2_SOFTRESET>;
+       reset-names     = "powerdown",
+                         "softreset";
+       #address-cells  = <1>;
+       #size-cells     = <1>;
+       pinctrl-names   = "default";
+       pinctrl-0       = <&pinctrl_usb3>;
+       ranges;
+
+       dwc3: dwc3@9900000 {
+               compatible      = "snps,dwc3";
+               reg             = <0x09900000 0x100000>;
+               interrupts      = <GIC_SPI 155 IRQ_TYPE_NONE>;
+               dr_mode         = "host";
+               phys-names      = "usb2-phy", "usb3-phy";
+               phys            = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>;
+       };
+};
index cef181a9d8bd6506699cd98889fd36f8f6bbc306..fe3eed89e4c3b93d37c91857cbd67628aae94da1 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
        * "fsl,imx23-usbphy" for imx23 and imx28
        * "fsl,imx6q-usbphy" for imx6dq and imx6dl
        * "fsl,imx6sl-usbphy" for imx6sl
+       * "fsl,vf610-usbphy" for Vybrid vf610
   "fsl,imx23-usbphy" is still a fallback for other strings
 - reg: Should contain registers location and length
 - interrupts: Should contain phy interrupt
diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
new file mode 100644 (file)
index 0000000..b08c903
--- /dev/null
@@ -0,0 +1,24 @@
+Renesas Electronics USBHS driver
+
+Required properties:
+  - compatible: Must contain one of the following:
+       - "renesas,usbhs-r8a7790"
+       - "renesas,usbhs-r8a7791"
+  - reg: Base address and length of the register for the USBHS
+  - interrupts: Interrupt specifier for the USBHS
+  - clocks: A list of phandle + clock specifier pairs
+
+Optional properties:
+  - renesas,buswait: Integer to use BUSWAIT register
+  - renesas,enable-gpio: A gpio specifier to check GPIO determining if USB
+                        function should be enabled
+  - phys: phandle + phy specifier pair
+  - phy-names: must be "usb"
+
+Example:
+       usbhs: usb@e6590000 {
+               compatible = "renesas,usbhs-r8a7790";
+               reg = <0 0xe6590000 0 0x100>;
+               interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
+       };
index 5e7866a486b0c3310b64ca5c9aa6cd6d9c16d354..5d66c037be183c644196725e4651ec8d8dff0feb 100644 (file)
@@ -1398,6 +1398,7 @@ F:        drivers/media/rc/st_rc.c
 F:     drivers/i2c/busses/i2c-st.c
 F:     drivers/tty/serial/st-asc.c
 F:     drivers/mmc/host/sdhci-st.c
+F:     drivers/usb/dwc3/dwc3-st.c
 
 ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
 M:     Lennert Buytenhek <kernel@wantstofly.org>
index 785510a0a0c3fdff54a8627472eb07c5de73a3df..5238251ec653c408906d3f14ae128be5c5dc5751 100644 (file)
@@ -80,6 +80,15 @@ config USB_DWC3_KEYSTONE
          Support of USB2/3 functionality in TI Keystone2 platforms.
          Say 'Y' or 'M' here if you have one such device
 
+config USB_DWC3_ST
+       tristate "STMicroelectronics Platforms"
+       depends on ARCH_STI && OF
+       default USB_DWC3
+       help
+         STMicroelectronics SoCs with one DesignWare Core USB3 IP
+         inside (i.e. STiH407).
+         Say 'Y' or 'M' if you have one such device.
+
 comment "Debugging features"
 
 config USB_DWC3_DEBUG
index 10ac3e72482ea52e8924533ad9ed93490eac9e46..3e88c77483c1d86f1b5a36ab5ee8cecefaeea08e 100644 (file)
@@ -1,9 +1,12 @@
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o                         := -I$(src)
+
 ccflags-$(CONFIG_USB_DWC3_DEBUG)       := -DDEBUG
 ccflags-$(CONFIG_USB_DWC3_VERBOSE)     += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_USB_DWC3)                 += dwc3.o
 
-dwc3-y                                 := core.o
+dwc3-y                                 := core.o debug.o trace.o
 
 ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
        dwc3-y                          += host.o
@@ -33,3 +36,4 @@ obj-$(CONFIG_USB_DWC3_OMAP)           += dwc3-omap.o
 obj-$(CONFIG_USB_DWC3_EXYNOS)          += dwc3-exynos.o
 obj-$(CONFIG_USB_DWC3_PCI)             += dwc3-pci.o
 obj-$(CONFIG_USB_DWC3_KEYSTONE)                += dwc3-keystone.o
+obj-$(CONFIG_USB_DWC3_ST)              += dwc3-st.o
index b769c1faaf0385dff2543592e2fe68745d1fc7c5..f2050e844b4bae97dec305180287672c69781a92 100644 (file)
@@ -186,10 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
 
        dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
                        GFP_KERNEL);
-       if (!dwc->ev_buffs) {
-               dev_err(dwc->dev, "can't allocate event buffers array\n");
+       if (!dwc->ev_buffs)
                return -ENOMEM;
-       }
 
        for (i = 0; i < num; i++) {
                struct dwc3_event_buffer        *evt;
@@ -639,10 +637,9 @@ static int dwc3_probe(struct platform_device *pdev)
        void                    *mem;
 
        mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
-       if (!mem) {
-               dev_err(dev, "not enough memory\n");
+       if (!mem)
                return -ENOMEM;
-       }
+
        dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
        dwc->mem = mem;
        dwc->dev = dev;
index 48fb264065db3218ef43f7fe4cdfe7f2245ae84d..66f62563bcf94e970b01b8661dd3572a647ede5b 100644 (file)
@@ -33,6 +33,8 @@
 
 #include <linux/phy/phy.h>
 
+#define DWC3_MSG_MAX   500
+
 /* Global constants */
 #define DWC3_EP0_BOUNCE_SIZE   512
 #define DWC3_ENDPOINTS_NUM     32
@@ -938,7 +940,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc);
 int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
 int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
                unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
 #else
 static inline int dwc3_gadget_init(struct dwc3 *dwc)
 { return 0; }
diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c
new file mode 100644 (file)
index 0000000..0be6885
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "debug.h"
+
+void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       trace(&vaf);
+
+       va_end(args);
+}
index fceb39dc4bba331a89e78f8d1298d566fb3d4b18..07fbc2d94fd4f9961b7ae1b8c2cb60c9456fae9a 100644 (file)
  * GNU General Public License for more details.
  */
 
+#ifndef __DWC3_DEBUG_H
+#define __DWC3_DEBUG_H
+
 #include "core.h"
 
+/**
+ * dwc3_gadget_ep_cmd_string - returns endpoint command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_ep_cmd_string(u8 cmd)
+{
+       switch (cmd) {
+       case DWC3_DEPCMD_DEPSTARTCFG:
+               return "Start New Configuration";
+       case DWC3_DEPCMD_ENDTRANSFER:
+               return "End Transfer";
+       case DWC3_DEPCMD_UPDATETRANSFER:
+               return "Update Transfer";
+       case DWC3_DEPCMD_STARTTRANSFER:
+               return "Start Transfer";
+       case DWC3_DEPCMD_CLEARSTALL:
+               return "Clear Stall";
+       case DWC3_DEPCMD_SETSTALL:
+               return "Set Stall";
+       case DWC3_DEPCMD_GETEPSTATE:
+               return "Get Endpoint State";
+       case DWC3_DEPCMD_SETTRANSFRESOURCE:
+               return "Set Endpoint Transfer Resource";
+       case DWC3_DEPCMD_SETEPCONFIG:
+               return "Set Endpoint Configuration";
+       default:
+               return "UNKNOWN command";
+       }
+}
+
+/**
+ * dwc3_gadget_generic_cmd_string - returns generic command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_generic_cmd_string(u8 cmd)
+{
+       switch (cmd) {
+       case DWC3_DGCMD_SET_LMP:
+               return "Set LMP";
+       case DWC3_DGCMD_SET_PERIODIC_PAR:
+               return "Set Periodic Parameters";
+       case DWC3_DGCMD_XMIT_FUNCTION:
+               return "Transmit Function Wake Device Notification";
+       case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
+               return "Set Scratchpad Buffer Array Address Lo";
+       case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
+               return "Set Scratchpad Buffer Array Address Hi";
+       case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
+               return "Selected FIFO Flush";
+       case DWC3_DGCMD_ALL_FIFO_FLUSH:
+               return "All FIFO Flush";
+       case DWC3_DGCMD_SET_ENDPOINT_NRDY:
+               return "Set Endpoint NRDY";
+       case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
+               return "Run SoC Bus Loopback Test";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+/**
+ * dwc3_gadget_link_string - returns link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_link_string(enum dwc3_link_state link_state)
+{
+       switch (link_state) {
+       case DWC3_LINK_STATE_U0:
+               return "U0";
+       case DWC3_LINK_STATE_U1:
+               return "U1";
+       case DWC3_LINK_STATE_U2:
+               return "U2";
+       case DWC3_LINK_STATE_U3:
+               return "U3";
+       case DWC3_LINK_STATE_SS_DIS:
+               return "SS.Disabled";
+       case DWC3_LINK_STATE_RX_DET:
+               return "RX.Detect";
+       case DWC3_LINK_STATE_SS_INACT:
+               return "SS.Inactive";
+       case DWC3_LINK_STATE_POLL:
+               return "Polling";
+       case DWC3_LINK_STATE_RECOV:
+               return "Recovery";
+       case DWC3_LINK_STATE_HRESET:
+               return "Hot Reset";
+       case DWC3_LINK_STATE_CMPLY:
+               return "Compliance";
+       case DWC3_LINK_STATE_LPBK:
+               return "Loopback";
+       case DWC3_LINK_STATE_RESET:
+               return "Reset";
+       case DWC3_LINK_STATE_RESUME:
+               return "Resume";
+       default:
+               return "UNKNOWN link state\n";
+       }
+}
+
+/**
+ * dwc3_gadget_event_string - returns event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_string(u8 event)
+{
+       switch (event) {
+       case DWC3_DEVICE_EVENT_DISCONNECT:
+               return "Disconnect";
+       case DWC3_DEVICE_EVENT_RESET:
+               return "Reset";
+       case DWC3_DEVICE_EVENT_CONNECT_DONE:
+               return "Connection Done";
+       case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+               return "Link Status Change";
+       case DWC3_DEVICE_EVENT_WAKEUP:
+               return "WakeUp";
+       case DWC3_DEVICE_EVENT_EOPF:
+               return "End-Of-Frame";
+       case DWC3_DEVICE_EVENT_SOF:
+               return "Start-Of-Frame";
+       case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+               return "Erratic Error";
+       case DWC3_DEVICE_EVENT_CMD_CMPL:
+               return "Command Complete";
+       case DWC3_DEVICE_EVENT_OVERFLOW:
+               return "Overflow";
+       }
+
+       return "UNKNOWN";
+}
+
+/**
+ * dwc3_ep_event_string - returns event name
+ * @event: then event code
+ */
+static inline const char *dwc3_ep_event_string(u8 event)
+{
+       switch (event) {
+       case DWC3_DEPEVT_XFERCOMPLETE:
+               return "Transfer Complete";
+       case DWC3_DEPEVT_XFERINPROGRESS:
+               return "Transfer In-Progress";
+       case DWC3_DEPEVT_XFERNOTREADY:
+               return "Transfer Not Ready";
+       case DWC3_DEPEVT_RXTXFIFOEVT:
+               return "FIFO";
+       case DWC3_DEPEVT_STREAMEVT:
+               return "Stream";
+       case DWC3_DEPEVT_EPCMDCMPLT:
+               return "Endpoint Command Complete";
+       }
+
+       return "UNKNOWN";
+}
+
+/**
+ * dwc3_gadget_event_type_string - return event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_type_string(u8 event)
+{
+       switch (event) {
+       case DWC3_DEVICE_EVENT_DISCONNECT:
+               return "Disconnect";
+       case DWC3_DEVICE_EVENT_RESET:
+               return "Reset";
+       case DWC3_DEVICE_EVENT_CONNECT_DONE:
+               return "Connect Done";
+       case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+               return "Link Status Change";
+       case DWC3_DEVICE_EVENT_WAKEUP:
+               return "Wake-Up";
+       case DWC3_DEVICE_EVENT_HIBER_REQ:
+               return "Hibernation";
+       case DWC3_DEVICE_EVENT_EOPF:
+               return "End of Periodic Frame";
+       case DWC3_DEVICE_EVENT_SOF:
+               return "Start of Frame";
+       case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+               return "Erratic Error";
+       case DWC3_DEVICE_EVENT_CMD_CMPL:
+               return "Command Complete";
+       case DWC3_DEVICE_EVENT_OVERFLOW:
+               return "Overflow";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
+
 #ifdef CONFIG_DEBUG_FS
 extern int dwc3_debugfs_init(struct dwc3 *);
 extern void dwc3_debugfs_exit(struct dwc3 *);
@@ -27,4 +225,4 @@ static inline int dwc3_debugfs_init(struct dwc3 *d)
 static inline void dwc3_debugfs_exit(struct dwc3 *d)
 {  }
 #endif
-
+#endif /* __DWC3_DEBUG_H */
index f9fb8adb785b7eb35c158dd2e9ad8e5f577fe6d8..3951a65fea048c51d6fec6b2eda72563fff5cd8a 100644 (file)
@@ -113,10 +113,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
        int                     ret;
 
        exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
-       if (!exynos) {
-               dev_err(dev, "not enough memory\n");
+       if (!exynos)
                return -ENOMEM;
-       }
 
        /*
         * Right now device-tree probed devices don't get dma_mask set.
index 9dcfbe7cd5f5d5cfc58239ee578cd68cae09a63f..3ef5e4fc315b19efcb9fb8c9a70828643c690163 100644 (file)
@@ -481,10 +481,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
        }
 
        omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
-       if (!omap) {
-               dev_err(dev, "not enough memory\n");
+       if (!omap)
                return -ENOMEM;
-       }
 
        platform_set_drvdata(pdev, omap);
 
index a60bab7dfa0a431cce7fb79e9b33eaf9a65f14da..436fb08c40b8785f16e224241039e7c63899fde9 100644 (file)
@@ -103,10 +103,8 @@ static int dwc3_pci_probe(struct pci_dev *pci,
        struct device           *dev = &pci->dev;
 
        glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
-       if (!glue) {
-               dev_err(dev, "not enough memory\n");
+       if (!glue)
                return -ENOMEM;
-       }
 
        glue->dev = dev;
 
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
new file mode 100644 (file)
index 0000000..c7602b5
--- /dev/null
@@ -0,0 +1,367 @@
+/**
+ * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms
+ *
+ * This is a small driver for the dwc3 to provide the glue logic
+ * to configure the controller. Tested on STi platforms.
+ *
+ * Copyright (C) 2014 Stmicroelectronics
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ * Contributors: Aymen Bouattay <aymen.bouattay@st.com>
+ *               Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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.
+ *
+ * Inspired by dwc3-omap.c and dwc3-exynos.c.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/usb/of.h>
+
+#include "core.h"
+#include "io.h"
+
+/* glue registers */
+#define CLKRST_CTRL            0x00
+#define AUX_CLK_EN             BIT(0)
+#define SW_PIPEW_RESET_N       BIT(4)
+#define EXT_CFG_RESET_N                BIT(8)
+/*
+ * 1'b0 : The host controller complies with the xHCI revision 0.96
+ * 1'b1 : The host controller complies with the xHCI revision 1.0
+ */
+#define XHCI_REVISION          BIT(12)
+
+#define USB2_VBUS_MNGMNT_SEL1  0x2C
+/*
+ * For all fields in USB2_VBUS_MNGMNT_SEL1
+ * 2’b00 : Override value from Reg 0x30 is selected
+ * 2’b01 : utmiotg_<signal_name> from usb3_top is selected
+ * 2’b10 : pipew_<signal_name> from PIPEW instance is selected
+ * 2’b11 : value is 1'b0
+ */
+#define USB2_VBUS_REG30                0x0
+#define USB2_VBUS_UTMIOTG      0x1
+#define USB2_VBUS_PIPEW                0x2
+#define USB2_VBUS_ZERO         0x3
+
+#define SEL_OVERRIDE_VBUSVALID(n)      (n << 0)
+#define SEL_OVERRIDE_POWERPRESENT(n)   (n << 4)
+#define SEL_OVERRIDE_BVALID(n)         (n << 8)
+
+/* Static DRD configuration */
+#define USB3_CONTROL_MASK              0xf77
+
+#define USB3_DEVICE_NOT_HOST           BIT(0)
+#define USB3_FORCE_VBUSVALID           BIT(1)
+#define USB3_DELAY_VBUSVALID           BIT(2)
+#define USB3_SEL_FORCE_OPMODE          BIT(4)
+#define USB3_FORCE_OPMODE(n)           (n << 5)
+#define USB3_SEL_FORCE_DPPULLDOWN2     BIT(8)
+#define USB3_FORCE_DPPULLDOWN2         BIT(9)
+#define USB3_SEL_FORCE_DMPULLDOWN2     BIT(10)
+#define USB3_FORCE_DMPULLDOWN2         BIT(11)
+
+/**
+ * struct st_dwc3 - dwc3-st driver private structure
+ * @dev:               device pointer
+ * @glue_base:         ioaddr for the glue registers
+ * @regmap:            regmap pointer for getting syscfg
+ * @syscfg_reg_off:    usb syscfg control offset
+ * @dr_mode:           drd static host/device config
+ * @rstc_pwrdn:                rest controller for powerdown signal
+ * @rstc_rst:          reset controller for softreset signal
+ */
+
+struct st_dwc3 {
+       struct device *dev;
+       void __iomem *glue_base;
+       struct regmap *regmap;
+       int syscfg_reg_off;
+       enum usb_dr_mode dr_mode;
+       struct reset_control *rstc_pwrdn;
+       struct reset_control *rstc_rst;
+};
+
+static inline u32 st_dwc3_readl(void __iomem *base, u32 offset)
+{
+       return readl_relaxed(base + offset);
+}
+
+static inline void st_dwc3_writel(void __iomem *base, u32 offset, u32 value)
+{
+       writel_relaxed(value, base + offset);
+}
+
+/**
+ * st_dwc3_drd_init: program the port
+ * @dwc3_data: driver private structure
+ * Description: this function is to program the port as either host or device
+ * according to the static configuration passed from devicetree.
+ * OTG and dual role are not yet supported!
+ */
+static int st_dwc3_drd_init(struct st_dwc3 *dwc3_data)
+{
+       u32 val;
+       int err;
+
+       err = regmap_read(dwc3_data->regmap, dwc3_data->syscfg_reg_off, &val);
+       if (err)
+               return err;
+
+       val &= USB3_CONTROL_MASK;
+
+       switch (dwc3_data->dr_mode) {
+       case USB_DR_MODE_PERIPHERAL:
+
+               val &= ~(USB3_FORCE_VBUSVALID | USB3_DELAY_VBUSVALID
+                       | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
+                       | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
+                       | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
+
+               val |= USB3_DEVICE_NOT_HOST;
+
+               dev_dbg(dwc3_data->dev, "Configuring as Device\n");
+               break;
+
+       case USB_DR_MODE_HOST:
+
+               val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID
+                       | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
+                       | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
+                       | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
+
+               /*
+                * USB3_DELAY_VBUSVALID is ANDed with USB_C_VBUSVALID. Thus,
+                * when set to â€˜0‘, it can delay the arrival of VBUSVALID
+                * information to VBUSVLDEXT2 input of the pico PHY.
+                * We don't want to do that so we set the bit to '1'.
+                */
+
+               val |= USB3_DELAY_VBUSVALID;
+
+               dev_dbg(dwc3_data->dev, "Configuring as Host\n");
+               break;
+
+       default:
+               dev_err(dwc3_data->dev, "Unsupported mode of operation %d\n",
+                       dwc3_data->dr_mode);
+               return -EINVAL;
+       }
+
+       return regmap_write(dwc3_data->regmap, dwc3_data->syscfg_reg_off, val);
+}
+
+/**
+ * st_dwc3_init: init the controller via glue logic
+ * @dwc3_data: driver private structure
+ */
+static void st_dwc3_init(struct st_dwc3 *dwc3_data)
+{
+       u32 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
+
+       reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION;
+       reg &= ~SW_PIPEW_RESET_N;
+       st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
+
+       /* configure mux for vbus, powerpresent and bvalid signals */
+       reg = st_dwc3_readl(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1);
+
+       reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) |
+               SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) |
+               SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG);
+
+       st_dwc3_writel(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1, reg);
+
+       reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
+       reg |= SW_PIPEW_RESET_N;
+       st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
+}
+
+static int st_dwc3_probe(struct platform_device *pdev)
+{
+       struct st_dwc3 *dwc3_data;
+       struct resource *res;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node, *child;
+       struct regmap *regmap;
+       int ret;
+
+       dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL);
+       if (!dwc3_data)
+               return -ENOMEM;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue");
+       dwc3_data->glue_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(dwc3_data->glue_base))
+               return PTR_ERR(dwc3_data->glue_base);
+
+       regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       dma_set_coherent_mask(dev, dev->coherent_dma_mask);
+       dwc3_data->dev = dev;
+       dwc3_data->regmap = regmap;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg");
+       if (!res) {
+               ret = -ENXIO;
+               goto undo_platform_dev_alloc;
+       }
+
+       dwc3_data->syscfg_reg_off = res->start;
+
+       dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
+                dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
+
+       dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown");
+       if (IS_ERR(dwc3_data->rstc_pwrdn)) {
+               dev_err(&pdev->dev, "could not get power controller\n");
+               ret = PTR_ERR(dwc3_data->rstc_pwrdn);
+               goto undo_platform_dev_alloc;
+       }
+
+       /* Manage PowerDown */
+       reset_control_deassert(dwc3_data->rstc_pwrdn);
+
+       dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
+       if (IS_ERR(dwc3_data->rstc_rst)) {
+               dev_err(&pdev->dev, "could not get reset controller\n");
+               ret = PTR_ERR(dwc3_data->rstc_pwrdn);
+               goto undo_powerdown;
+       }
+
+       /* Manage SoftReset */
+       reset_control_deassert(dwc3_data->rstc_rst);
+
+       child = of_get_child_by_name(node, "dwc3");
+       if (!child) {
+               dev_err(&pdev->dev, "failed to find dwc3 core node\n");
+               ret = -ENODEV;
+               goto undo_softreset;
+       }
+
+       dwc3_data->dr_mode = of_usb_get_dr_mode(child);
+
+       /* Allocate and initialize the core */
+       ret = of_platform_populate(node, NULL, NULL, dev);
+       if (ret) {
+               dev_err(dev, "failed to add dwc3 core\n");
+               goto undo_softreset;
+       }
+
+       /*
+        * Configure the USB port as device or host according to the static
+        * configuration passed from DT.
+        * DRD is the only mode currently supported so this will be enhanced
+        * as soon as OTG is available.
+        */
+       ret = st_dwc3_drd_init(dwc3_data);
+       if (ret) {
+               dev_err(dev, "drd initialisation failed\n");
+               goto undo_softreset;
+       }
+
+       /* ST glue logic init */
+       st_dwc3_init(dwc3_data);
+
+       platform_set_drvdata(pdev, dwc3_data);
+       return 0;
+
+undo_softreset:
+       reset_control_assert(dwc3_data->rstc_rst);
+undo_powerdown:
+       reset_control_assert(dwc3_data->rstc_pwrdn);
+undo_platform_dev_alloc:
+       platform_device_put(pdev);
+       return ret;
+}
+
+static int st_dwc3_remove(struct platform_device *pdev)
+{
+       struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev);
+
+       of_platform_depopulate(&pdev->dev);
+
+       reset_control_assert(dwc3_data->rstc_pwrdn);
+       reset_control_assert(dwc3_data->rstc_rst);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_dwc3_suspend(struct device *dev)
+{
+       struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
+
+       reset_control_assert(dwc3_data->rstc_pwrdn);
+       reset_control_assert(dwc3_data->rstc_rst);
+
+       pinctrl_pm_select_sleep_state(dev);
+
+       return 0;
+}
+
+static int st_dwc3_resume(struct device *dev)
+{
+       struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
+       int ret;
+
+       pinctrl_pm_select_default_state(dev);
+
+       reset_control_deassert(dwc3_data->rstc_pwrdn);
+       reset_control_deassert(dwc3_data->rstc_rst);
+
+       ret = st_dwc3_drd_init(dwc3_data);
+       if (ret) {
+               dev_err(dev, "drd initialisation failed\n");
+               return ret;
+       }
+
+       /* ST glue logic init */
+       st_dwc3_init(dwc3_data);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
+
+static const struct of_device_id st_dwc3_match[] = {
+       { .compatible = "st,stih407-dwc3" },
+       { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, st_dwc3_match);
+
+static struct platform_driver st_dwc3_driver = {
+       .probe = st_dwc3_probe,
+       .remove = st_dwc3_remove,
+       .driver = {
+               .name = "usb-st-dwc3",
+               .of_match_table = st_dwc3_match,
+               .pm = &st_dwc3_dev_pm_ops,
+       },
+};
+
+module_platform_driver(st_dwc3_driver);
+
+MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
+MODULE_DESCRIPTION("DesignWare USB3 STi Glue Layer");
+MODULE_LICENSE("GPL v2");
index 21a352079bc25fdd0fe33ddba1a3191b29b812f0..b35938777ddeaa03954bb567fb1fb0cf096b5982 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/usb/composite.h>
 
 #include "core.h"
+#include "debug.h"
 #include "gadget.h"
 #include "io.h"
 
@@ -65,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
 
        dep = dwc->eps[epnum];
        if (dep->flags & DWC3_EP_BUSY) {
-               dev_vdbg(dwc->dev, "%s: still busy\n", dep->name);
+               dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
                return 0;
        }
 
@@ -88,7 +89,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
        ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_STARTTRANSFER, &params);
        if (ret < 0) {
-               dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
+               dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
+                               dep->name);
                return ret;
        }
 
@@ -153,7 +155,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
                if (dwc->ep0state == EP0_STATUS_PHASE)
                        __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
                else
-                       dev_dbg(dwc->dev, "too early for delayed status\n");
+                       dwc3_trace(trace_dwc3_ep0,
+                                       "too early for delayed status");
 
                return 0;
        }
@@ -217,7 +220,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
 
        spin_lock_irqsave(&dwc->lock, flags);
        if (!dep->endpoint.desc) {
-               dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
+               dwc3_trace(trace_dwc3_ep0,
+                               "trying to queue request %p to disabled %s",
                                request, dep->name);
                ret = -ESHUTDOWN;
                goto out;
@@ -229,7 +233,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
                goto out;
        }
 
-       dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n",
+       dwc3_trace(trace_dwc3_ep0,
+                       "queueing request %p to %s length %d state '%s'",
                        request, dep->name, request->length,
                        dwc3_ep0_state_string(dwc->ep0state));
 
@@ -485,12 +490,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 
        addr = le16_to_cpu(ctrl->wValue);
        if (addr > 127) {
-               dev_dbg(dwc->dev, "invalid device address %d\n", addr);
+               dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr);
                return -EINVAL;
        }
 
        if (state == USB_STATE_CONFIGURED) {
-               dev_dbg(dwc->dev, "trying to set address when configured\n");
+               dwc3_trace(trace_dwc3_ep0,
+                               "trying to set address when configured");
                return -EINVAL;
        }
 
@@ -556,7 +562,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
                        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 
                        dwc->resize_fifos = true;
-                       dev_dbg(dwc->dev, "resize fifos flag SET\n");
+                       dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
                }
                break;
 
@@ -680,35 +686,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 
        switch (ctrl->bRequest) {
        case USB_REQ_GET_STATUS:
-               dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n");
                ret = dwc3_ep0_handle_status(dwc, ctrl);
                break;
        case USB_REQ_CLEAR_FEATURE:
-               dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n");
                ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
                break;
        case USB_REQ_SET_FEATURE:
-               dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n");
                ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
                break;
        case USB_REQ_SET_ADDRESS:
-               dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n");
                ret = dwc3_ep0_set_address(dwc, ctrl);
                break;
        case USB_REQ_SET_CONFIGURATION:
-               dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n");
                ret = dwc3_ep0_set_config(dwc, ctrl);
                break;
        case USB_REQ_SET_SEL:
-               dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n");
                ret = dwc3_ep0_set_sel(dwc, ctrl);
                break;
        case USB_REQ_SET_ISOCH_DELAY:
-               dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+               dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n");
                ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
                break;
        default:
-               dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
+               dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n");
                ret = dwc3_ep0_delegate_req(dwc, ctrl);
                break;
        }
@@ -726,6 +732,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
        if (!dwc->gadget_driver)
                goto out;
 
+       trace_dwc3_ctrl_req(ctrl);
+
        len = le16_to_cpu(ctrl->wLength);
        if (!len) {
                dwc->three_stage_setup = false;
@@ -774,7 +782,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
 
        status = DWC3_TRB_SIZE_TRBSTS(trb->size);
        if (status == DWC3_TRBSTS_SETUP_PENDING) {
-               dev_dbg(dwc->dev, "Setup Pending received\n");
+               dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
 
                if (r)
                        dwc3_gadget_giveback(ep0, r, -ECONNRESET);
@@ -834,7 +842,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
 
                ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
                if (ret < 0) {
-                       dev_dbg(dwc->dev, "Invalid Test #%d\n",
+                       dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
                                        dwc->test_mode_nr);
                        dwc3_ep0_stall_and_restart(dwc);
                        return;
@@ -843,7 +851,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
 
        status = DWC3_TRB_SIZE_TRBSTS(trb->size);
        if (status == DWC3_TRBSTS_SETUP_PENDING)
-               dev_dbg(dwc->dev, "Setup Pending received\n");
+               dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n");
 
        dwc->ep0state = EP0_SETUP_PHASE;
        dwc3_ep0_out_start(dwc);
@@ -860,17 +868,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
 
        switch (dwc->ep0state) {
        case EP0_SETUP_PHASE:
-               dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n");
+               dwc3_trace(trace_dwc3_ep0, "Setup Phase");
                dwc3_ep0_inspect_setup(dwc, event);
                break;
 
        case EP0_DATA_PHASE:
-               dev_vdbg(dwc->dev, "Data Phase\n");
+               dwc3_trace(trace_dwc3_ep0, "Data Phase");
                dwc3_ep0_complete_data(dwc, event);
                break;
 
        case EP0_STATUS_PHASE:
-               dev_vdbg(dwc->dev, "Status Phase\n");
+               dwc3_trace(trace_dwc3_ep0, "Status Phase");
                dwc3_ep0_complete_status(dwc, event);
                break;
        default:
@@ -946,7 +954,7 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
 static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        if (dwc->resize_fifos) {
-               dev_dbg(dwc->dev, "starting to resize fifos\n");
+               dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
                dwc3_gadget_resize_tx_fifos(dwc);
                dwc->resize_fifos = 0;
        }
@@ -987,7 +995,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
 
        switch (event->status) {
        case DEPEVT_STATUS_CONTROL_DATA:
-               dev_vdbg(dwc->dev, "Control Data\n");
+               dwc3_trace(trace_dwc3_ep0, "Control Data");
 
                /*
                 * We already have a DATA transfer in the controller's cache,
@@ -1001,7 +1009,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                if (dwc->ep0_expect_in != event->endpoint_number) {
                        struct dwc3_ep  *dep = dwc->eps[dwc->ep0_expect_in];
 
-                       dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+                       dwc3_trace(trace_dwc3_ep0,
+                                       "Wrong direction for Data phase");
                        dwc3_ep0_end_control_data(dwc, dep);
                        dwc3_ep0_stall_and_restart(dwc);
                        return;
@@ -1013,13 +1022,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
                        return;
 
-               dev_vdbg(dwc->dev, "Control Status\n");
+               dwc3_trace(trace_dwc3_ep0, "Control Status");
 
                dwc->ep0state = EP0_STATUS_PHASE;
 
                if (dwc->delayed_status) {
                        WARN_ON_ONCE(event->endpoint_number != 1);
-                       dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
+                       dwc3_trace(trace_dwc3_ep0, "Delayed Status");
                        return;
                }
 
@@ -1032,7 +1041,7 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
 {
        u8                      epnum = event->endpoint_number;
 
-       dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
+       dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
                        dwc3_ep_event_string(event->endpoint_event),
                        epnum >> 1, (epnum & 1) ? "in" : "out",
                        dwc3_ep0_state_string(dwc->ep0state));
index 349cacc577d81a679cacd4494bb7ead82f11cfa4..f2dbaca5df2cf6de7afc958dcba50f67894f1ec6 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
+#include "debug.h"
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
@@ -266,107 +267,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
        dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
                        req, dep->name, req->request.actual,
                        req->request.length, status);
+       trace_dwc3_gadget_giveback(req);
 
        spin_unlock(&dwc->lock);
        req->request.complete(&dep->endpoint, &req->request);
        spin_lock(&dwc->lock);
 }
 
-static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
-{
-       switch (cmd) {
-       case DWC3_DEPCMD_DEPSTARTCFG:
-               return "Start New Configuration";
-       case DWC3_DEPCMD_ENDTRANSFER:
-               return "End Transfer";
-       case DWC3_DEPCMD_UPDATETRANSFER:
-               return "Update Transfer";
-       case DWC3_DEPCMD_STARTTRANSFER:
-               return "Start Transfer";
-       case DWC3_DEPCMD_CLEARSTALL:
-               return "Clear Stall";
-       case DWC3_DEPCMD_SETSTALL:
-               return "Set Stall";
-       case DWC3_DEPCMD_GETEPSTATE:
-               return "Get Endpoint State";
-       case DWC3_DEPCMD_SETTRANSFRESOURCE:
-               return "Set Endpoint Transfer Resource";
-       case DWC3_DEPCMD_SETEPCONFIG:
-               return "Set Endpoint Configuration";
-       default:
-               return "UNKNOWN command";
-       }
-}
-
-static const char *dwc3_gadget_generic_cmd_string(u8 cmd)
-{
-       switch (cmd) {
-       case DWC3_DGCMD_SET_LMP:
-               return "Set LMP";
-       case DWC3_DGCMD_SET_PERIODIC_PAR:
-               return "Set Periodic Parameters";
-       case DWC3_DGCMD_XMIT_FUNCTION:
-               return "Transmit Function Wake Device Notification";
-       case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
-               return "Set Scratchpad Buffer Array Address Lo";
-       case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
-               return "Set Scratchpad Buffer Array Address Hi";
-       case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
-               return "Selected FIFO Flush";
-       case DWC3_DGCMD_ALL_FIFO_FLUSH:
-               return "All FIFO Flush";
-       case DWC3_DGCMD_SET_ENDPOINT_NRDY:
-               return "Set Endpoint NRDY";
-       case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
-               return "Run SoC Bus Loopback Test";
-       default:
-               return "UNKNOWN";
-       }
-}
-
-static const char *dwc3_gadget_link_string(enum dwc3_link_state link_state)
-{
-       switch (link_state) {
-       case DWC3_LINK_STATE_U0:
-               return "U0";
-       case DWC3_LINK_STATE_U1:
-               return "U1";
-       case DWC3_LINK_STATE_U2:
-               return "U2";
-       case DWC3_LINK_STATE_U3:
-               return "U3";
-       case DWC3_LINK_STATE_SS_DIS:
-               return "SS.Disabled";
-       case DWC3_LINK_STATE_RX_DET:
-               return "RX.Detect";
-       case DWC3_LINK_STATE_SS_INACT:
-               return "SS.Inactive";
-       case DWC3_LINK_STATE_POLL:
-               return "Polling";
-       case DWC3_LINK_STATE_RECOV:
-               return "Recovery";
-       case DWC3_LINK_STATE_HRESET:
-               return "Hot Reset";
-       case DWC3_LINK_STATE_CMPLY:
-               return "Compliance";
-       case DWC3_LINK_STATE_LPBK:
-               return "Loopback";
-       case DWC3_LINK_STATE_RESET:
-               return "Reset";
-       case DWC3_LINK_STATE_RESUME:
-               return "Resume";
-       default:
-               return "UNKNOWN link state\n";
-       }
-}
-
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
 {
        u32             timeout = 500;
        u32             reg;
 
-       dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n",
-                       dwc3_gadget_generic_cmd_string(cmd), cmd, param);
+       trace_dwc3_gadget_generic_cmd(cmd, param);
 
        dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
        dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
@@ -397,10 +310,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
        u32                     timeout = 500;
        u32                     reg;
 
-       dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n",
-                       dep->name,
-                       dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0,
-                       params->param1, params->param2);
+       trace_dwc3_gadget_ep_cmd(dep, cmd, params);
 
        dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
        dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
@@ -789,17 +699,16 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
 {
        struct dwc3_request             *req;
        struct dwc3_ep                  *dep = to_dwc3_ep(ep);
-       struct dwc3                     *dwc = dep->dwc;
 
        req = kzalloc(sizeof(*req), gfp_flags);
-       if (!req) {
-               dev_err(dwc->dev, "not enough memory\n");
+       if (!req)
                return NULL;
-       }
 
        req->epnum      = dep->number;
        req->dep        = dep;
 
+       trace_dwc3_alloc_request(req);
+
        return &req->request;
 }
 
@@ -808,6 +717,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
 {
        struct dwc3_request             *req = to_dwc3_request(request);
 
+       trace_dwc3_free_request(req);
        kfree(req);
 }
 
@@ -889,6 +799,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
 
        trb->ctrl |= DWC3_TRB_CTRL_HWO;
+
+       trace_dwc3_prepare_trb(dep, trb);
 }
 
 /*
@@ -1233,6 +1145,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
 
        dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
                        request, ep->name, request->length);
+       trace_dwc3_ep_queue(req);
 
        spin_lock_irqsave(&dwc->lock, flags);
        ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1253,6 +1166,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
        unsigned long                   flags;
        int                             ret = 0;
 
+       trace_dwc3_ep_dequeue(req);
+
        spin_lock_irqsave(&dwc->lock, flags);
 
        list_for_each_entry(r, &dep->request_list, list) {
@@ -1743,11 +1658,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
                u8 epnum = (i << 1) | (!!direction);
 
                dep = kzalloc(sizeof(*dep), GFP_KERNEL);
-               if (!dep) {
-                       dev_err(dwc->dev, "can't allocate endpoint %d\n",
-                                       epnum);
+               if (!dep)
                        return -ENOMEM;
-               }
 
                dep->dwc = dwc;
                dep->number = epnum;
@@ -1846,6 +1758,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
        unsigned int            s_pkt = 0;
        unsigned int            trb_status;
 
+       trace_dwc3_complete_trb(dep, trb);
+
        if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
                /*
                 * We continue despite the error. There is not much we
@@ -2020,9 +1934,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
        if (!(dep->flags & DWC3_EP_ENABLED))
                return;
 
-       dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
-                       dwc3_ep_event_string(event->endpoint_event));
-
        if (epnum == 0 || epnum == 1) {
                dwc3_ep0_interrupt(dwc, event);
                return;
@@ -2215,8 +2126,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 {
        int                     reg;
 
-       dev_vdbg(dwc->dev, "%s\n", __func__);
-
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_INITU1ENA;
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -2235,8 +2144,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
 {
        u32                     reg;
 
-       dev_vdbg(dwc->dev, "%s\n", __func__);
-
        /*
         * WORKAROUND: DWC3 revisions <1.88a have an issue which
         * would cause a missing Disconnect Event if there's a
@@ -2321,8 +2228,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
        u32                     reg;
        u8                      speed;
 
-       dev_vdbg(dwc->dev, "%s\n", __func__);
-
        reg = dwc3_readl(dwc->regs, DWC3_DSTS);
        speed = reg & DWC3_DSTS_CONNECTSPD;
        dwc->speed = speed;
@@ -2420,8 +2325,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 
 static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
 {
-       dev_vdbg(dwc->dev, "%s\n", __func__);
-
        /*
         * TODO take core out of low power mode when that's
         * implemented.
@@ -2526,10 +2429,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
                break;
        }
 
-       dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n",
-                       dwc3_gadget_link_string(dwc->link_state),
-                       dwc->link_state, dwc3_gadget_link_string(next), next);
-
        dwc->link_state = next;
 }
 
@@ -2606,6 +2505,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
 static void dwc3_process_event_entry(struct dwc3 *dwc,
                const union dwc3_event *event)
 {
+       trace_dwc3_event(event->raw);
+
        /* Endpoint IRQ, handle it and return early */
        if (event->type.is_devspec == 0) {
                /* depevt */
@@ -2759,7 +2660,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 
        dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
        if (!dwc->setup_buf) {
-               dev_err(dwc->dev, "failed to allocate setup buffer\n");
                ret = -ENOMEM;
                goto err2;
        }
index a0ee75b68a803bd74d40a925930ff6767c29c392..178ad89822065877c72a53b21fc6684c5e456933 100644 (file)
@@ -103,60 +103,4 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
        return DWC3_DEPCMD_GET_RSC_IDX(res_id);
 }
 
-/**
- * dwc3_gadget_event_string - returns event name
- * @event: the event code
- */
-static inline const char *dwc3_gadget_event_string(u8 event)
-{
-       switch (event) {
-       case DWC3_DEVICE_EVENT_DISCONNECT:
-               return "Disconnect";
-       case DWC3_DEVICE_EVENT_RESET:
-               return "Reset";
-       case DWC3_DEVICE_EVENT_CONNECT_DONE:
-               return "Connection Done";
-       case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
-               return "Link Status Change";
-       case DWC3_DEVICE_EVENT_WAKEUP:
-               return "WakeUp";
-       case DWC3_DEVICE_EVENT_EOPF:
-               return "End-Of-Frame";
-       case DWC3_DEVICE_EVENT_SOF:
-               return "Start-Of-Frame";
-       case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
-               return "Erratic Error";
-       case DWC3_DEVICE_EVENT_CMD_CMPL:
-               return "Command Complete";
-       case DWC3_DEVICE_EVENT_OVERFLOW:
-               return "Overflow";
-       }
-
-       return "UNKNOWN";
-}
-
-/**
- * dwc3_ep_event_string - returns event name
- * @event: then event code
- */
-static inline const char *dwc3_ep_event_string(u8 event)
-{
-       switch (event) {
-       case DWC3_DEPEVT_XFERCOMPLETE:
-               return "Transfer Complete";
-       case DWC3_DEPEVT_XFERINPROGRESS:
-               return "Transfer In-Progress";
-       case DWC3_DEPEVT_XFERNOTREADY:
-               return "Transfer Not Ready";
-       case DWC3_DEPEVT_RXTXFIFOEVT:
-               return "FIFO";
-       case DWC3_DEPEVT_STREAMEVT:
-               return "Stream";
-       case DWC3_DEPEVT_EPCMDCMPLT:
-               return "Endpoint Command Complete";
-       }
-
-       return "UNKNOWN";
-}
-
 #endif /* __DRIVERS_USB_DWC3_GADGET_H */
index d94441c14d8cb7ebedff18c986aa0710ffd6d31a..6a79c8e66bbcce87ae1b0d7f38bd345687504894 100644 (file)
 #define __DRIVERS_USB_DWC3_IO_H
 
 #include <linux/io.h>
-
+#include "trace.h"
+#include "debug.h"
 #include "core.h"
 
 static inline u32 dwc3_readl(void __iomem *base, u32 offset)
 {
+       u32 offs = offset - DWC3_GLOBALS_REGS_START;
+       u32 value;
+
        /*
         * We requested the mem region starting from the Globals address
         * space, see dwc3_probe in core.c.
         * However, the offsets are given starting from xHCI address space.
         */
-       return readl(base + (offset - DWC3_GLOBALS_REGS_START));
+       value = readl(base + offs);
+
+       /*
+        * When tracing we want to make it easy to find the correct address on
+        * documentation, so we revert it back to the proper addresses, the
+        * same way they are described on SNPS documentation
+        */
+       dwc3_trace(trace_dwc3_readl, "addr %p value %08x",
+                       base - DWC3_GLOBALS_REGS_START + offset, value);
+
+       return value;
 }
 
 static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
 {
+       u32 offs = offset - DWC3_GLOBALS_REGS_START;
+
        /*
         * We requested the mem region starting from the Globals address
         * space, see dwc3_probe in core.c.
         * However, the offsets are given starting from xHCI address space.
         */
-       writel(value, base + (offset - DWC3_GLOBALS_REGS_START));
+       writel(value, base + offs);
+
+       /*
+        * When tracing we want to make it easy to find the correct address on
+        * documentation, so we revert it back to the proper addresses, the
+        * same way they are described on SNPS documentation
+        */
+       dwc3_trace(trace_dwc3_writel, "addr %p value %08x",
+                       base - DWC3_GLOBALS_REGS_START + offset, value);
 }
 
 #endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c
new file mode 100644 (file)
index 0000000..6cd1664
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * trace.c - DesignWare USB3 DRD Controller Trace Support
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
new file mode 100644 (file)
index 0000000..78aff1d
--- /dev/null
@@ -0,0 +1,220 @@
+/**
+ * trace.h - DesignWare USB3 DRD Controller Trace Support
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM dwc3
+
+#if !defined(__DWC3_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __DWC3_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include "core.h"
+#include "debug.h"
+
+DECLARE_EVENT_CLASS(dwc3_log_msg,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)),
+       TP_fast_assign(
+               vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(dwc3_log_msg, dwc3_readl,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_event,
+       TP_PROTO(u32 event),
+       TP_ARGS(event),
+       TP_STRUCT__entry(
+               __field(u32, event)
+       ),
+       TP_fast_assign(
+               __entry->event = event;
+       ),
+       TP_printk("event %08x\n", __entry->event)
+);
+
+DEFINE_EVENT(dwc3_log_event, dwc3_event,
+       TP_PROTO(u32 event),
+       TP_ARGS(event)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_ctrl,
+       TP_PROTO(struct usb_ctrlrequest *ctrl),
+       TP_ARGS(ctrl),
+       TP_STRUCT__entry(
+               __field(struct usb_ctrlrequest *, ctrl)
+       ),
+       TP_fast_assign(
+               __entry->ctrl = ctrl;
+       ),
+       TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d",
+               __entry->ctrl->bRequestType, __entry->ctrl->bRequest,
+               le16_to_cpu(__entry->ctrl->wValue), le16_to_cpu(__entry->ctrl->wIndex),
+               le16_to_cpu(__entry->ctrl->wLength)
+       )
+);
+
+DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
+       TP_PROTO(struct usb_ctrlrequest *ctrl),
+       TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_request,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req),
+       TP_STRUCT__entry(
+               __field(struct dwc3_request *, req)
+       ),
+       TP_fast_assign(
+               __entry->req = req;
+       ),
+       TP_printk("%s: req %p length %u/%u ==> %d",
+               __entry->req->dep->name, __entry->req,
+               __entry->req->request.actual, __entry->req->request.length,
+               __entry->req->request.status
+       )
+);
+
+DEFINE_EVENT(dwc3_log_request, dwc3_alloc_request,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req)
+);
+
+DEFINE_EVENT(dwc3_log_request, dwc3_free_request,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req)
+);
+
+DEFINE_EVENT(dwc3_log_request, dwc3_ep_queue,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req)
+);
+
+DEFINE_EVENT(dwc3_log_request, dwc3_ep_dequeue,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req)
+);
+
+DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
+       TP_PROTO(struct dwc3_request *req),
+       TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
+       TP_PROTO(unsigned int cmd, u32 param),
+       TP_ARGS(cmd, param),
+       TP_STRUCT__entry(
+               __field(unsigned int, cmd)
+               __field(u32, param)
+       ),
+       TP_fast_assign(
+               __entry->cmd = cmd;
+               __entry->param = param;
+       ),
+       TP_printk("cmd '%s' [%d] param %08x\n",
+               dwc3_gadget_generic_cmd_string(__entry->cmd),
+               __entry->cmd, __entry->param
+       )
+);
+
+DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
+       TP_PROTO(unsigned int cmd, u32 param),
+       TP_ARGS(cmd, param)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
+       TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
+               struct dwc3_gadget_ep_cmd_params *params),
+       TP_ARGS(dep, cmd, params),
+       TP_STRUCT__entry(
+               __field(struct dwc3_ep *, dep)
+               __field(unsigned int, cmd)
+               __field(struct dwc3_gadget_ep_cmd_params *, params)
+       ),
+       TP_fast_assign(
+               __entry->dep = dep;
+               __entry->cmd = cmd;
+               __entry->params = params;
+       ),
+       TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n",
+               __entry->dep->name, dwc3_gadget_ep_cmd_string(__entry->cmd),
+               __entry->cmd, __entry->params->param0,
+               __entry->params->param1, __entry->params->param2
+       )
+);
+
+DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd,
+       TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
+               struct dwc3_gadget_ep_cmd_params *params),
+       TP_ARGS(dep, cmd, params)
+);
+
+DECLARE_EVENT_CLASS(dwc3_log_trb,
+       TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
+       TP_ARGS(dep, trb),
+       TP_STRUCT__entry(
+               __field(struct dwc3_ep *, dep)
+               __field(struct dwc3_trb *, trb)
+       ),
+       TP_fast_assign(
+               __entry->dep = dep;
+               __entry->trb = trb;
+       ),
+       TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n",
+               __entry->dep->name, __entry->trb, __entry->trb->bph,
+               __entry->trb->bpl, __entry->trb->size, __entry->trb->ctrl
+       )
+);
+
+DEFINE_EVENT(dwc3_log_trb, dwc3_prepare_trb,
+       TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
+       TP_ARGS(dep, trb)
+);
+
+DEFINE_EVENT(dwc3_log_trb, dwc3_complete_trb,
+       TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
+       TP_ARGS(dep, trb)
+);
+
+#endif /* __DWC3_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>
index 5c822afb6d70fc1ae2b8308678b1c0a400d70d23..68302aa604be37fa8add350ac2aff30c2f7399a1 100644 (file)
@@ -181,6 +181,12 @@ config USB_F_MASS_STORAGE
 config USB_F_FS
        tristate
 
+config USB_F_UAC1
+       tristate
+
+config USB_F_UAC2
+       tristate
+
 choice
        tristate "USB Gadget Drivers"
        default USB_ETH
index 9add915d41f73521d369279bdbcd443d9d552fa2..598a67d6ba05fcc5c5ebd159f1384725acf25c8b 100644 (file)
@@ -3,7 +3,7 @@
 #
 subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG)      := -DDEBUG
 subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE)    += -DVERBOSE_DEBUG
-ccflags-y                              += -Idrivers/usb/gadget/udc
+ccflags-y                              += -I$(srctree)/drivers/usb/gadget/udc
 
 obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
 libcomposite-y                 := usbstring.o config.o epautoconf.o
index 6935a822ce2b871553ff7074b7a0f781f057b4b3..4514e73d9e7090c33bcbb82da4093598f408a185 100644 (file)
@@ -1955,8 +1955,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
                usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
        }
        if (cdev->req) {
-               kfree(cdev->req->buf);
                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;
index 83ae1065149d3dd21f91bef6902168b9908f73ff..a49fdf7b59e1efdb65ae7d086b1c75044bc731a3 100644 (file)
@@ -2,8 +2,8 @@
 # USB peripheral controller drivers
 #
 
-ccflags-y                      := -Idrivers/usb/gadget/
-ccflags-y                      += -Idrivers/usb/gadget/udc/
+ccflags-y                      := -I$(srctree)/drivers/usb/gadget/
+ccflags-y                      += -I$(srctree)/drivers/usb/gadget/udc/
 
 # USB Functions
 usb_f_acm-y                    := f_acm.o
@@ -32,3 +32,7 @@ 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
+usb_f_uac1-y                   := f_uac1.o u_uac1.o
+obj-$(CONFIG_USB_F_UAC1)       += usb_f_uac1.o
+usb_f_uac2-y                   := f_uac2.o
+obj-$(CONFIG_USB_F_UAC2)       += usb_f_uac2.o
index ab1065afbbd0c0cac55788dad3b805f9a240b2a9..6da4685490ef7668dfdbc8be950cfbd63dd2d3cb 100644 (file)
@@ -313,15 +313,15 @@ static void acm_complete_set_line_coding(struct usb_ep *ep,
        struct usb_composite_dev *cdev = acm->port.func.config->cdev;
 
        if (req->status != 0) {
-               DBG(cdev, "acm ttyGS%d completion, err %d\n",
-                               acm->port_num, req->status);
+               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)) {
-               DBG(cdev, "acm ttyGS%d short resp, len %d\n",
-                               acm->port_num, req->actual);
+               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;
@@ -397,14 +397,16 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
 
        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);
+               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) {
-               DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+               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;
@@ -428,10 +430,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
        if (intf == acm->ctrl_id) {
                if (acm->notify->driver_data) {
-                       VDBG(cdev, "reset acm control interface %d\n", intf);
+                       dev_vdbg(&cdev->gadget->dev,
+                                "reset acm control interface %d\n", intf);
                        usb_ep_disable(acm->notify);
                } else {
-                       VDBG(cdev, "init acm ctrl interface %d\n", intf);
+                       dev_vdbg(&cdev->gadget->dev,
+                                "init acm ctrl interface %d\n", intf);
                        if (config_ep_by_speed(cdev->gadget, f, acm->notify))
                                return -EINVAL;
                }
@@ -440,11 +444,13 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
        } else if (intf == acm->data_id) {
                if (acm->port.in->driver_data) {
-                       DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+                       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) {
-                       DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+                       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,
@@ -467,7 +473,7 @@ static void acm_disable(struct usb_function *f)
        struct f_acm    *acm = func_to_acm(f);
        struct usb_composite_dev *cdev = f->config->cdev;
 
-       DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+       dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num);
        gserial_disconnect(&acm->port);
        usb_ep_disable(acm->notify);
        acm->notify->driver_data = NULL;
@@ -537,8 +543,8 @@ static int acm_notify_serial_state(struct f_acm *acm)
 
        spin_lock(&acm->lock);
        if (acm->notify_req) {
-               DBG(cdev, "acm ttyGS%d serial state %04x\n",
-                               acm->port_num, acm->serial_state);
+               dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
+                       acm->port_num, acm->serial_state);
                status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
                                0, &acm->serial_state, sizeof(acm->serial_state));
        } else {
@@ -691,12 +697,13 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
        if (status)
                goto fail;
 
-       DBG(cdev, "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);
+       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:
index 4557cd03f0b144b7fe8b2535d1d2d6d177173a37..bf04389137e67cbd0790856e08136e968eccd180 100644 (file)
@@ -298,7 +298,8 @@ 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);
+       disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
+                       NULL);
        VDBG(cdev, "%s disabled\n", loop->function.name);
 }
 
index b963939088606e7e9252ec1f55d42ff9fdd1dcd3..811929cd4c9e21b7c59061375e4ad9de335e0c03 100644 (file)
@@ -566,22 +566,22 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
        *pbusy = 1;
        *state = BUF_STATE_BUSY;
        spin_unlock_irq(&fsg->common->lock);
+
        rc = usb_ep_queue(ep, req, GFP_KERNEL);
-       if (rc != 0) {
-               *pbusy = 0;
-               *state = BUF_STATE_EMPTY;
+       if (rc == 0)
+               return;  /* All good, we're done */
 
-               /* We can't do much more than wait for a reset */
+       *pbusy = 0;
+       *state = BUF_STATE_EMPTY;
 
-               /*
-                * 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);
-       }
+       /* We can't do much more than wait for a reset */
+
+       /*
+        * 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);
 }
 
 static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
@@ -3665,4 +3665,3 @@ void fsg_config_from_params(struct fsg_config *cfg,
        cfg->fsg_num_buffers = fsg_num_buffers;
 }
 EXPORT_SYMBOL_GPL(fsg_config_from_params);
-
index aebae1853bcea2b64050ee43ee23feaf39901706..5f40080c92ccfbe8466319cd848c04c6d1b6038d 100644 (file)
@@ -200,19 +200,22 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
                if (alt != 0)
                        goto fail;
                /* NOP */
-               DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
+               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->driver_data) {
-                       DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
+                       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) {
-                       DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
+                       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,
@@ -224,7 +227,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
                }
 
                if (alt == 1) {
-                       DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
+                       dev_dbg(&cdev->gadget->dev,
+                               "activate obex ttyGS%d\n", obex->port_num);
                        gserial_connect(&obex->port, obex->port_num);
                }
 
@@ -252,7 +256,7 @@ static void obex_disable(struct usb_function *f)
        struct f_obex   *obex = func_to_obex(f);
        struct usb_composite_dev *cdev = f->config->cdev;
 
-       DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
+       dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
        gserial_disconnect(&obex->port);
 }
 
@@ -269,7 +273,8 @@ static void obex_connect(struct gserial *g)
 
        status = usb_function_activate(&g->func);
        if (status)
-               DBG(cdev, "obex ttyGS%d function activate --> %d\n",
+               dev_dbg(&cdev->gadget->dev,
+                       "obex ttyGS%d function activate --> %d\n",
                        obex->port_num, status);
 }
 
@@ -284,7 +289,8 @@ static void obex_disconnect(struct gserial *g)
 
        status = usb_function_deactivate(&g->func);
        if (status)
-               DBG(cdev, "obex ttyGS%d function deactivate --> %d\n",
+               dev_dbg(&cdev->gadget->dev,
+                       "obex ttyGS%d function deactivate --> %d\n",
                        obex->port_num, status);
 }
 
@@ -383,10 +389,10 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
                obex->can_activate = true;
 
 
-       DBG(cdev, "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);
+       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;
 
index 9ecbcbf36a45d34168e506e84b498cc1f7331253..2e02dfabc7ae18394315de1f692d6abfc9f0f996 100644 (file)
@@ -155,11 +155,13 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
        /* we know alt == 0, so this is an activation or a reset */
 
        if (gser->port.in->driver_data) {
-               DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
+               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) {
-               DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
+               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;
@@ -176,7 +178,8 @@ static void gser_disable(struct usb_function *f)
        struct f_gser   *gser = func_to_gser(f);
        struct usb_composite_dev *cdev = f->config->cdev;
 
-       DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+       dev_dbg(&cdev->gadget->dev,
+               "generic ttyGS%d deactivated\n", gser->port_num);
        gserial_disconnect(&gser->port);
 }
 
@@ -239,11 +242,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
                        gser_ss_function);
        if (status)
                goto fail;
-       DBG(cdev, "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);
+       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:
index d3cd52db78fe169917d09ebe68dc648a3d8ec529..7c091a3282288476719602737a3d37870e0b108a 100644 (file)
 #include "gadget_chips.h"
 #include "u_f.h"
 
+#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x)
+
+enum eptype {
+       EP_CONTROL = 0,
+       EP_BULK,
+       EP_ISOC,
+       EP_INTERRUPT,
+};
+
 /*
  * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
  * controller drivers.
@@ -55,6 +64,8 @@ struct f_sourcesink {
        struct usb_ep           *out_ep;
        struct usb_ep           *iso_in_ep;
        struct usb_ep           *iso_out_ep;
+       struct usb_ep           *int_in_ep;
+       struct usb_ep           *int_out_ep;
        int                     cur_alt;
 };
 
@@ -68,6 +79,10 @@ static unsigned isoc_interval;
 static unsigned isoc_maxpacket;
 static unsigned isoc_mult;
 static unsigned isoc_maxburst;
+static unsigned int_interval; /* In ms */
+static unsigned int_maxpacket;
+static unsigned int_mult;
+static unsigned int_maxburst;
 static unsigned buflen;
 
 /*-------------------------------------------------------------------------*/
@@ -92,6 +107,16 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = {
        /* .iInterface          = DYNAMIC */
 };
 
+static struct usb_interface_descriptor source_sink_intf_alt2 = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bAlternateSetting =    2,
+       .bNumEndpoints =        2,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       /* .iInterface          = DYNAMIC */
+};
+
 /* full speed support: */
 
 static struct usb_endpoint_descriptor fs_source_desc = {
@@ -130,6 +155,26 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = {
        .bInterval =            4,
 };
 
+static struct usb_endpoint_descriptor fs_int_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(64),
+       .bInterval =            GZERO_INT_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor fs_int_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(64),
+       .bInterval =            GZERO_INT_INTERVAL,
+};
+
 static struct usb_descriptor_header *fs_source_sink_descs[] = {
        (struct usb_descriptor_header *) &source_sink_intf_alt0,
        (struct usb_descriptor_header *) &fs_sink_desc,
@@ -140,6 +185,10 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = {
        (struct usb_descriptor_header *) &fs_source_desc,
        (struct usb_descriptor_header *) &fs_iso_sink_desc,
        (struct usb_descriptor_header *) &fs_iso_source_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt2,
+#define FS_ALT_IFC_2_OFFSET    8
+       (struct usb_descriptor_header *) &fs_int_sink_desc,
+       (struct usb_descriptor_header *) &fs_int_source_desc,
        NULL,
 };
 
@@ -179,6 +228,24 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = {
        .bInterval =            4,
 };
 
+static struct usb_endpoint_descriptor hs_int_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
+};
+
+static struct usb_endpoint_descriptor hs_int_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
+};
+
 static struct usb_descriptor_header *hs_source_sink_descs[] = {
        (struct usb_descriptor_header *) &source_sink_intf_alt0,
        (struct usb_descriptor_header *) &hs_source_desc,
@@ -189,6 +256,10 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = {
        (struct usb_descriptor_header *) &hs_sink_desc,
        (struct usb_descriptor_header *) &hs_iso_source_desc,
        (struct usb_descriptor_header *) &hs_iso_sink_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt2,
+#define HS_ALT_IFC_2_OFFSET    8
+       (struct usb_descriptor_header *) &hs_int_source_desc,
+       (struct usb_descriptor_header *) &hs_int_sink_desc,
        NULL,
 };
 
@@ -264,6 +335,42 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
        .wBytesPerInterval =    cpu_to_le16(1024),
 };
 
+static struct usb_endpoint_descriptor ss_int_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
+};
+
+struct usb_ss_ep_comp_descriptor ss_int_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_int_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_INT,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
+};
+
+struct usb_ss_ep_comp_descriptor ss_int_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,
@@ -280,6 +387,12 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = {
        (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,
+       (struct usb_descriptor_header *) &source_sink_intf_alt2,
+#define SS_ALT_IFC_2_OFFSET    14
+       (struct usb_descriptor_header *) &ss_int_source_desc,
+       (struct usb_descriptor_header *) &ss_int_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_int_sink_desc,
+       (struct usb_descriptor_header *) &ss_int_sink_comp_desc,
        NULL,
 };
 
@@ -301,6 +414,21 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
 };
 
 /*-------------------------------------------------------------------------*/
+static const char *get_ep_string(enum eptype ep_type)
+{
+       switch (ep_type) {
+       case EP_ISOC:
+               return "ISOC-";
+       case EP_INTERRUPT:
+               return "INTERRUPT-";
+       case EP_CONTROL:
+               return "CTRL-";
+       case EP_BULK:
+               return "BULK-";
+       default:
+               return "UNKNOWN-";
+       }
+}
 
 static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
 {
@@ -328,7 +456,8 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
 
 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)
+               struct usb_ep *iso_in, struct usb_ep *iso_out,
+               struct usb_ep *int_in, struct usb_ep *int_out)
 {
        disable_ep(cdev, in);
        disable_ep(cdev, out);
@@ -336,6 +465,10 @@ void disable_endpoints(struct usb_composite_dev *cdev,
                disable_ep(cdev, iso_in);
        if (iso_out)
                disable_ep(cdev, iso_out);
+       if (int_in)
+               disable_ep(cdev, int_in);
+       if (int_out)
+               disable_ep(cdev, int_out);
 }
 
 static int
@@ -352,6 +485,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
                return id;
        source_sink_intf_alt0.bInterfaceNumber = id;
        source_sink_intf_alt1.bInterfaceNumber = id;
+       source_sink_intf_alt2.bInterfaceNumber = id;
 
        /* allocate bulk endpoints */
        ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
@@ -412,14 +546,55 @@ no_iso:
        if (isoc_maxpacket > 1024)
                isoc_maxpacket = 1024;
 
+       /* sanity check the interrupt module parameters */
+       if (int_interval < 1)
+               int_interval = 1;
+       if (int_interval > 4096)
+               int_interval = 4096;
+       if (int_mult > 2)
+               int_mult = 2;
+       if (int_maxburst > 15)
+               int_maxburst = 15;
+
+       /* fill in the FS interrupt descriptors from the module parameters */
+       fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ?
+                                               64 : int_maxpacket;
+       fs_int_source_desc.bInterval = int_interval > 255 ?
+                                               255 : int_interval;
+       fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ?
+                                               64 : int_maxpacket;
+       fs_int_sink_desc.bInterval = int_interval > 255 ?
+                                               255 : int_interval;
+
+       /* allocate int endpoints */
+       ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc);
+       if (!ss->int_in_ep)
+               goto no_int;
+       ss->int_in_ep->driver_data = cdev;      /* claim */
+
+       ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc);
+       if (ss->int_out_ep) {
+               ss->int_out_ep->driver_data = cdev;     /* claim */
+       } else {
+               ss->int_in_ep->driver_data = NULL;
+               ss->int_in_ep = NULL;
+no_int:
+               fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL;
+               hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL;
+               ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL;
+       }
+
+       if (int_maxpacket > 1024)
+               int_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.
+        * Fill in the HS isoc and interrupt 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 = isoc_maxpacket;
        hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
@@ -432,6 +607,17 @@ no_iso:
        hs_iso_sink_desc.bInterval = isoc_interval;
        hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
 
+       hs_int_source_desc.wMaxPacketSize = int_maxpacket;
+       hs_int_source_desc.wMaxPacketSize |= int_mult << 11;
+       hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
+       hs_int_source_desc.bEndpointAddress =
+               fs_int_source_desc.bEndpointAddress;
+
+       hs_int_sink_desc.wMaxPacketSize = int_maxpacket;
+       hs_int_sink_desc.wMaxPacketSize |= int_mult << 11;
+       hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
+       hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
+
        /* support super speed hardware */
        ss_source_desc.bEndpointAddress =
                fs_source_desc.bEndpointAddress;
@@ -439,9 +625,9 @@ no_iso:
                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.
+        * Fill in the SS isoc and interrupt 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 = isoc_maxpacket;
        ss_iso_source_desc.bInterval = isoc_interval;
@@ -460,17 +646,37 @@ no_iso:
                isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
        ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
 
+       ss_int_source_desc.wMaxPacketSize = int_maxpacket;
+       ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
+       ss_int_source_comp_desc.bmAttributes = int_mult;
+       ss_int_source_comp_desc.bMaxBurst = int_maxburst;
+       ss_int_source_comp_desc.wBytesPerInterval =
+               int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
+       ss_int_source_desc.bEndpointAddress =
+               fs_int_source_desc.bEndpointAddress;
+
+       ss_int_sink_desc.wMaxPacketSize = int_maxpacket;
+       ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
+       ss_int_sink_comp_desc.bmAttributes = int_mult;
+       ss_int_sink_comp_desc.bMaxBurst = int_maxburst;
+       ss_int_sink_comp_desc.wBytesPerInterval =
+               int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
+       ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
+
        ret = usb_assign_descriptors(f, fs_source_sink_descs,
                        hs_source_sink_descs, ss_source_sink_descs);
        if (ret)
                return ret;
 
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
+       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, "
+                       "INT-IN/%s, INT-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>");
+                       ss->iso_out_ep ? ss->iso_out_ep->name : "<none>",
+                       ss->int_in_ep ? ss->int_in_ep->name : "<none>",
+                       ss->int_out_ep ? ss->int_out_ep->name : "<none>");
        return 0;
 }
 
@@ -601,14 +807,15 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 }
 
 static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
-               bool is_iso, int speed)
+               enum eptype ep_type, int speed)
 {
        struct usb_ep           *ep;
        struct usb_request      *req;
        int                     i, size, status;
 
        for (i = 0; i < 8; i++) {
-               if (is_iso) {
+               switch (ep_type) {
+               case EP_ISOC:
                        switch (speed) {
                        case USB_SPEED_SUPER:
                                size = isoc_maxpacket * (isoc_mult + 1) *
@@ -624,9 +831,28 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
                        }
                        ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
                        req = ss_alloc_ep_req(ep, size);
-               } else {
+                       break;
+               case EP_INTERRUPT:
+                       switch (speed) {
+                       case USB_SPEED_SUPER:
+                               size = int_maxpacket * (int_mult + 1) *
+                                               (int_maxburst + 1);
+                               break;
+                       case USB_SPEED_HIGH:
+                               size = int_maxpacket * (int_mult + 1);
+                               break;
+                       default:
+                               size = int_maxpacket > 1023 ?
+                                               1023 : int_maxpacket;
+                               break;
+                       }
+                       ep = is_in ? ss->int_in_ep : ss->int_out_ep;
+                       req = ss_alloc_ep_req(ep, size);
+                       break;
+               default:
                        ep = is_in ? ss->in_ep : ss->out_ep;
                        req = ss_alloc_ep_req(ep, 0);
+                       break;
                }
 
                if (!req)
@@ -644,12 +870,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
 
                        cdev = ss->function.config->cdev;
                        ERROR(cdev, "start %s%s %s --> %d\n",
-                             is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
-                             ep->name, status);
+                               get_ep_string(ep_type), is_in ? "IN" : "OUT",
+                               ep->name, status);
                        free_ep_req(ep, req);
                }
 
-               if (!is_iso)
+               if (!(ep_type == EP_ISOC))
                        break;
        }
 
@@ -662,7 +888,7 @@ static void disable_source_sink(struct f_sourcesink *ss)
 
        cdev = ss->function.config->cdev;
        disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
-                       ss->iso_out_ep);
+                       ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep);
        VDBG(cdev, "%s disabled\n", ss->function.name);
 }
 
@@ -674,6 +900,62 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
        int                                     speed = cdev->gadget->speed;
        struct usb_ep                           *ep;
 
+       if (alt == 2) {
+               /* Configure for periodic interrupt endpoint */
+               ep = ss->int_in_ep;
+               if (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, EP_INTERRUPT,
+                                       speed);
+                       if (result < 0) {
+fail1:
+                               ep = ss->int_in_ep;
+                               if (ep) {
+                                       usb_ep_disable(ep);
+                                       ep->driver_data = NULL;
+                               }
+                               return result;
+                       }
+               }
+
+               /*
+                * one interrupt endpoint reads (sinks) anything OUT (from the
+                * host)
+                */
+               ep = ss->int_out_ep;
+               if (ep) {
+                       result = config_ep_by_speed(cdev->gadget,
+                                       &(ss->function), ep);
+                       if (result)
+                               goto fail1;
+
+                       result = usb_ep_enable(ep);
+                       if (result < 0)
+                               goto fail1;
+
+                       ep->driver_data = ss;
+                       result = source_sink_start_ep(ss, false, EP_INTERRUPT,
+                                       speed);
+                       if (result < 0) {
+                               ep = ss->int_out_ep;
+                               usb_ep_disable(ep);
+                               ep->driver_data = NULL;
+                               goto fail1;
+                       }
+               }
+
+               goto out;
+       }
+
        /* one bulk endpoint writes (sources) zeroes IN (to the host) */
        ep = ss->in_ep;
        result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
@@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
                return result;
        ep->driver_data = ss;
 
-       result = source_sink_start_ep(ss, true, false, speed);
+       result = source_sink_start_ep(ss, true, EP_BULK, speed);
        if (result < 0) {
 fail:
                ep = ss->in_ep;
@@ -703,7 +985,7 @@ fail:
                goto fail;
        ep->driver_data = ss;
 
-       result = source_sink_start_ep(ss, false, false, speed);
+       result = source_sink_start_ep(ss, false, EP_BULK, speed);
        if (result < 0) {
 fail2:
                ep = ss->out_ep;
@@ -726,7 +1008,7 @@ fail2:
                        goto fail2;
                ep->driver_data = ss;
 
-               result = source_sink_start_ep(ss, true, true, speed);
+               result = source_sink_start_ep(ss, true, EP_ISOC, speed);
                if (result < 0) {
 fail3:
                        ep = ss->iso_in_ep;
@@ -749,13 +1031,14 @@ fail3:
                        goto fail3;
                ep->driver_data = ss;
 
-               result = source_sink_start_ep(ss, false, true, speed);
+               result = source_sink_start_ep(ss, false, EP_ISOC, speed);
                if (result < 0) {
                        usb_ep_disable(ep);
                        ep->driver_data = NULL;
                        goto fail3;
                }
        }
+
 out:
        ss->cur_alt = alt;
 
@@ -771,6 +1054,8 @@ static int sourcesink_set_alt(struct usb_function *f,
 
        if (ss->in_ep->driver_data)
                disable_source_sink(ss);
+       else if (alt == 2 && ss->int_in_ep->driver_data)
+               disable_source_sink(ss);
        return enable_source_sink(cdev, ss, alt);
 }
 
@@ -883,6 +1168,10 @@ static struct usb_function *source_sink_alloc_func(
        isoc_maxpacket = ss_opts->isoc_maxpacket;
        isoc_mult = ss_opts->isoc_mult;
        isoc_maxburst = ss_opts->isoc_maxburst;
+       int_interval = ss_opts->int_interval;
+       int_maxpacket = ss_opts->int_maxpacket;
+       int_mult = ss_opts->int_mult;
+       int_maxburst = ss_opts->int_maxburst;
        buflen = ss_opts->bulk_buflen;
 
        ss->function.name = "source/sink";
@@ -1179,6 +1468,182 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
                        f_ss_opts_bulk_buflen_show,
                        f_ss_opts_bulk_buflen_store);
 
+static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->int_interval);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       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 > 4096) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       opts->int_interval = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_int_interval =
+       __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR,
+                       f_ss_opts_int_interval_show,
+                       f_ss_opts_int_interval_store);
+
+static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->int_maxpacket);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       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->int_maxpacket = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_int_maxpacket =
+       __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR,
+                       f_ss_opts_int_maxpacket_show,
+                       f_ss_opts_int_maxpacket_store);
+
+static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->int_mult);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       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->int_mult = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_int_mult =
+       __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR,
+                       f_ss_opts_int_mult_show,
+                       f_ss_opts_int_mult_store);
+
+static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, "%d", opts->int_maxburst);
+       mutex_unlock(&opts->lock);
+
+       return result;
+}
+
+static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts,
+                                      const char *page, size_t len)
+{
+       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->int_maxburst = num;
+       ret = len;
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_int_maxburst =
+       __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR,
+                       f_ss_opts_int_maxburst_show,
+                       f_ss_opts_int_maxburst_store);
+
 static struct configfs_attribute *ss_attrs[] = {
        &f_ss_opts_pattern.attr,
        &f_ss_opts_isoc_interval.attr,
@@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = {
        &f_ss_opts_isoc_mult.attr,
        &f_ss_opts_isoc_maxburst.attr,
        &f_ss_opts_bulk_buflen.attr,
+       &f_ss_opts_int_interval.attr,
+       &f_ss_opts_int_maxpacket.attr,
+       &f_ss_opts_int_mult.attr,
+       &f_ss_opts_int_maxburst.attr,
        NULL,
 };
 
@@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
        ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
        ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
        ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+       ss_opts->int_interval = GZERO_INT_INTERVAL;
+       ss_opts->int_maxpacket = GZERO_INT_MAXPACKET;
 
        config_group_init_type_name(&ss_opts->func_inst.group, "",
                                    &ss_func_type);
index 2b4c82d84bfcf3da693197496d28971fc6f73e31..f7b20329320583d05c096882d8e04cec7d905097 100644 (file)
 
 #include <linux/slab.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/device.h>
 #include <linux/atomic.h>
 
 #include "u_uac1.h"
 
-#define OUT_EP_MAX_PACKET_SIZE 200
-static int req_buf_size = 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 = 256;
-module_param(req_count, int, S_IRUGO);
-MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
-
-static int audio_buf_size = 48000;
-module_param(audio_buf_size, int, S_IRUGO);
-MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
-
 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);
 
@@ -46,7 +34,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
 #define F_AUDIO_NUM_INTERFACES 2
 
 /* B.3.1  Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
+static struct usb_interface_descriptor ac_interface_desc = {
        .bLength =              USB_DT_INTERFACE_SIZE,
        .bDescriptorType =      USB_DT_INTERFACE,
        .bNumEndpoints =        0,
@@ -183,12 +171,12 @@ static struct usb_endpoint_descriptor as_out_ep_desc  = {
        .bEndpointAddress =     USB_DIR_OUT,
        .bmAttributes =         USB_ENDPOINT_SYNC_ADAPTIVE
                                | USB_ENDPOINT_XFER_ISOC,
-       .wMaxPacketSize =       __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
+       .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 __initdata = {
+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,
@@ -197,7 +185,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
        .wLockDelay =           __constant_cpu_to_le16(1),
 };
 
-static struct usb_descriptor_header *f_audio_desc[] __initdata = {
+static struct usb_descriptor_header *f_audio_desc[] = {
        (struct usb_descriptor_header *)&ac_interface_desc,
        (struct usb_descriptor_header *)&ac_header_desc,
 
@@ -216,6 +204,37 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = {
        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.
  */
@@ -300,8 +319,14 @@ 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_opts *opts;
+       int audio_buf_size;
        int err;
 
+       opts = container_of(audio->card.func.fi, struct f_uac1_opts,
+                           func_inst);
+       audio_buf_size = opts->audio_buf_size;
+
        if (!copy_buf)
                return -EINVAL;
 
@@ -546,10 +571,17 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
        struct usb_composite_dev *cdev = f->config->cdev;
        struct usb_ep *out_ep = audio->out_ep;
        struct usb_request *req;
+       struct f_uac1_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_opts, func_inst);
+       req_buf_size = opts->req_buf_size;
+       req_count = opts->req_count;
+       audio_buf_size = opts->audio_buf_size;
+
        if (intf == 1) {
                if (alt == 1) {
                        usb_ep_enable(out_ep);
@@ -625,13 +657,37 @@ static void f_audio_build_desc(struct f_audio *audio)
 }
 
 /* audio function driver setup/binding */
-static int __init
+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_opts      *audio_opts;
+
+       audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+       audio->card.gadget = c->cdev->gadget;
+       audio_opts->card = &audio->card;
+       /* 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);
 
@@ -666,20 +722,12 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
        return 0;
 
 fail:
+       gaudio_cleanup(&audio->card);
        if (ep)
                ep->driver_data = NULL;
        return status;
 }
 
-static void
-f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
-{
-       struct f_audio          *audio = func_to_audio(f);
-
-       usb_free_all_descriptors(f);
-       kfree(audio);
-}
-
 /*-------------------------------------------------------------------------*/
 
 static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
@@ -695,7 +743,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
 }
 
 /* Todo: add more control selecotor dynamically */
-static int __init control_selector_init(struct f_audio *audio)
+static int control_selector_init(struct f_audio *audio)
 {
        INIT_LIST_HEAD(&audio->cs);
        list_add(&feature_unit.list, &audio->cs);
@@ -712,57 +760,226 @@ static int __init control_selector_init(struct f_audio *audio)
        return 0;
 }
 
-/**
- * audio_bind_config - add USB audio function to a configuration
- * @c: the configuration to supcard the USB audio function
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-static int __init audio_bind_config(struct usb_configuration *c)
+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);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uac1_opts);
+CONFIGFS_ATTR_OPS(f_uac1_opts);
+
+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,
+       .show_attribute = f_uac1_opts_attr_show,
+       .store_attribute = f_uac1_opts_attr_store,
+};
+
+#define UAC1_INT_ATTRIBUTE(name)                                       \
+static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts,     \
+                                        char *page)                    \
+{                                                                      \
+       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 f_uac1_opts *opts,    \
+                                         const char *page, size_t len) \
+{                                                                      \
+       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;                                                     \
+}                                                                      \
+                                                                       \
+static struct f_uac1_opts_attribute f_uac1_opts_##name =               \
+       __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR,                        \
+                       f_uac1_opts_##name##_show,                      \
+                       f_uac1_opts_##name##_store)
+
+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 f_uac1_opts *opts,     \
+                                        char *page)                    \
+{                                                                      \
+       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 f_uac1_opts *opts,    \
+                                         const char *page, size_t len) \
+{                                                                      \
+       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;                                                     \
+}                                                                      \
+                                                                       \
+static struct f_uac1_opts_attribute f_uac1_opts_##name =               \
+       __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR,                        \
+                       f_uac1_opts_##name##_show,                      \
+                       f_uac1_opts_##name##_store)
+
+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_req_buf_size.attr,
+       &f_uac1_opts_req_count.attr,
+       &f_uac1_opts_audio_buf_size.attr,
+       &f_uac1_opts_fn_play.attr,
+       &f_uac1_opts_fn_cap.attr,
+       &f_uac1_opts_fn_cntl.attr,
+       NULL,
+};
+
+static 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);
+       gaudio_cleanup(opts->card);
+       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_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_opts *opts;
+
+       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)
+{
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
 {
        struct f_audio *audio;
-       int status;
+       struct f_uac1_opts *opts;
 
        /* allocate and initialize one new instance */
-       audio = kzalloc(sizeof *audio, GFP_KERNEL);
+       audio = kzalloc(sizeof(*audio), GFP_KERNEL);
        if (!audio)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        audio->card.func.name = "g_audio";
-       audio->card.gadget = c->cdev->gadget;
 
+       opts = container_of(fi, struct f_uac1_opts, func_inst);
+       mutex_lock(&opts->lock);
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
        INIT_LIST_HEAD(&audio->play_queue);
        spin_lock_init(&audio->lock);
 
-       /* set up ASLA audio devices */
-       status = gaudio_setup(&audio->card);
-       if (status < 0)
-               goto setup_fail;
-
-       audio->card.func.strings = audio_strings;
        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.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);
 
-       status = usb_add_function(c, &audio->card.func);
-       if (status)
-               goto add_fail;
-
-       INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
-               audio_buf_size, req_buf_size, req_count);
-
-       return status;
-
-add_fail:
-       gaudio_cleanup();
-setup_fail:
-       kfree(audio);
-       return status;
+       return &audio->card.func;
 }
+
+DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bryan Wu");
index 3ed89ecc8d6dc9a3afdf5f3be82d590236501611..a5a27a504d67099e09670cd7ca9e48355c1c4f70 100644 (file)
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 
-/* Playback(USB-IN) Default Stereo - Fl/Fr */
-static int p_chmask = 0x3;
-module_param(p_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
-
-/* Playback Default 48 KHz */
-static int p_srate = 48000;
-module_param(p_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
-
-/* Playback Default 16bits/sample */
-static int p_ssize = 2;
-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 = 0x3;
-module_param(c_chmask, uint, S_IRUGO);
-MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
-
-/* Capture Default 64 KHz */
-static int c_srate = 64000;
-module_param(c_srate, uint, S_IRUGO);
-MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
-
-/* Capture Default 16bits/sample */
-static int c_ssize = 2;
-module_param(c_ssize, uint, S_IRUGO);
-MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
+#include "u_uac2.h"
 
 /* Keep everyone on toes */
 #define USB_XFERS      2
@@ -120,6 +92,15 @@ struct snd_uac2_chip {
 
        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;
 };
 
 #define BUFF_SIZE_MAX  (PAGE_SIZE * 16)
@@ -149,8 +130,6 @@ struct audio_dev {
        struct snd_uac2_chip uac2;
 };
 
-static struct audio_dev *agdev_g;
-
 static inline
 struct audio_dev *func_to_agdev(struct usb_function *f)
 {
@@ -169,6 +148,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
        return container_of(p, struct snd_uac2_chip, pdev);
 }
 
+static inline
+struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev)
+{
+       return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
+}
+
 static inline
 uint num_channels(uint chanmask)
 {
@@ -187,8 +172,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
 {
        unsigned pending;
        unsigned long flags;
+       unsigned int hw_ptr;
        bool update_alsa = false;
-       unsigned char *src, *dst;
        int status = req->status;
        struct uac2_req *ur = req->context;
        struct snd_pcm_substream *substream;
@@ -216,12 +201,27 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
        spin_lock_irqsave(&prm->lock, flags);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               src = prm->dma_area + prm->hw_ptr;
+               /*
+                * 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 = uac2->p_pktsize;
+               uac2->p_residue += uac2->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 (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
+                       req->length += uac2->p_framesize;
+                       uac2->p_residue -= uac2->p_framesize *
+                                          uac2->p_interval;
+               }
+
                req->actual = req->length;
-               dst = req->buf;
-       } else {
-               dst = prm->dma_area + prm->hw_ptr;
-               src = req->buf;
        }
 
        pending = prm->hw_ptr % prm->period_size;
@@ -229,12 +229,32 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
        if (pending >= prm->period_size)
                update_alsa = true;
 
+       hw_ptr = prm->hw_ptr;
        prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
 
        spin_unlock_irqrestore(&prm->lock, flags);
 
        /* Pack USB load in ALSA ring buffer */
-       memcpy(dst, src, req->actual);
+       pending = prm->dma_bytes - hw_ptr;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (unlikely(pending < req->actual)) {
+                       memcpy(req->buf, prm->dma_area + hw_ptr, pending);
+                       memcpy(req->buf + pending, prm->dma_area,
+                              req->actual - pending);
+               } else {
+                       memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
+               }
+       } else {
+               if (unlikely(pending < req->actual)) {
+                       memcpy(prm->dma_area + hw_ptr, req->buf, pending);
+                       memcpy(prm->dma_area, req->buf + pending,
+                              req->actual - pending);
+               } else {
+                       memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
+               }
+       }
+
 exit:
        if (usb_ep_queue(ep, req, GFP_ATOMIC))
                dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
@@ -342,6 +362,21 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
+       struct audio_dev *audio_dev;
+       struct f_uac2_opts *opts;
+       int p_ssize, c_ssize;
+       int p_srate, c_srate;
+       int p_chmask, c_chmask;
+
+       audio_dev = uac2_to_agdev(uac2);
+       opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
+       p_ssize = opts->p_ssize;
+       c_ssize = opts->c_ssize;
+       p_srate = opts->p_srate;
+       c_srate = opts->c_srate;
+       p_chmask = opts->p_chmask;
+       c_chmask = opts->c_chmask;
+       uac2->p_residue = 0;
 
        runtime->hw = uac2_pcm_hardware;
 
@@ -411,7 +446,15 @@ static int snd_uac2_probe(struct platform_device *pdev)
        struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
        struct snd_card *card;
        struct snd_pcm *pcm;
+       struct audio_dev *audio_dev;
+       struct f_uac2_opts *opts;
        int err;
+       int p_chmask, c_chmask;
+
+       audio_dev = uac2_to_agdev(uac2);
+       opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
+       p_chmask = opts->p_chmask;
+       c_chmask = opts->c_chmask;
 
        /* Choose any slot, with no id */
        err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
@@ -919,20 +962,58 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
                        "%s:%d Error!\n", __func__, __LINE__);
 }
 
-static int __init
+static int
 afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 {
        struct audio_dev *agdev = func_to_agdev(fn);
        struct snd_uac2_chip *uac2 = &agdev->uac2;
        struct usb_composite_dev *cdev = cfg->cdev;
        struct usb_gadget *gadget = cdev->gadget;
+       struct device *dev = &uac2->pdev.dev;
        struct uac2_rtd_params *prm;
+       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(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return ret;
        }
        std_ac_if_desc.bInterfaceNumber = ret;
@@ -941,8 +1022,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 
        ret = usb_interface_id(cfg, fn);
        if (ret < 0) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return ret;
        }
        std_as_out_if0_desc.bInterfaceNumber = ret;
@@ -952,8 +1032,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 
        ret = usb_interface_id(cfg, fn);
        if (ret < 0) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return ret;
        }
        std_as_in_if0_desc.bInterfaceNumber = ret;
@@ -963,16 +1042,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
 
        agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
        if (!agdev->out_ep) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                goto err;
        }
        agdev->out_ep->driver_data = agdev;
 
        agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
        if (!agdev->in_ep) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                goto err;
        }
        agdev->in_ep->driver_data = agdev;
@@ -1020,27 +1097,6 @@ err:
        return -EINVAL;
 }
 
-static void
-afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn)
-{
-       struct audio_dev *agdev = func_to_agdev(fn);
-       struct uac2_rtd_params *prm;
-
-       alsa_uac2_exit(agdev);
-
-       prm = &agdev->uac2.p_prm;
-       kfree(prm->rbuf);
-
-       prm = &agdev->uac2.c_prm;
-       kfree(prm->rbuf);
-       usb_free_all_descriptors(fn);
-
-       if (agdev->in_ep)
-               agdev->in_ep->driver_data = NULL;
-       if (agdev->out_ep)
-               agdev->out_ep->driver_data = NULL;
-}
-
 static int
 afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
 {
@@ -1048,23 +1104,22 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
        struct audio_dev *agdev = func_to_agdev(fn);
        struct snd_uac2_chip *uac2 = &agdev->uac2;
        struct usb_gadget *gadget = cdev->gadget;
+       struct device *dev = &uac2->pdev.dev;
        struct usb_request *req;
        struct usb_ep *ep;
        struct uac2_rtd_params *prm;
-       int i;
+       int req_len, i;
 
        /* No i/f has more than 2 alt settings */
        if (alt > 1) {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return -EINVAL;
        }
 
        if (intf == agdev->ac_intf) {
                /* Control I/f has only 1 AltSetting - 0 */
                if (alt) {
-                       dev_err(&uac2->pdev.dev,
-                               "%s:%d Error!\n", __func__, __LINE__);
+                       dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                        return -EINVAL;
                }
                return 0;
@@ -1075,14 +1130,43 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
                prm = &uac2->c_prm;
                config_ep_by_speed(gadget, fn, ep);
                agdev->as_out_alt = alt;
+               req_len = prm->max_psize;
        } else if (intf == agdev->as_in_intf) {
+               struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
+               unsigned int factor, rate;
+               struct usb_endpoint_descriptor *ep_desc;
+
                ep = agdev->in_ep;
                prm = &uac2->p_prm;
                config_ep_by_speed(gadget, fn, ep);
                agdev->as_in_alt = alt;
+
+               /* pre-calculate the playback endpoint's interval */
+               if (gadget->speed == USB_SPEED_FULL) {
+                       ep_desc = &fs_epin_desc;
+                       factor = 1000;
+               } else {
+                       ep_desc = &hs_epin_desc;
+                       factor = 125;
+               }
+
+               /* pre-compute some values for iso_complete() */
+               uac2->p_framesize = opts->p_ssize *
+                                   num_channels(opts->p_chmask);
+               rate = opts->p_srate * uac2->p_framesize;
+               uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
+               uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
+                                       prm->max_psize);
+
+               if (uac2->p_pktsize < prm->max_psize)
+                       uac2->p_pktsize_residue = rate % uac2->p_interval;
+               else
+                       uac2->p_pktsize_residue = 0;
+
+               req_len = uac2->p_pktsize;
+               uac2->p_residue = 0;
        } else {
-               dev_err(&uac2->pdev.dev,
-                       "%s:%d Error!\n", __func__, __LINE__);
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
                return -EINVAL;
        }
 
@@ -1095,31 +1179,23 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
        usb_ep_enable(ep);
 
        for (i = 0; i < USB_XFERS; i++) {
-               if (prm->ureq[i].req) {
-                       if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
-                               dev_err(&uac2->pdev.dev, "%d Error!\n",
-                                       __LINE__);
-                       continue;
-               }
-
-               req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-               if (req == NULL) {
-                       dev_err(&uac2->pdev.dev,
-                               "%s:%d Error!\n", __func__, __LINE__);
-                       return -EINVAL;
+               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 = agdev_iso_complete;
+                       req->buf = prm->rbuf + i * prm->max_psize;
                }
 
-               prm->ureq[i].req = req;
-               prm->ureq[i].pp = prm;
-
-               req->zero = 0;
-               req->context = &prm->ureq[i];
-               req->length = prm->max_psize;
-               req->complete = agdev_iso_complete;
-               req->buf = prm->rbuf + i * req->length;
-
-               if (usb_ep_queue(ep, req, GFP_ATOMIC))
-                       dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
+               if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+                       dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
        }
 
        return 0;
@@ -1164,12 +1240,18 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
        struct usb_request *req = fn->config->cdev->req;
        struct audio_dev *agdev = func_to_agdev(fn);
        struct snd_uac2_chip *uac2 = &agdev->uac2;
+       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 = agdev_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;
@@ -1199,6 +1281,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
        struct usb_request *req = fn->config->cdev->req;
        struct audio_dev *agdev = func_to_agdev(fn);
        struct snd_uac2_chip *uac2 = &agdev->uac2;
+       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);
@@ -1206,6 +1289,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
        u8 control_selector = w_value >> 8;
        struct cntrl_range_lay3 r;
        int value = -EOPNOTSUPP;
+       int p_srate, c_srate;
+
+       opts = agdev_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)
@@ -1309,66 +1397,184 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
        return value;
 }
 
-static int audio_bind_config(struct usb_configuration *cfg)
+static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item)
 {
-       int res;
-
-       agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
-       if (agdev_g == NULL)
-               return -ENOMEM;
-
-       res = usb_string_ids_tab(cfg->cdev, strings_fn);
-       if (res)
-               return res;
-       iad_desc.iFunction = strings_fn[STR_ASSOC].id;
-       std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id;
-       in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id;
-       out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id;
-       usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id;
-       io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id;
-       usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id;
-       io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id;
-       std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id;
-       std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id;
-       std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id;
-       std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id;
-
-       agdev_g->func.name = "uac2_func";
-       agdev_g->func.strings = fn_strings;
-       agdev_g->func.bind = afunc_bind;
-       agdev_g->func.unbind = afunc_unbind;
-       agdev_g->func.set_alt = afunc_set_alt;
-       agdev_g->func.get_alt = afunc_get_alt;
-       agdev_g->func.disable = afunc_disable;
-       agdev_g->func.setup = afunc_setup;
+       return container_of(to_config_group(item), struct f_uac2_opts,
+                           func_inst.group);
+}
 
-       /* Initialize the configurable parameters */
-       usb_out_it_desc.bNrChannels = num_channels(c_chmask);
-       usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask);
-       io_in_it_desc.bNrChannels = num_channels(p_chmask);
-       io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask);
-       as_out_hdr_desc.bNrChannels = num_channels(c_chmask);
-       as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask);
-       as_in_hdr_desc.bNrChannels = num_channels(p_chmask);
-       as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask);
-       as_out_fmt1_desc.bSubslotSize = c_ssize;
-       as_out_fmt1_desc.bBitResolution = c_ssize * 8;
-       as_in_fmt1_desc.bSubslotSize = p_ssize;
-       as_in_fmt1_desc.bBitResolution = p_ssize * 8;
-
-       snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate);
-       snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate);
-
-       res = usb_add_function(cfg, &agdev_g->func);
-       if (res < 0)
-               kfree(agdev_g);
-
-       return res;
+CONFIGFS_ATTR_STRUCT(f_uac2_opts);
+CONFIGFS_ATTR_OPS(f_uac2_opts);
+
+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 void
-uac2_unbind_config(struct usb_configuration *cfg)
+static struct configfs_item_operations f_uac2_item_ops = {
+       .release        = f_uac2_attr_release,
+       .show_attribute = f_uac2_opts_attr_show,
+       .store_attribute = f_uac2_opts_attr_store,
+};
+
+#define UAC2_ATTRIBUTE(name)                                           \
+static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts,     \
+                                        char *page)                    \
+{                                                                      \
+       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 f_uac2_opts *opts,    \
+                                         const char *page, size_t len) \
+{                                                                      \
+       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;                                                     \
+}                                                                      \
+                                                                       \
+static struct f_uac2_opts_attribute f_uac2_opts_##name =               \
+       __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR,                        \
+                       f_uac2_opts_##name##_show,                      \
+                       f_uac2_opts_##name##_store)
+
+UAC2_ATTRIBUTE(p_chmask);
+UAC2_ATTRIBUTE(p_srate);
+UAC2_ATTRIBUTE(p_ssize);
+UAC2_ATTRIBUTE(c_chmask);
+UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE(c_ssize);
+
+static struct configfs_attribute *f_uac2_attrs[] = {
+       &f_uac2_opts_p_chmask.attr,
+       &f_uac2_opts_p_srate.attr,
+       &f_uac2_opts_p_ssize.attr,
+       &f_uac2_opts_c_chmask.attr,
+       &f_uac2_opts_c_srate.attr,
+       &f_uac2_opts_c_ssize.attr,
+       NULL,
+};
+
+static 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)
 {
-       kfree(agdev_g);
-       agdev_g = NULL;
+       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;
+       return &opts->func_inst;
+}
+
+static void afunc_free(struct usb_function *f)
+{
+       struct audio_dev *agdev;
+       struct f_uac2_opts *opts;
+
+       agdev = func_to_agdev(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 audio_dev *agdev = func_to_agdev(f);
+       struct uac2_rtd_params *prm;
+
+       alsa_uac2_exit(agdev);
+
+       prm = &agdev->uac2.p_prm;
+       kfree(prm->rbuf);
+
+       prm = &agdev->uac2.c_prm;
+       kfree(prm->rbuf);
+       usb_free_all_descriptors(f);
+
+       if (agdev->in_ep)
+               agdev->in_ep->driver_data = NULL;
+       if (agdev->out_ep)
+               agdev->out_ep->driver_data = NULL;
+}
+
+struct usb_function *afunc_alloc(struct usb_function_instance *fi)
+{
+       struct audio_dev *agdev;
+       struct f_uac2_opts *opts;
+
+       agdev = kzalloc(sizeof(*agdev), GFP_KERNEL);
+       if (agdev == 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);
+
+       agdev->func.name = "uac2_func";
+       agdev->func.bind = afunc_bind;
+       agdev->func.unbind = afunc_unbind;
+       agdev->func.set_alt = afunc_set_alt;
+       agdev->func.get_alt = afunc_get_alt;
+       agdev->func.disable = afunc_disable;
+       agdev->func.setup = afunc_setup;
+       agdev->func.free_func = afunc_free;
+
+       return &agdev->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yadwinder Singh");
+MODULE_AUTHOR("Jaswinder Singh");
index e2a1f50bd93c4b7740c6592d5446c965ec107b04..187c3a04cf7043ab9bb5580f1c477935005d06e4 100644 (file)
 #include "uvc.h"
 
 unsigned int uvc_gadget_trace_param;
-
-/*-------------------------------------------------------------------------*/
-
-/* 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_interval;
+static unsigned int streaming_maxpacket;
 static unsigned int streaming_maxburst;
-module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
 
 /* --------------------------------------------------------------------------
  * Function descriptors
@@ -251,6 +239,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
        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));
@@ -408,7 +402,9 @@ uvc_register_video(struct uvc_device *uvc)
 
        video->v4l2_dev = &uvc->v4l2_dev;
        video->fops = &uvc_v4l2_fops;
+       video->ioctl_ops = &uvc_v4l2_ioctl_ops;
        video->release = video_device_release;
+       video->vfl_dir = VFL_DIR_TX;
        strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
 
        uvc->vdev = video;
@@ -720,10 +716,9 @@ error:
        if (uvc->video.ep)
                uvc->video.ep->driver_data = NULL;
 
-       if (uvc->control_req) {
+       if (uvc->control_req)
                usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
-               kfree(uvc->control_buf);
-       }
+       kfree(uvc->control_buf);
 
        usb_free_all_descriptors(f);
        return ret;
@@ -749,7 +744,9 @@ uvc_bind_config(struct usb_configuration *c,
                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)
+               const struct uvc_descriptor_header * const *ss_streaming,
+               unsigned int stream_interv, unsigned int stream_maxpkt,
+               unsigned int stream_maxburst, unsigned int trace)
 {
        struct uvc_device *uvc;
        int ret = 0;
@@ -787,6 +784,10 @@ uvc_bind_config(struct usb_configuration *c,
            ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
                goto error;
 
+       streaming_interval = stream_interv;
+       streaming_maxpacket = stream_maxpkt;
+       streaming_maxburst = stream_maxburst;
+       uvc_gadget_trace_param = trace;
        uvc->desc.fs_control = fs_control;
        uvc->desc.ss_control = ss_control;
        uvc->desc.fs_streaming = fs_streaming;
@@ -831,6 +832,4 @@ error:
        return ret;
 }
 
-module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
 
index ec52752f7326d2d422be5e8ea054bb4932f113dc..74b9602ef2d87a7167668a1bcc97a97d2b052a0d 100644 (file)
@@ -21,7 +21,11 @@ int uvc_bind_config(struct usb_configuration *c,
                    const struct uvc_descriptor_header * const *hs_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);
+                   const struct uvc_descriptor_header * const *ss_streaming,
+                   unsigned int streaming_interval_webcam,
+                   unsigned int streaming_maxpacket_webcam,
+                   unsigned int streaming_maxburst_webcam,
+                   unsigned int uvc_gadget_trace_webcam);
 
 #endif /* _F_UVC_H_ */
 
index 15f180904f8a3980e2845c38bf327e5174b8df30..2ce28b9d97cc81a519cff3adb6e07fa89aeb9e2a 100644 (file)
@@ -10,6 +10,8 @@
 #define GZERO_QLEN             32
 #define GZERO_ISOC_INTERVAL    4
 #define GZERO_ISOC_MAXPACKET   1024
+#define GZERO_INT_INTERVAL     1 /* Default interrupt interval = 1 ms */
+#define GZERO_INT_MAXPACKET    1024
 
 struct usb_zero_options {
        unsigned pattern;
@@ -17,6 +19,10 @@ struct usb_zero_options {
        unsigned isoc_maxpacket;
        unsigned isoc_mult;
        unsigned isoc_maxburst;
+       unsigned int_interval; /* In ms */
+       unsigned int_maxpacket;
+       unsigned int_mult;
+       unsigned int_maxburst;
        unsigned bulk_buflen;
        unsigned qlen;
 };
@@ -28,6 +34,10 @@ struct f_ss_opts {
        unsigned isoc_maxpacket;
        unsigned isoc_mult;
        unsigned isoc_maxburst;
+       unsigned int_interval; /* In ms */
+       unsigned int_maxpacket;
+       unsigned int_mult;
+       unsigned int_maxburst;
        unsigned bulk_buflen;
 
        /*
@@ -62,6 +72,7 @@ int lb_modinit(void);
 void free_ep_req(struct usb_ep *ep, struct usb_request *req);
 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);
+               struct usb_ep *iso_in, struct usb_ep *iso_out,
+               struct usb_ep *int_in, struct usb_ep *int_out);
 
 #endif /* __G_ZERO_H */
index ad0aca8120020f92f15186610790d073cdcc94b4..491082aaf1039fa7cdef927a371a5b3514974e16 100644 (file)
  * 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.
- */
-
-#define PREFIX "ttyGS"
-
-/*
+ *
+ *
  * 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
@@ -385,9 +382,9 @@ __acquires(&port->port_lock)
                list_del(&req->list);
                req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
 
-               pr_vdebug(PREFIX "%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));
+               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
@@ -503,13 +500,13 @@ static void gs_rx_push(unsigned long _port)
                switch (req->status) {
                case -ESHUTDOWN:
                        disconnect = true;
-                       pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
+                       pr_vdebug("ttyGS%d: shutdown\n", port->port_num);
                        break;
 
                default:
                        /* presumably a transient fault */
-                       pr_warning(PREFIX "%d: unexpected RX status %d\n",
-                                       port->port_num, req->status);
+                       pr_warn("ttyGS%d: unexpected RX status %d\n",
+                               port->port_num, req->status);
                        /* FALLTHROUGH */
                case 0:
                        /* normal completion */
@@ -537,9 +534,8 @@ static void gs_rx_push(unsigned long _port)
                        if (count != size) {
                                /* stop pushing; TTY layer can't handle more */
                                port->n_read += count;
-                               pr_vdebug(PREFIX "%d: rx block %d/%d\n",
-                                               port->port_num,
-                                               count, req->actual);
+                               pr_vdebug("ttyGS%d: rx block %d/%d\n",
+                                         port->port_num, count, req->actual);
                                break;
                        }
                        port->n_read = 0;
@@ -569,7 +565,7 @@ static void gs_rx_push(unsigned long _port)
                        if (do_push)
                                tasklet_schedule(&port->push);
                        else
-                               pr_warning(PREFIX "%d: RX not scheduled?\n",
+                               pr_warn("ttyGS%d: RX not scheduled?\n",
                                        port->port_num);
                }
        }
@@ -985,7 +981,7 @@ static void gs_unthrottle(struct tty_struct *tty)
                 * read queue backs up enough we'll be NAKing OUT packets.
                 */
                tasklet_schedule(&port->push);
-               pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
+               pr_vdebug("ttyGS%d: unthrottle\n", port->port_num);
        }
        spin_unlock_irqrestore(&port->port_lock, flags);
 }
@@ -1295,7 +1291,7 @@ static int userial_init(void)
                return -ENOMEM;
 
        gs_tty_driver->driver_name = "g_serial";
-       gs_tty_driver->name = PREFIX;
+       gs_tty_driver->name = "ttyGS";
        /* uses dynamically assigned dev_t values */
 
        gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
index 7a55fea43430551b9f7d83d11cebe02a351ccf44..a44a07f30281ed09c527809d0526b7db06bd0628 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/delay.h>
  * This component encapsulates the ALSA devices for USB audio gadget
  */
 
-#define FILE_PCM_PLAYBACK      "/dev/snd/pcmC0D0p"
-#define FILE_PCM_CAPTURE       "/dev/snd/pcmC0D0c"
-#define FILE_CONTROL           "/dev/snd/controlC0"
-
-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");
-
 /*-------------------------------------------------------------------------*/
 
 /**
@@ -167,7 +152,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd)
 /**
  * Playback audio buffer data by ALSA PCM device
  */
-static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
+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;
@@ -202,12 +187,12 @@ try_again:
        return 0;
 }
 
-static int u_audio_get_playback_channels(struct gaudio *card)
+int u_audio_get_playback_channels(struct gaudio *card)
 {
        return card->playback.channels;
 }
 
-static int u_audio_get_playback_rate(struct gaudio *card)
+int u_audio_get_playback_rate(struct gaudio *card)
 {
        return card->playback.rate;
 }
@@ -220,6 +205,13 @@ static int gaudio_open_snd_dev(struct gaudio *card)
 {
        struct snd_pcm_file *pcm_file;
        struct gaudio_snd_dev *snd;
+       struct f_uac1_opts *opts;
+       char *fn_play, *fn_cap, *fn_cntl;
+
+       opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
+       fn_play = opts->fn_play;
+       fn_cap = opts->fn_cap;
+       fn_cntl = opts->fn_cntl;
 
        if (!card)
                return -ENODEV;
@@ -293,7 +285,6 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
        return 0;
 }
 
-static struct gaudio *the_card;
 /**
  * gaudio_setup - setup ALSA interface and preparing for USB transfer
  *
@@ -301,15 +292,13 @@ static struct gaudio *the_card;
  *
  * Returns negative errno, or zero on success
  */
-int __init gaudio_setup(struct gaudio *card)
+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");
-       else if (!the_card)
-               the_card = card;
 
        return ret;
 
@@ -320,11 +309,10 @@ int __init gaudio_setup(struct gaudio *card)
  *
  * This is called to free all resources allocated by @gaudio_setup().
  */
-void gaudio_cleanup(void)
+void gaudio_cleanup(struct gaudio *the_card)
 {
        if (the_card) {
                gaudio_close_snd_dev(the_card);
-               the_card = NULL;
        }
 }
 
index 18c2e729faf6fc203da34bf2a3dd78c64f7ea58e..f8b17fe82efedf054e92e5351bbc92d542e939ac 100644 (file)
 
 #include "gadget_chips.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.
@@ -50,7 +58,28 @@ struct gaudio {
        /* TODO */
 };
 
+struct f_uac1_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 gaudio                   *card;
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
 int gaudio_setup(struct gaudio *card);
-void gaudio_cleanup(void);
+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_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
new file mode 100644 (file)
index 0000000..78dd372
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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
+
+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;
+       bool                            bound;
+
+       struct mutex                    lock;
+       int                             refcnt;
+};
+
+#endif
index 7a9111de80542b0877f9cef36a1f788353b4e2ab..0a283b1237d518a67a615d8a8d49e8c030dde67a 100644 (file)
@@ -96,9 +96,6 @@ extern unsigned int uvc_gadget_trace_param;
  * Driver specific constants
  */
 
-#define DRIVER_VERSION                         "0.1.0"
-#define DRIVER_VERSION_NUMBER                  KERNEL_VERSION(0, 1, 0)
-
 #define UVC_NUM_REQUESTS                       4
 #define UVC_MAX_REQUEST_SIZE                   64
 #define UVC_MAX_EVENTS                         4
index 1c29bc954db9f02f1954073861629e2b9be74c4d..8590f9ff0849d474ecb47c0740cee7ce30f4cab5 100644 (file)
@@ -132,7 +132,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue,
        int ret;
 
        queue->queue.type = type;
-       queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+       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;
index ad48e81155e2c0c838f0092e2da671a7bdaddf5f..14c3a3734b95cbd4d4ae3b06a9d1c6330a65ceb2 100644 (file)
@@ -48,7 +48,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
 }
 
 /* --------------------------------------------------------------------------
- * V4L2
+ * V4L2 ioctls
  */
 
 struct uvc_format
@@ -63,8 +63,29 @@ static struct uvc_format uvc_formats[] = {
 };
 
 static int
-uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
+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->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+       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;
@@ -78,8 +99,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
 }
 
 static int
-uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
+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;
@@ -116,209 +140,184 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
 }
 
 static int
-uvc_v4l2_open(struct file *file)
+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_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);
+       struct uvc_video *video = &uvc->video;
 
-       handle->device = &uvc->video;
-       file->private_data = &handle->vfh;
+       if (b->type != video->queue.queue.type)
+               return -EINVAL;
 
-       uvc_function_connect(uvc);
-       return 0;
+       return uvc_alloc_buffers(&video->queue, b);
 }
 
 static int
-uvc_v4l2_release(struct file *file)
+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_file_handle *handle = to_uvc_file_handle(file->private_data);
-       struct uvc_video *video = handle->device;
-
-       uvc_function_disconnect(uvc);
-
-       uvc_video_enable(video, 0);
-       uvc_free_buffers(&video->queue);
-
-       file->private_data = NULL;
-       v4l2_fh_del(&handle->vfh);
-       v4l2_fh_exit(&handle->vfh);
-       kfree(handle);
+       struct uvc_video *video = &uvc->video;
 
-       return 0;
+       return uvc_query_buffer(&video->queue, b);
 }
 
-static long
-uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+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_file_handle *handle = to_uvc_file_handle(file->private_data);
-       struct usb_composite_dev *cdev = uvc->func.config->cdev;
        struct uvc_video *video = &uvc->video;
-       int ret = 0;
-
-       switch (cmd) {
-       /* Query capabilities */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = arg;
-
-               memset(cap, 0, sizeof *cap);
-               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->version = DRIVER_VERSION_NUMBER;
-               cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
-               break;
-       }
-
-       /* Get & Set format */
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format *fmt = arg;
+       int ret;
 
-               if (fmt->type != video->queue.queue.type)
-                       return -EINVAL;
+       ret = uvc_queue_buffer(&video->queue, b);
+       if (ret < 0)
+               return ret;
 
-               return uvc_v4l2_get_format(video, fmt);
-       }
+       return uvc_video_pump(video);
+}
 
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *fmt = arg;
+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;
 
-               if (fmt->type != video->queue.queue.type)
-                       return -EINVAL;
+       return uvc_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
+}
 
-               return uvc_v4l2_set_format(video, fmt);
-       }
+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;
 
-       /* Buffers & streaming */
-       case VIDIOC_REQBUFS:
-       {
-               struct v4l2_requestbuffers *rb = arg;
+       if (type != video->queue.queue.type)
+               return -EINVAL;
 
-               if (rb->type != video->queue.queue.type)
-                       return -EINVAL;
+       /* Enable UVC video. */
+       ret = uvc_video_enable(video, 1);
+       if (ret < 0)
+               return ret;
 
-               ret = uvc_alloc_buffers(&video->queue, rb);
-               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;
 
-               ret = 0;
-               break;
-       }
-
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer *buf = arg;
+       return 0;
+}
 
-               return uvc_query_buffer(&video->queue, buf);
-       }
+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;
 
-       case VIDIOC_QBUF:
-               if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
-                       return ret;
+       if (type != video->queue.queue.type)
+               return -EINVAL;
 
-               return uvc_video_pump(video);
+       return uvc_video_enable(video, 0);
+}
 
-       case VIDIOC_DQBUF:
-               return uvc_dequeue_buffer(&video->queue, arg,
-                       file->f_flags & O_NONBLOCK);
+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;
 
-       case VIDIOC_STREAMON:
-       {
-               int *type = arg;
+       return v4l2_event_subscribe(fh, sub, 2, NULL);
+}
 
-               if (*type != video->queue.queue.type)
-                       return -EINVAL;
+static int
+uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+                          const struct v4l2_event_subscription *sub)
+{
+       return v4l2_event_unsubscribe(fh, sub);
+}
 
-               /* Enable UVC video. */
-               ret = uvc_video_enable(video, 1);
-               if (ret < 0)
-                       return ret;
+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);
 
-               /*
-                * 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;
+       switch (cmd) {
+       case UVCIOC_SEND_RESPONSE:
+               return uvc_send_response(uvc, arg);
 
-               return 0;
+       default:
+               return -ENOIOCTLCMD;
        }
+}
 
-       case VIDIOC_STREAMOFF:
-       {
-               int *type = arg;
-
-               if (*type != video->queue.queue.type)
-                       return -EINVAL;
+static 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,
+};
 
-               return uvc_video_enable(video, 0);
-       }
+/* --------------------------------------------------------------------------
+ * V4L2
+ */
 
-       /* Events */
-       case VIDIOC_DQEVENT:
-       {
-               struct v4l2_event *event = arg;
-
-               ret = v4l2_event_dequeue(&handle->vfh, event,
-                                        file->f_flags & O_NONBLOCK);
-               if (ret == 0 && event->type == UVC_EVENT_SETUP) {
-                       struct uvc_event *uvc_event = (void *)&event->u.data;
-
-                       /* Tell the complete callback to generate an event for
-                        * the next request that will be enqueued by
-                        * uvc_event_write.
-                        */
-                       uvc->event_setup_out =
-                               !(uvc_event->req.bRequestType & USB_DIR_IN);
-                       uvc->event_length = uvc_event->req.wLength;
-               }
+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;
 
-               return ret;
-       }
+       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (handle == NULL)
+               return -ENOMEM;
 
-       case VIDIOC_SUBSCRIBE_EVENT:
-       {
-               struct v4l2_event_subscription *sub = arg;
+       v4l2_fh_init(&handle->vfh, vdev);
+       v4l2_fh_add(&handle->vfh);
 
-               if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
-                       return -EINVAL;
+       handle->device = &uvc->video;
+       file->private_data = &handle->vfh;
 
-               return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
-       }
+       uvc_function_connect(uvc);
+       return 0;
+}
 
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-               return v4l2_event_unsubscribe(&handle->vfh, arg);
+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;
 
-       case UVCIOC_SEND_RESPONSE:
-               ret = uvc_send_response(uvc, arg);
-               break;
+       uvc_function_disconnect(uvc);
 
-       default:
-               return -ENOIOCTLCMD;
-       }
+       uvc_video_enable(video, 0);
+       uvc_free_buffers(&video->queue);
 
-       return ret;
-}
+       file->private_data = NULL;
+       v4l2_fh_del(&handle->vfh);
+       v4l2_fh_exit(&handle->vfh);
+       kfree(handle);
 
-static long
-uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
+       return 0;
 }
 
 static int
@@ -355,7 +354,7 @@ static struct v4l2_file_operations uvc_v4l2_fops = {
        .owner          = THIS_MODULE,
        .open           = uvc_v4l2_open,
        .release        = uvc_v4l2_release,
-       .ioctl          = uvc_v4l2_ioctl,
+       .ioctl          = video_ioctl2,
        .mmap           = uvc_v4l2_mmap,
        .poll           = uvc_v4l2_poll,
 #ifndef CONFIG_MMU
index a5eb9a3fbb7a3c7c40306e516abfd5244986f436..1ff478a961a6253670ecd310dd0d89c5251e6ecd 100644 (file)
@@ -171,7 +171,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
                break;
 
        case -ESHUTDOWN:        /* disconnect from host. */
-               printk(KERN_INFO "VS request cancelled.\n");
+               printk(KERN_DEBUG "VS request cancelled.\n");
                uvc_queue_cancel(queue, 1);
                goto requeue;
 
index aa376f006333f4232998bed29a1594b2ee747dc8..bbd4b8545b9d478ff88cd9134cd7d4f7ca64d679 100644 (file)
@@ -54,6 +54,8 @@ config USB_AUDIO
        depends on SND
        select USB_LIBCOMPOSITE
        select SND_PCM
+       select USB_F_UAC1 if GADGET_UAC1
+       select USB_F_UAC2 if !GADGET_UAC1
        help
          This Gadget Audio driver is compatible with USB Audio Class
          specification 2.0. It implements 1 AudioControl interface,
index edba2d1ee0f3fc65e2af5366b503245857080bf8..7f485f25705eff517fb1615200bca1c2d77b9fae 100644 (file)
@@ -2,9 +2,9 @@
 # USB gadget drivers
 #
 
-ccflags-y                      := -Idrivers/usb/gadget/
-ccflags-y                      += -Idrivers/usb/gadget/udc/
-ccflags-y                      += -Idrivers/usb/gadget/function/
+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
index 6eb695e5e43a15e3c9cb3bb2aca5a59f8faf86dd..f46a3956e43d1f334d9dafd2d3055aa99eee5b58 100644 (file)
 
 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
+#include "u_uac1.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
+
 /* string IDs are assigned dynamically */
 
 static struct usb_string strings_dev[] = {
@@ -40,12 +100,12 @@ static struct usb_gadget_strings *audio_strings[] = {
        NULL,
 };
 
-#ifdef CONFIG_GADGET_UAC1
-#include "u_uac1.h"
-#include "u_uac1.c"
-#include "f_uac1.c"
+#ifndef CONFIG_GADGET_UAC1
+static struct usb_function_instance *fi_uac2;
+static struct usb_function *f_uac2;
 #else
-#include "f_uac2.c"
+static struct usb_function_instance *fi_uac1;
+static struct usb_function *f_uac1;
 #endif
 
 /*-------------------------------------------------------------------------*/
@@ -109,6 +169,8 @@ static const struct usb_descriptor_header *otg_desc[] = {
 
 static int __init 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)) {
@@ -116,7 +178,31 @@ static int __init audio_do_config(struct usb_configuration *c)
                c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
-       audio_bind_config(c);
+#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;
 }
@@ -126,17 +212,47 @@ static struct usb_configuration audio_config_driver = {
        .bConfigurationValue    = 1,
        /* .iConfiguration = DYNAMIC */
        .bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
-#ifndef CONFIG_GADGET_UAC1
-       .unbind                 = uac2_unbind_config,
-#endif
 };
 
 /*-------------------------------------------------------------------------*/
 
 static int __init audio_bind(struct usb_composite_dev *cdev)
 {
+#ifndef CONFIG_GADGET_UAC1
+       struct f_uac2_opts      *uac2_opts;
+#else
+       struct f_uac1_opts      *uac1_opts;
+#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
+       fi_uac1 = usb_get_function_instance("uac1");
+       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;
+#else
+       uac1_opts = container_of(fi_uac1, struct f_uac1_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
+
        status = usb_string_ids_tab(cdev, strings_dev);
        if (status < 0)
                goto fail;
@@ -152,13 +268,26 @@ static int __init audio_bind(struct usb_composite_dev *cdev)
        return 0;
 
 fail:
+#ifndef CONFIG_GADGET_UAC1
+       usb_put_function_instance(fi_uac2);
+#else
+       usb_put_function_instance(fi_uac1);
+#endif
        return status;
 }
 
 static int __exit audio_unbind(struct usb_composite_dev *cdev)
 {
 #ifdef CONFIG_GADGET_UAC1
-       gaudio_cleanup();
+       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
        return 0;
 }
index a11d8e420bfee45e13bc4d588cc13ba137f5d1f4..f826622d1dc2630d7591089edb648cc8e0a44979 100644 (file)
 #include "f_uvc.c"
 
 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)");
+
+static unsigned int trace;
+module_param(trace, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
 /* --------------------------------------------------------------------------
  * Device descriptor
  */
@@ -326,9 +345,11 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
 static int __init
 webcam_config_bind(struct usb_configuration *c)
 {
-       return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,
-               uvc_fs_streaming_cls, uvc_hs_streaming_cls,
-               uvc_ss_streaming_cls);
+       return uvc_bind_config(c, uvc_fs_control_cls,
+               uvc_ss_control_cls, uvc_fs_streaming_cls,
+               uvc_hs_streaming_cls, uvc_ss_streaming_cls,
+               streaming_interval, streaming_maxpacket,
+               streaming_maxburst, trace);
 }
 
 static struct usb_configuration webcam_config_driver = {
index c3d496828b74963f57619ad560f306a302cf14f4..ebf09f439f3aaf13a78d149d3231dcbc272a02f8 100644 (file)
@@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = {
        .isoc_maxpacket = GZERO_ISOC_MAXPACKET,
        .bulk_buflen = GZERO_BULK_BUFLEN,
        .qlen = GZERO_QLEN,
+       .int_interval = GZERO_INT_INTERVAL,
+       .int_maxpacket = GZERO_INT_MAXPACKET,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -266,6 +268,21 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
                S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
 
+module_param_named(int_interval, gzero_options.int_interval, uint,
+               S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(int_interval, "1 - 16");
+
+module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint,
+               S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)");
+
+module_param_named(int_maxburst, gzero_options.int_maxburst, uint,
+               S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)");
+
 static struct usb_function *func_lb;
 static struct usb_function_instance *func_inst_lb;
 
@@ -301,6 +318,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
        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->int_interval = gzero_options.int_interval;
+       ss_opts->int_maxpacket = gzero_options.int_maxpacket;
+       ss_opts->int_mult = gzero_options.int_mult;
+       ss_opts->int_maxburst = gzero_options.int_maxburst;
        ss_opts->bulk_buflen = gzero_options.bulk_buflen;
 
        func_ss = usb_get_function(func_inst_ss);
index 34ebaa68504c1b68c5e697ccfe0ed74524f1a4fc..0c172809b53b13f84a72856a8b99d9f4cef473dd 100644 (file)
@@ -163,7 +163,7 @@ config USB_R8A66597
 
 config USB_RENESAS_USBHS_UDC
        tristate 'Renesas USBHS controller'
-       depends on USB_RENESAS_USBHS
+       depends on USB_RENESAS_USBHS && HAS_DMA
        help
           Renesas USBHS is a discrete USB host and peripheral controller chip
           that supports both full and high speed USB 2.0 data transfers.
index de2a8713b42884994cafda67990e06a2e93fcb23..de9c400b9944a6de45ea3d6223efa0d848e86847 100644 (file)
@@ -430,7 +430,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
        ep->pipenum = pipenum;
        ep->ep.maxpacket = usb_endpoint_maxp(desc);
        r8a66597->pipenum2ep[pipenum] = ep;
-       r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK]
+       r8a66597->epaddr2ep[usb_endpoint_num(desc)]
                = ep;
        INIT_LIST_HEAD(&ep->queue);
 }
@@ -464,7 +464,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
        if (ep->pipenum)        /* already allocated pipe  */
                return 0;
 
-       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_BULK:
                if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) {
                        if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) {
@@ -509,7 +509,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
        }
        ep->type = info.type;
 
-       info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+       info.epnum = usb_endpoint_num(desc);
        info.maxpacket = usb_endpoint_maxp(desc);
        info.interval = desc->bInterval;
        if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
@@ -1846,10 +1846,8 @@ static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
        r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(r8a66597->sudmac_reg)) {
-               dev_err(&pdev->dev, "ioremap error(sudmac).\n");
+       if (IS_ERR(r8a66597->sudmac_reg))
                return PTR_ERR(r8a66597->sudmac_reg);
-       }
 
        return 0;
 }
index 829f446064ea405d032acf8cb082b07ddeed6dea..90e6644dc8869a5546ce15615c42468232a73f8e 100644 (file)
@@ -54,6 +54,7 @@ struct usbtest_info {
        unsigned                autoconf:1;
        unsigned                ctrl_out:1;
        unsigned                iso:1;          /* try iso in/out */
+       unsigned                intr:1;         /* try interrupt in/out */
        int                     alt;
 };
 
@@ -70,7 +71,10 @@ struct usbtest_dev {
        int                     out_pipe;
        int                     in_iso_pipe;
        int                     out_iso_pipe;
+       int                     in_int_pipe;
+       int                     out_int_pipe;
        struct usb_endpoint_descriptor  *iso_in, *iso_out;
+       struct usb_endpoint_descriptor  *int_in, *int_out;
        struct mutex            lock;
 
 #define TBUF_SIZE      256
@@ -101,6 +105,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
        struct usb_host_interface       *alt;
        struct usb_host_endpoint        *in, *out;
        struct usb_host_endpoint        *iso_in, *iso_out;
+       struct usb_host_endpoint        *int_in, *int_out;
        struct usb_device               *udev;
 
        for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
@@ -108,6 +113,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
 
                in = out = NULL;
                iso_in = iso_out = NULL;
+               int_in = int_out = NULL;
                alt = intf->altsetting + tmp;
 
                if (override_alt >= 0 &&
@@ -124,6 +130,9 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
                        switch (usb_endpoint_type(&e->desc)) {
                        case USB_ENDPOINT_XFER_BULK:
                                break;
+                       case USB_ENDPOINT_XFER_INT:
+                               if (dev->info->intr)
+                                       goto try_intr;
                        case USB_ENDPOINT_XFER_ISOC:
                                if (dev->info->iso)
                                        goto try_iso;
@@ -139,6 +148,15 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
                                        out = e;
                        }
                        continue;
+try_intr:
+                       if (usb_endpoint_dir_in(&e->desc)) {
+                               if (!int_in)
+                                       int_in = e;
+                       } else {
+                               if (!int_out)
+                                       int_out = e;
+                       }
+                       continue;
 try_iso:
                        if (usb_endpoint_dir_in(&e->desc)) {
                                if (!iso_in)
@@ -148,7 +166,7 @@ try_iso:
                                        iso_out = e;
                        }
                }
-               if ((in && out)  ||  iso_in || iso_out)
+               if ((in && out)  ||  iso_in || iso_out || int_in || int_out)
                        goto found;
        }
        return -EINVAL;
@@ -183,6 +201,20 @@ found:
                                iso_out->desc.bEndpointAddress
                                        & USB_ENDPOINT_NUMBER_MASK);
        }
+
+       if (int_in) {
+               dev->int_in = &int_in->desc;
+               dev->in_int_pipe = usb_rcvintpipe(udev,
+                               int_in->desc.bEndpointAddress
+                                       & USB_ENDPOINT_NUMBER_MASK);
+       }
+
+       if (int_out) {
+               dev->int_out = &int_out->desc;
+               dev->out_int_pipe = usb_sndintpipe(udev,
+                               int_out->desc.bEndpointAddress
+                                       & USB_ENDPOINT_NUMBER_MASK);
+       }
        return 0;
 }
 
@@ -205,14 +237,22 @@ static struct urb *usbtest_alloc_urb(
        int                     pipe,
        unsigned long           bytes,
        unsigned                transfer_flags,
-       unsigned                offset)
+       unsigned                offset,
+       u8                      bInterval)
 {
        struct urb              *urb;
 
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb)
                return urb;
-       usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, NULL);
+
+       if (bInterval)
+               usb_fill_int_urb(urb, udev, pipe, NULL, bytes, simple_callback,
+                               NULL, bInterval);
+       else
+               usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback,
+                               NULL);
+
        urb->interval = (udev->speed == USB_SPEED_HIGH)
                        ? (INTERRUPT_RATE << 3)
                        : INTERRUPT_RATE;
@@ -251,9 +291,11 @@ static struct urb *usbtest_alloc_urb(
 static struct urb *simple_alloc_urb(
        struct usb_device       *udev,
        int                     pipe,
-       unsigned long           bytes)
+       unsigned long           bytes,
+       u8                      bInterval)
 {
-       return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0);
+       return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
+                       bInterval);
 }
 
 static unsigned pattern;
@@ -1255,7 +1297,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
                        goto cleanup;
                }
                req.wLength = cpu_to_le16(len);
-               urb[i] = u = simple_alloc_urb(udev, pipe, len);
+               urb[i] = u = simple_alloc_urb(udev, pipe, len, 0);
                if (!u)
                        goto cleanup;
 
@@ -1328,7 +1370,7 @@ static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async)
        int                     retval = 0;
 
        init_completion(&completion);
-       urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size);
+       urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size, 0);
        if (!urb)
                return -ENOMEM;
        urb->context = &completion;
@@ -1616,9 +1658,9 @@ static int halt_simple(struct usbtest_dev *dev)
        struct usb_device       *udev = testdev_to_usbdev(dev);
 
        if (udev->speed == USB_SPEED_SUPER)
-               urb = simple_alloc_urb(udev, 0, 1024);
+               urb = simple_alloc_urb(udev, 0, 1024, 0);
        else
-               urb = simple_alloc_urb(udev, 0, 512);
+               urb = simple_alloc_urb(udev, 0, 512, 0);
        if (urb == NULL)
                return -ENOMEM;
 
@@ -1962,7 +2004,7 @@ static int test_unaligned_bulk(
 {
        int retval;
        struct urb *urb = usbtest_alloc_urb(
-               testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1);
+               testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1, 0);
 
        if (!urb)
                return -ENOMEM;
@@ -2068,7 +2110,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                dev_info(&intf->dev,
                                "TEST 1:  write %d bytes %u times\n",
                                param->length, param->iterations);
-               urb = simple_alloc_urb(udev, dev->out_pipe, param->length);
+               urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
                if (!urb) {
                        retval = -ENOMEM;
                        break;
@@ -2083,7 +2125,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                dev_info(&intf->dev,
                                "TEST 2:  read %d bytes %u times\n",
                                param->length, param->iterations);
-               urb = simple_alloc_urb(udev, dev->in_pipe, param->length);
+               urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
                if (!urb) {
                        retval = -ENOMEM;
                        break;
@@ -2098,7 +2140,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                dev_info(&intf->dev,
                                "TEST 3:  write/%d 0..%d bytes %u times\n",
                                param->vary, param->length, param->iterations);
-               urb = simple_alloc_urb(udev, dev->out_pipe, param->length);
+               urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
                if (!urb) {
                        retval = -ENOMEM;
                        break;
@@ -2114,7 +2156,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                dev_info(&intf->dev,
                                "TEST 4:  read/%d 0..%d bytes %u times\n",
                                param->vary, param->length, param->iterations);
-               urb = simple_alloc_urb(udev, dev->in_pipe, param->length);
+               urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
                if (!urb) {
                        retval = -ENOMEM;
                        break;
@@ -2411,6 +2453,39 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
                }
                break;
 
+       /* Simple non-queued interrupt I/O tests */
+       case 25:
+               if (dev->out_int_pipe == 0)
+                       break;
+               dev_info(&intf->dev,
+                               "TEST 25: write %d bytes %u times\n",
+                               param->length, param->iterations);
+               urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length,
+                               dev->int_out->bInterval);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               /* FIRMWARE: interrupt sink (maybe accepts short writes) */
+               retval = simple_io(dev, urb, param->iterations, 0, 0, "test25");
+               simple_free_urb(urb);
+               break;
+       case 26:
+               if (dev->in_int_pipe == 0)
+                       break;
+               dev_info(&intf->dev,
+                               "TEST 26: read %d bytes %u times\n",
+                               param->length, param->iterations);
+               urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length,
+                               dev->int_in->bInterval);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       break;
+               }
+               /* FIRMWARE: interrupt source (maybe generates short writes) */
+               retval = simple_io(dev, urb, param->iterations, 0, 0, "test26");
+               simple_free_urb(urb);
+               break;
        }
        do_gettimeofday(&param->duration);
        param->duration.tv_sec -= start.tv_sec;
@@ -2447,6 +2522,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
        struct usbtest_info     *info;
        char                    *rtest, *wtest;
        char                    *irtest, *iwtest;
+       char                    *intrtest, *intwtest;
 
        udev = interface_to_usbdev(intf);
 
@@ -2487,6 +2563,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
         */
        rtest = wtest = "";
        irtest = iwtest = "";
+       intrtest = intwtest = "";
        if (force_interrupt || udev->speed == USB_SPEED_LOW) {
                if (info->ep_in) {
                        dev->in_pipe = usb_rcvintpipe(udev, info->ep_in);
@@ -2525,15 +2602,20 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
                        irtest = " iso-in";
                if (dev->out_iso_pipe)
                        iwtest = " iso-out";
+               if (dev->in_int_pipe)
+                       intrtest = " int-in";
+               if (dev->out_int_pipe)
+                       intwtest = " int-out";
        }
 
        usb_set_intfdata(intf, dev);
        dev_info(&intf->dev, "%s\n", info->name);
-       dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n",
+       dev_info(&intf->dev, "%s {control%s%s%s%s%s%s%s} tests%s\n",
                        usb_speed_string(udev->speed),
                        info->ctrl_out ? " in/out" : "",
                        rtest, wtest,
                        irtest, iwtest,
+                       intrtest, intwtest,
                        info->alt >= 0 ? " (+alt)" : "");
        return 0;
 }
@@ -2607,6 +2689,7 @@ static struct usbtest_info gz_info = {
        .autoconf       = 1,
        .ctrl_out       = 1,
        .iso            = 1,
+       .intr           = 1,
        .alt            = 0,
 };
 
index 47ae6455d0733c2d3ef4093aa4408e98969b3644..ecdd6328aafbb1013ae995ef545f5ec4472ad522 100644 (file)
@@ -200,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
        if (!list_empty(&controller->early_tx_list)) {
                ret = HRTIMER_RESTART;
                hrtimer_forward_now(&controller->early_tx,
-                               ktime_set(0, 50 * NSEC_PER_USEC));
+                               ktime_set(0, 20 * NSEC_PER_USEC));
        }
 
        spin_unlock_irqrestore(&musb->lock, flags);
@@ -278,7 +278,7 @@ static void cppi41_dma_callback(void *private_data)
 
                        hrtimer_start_range_ns(&controller->early_tx,
                                ktime_set(0, usecs * NSEC_PER_USEC),
-                               40 * NSEC_PER_USEC,
+                               20 * NSEC_PER_USEC,
                                HRTIMER_MODE_REL);
                }
        }
index e253fa05be6840d6d0eca25775071d1bc61227bc..0cd1f44f0ee8d146fcffa132092f570f44d0e48e 100644 (file)
@@ -78,22 +78,6 @@ config SAMSUNG_USBPHY
          This driver provides common interface to interact, for Samsung USB 2.0 PHY
          driver and later for Samsung USB 3.0 PHY driver.
 
-config SAMSUNG_USB2PHY
-       tristate "Samsung USB 2.0 PHY controller Driver"
-       select SAMSUNG_USBPHY
-       select USB_PHY
-       help
-         Enable this to support Samsung USB 2.0 (High Speed) PHY controller
-         driver for Samsung SoCs.
-
-config SAMSUNG_USB3PHY
-       tristate "Samsung USB 3.0 PHY controller Driver"
-       select SAMSUNG_USBPHY
-       select USB_PHY
-       help
-         Enable this to support Samsung USB 3.0 (Super Speed) phy controller
-         for samsung SoCs.
-
 config TWL6030_USB
        tristate "TWL6030 USB Transceiver Driver"
        depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
index 24a91332d4ad4dff7076f1e7ae98864626349f36..75f2bba58c84c9d44c6fdb9e6198841a3427044a 100644 (file)
@@ -15,8 +15,6 @@ obj-$(CONFIG_AM335X_CONTROL_USB)      += phy-am335x-control.o
 obj-$(CONFIG_AM335X_PHY_USB)           += phy-am335x.o
 obj-$(CONFIG_OMAP_OTG)                 += phy-omap-otg.o
 obj-$(CONFIG_SAMSUNG_USBPHY)           += phy-samsung-usb.o
-obj-$(CONFIG_SAMSUNG_USB2PHY)          += phy-samsung-usb2.o
-obj-$(CONFIG_SAMSUNG_USB3PHY)          += phy-samsung-usb3.o
 obj-$(CONFIG_TWL6030_USB)              += phy-twl6030-usb.o
 obj-$(CONFIG_USB_EHCI_TEGRA)           += phy-tegra-usb.o
 obj-$(CONFIG_USB_GPIO_VBUS)            += phy-gpio-vbus-usb.o
index afc09087ec3630217079dd074f6da406ab3eed68..7bb48af9e02776f8dac102ae49adbfc492cfe096 100644 (file)
@@ -281,7 +281,7 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg)
 {
        int ret = 0;
 
-       if (motg->pdata->phy_clk_reset && motg->phy_reset_clk)
+       if (motg->pdata->phy_clk_reset)
                ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
        else if (motg->phy_rst)
                ret = reset_control_reset(motg->phy_rst);
@@ -1394,7 +1394,7 @@ out:
        return status;
 }
 
-const struct file_operations msm_otg_mode_fops = {
+static const struct file_operations msm_otg_mode_fops = {
        .open = msm_otg_mode_open,
        .read = seq_read,
        .write = msm_otg_mode_write,
@@ -1554,11 +1554,14 @@ static int msm_otg_probe(struct platform_device *pdev)
        phy = &motg->phy;
        phy->dev = &pdev->dev;
 
-       motg->phy_reset_clk = devm_clk_get(&pdev->dev,
+       if (motg->pdata->phy_clk_reset) {
+               motg->phy_reset_clk = devm_clk_get(&pdev->dev,
                                           np ? "phy" : "usb_phy_clk");
-       if (IS_ERR(motg->phy_reset_clk)) {
-               dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
-               motg->phy_reset_clk = NULL;
+
+               if (IS_ERR(motg->phy_reset_clk)) {
+                       dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
+                       return PTR_ERR(motg->phy_reset_clk);
+               }
        }
 
        motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
index c42bdf0c4a1f7322eed6ba4f7ce6b058f7c16fd9..8c2f23b75d6d19267f532513ab4ef1811d3b9a7f 100644 (file)
@@ -125,10 +125,16 @@ static const struct mxs_phy_data imx6sl_phy_data = {
                MXS_PHY_NEED_IP_FIX,
 };
 
+static const struct mxs_phy_data vf610_phy_data = {
+       .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+               MXS_PHY_NEED_IP_FIX,
+};
+
 static const struct of_device_id mxs_phy_dt_ids[] = {
        { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
        { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
        { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
+       { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c
deleted file mode 100644 (file)
index ac025ca..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/* linux/drivers/usb/phy/phy-samsung-usb.c
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *              http://www.samsung.com
- *
- * Author: Praveen Paneri <p.paneri@samsung.com>
- *
- * Samsung USB-PHY helper driver with common function calls;
- * interacts with Samsung USB 2.0 PHY controller driver and later
- * with Samsung USB 3.0 PHY driver.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/usb/samsung_usb_phy.h>
-
-#include "phy-samsung-usb.h"
-
-int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
-{
-       struct device_node *usbphy_sys;
-
-       /* Getting node for system controller interface for usb-phy */
-       usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
-       if (!usbphy_sys) {
-               dev_err(sphy->dev, "No sys-controller interface for usb-phy\n");
-               return -ENODEV;
-       }
-
-       sphy->pmuregs = of_iomap(usbphy_sys, 0);
-
-       if (sphy->pmuregs == NULL) {
-               dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
-               goto err0;
-       }
-
-       sphy->sysreg = of_iomap(usbphy_sys, 1);
-
-       /*
-        * Not returning error code here, since this situation is not fatal.
-        * Few SoCs may not have this switch available
-        */
-       if (sphy->sysreg == NULL)
-               dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");
-
-       of_node_put(usbphy_sys);
-
-       return 0;
-
-err0:
-       of_node_put(usbphy_sys);
-       return -ENXIO;
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt);
-
-/*
- * Set isolation here for phy.
- * Here 'on = true' would mean USB PHY block is isolated, hence
- * de-activated and vice-versa.
- */
-void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on)
-{
-       void __iomem *reg = NULL;
-       u32 reg_val;
-       u32 en_mask = 0;
-
-       if (!sphy->pmuregs) {
-               dev_warn(sphy->dev, "Can't set pmu isolation\n");
-               return;
-       }
-
-       if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
-               reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset;
-               en_mask = sphy->drv_data->devphy_en_mask;
-       } else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
-               reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset;
-               en_mask = sphy->drv_data->hostphy_en_mask;
-       }
-
-       reg_val = readl(reg);
-
-       if (on)
-               reg_val &= ~en_mask;
-       else
-               reg_val |= en_mask;
-
-       writel(reg_val, reg);
-
-       if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) {
-               writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0);
-               writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1);
-       }
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210);
-
-/*
- * Configure the mode of working of usb-phy here: HOST/DEVICE.
- */
-void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy)
-{
-       u32 reg;
-
-       if (!sphy->sysreg) {
-               dev_warn(sphy->dev, "Can't configure specified phy mode\n");
-               return;
-       }
-
-       reg = readl(sphy->sysreg);
-
-       if (sphy->phy_type == USB_PHY_TYPE_DEVICE)
-               reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK;
-       else if (sphy->phy_type == USB_PHY_TYPE_HOST)
-               reg |= EXYNOS_USB20PHY_CFG_HOST_LINK;
-
-       writel(reg, sphy->sysreg);
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel);
-
-/*
- * PHYs are different for USB Device and USB Host.
- * This make sure that correct PHY type is selected before
- * any operation on PHY.
- */
-int samsung_usbphy_set_type(struct usb_phy *phy,
-                               enum samsung_usb_phy_type phy_type)
-{
-       struct samsung_usbphy *sphy = phy_to_sphy(phy);
-
-       sphy->phy_type = phy_type;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_set_type);
-
-int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
-                                                       unsigned long rate)
-{
-       unsigned int clksel;
-
-       switch (rate) {
-       case 12 * MHZ:
-               clksel = PHYCLK_CLKSEL_12M;
-               break;
-       case 24 * MHZ:
-               clksel = PHYCLK_CLKSEL_24M;
-               break;
-       case 48 * MHZ:
-               clksel = PHYCLK_CLKSEL_48M;
-               break;
-       default:
-               dev_err(sphy->dev,
-                       "Invalid reference clock frequency: %lu\n", rate);
-               return -EINVAL;
-       }
-
-       return clksel;
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx);
-
-int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
-                                                       unsigned long rate)
-{
-       unsigned int clksel;
-
-       switch (rate) {
-       case 9600 * KHZ:
-               clksel = FSEL_CLKSEL_9600K;
-               break;
-       case 10 * MHZ:
-               clksel = FSEL_CLKSEL_10M;
-               break;
-       case 12 * MHZ:
-               clksel = FSEL_CLKSEL_12M;
-               break;
-       case 19200 * KHZ:
-               clksel = FSEL_CLKSEL_19200K;
-               break;
-       case 20 * MHZ:
-               clksel = FSEL_CLKSEL_20M;
-               break;
-       case 24 * MHZ:
-               clksel = FSEL_CLKSEL_24M;
-               break;
-       case 50 * MHZ:
-               clksel = FSEL_CLKSEL_50M;
-               break;
-       default:
-               dev_err(sphy->dev,
-                       "Invalid reference clock frequency: %lu\n", rate);
-               return -EINVAL;
-       }
-
-       return clksel;
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12);
-
-/*
- * Returns reference clock frequency selection value
- */
-int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
-{
-       struct clk *ref_clk;
-       unsigned long rate;
-       int refclk_freq;
-
-       /*
-        * In exynos5250 USB host and device PHY use
-        * external crystal clock XXTI
-        */
-       if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
-               ref_clk = clk_get(sphy->dev, "ext_xtal");
-       else
-               ref_clk = clk_get(sphy->dev, "xusbxti");
-       if (IS_ERR(ref_clk)) {
-               dev_err(sphy->dev, "Failed to get reference clock\n");
-               return PTR_ERR(ref_clk);
-       }
-
-       rate = clk_get_rate(ref_clk);
-       refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate);
-
-       clk_put(ref_clk);
-
-       return refclk_freq;
-}
-EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq);
diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h
deleted file mode 100644 (file)
index 80eedd4..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/* linux/drivers/usb/phy/phy-samsung-usb.h
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *              http://www.samsung.com
- *
- * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and
- * OHCI-EXYNOS controllers.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/usb/phy.h>
-
-/* Register definitions */
-
-#define SAMSUNG_PHYPWR                         (0x00)
-
-#define PHYPWR_NORMAL_MASK                     (0x19 << 0)
-#define PHYPWR_OTG_DISABLE                     (0x1 << 4)
-#define PHYPWR_ANALOG_POWERDOWN                        (0x1 << 3)
-#define PHYPWR_FORCE_SUSPEND                   (0x1 << 1)
-/* For Exynos4 */
-#define PHYPWR_NORMAL_MASK_PHY0                        (0x39 << 0)
-#define PHYPWR_SLEEP_PHY0                      (0x1 << 5)
-
-#define SAMSUNG_PHYCLK                         (0x04)
-
-#define PHYCLK_MODE_USB11                      (0x1 << 6)
-#define PHYCLK_EXT_OSC                         (0x1 << 5)
-#define PHYCLK_COMMON_ON_N                     (0x1 << 4)
-#define PHYCLK_ID_PULL                         (0x1 << 2)
-#define PHYCLK_CLKSEL_MASK                     (0x3 << 0)
-#define PHYCLK_CLKSEL_48M                      (0x0 << 0)
-#define PHYCLK_CLKSEL_12M                      (0x2 << 0)
-#define PHYCLK_CLKSEL_24M                      (0x3 << 0)
-
-#define SAMSUNG_RSTCON                         (0x08)
-
-#define RSTCON_PHYLINK_SWRST                   (0x1 << 2)
-#define RSTCON_HLINK_SWRST                     (0x1 << 1)
-#define RSTCON_SWRST                           (0x1 << 0)
-
-/* EXYNOS4X12 */
-#define EXYNOS4X12_PHY_HSIC_CTRL0              (0x04)
-#define EXYNOS4X12_PHY_HSIC_CTRL1              (0x08)
-
-#define PHYPWR_NORMAL_MASK_HSIC1               (0x7 << 12)
-#define PHYPWR_NORMAL_MASK_HSIC0               (0x7 << 9)
-#define PHYPWR_NORMAL_MASK_PHY1                        (0x7 << 6)
-
-#define RSTCON_HOSTPHY_SWRST                   (0xf << 3)
-
-/* EXYNOS5 */
-#define EXYNOS5_PHY_HOST_CTRL0                 (0x00)
-
-#define HOST_CTRL0_PHYSWRSTALL                 (0x1 << 31)
-
-#define HOST_CTRL0_REFCLKSEL_MASK              (0x3 << 19)
-#define HOST_CTRL0_REFCLKSEL_XTAL              (0x0 << 19)
-#define HOST_CTRL0_REFCLKSEL_EXTL              (0x1 << 19)
-#define HOST_CTRL0_REFCLKSEL_CLKCORE           (0x2 << 19)
-
-#define HOST_CTRL0_FSEL_MASK                   (0x7 << 16)
-#define HOST_CTRL0_FSEL(_x)                    ((_x) << 16)
-
-#define FSEL_CLKSEL_50M                                (0x7)
-#define FSEL_CLKSEL_24M                                (0x5)
-#define FSEL_CLKSEL_20M                                (0x4)
-#define FSEL_CLKSEL_19200K                     (0x3)
-#define FSEL_CLKSEL_12M                                (0x2)
-#define FSEL_CLKSEL_10M                                (0x1)
-#define FSEL_CLKSEL_9600K                      (0x0)
-
-#define HOST_CTRL0_TESTBURNIN                  (0x1 << 11)
-#define HOST_CTRL0_RETENABLE                   (0x1 << 10)
-#define HOST_CTRL0_COMMONON_N                  (0x1 << 9)
-#define HOST_CTRL0_SIDDQ                       (0x1 << 6)
-#define HOST_CTRL0_FORCESLEEP                  (0x1 << 5)
-#define HOST_CTRL0_FORCESUSPEND                        (0x1 << 4)
-#define HOST_CTRL0_WORDINTERFACE               (0x1 << 3)
-#define HOST_CTRL0_UTMISWRST                   (0x1 << 2)
-#define HOST_CTRL0_LINKSWRST                   (0x1 << 1)
-#define HOST_CTRL0_PHYSWRST                    (0x1 << 0)
-
-#define EXYNOS5_PHY_HOST_TUNE0                 (0x04)
-
-#define EXYNOS5_PHY_HSIC_CTRL1                 (0x10)
-
-#define EXYNOS5_PHY_HSIC_TUNE1                 (0x14)
-
-#define EXYNOS5_PHY_HSIC_CTRL2                 (0x20)
-
-#define EXYNOS5_PHY_HSIC_TUNE2                 (0x24)
-
-#define HSIC_CTRL_REFCLKSEL_MASK               (0x3 << 23)
-#define HSIC_CTRL_REFCLKSEL                    (0x2 << 23)
-
-#define HSIC_CTRL_REFCLKDIV_MASK               (0x7f << 16)
-#define HSIC_CTRL_REFCLKDIV(_x)                        ((_x) << 16)
-#define HSIC_CTRL_REFCLKDIV_12                 (0x24 << 16)
-#define HSIC_CTRL_REFCLKDIV_15                 (0x1c << 16)
-#define HSIC_CTRL_REFCLKDIV_16                 (0x1a << 16)
-#define HSIC_CTRL_REFCLKDIV_19_2               (0x15 << 16)
-#define HSIC_CTRL_REFCLKDIV_20                 (0x14 << 16)
-
-#define HSIC_CTRL_SIDDQ                                (0x1 << 6)
-#define HSIC_CTRL_FORCESLEEP                   (0x1 << 5)
-#define HSIC_CTRL_FORCESUSPEND                 (0x1 << 4)
-#define HSIC_CTRL_WORDINTERFACE                        (0x1 << 3)
-#define HSIC_CTRL_UTMISWRST                    (0x1 << 2)
-#define HSIC_CTRL_PHYSWRST                     (0x1 << 0)
-
-#define EXYNOS5_PHY_HOST_EHCICTRL              (0x30)
-
-#define HOST_EHCICTRL_ENAINCRXALIGN            (0x1 << 29)
-#define HOST_EHCICTRL_ENAINCR4                 (0x1 << 28)
-#define HOST_EHCICTRL_ENAINCR8                 (0x1 << 27)
-#define HOST_EHCICTRL_ENAINCR16                        (0x1 << 26)
-
-#define EXYNOS5_PHY_HOST_OHCICTRL              (0x34)
-
-#define HOST_OHCICTRL_SUSPLGCY                 (0x1 << 3)
-#define HOST_OHCICTRL_APPSTARTCLK              (0x1 << 2)
-#define HOST_OHCICTRL_CNTSEL                   (0x1 << 1)
-#define HOST_OHCICTRL_CLKCKTRST                        (0x1 << 0)
-
-#define EXYNOS5_PHY_OTG_SYS                    (0x38)
-
-#define OTG_SYS_PHYLINK_SWRESET                        (0x1 << 14)
-#define OTG_SYS_LINKSWRST_UOTG                 (0x1 << 13)
-#define OTG_SYS_PHY0_SWRST                     (0x1 << 12)
-
-#define OTG_SYS_REFCLKSEL_MASK                 (0x3 << 9)
-#define OTG_SYS_REFCLKSEL_XTAL                 (0x0 << 9)
-#define OTG_SYS_REFCLKSEL_EXTL                 (0x1 << 9)
-#define OTG_SYS_REFCLKSEL_CLKCORE              (0x2 << 9)
-
-#define OTG_SYS_IDPULLUP_UOTG                  (0x1 << 8)
-#define OTG_SYS_COMMON_ON                      (0x1 << 7)
-
-#define OTG_SYS_FSEL_MASK                      (0x7 << 4)
-#define OTG_SYS_FSEL(_x)                       ((_x) << 4)
-
-#define OTG_SYS_FORCESLEEP                     (0x1 << 3)
-#define OTG_SYS_OTGDISABLE                     (0x1 << 2)
-#define OTG_SYS_SIDDQ_UOTG                     (0x1 << 1)
-#define OTG_SYS_FORCESUSPEND                   (0x1 << 0)
-
-#define EXYNOS5_PHY_OTG_TUNE                   (0x40)
-
-/* EXYNOS5: USB 3.0 DRD */
-#define EXYNOS5_DRD_LINKSYSTEM                 (0x04)
-
-#define LINKSYSTEM_FLADJ_MASK                  (0x3f << 1)
-#define LINKSYSTEM_FLADJ(_x)                   ((_x) << 1)
-#define LINKSYSTEM_XHCI_VERSION_CONTROL                (0x1 << 27)
-
-#define EXYNOS5_DRD_PHYUTMI                    (0x08)
-
-#define PHYUTMI_OTGDISABLE                     (0x1 << 6)
-#define PHYUTMI_FORCESUSPEND                   (0x1 << 1)
-#define PHYUTMI_FORCESLEEP                     (0x1 << 0)
-
-#define EXYNOS5_DRD_PHYPIPE                    (0x0c)
-
-#define EXYNOS5_DRD_PHYCLKRST                  (0x10)
-
-#define PHYCLKRST_SSC_REFCLKSEL_MASK           (0xff << 23)
-#define PHYCLKRST_SSC_REFCLKSEL(_x)            ((_x) << 23)
-
-#define PHYCLKRST_SSC_RANGE_MASK               (0x03 << 21)
-#define PHYCLKRST_SSC_RANGE(_x)                        ((_x) << 21)
-
-#define PHYCLKRST_SSC_EN                       (0x1 << 20)
-#define PHYCLKRST_REF_SSP_EN                   (0x1 << 19)
-#define PHYCLKRST_REF_CLKDIV2                  (0x1 << 18)
-
-#define PHYCLKRST_MPLL_MULTIPLIER_MASK         (0x7f << 11)
-#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF   (0x19 << 11)
-#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF      (0x02 << 11)
-#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF    (0x68 << 11)
-#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF    (0x7d << 11)
-#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11)
-
-#define PHYCLKRST_FSEL_MASK                    (0x3f << 5)
-#define PHYCLKRST_FSEL(_x)                     ((_x) << 5)
-#define PHYCLKRST_FSEL_PAD_100MHZ              (0x27 << 5)
-#define PHYCLKRST_FSEL_PAD_24MHZ               (0x2a << 5)
-#define PHYCLKRST_FSEL_PAD_20MHZ               (0x31 << 5)
-#define PHYCLKRST_FSEL_PAD_19_2MHZ             (0x38 << 5)
-
-#define PHYCLKRST_RETENABLEN                   (0x1 << 4)
-
-#define PHYCLKRST_REFCLKSEL_MASK               (0x03 << 2)
-#define PHYCLKRST_REFCLKSEL_PAD_REFCLK         (0x2 << 2)
-#define PHYCLKRST_REFCLKSEL_EXT_REFCLK         (0x3 << 2)
-
-#define PHYCLKRST_PORTRESET                    (0x1 << 1)
-#define PHYCLKRST_COMMONONN                    (0x1 << 0)
-
-#define EXYNOS5_DRD_PHYREG0                    (0x14)
-#define EXYNOS5_DRD_PHYREG1                    (0x18)
-
-#define EXYNOS5_DRD_PHYPARAM0                  (0x1c)
-
-#define PHYPARAM0_REF_USE_PAD                  (0x1 << 31)
-#define PHYPARAM0_REF_LOSLEVEL_MASK            (0x1f << 26)
-#define PHYPARAM0_REF_LOSLEVEL                 (0x9 << 26)
-
-#define EXYNOS5_DRD_PHYPARAM1                  (0x20)
-
-#define PHYPARAM1_PCS_TXDEEMPH_MASK            (0x3f << 0)
-#define PHYPARAM1_PCS_TXDEEMPH                 (0x1c)
-
-#define EXYNOS5_DRD_PHYTERM                    (0x24)
-
-#define EXYNOS5_DRD_PHYTEST                    (0x28)
-
-#define PHYTEST_POWERDOWN_SSP                  (0x1 << 3)
-#define PHYTEST_POWERDOWN_HSP                  (0x1 << 2)
-
-#define EXYNOS5_DRD_PHYADP                     (0x2c)
-
-#define EXYNOS5_DRD_PHYBATCHG                  (0x30)
-
-#define PHYBATCHG_UTMI_CLKSEL                  (0x1 << 2)
-
-#define EXYNOS5_DRD_PHYRESUME                  (0x34)
-#define EXYNOS5_DRD_LINKPORT                   (0x44)
-
-#ifndef MHZ
-#define MHZ (1000*1000)
-#endif
-
-#ifndef KHZ
-#define KHZ (1000)
-#endif
-
-#define EXYNOS_USBHOST_PHY_CTRL_OFFSET         (0x4)
-#define S3C64XX_USBPHY_ENABLE                  (0x1 << 16)
-#define EXYNOS_USBPHY_ENABLE                   (0x1 << 0)
-#define EXYNOS_USB20PHY_CFG_HOST_LINK          (0x1 << 0)
-
-enum samsung_cpu_type {
-       TYPE_S3C64XX,
-       TYPE_EXYNOS4210,
-       TYPE_EXYNOS4X12,
-       TYPE_EXYNOS5250,
-};
-
-struct samsung_usbphy;
-
-/*
- * struct samsung_usbphy_drvdata - driver data for various SoC variants
- * @cpu_type: machine identifier
- * @devphy_en_mask: device phy enable mask for PHY CONTROL register
- * @hostphy_en_mask: host phy enable mask for PHY CONTROL register
- * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from
- *                    mapped address of system controller.
- * @hostphy_reg_offset: offset to HOST PHY CONTROL register from
- *                    mapped address of system controller.
- *
- *     Here we have a separate mask for device type phy.
- *     Having different masks for host and device type phy helps
- *     in setting independent masks in case of SoCs like S5PV210,
- *     in which PHY0 and PHY1 enable bits belong to same register
- *     placed at position 0 and 1 respectively.
- *     Although for newer SoCs like exynos these bits belong to
- *     different registers altogether placed at position 0.
- */
-struct samsung_usbphy_drvdata {
-       int cpu_type;
-       int devphy_en_mask;
-       int hostphy_en_mask;
-       u32 devphy_reg_offset;
-       u32 hostphy_reg_offset;
-       int (*rate_to_clksel)(struct samsung_usbphy *, unsigned long);
-       void (*set_isolation)(struct samsung_usbphy *, bool);
-       void (*phy_enable)(struct samsung_usbphy *);
-       void (*phy_disable)(struct samsung_usbphy *);
-};
-
-/*
- * struct samsung_usbphy - transceiver driver state
- * @phy: transceiver structure
- * @plat: platform data
- * @dev: The parent device supplied to the probe function
- * @clk: usb phy clock
- * @regs: usb phy controller registers memory base
- * @pmuregs: USB device PHY_CONTROL register memory base
- * @sysreg: USB2.0 PHY_CFG register memory base
- * @ref_clk_freq: reference clock frequency selection
- * @drv_data: driver data available for different SoCs
- * @phy_type: Samsung SoCs specific phy types: #HOST
- *                                             #DEVICE
- * @phy_usage: usage count for phy
- * @lock: lock for phy operations
- */
-struct samsung_usbphy {
-       struct usb_phy  phy;
-       struct samsung_usbphy_data *plat;
-       struct device   *dev;
-       struct clk      *clk;
-       void __iomem    *regs;
-       void __iomem    *pmuregs;
-       void __iomem    *sysreg;
-       int             ref_clk_freq;
-       const struct samsung_usbphy_drvdata *drv_data;
-       enum samsung_usb_phy_type phy_type;
-       atomic_t        phy_usage;
-       spinlock_t      lock;
-};
-
-#define phy_to_sphy(x)         container_of((x), struct samsung_usbphy, phy)
-
-static const struct of_device_id samsung_usbphy_dt_match[];
-
-static inline const struct samsung_usbphy_drvdata
-*samsung_usbphy_get_driver_data(struct platform_device *pdev)
-{
-       if (pdev->dev.of_node) {
-               const struct of_device_id *match;
-               match = of_match_node(samsung_usbphy_dt_match,
-                                                       pdev->dev.of_node);
-               return match->data;
-       }
-
-       return (struct samsung_usbphy_drvdata *)
-                               platform_get_device_id(pdev)->driver_data;
-}
-
-extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy);
-extern void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy,
-                                                               bool on);
-extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy);
-extern int samsung_usbphy_set_type(struct usb_phy *phy,
-                                       enum samsung_usb_phy_type phy_type);
-extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy);
-extern int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
-                                                       unsigned long rate);
-extern int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
-                                                       unsigned long rate);
diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c
deleted file mode 100644 (file)
index b3ba866..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-/* linux/drivers/usb/phy/phy-samsung-usb2.c
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- *              http://www.samsung.com
- *
- * Author: Praveen Paneri <p.paneri@samsung.com>
- *
- * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and
- * OHCI-EXYNOS controllers.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/samsung_usb_phy.h>
-#include <linux/platform_data/samsung-usbphy.h>
-
-#include "phy-samsung-usb.h"
-
-static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host)
-{
-       if (!otg)
-               return -ENODEV;
-
-       if (!otg->host)
-               otg->host = host;
-
-       return 0;
-}
-
-static bool exynos5_phyhost_is_on(void __iomem *regs)
-{
-       u32 reg;
-
-       reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
-
-       return !(reg & HOST_CTRL0_SIDDQ);
-}
-
-static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
-{
-       void __iomem *regs = sphy->regs;
-       u32 phyclk = sphy->ref_clk_freq;
-       u32 phyhost;
-       u32 phyotg;
-       u32 phyhsic;
-       u32 ehcictrl;
-       u32 ohcictrl;
-
-       /*
-        * phy_usage helps in keeping usage count for phy
-        * so that the first consumer enabling the phy is also
-        * the last consumer to disable it.
-        */
-
-       atomic_inc(&sphy->phy_usage);
-
-       if (exynos5_phyhost_is_on(regs)) {
-               dev_info(sphy->dev, "Already power on PHY\n");
-               return;
-       }
-
-       /* Host configuration */
-       phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
-
-       /* phy reference clock configuration */
-       phyhost &= ~HOST_CTRL0_FSEL_MASK;
-       phyhost |= HOST_CTRL0_FSEL(phyclk);
-
-       /* host phy reset */
-       phyhost &= ~(HOST_CTRL0_PHYSWRST |
-                       HOST_CTRL0_PHYSWRSTALL |
-                       HOST_CTRL0_SIDDQ |
-                       /* Enable normal mode of operation */
-                       HOST_CTRL0_FORCESUSPEND |
-                       HOST_CTRL0_FORCESLEEP);
-
-       /* Link reset */
-       phyhost |= (HOST_CTRL0_LINKSWRST |
-                       HOST_CTRL0_UTMISWRST |
-                       /* COMMON Block configuration during suspend */
-                       HOST_CTRL0_COMMONON_N);
-       writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
-       udelay(10);
-       phyhost &= ~(HOST_CTRL0_LINKSWRST |
-                       HOST_CTRL0_UTMISWRST);
-       writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
-
-       /* OTG configuration */
-       phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS);
-
-       /* phy reference clock configuration */
-       phyotg &= ~OTG_SYS_FSEL_MASK;
-       phyotg |= OTG_SYS_FSEL(phyclk);
-
-       /* Enable normal mode of operation */
-       phyotg &= ~(OTG_SYS_FORCESUSPEND |
-                       OTG_SYS_SIDDQ_UOTG |
-                       OTG_SYS_FORCESLEEP |
-                       OTG_SYS_REFCLKSEL_MASK |
-                       /* COMMON Block configuration during suspend */
-                       OTG_SYS_COMMON_ON);
-
-       /* OTG phy & link reset */
-       phyotg |= (OTG_SYS_PHY0_SWRST |
-                       OTG_SYS_LINKSWRST_UOTG |
-                       OTG_SYS_PHYLINK_SWRESET |
-                       OTG_SYS_OTGDISABLE |
-                       /* Set phy refclk */
-                       OTG_SYS_REFCLKSEL_CLKCORE);
-
-       writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
-       udelay(10);
-       phyotg &= ~(OTG_SYS_PHY0_SWRST |
-                       OTG_SYS_LINKSWRST_UOTG |
-                       OTG_SYS_PHYLINK_SWRESET);
-       writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
-
-       /* HSIC phy configuration */
-       phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
-                       HSIC_CTRL_REFCLKSEL |
-                       HSIC_CTRL_PHYSWRST);
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
-       udelay(10);
-       phyhsic &= ~HSIC_CTRL_PHYSWRST;
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
-
-       udelay(80);
-
-       /* enable EHCI DMA burst */
-       ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL);
-       ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN |
-                               HOST_EHCICTRL_ENAINCR4 |
-                               HOST_EHCICTRL_ENAINCR8 |
-                               HOST_EHCICTRL_ENAINCR16);
-       writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL);
-
-       /* set ohci_suspend_on_n */
-       ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL);
-       ohcictrl |= HOST_OHCICTRL_SUSPLGCY;
-       writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL);
-}
-
-static void samsung_usb2phy_enable(struct samsung_usbphy *sphy)
-{
-       void __iomem *regs = sphy->regs;
-       u32 phypwr;
-       u32 phyclk;
-       u32 rstcon;
-
-       /* set clock frequency for PLL */
-       phyclk = sphy->ref_clk_freq;
-       phypwr = readl(regs + SAMSUNG_PHYPWR);
-       rstcon = readl(regs + SAMSUNG_RSTCON);
-
-       switch (sphy->drv_data->cpu_type) {
-       case TYPE_S3C64XX:
-               phyclk &= ~PHYCLK_COMMON_ON_N;
-               phypwr &= ~PHYPWR_NORMAL_MASK;
-               rstcon |= RSTCON_SWRST;
-               break;
-       case TYPE_EXYNOS4X12:
-               phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 |
-                               PHYPWR_NORMAL_MASK_HSIC1 |
-                               PHYPWR_NORMAL_MASK_PHY1);
-               rstcon |= RSTCON_HOSTPHY_SWRST;
-       case TYPE_EXYNOS4210:
-               phypwr &= ~PHYPWR_NORMAL_MASK_PHY0;
-               rstcon |= RSTCON_SWRST;
-       default:
-               break;
-       }
-
-       writel(phyclk, regs + SAMSUNG_PHYCLK);
-       /* Configure PHY0 for normal operation*/
-       writel(phypwr, regs + SAMSUNG_PHYPWR);
-       /* reset all ports of PHY and Link */
-       writel(rstcon, regs + SAMSUNG_RSTCON);
-       udelay(10);
-       if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12)
-               rstcon &= ~RSTCON_HOSTPHY_SWRST;
-       rstcon &= ~RSTCON_SWRST;
-       writel(rstcon, regs + SAMSUNG_RSTCON);
-}
-
-static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy)
-{
-       void __iomem *regs = sphy->regs;
-       u32 phyhost;
-       u32 phyotg;
-       u32 phyhsic;
-
-       if (atomic_dec_return(&sphy->phy_usage) > 0) {
-               dev_info(sphy->dev, "still being used\n");
-               return;
-       }
-
-       phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
-                       HSIC_CTRL_REFCLKSEL |
-                       HSIC_CTRL_SIDDQ |
-                       HSIC_CTRL_FORCESLEEP |
-                       HSIC_CTRL_FORCESUSPEND);
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
-       writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
-
-       phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
-       phyhost |= (HOST_CTRL0_SIDDQ |
-                       HOST_CTRL0_FORCESUSPEND |
-                       HOST_CTRL0_FORCESLEEP |
-                       HOST_CTRL0_PHYSWRST |
-                       HOST_CTRL0_PHYSWRSTALL);
-       writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0);
-
-       phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS);
-       phyotg |= (OTG_SYS_FORCESUSPEND |
-                       OTG_SYS_SIDDQ_UOTG |
-                       OTG_SYS_FORCESLEEP);
-       writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
-}
-
-static void samsung_usb2phy_disable(struct samsung_usbphy *sphy)
-{
-       void __iomem *regs = sphy->regs;
-       u32 phypwr;
-
-       phypwr = readl(regs + SAMSUNG_PHYPWR);
-
-       switch (sphy->drv_data->cpu_type) {
-       case TYPE_S3C64XX:
-               phypwr |= PHYPWR_NORMAL_MASK;
-               break;
-       case TYPE_EXYNOS4X12:
-               phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 |
-                               PHYPWR_NORMAL_MASK_HSIC1 |
-                               PHYPWR_NORMAL_MASK_PHY1);
-       case TYPE_EXYNOS4210:
-               phypwr |= PHYPWR_NORMAL_MASK_PHY0;
-       default:
-               break;
-       }
-
-       /* Disable analog and otg block power */
-       writel(phypwr, regs + SAMSUNG_PHYPWR);
-}
-
-/*
- * The function passed to the usb driver for phy initialization
- */
-static int samsung_usb2phy_init(struct usb_phy *phy)
-{
-       struct samsung_usbphy *sphy;
-       struct usb_bus *host = NULL;
-       unsigned long flags;
-       int ret = 0;
-
-       sphy = phy_to_sphy(phy);
-
-       host = phy->otg->host;
-
-       /* Enable the phy clock */
-       ret = clk_prepare_enable(sphy->clk);
-       if (ret) {
-               dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
-               return ret;
-       }
-
-       spin_lock_irqsave(&sphy->lock, flags);
-
-       if (host) {
-               /* setting default phy-type for USB 2.0 */
-               if (!strstr(dev_name(host->controller), "ehci") ||
-                               !strstr(dev_name(host->controller), "ohci"))
-                       samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST);
-       } else {
-               samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
-       }
-
-       /* Disable phy isolation */
-       if (sphy->plat && sphy->plat->pmu_isolation)
-               sphy->plat->pmu_isolation(false);
-       else if (sphy->drv_data->set_isolation)
-               sphy->drv_data->set_isolation(sphy, false);
-
-       /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */
-       samsung_usbphy_cfg_sel(sphy);
-
-       /* Initialize usb phy registers */
-       sphy->drv_data->phy_enable(sphy);
-
-       spin_unlock_irqrestore(&sphy->lock, flags);
-
-       /* Disable the phy clock */
-       clk_disable_unprepare(sphy->clk);
-
-       return ret;
-}
-
-/*
- * The function passed to the usb driver for phy shutdown
- */
-static void samsung_usb2phy_shutdown(struct usb_phy *phy)
-{
-       struct samsung_usbphy *sphy;
-       struct usb_bus *host = NULL;
-       unsigned long flags;
-
-       sphy = phy_to_sphy(phy);
-
-       host = phy->otg->host;
-
-       if (clk_prepare_enable(sphy->clk)) {
-               dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
-               return;
-       }
-
-       spin_lock_irqsave(&sphy->lock, flags);
-
-       if (host) {
-               /* setting default phy-type for USB 2.0 */
-               if (!strstr(dev_name(host->controller), "ehci") ||
-                               !strstr(dev_name(host->controller), "ohci"))
-                       samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST);
-       } else {
-               samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
-       }
-
-       /* De-initialize usb phy registers */
-       sphy->drv_data->phy_disable(sphy);
-
-       /* Enable phy isolation */
-       if (sphy->plat && sphy->plat->pmu_isolation)
-               sphy->plat->pmu_isolation(true);
-       else if (sphy->drv_data->set_isolation)
-               sphy->drv_data->set_isolation(sphy, true);
-
-       spin_unlock_irqrestore(&sphy->lock, flags);
-
-       clk_disable_unprepare(sphy->clk);
-}
-
-static int samsung_usb2phy_probe(struct platform_device *pdev)
-{
-       struct samsung_usbphy *sphy;
-       struct usb_otg *otg;
-       struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev);
-       const struct samsung_usbphy_drvdata *drv_data;
-       struct device *dev = &pdev->dev;
-       struct resource *phy_mem;
-       void __iomem    *phy_base;
-       struct clk *clk;
-       int ret;
-
-       phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       phy_base = devm_ioremap_resource(dev, phy_mem);
-       if (IS_ERR(phy_base))
-               return PTR_ERR(phy_base);
-
-       sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
-       if (!sphy)
-               return -ENOMEM;
-
-       otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL);
-       if (!otg)
-               return -ENOMEM;
-
-       drv_data = samsung_usbphy_get_driver_data(pdev);
-
-       if (drv_data->cpu_type == TYPE_EXYNOS5250)
-               clk = devm_clk_get(dev, "usbhost");
-       else
-               clk = devm_clk_get(dev, "otg");
-
-       if (IS_ERR(clk)) {
-               dev_err(dev, "Failed to get usbhost/otg clock\n");
-               return PTR_ERR(clk);
-       }
-
-       sphy->dev = dev;
-
-       if (dev->of_node) {
-               ret = samsung_usbphy_parse_dt(sphy);
-               if (ret < 0)
-                       return ret;
-       } else {
-               if (!pdata) {
-                       dev_err(dev, "no platform data specified\n");
-                       return -EINVAL;
-               }
-       }
-
-       sphy->plat              = pdata;
-       sphy->regs              = phy_base;
-       sphy->clk               = clk;
-       sphy->drv_data          = drv_data;
-       sphy->phy.dev           = sphy->dev;
-       sphy->phy.label         = "samsung-usb2phy";
-       sphy->phy.type          = USB_PHY_TYPE_USB2;
-       sphy->phy.init          = samsung_usb2phy_init;
-       sphy->phy.shutdown      = samsung_usb2phy_shutdown;
-
-       sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
-       if (sphy->ref_clk_freq < 0)
-               return -EINVAL;
-
-       sphy->phy.otg           = otg;
-       sphy->phy.otg->phy      = &sphy->phy;
-       sphy->phy.otg->set_host = samsung_usbphy_set_host;
-
-       spin_lock_init(&sphy->lock);
-
-       platform_set_drvdata(pdev, sphy);
-
-       return usb_add_phy_dev(&sphy->phy);
-}
-
-static int samsung_usb2phy_remove(struct platform_device *pdev)
-{
-       struct samsung_usbphy *sphy = platform_get_drvdata(pdev);
-
-       usb_remove_phy(&sphy->phy);
-
-       if (sphy->pmuregs)
-               iounmap(sphy->pmuregs);
-       if (sphy->sysreg)
-               iounmap(sphy->sysreg);
-
-       return 0;
-}
-
-static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = {
-       .cpu_type               = TYPE_S3C64XX,
-       .devphy_en_mask         = S3C64XX_USBPHY_ENABLE,
-       .rate_to_clksel         = samsung_usbphy_rate_to_clksel_64xx,
-       .set_isolation          = NULL, /* TODO */
-       .phy_enable             = samsung_usb2phy_enable,
-       .phy_disable            = samsung_usb2phy_disable,
-};
-
-static const struct samsung_usbphy_drvdata usb2phy_exynos4 = {
-       .cpu_type               = TYPE_EXYNOS4210,
-       .devphy_en_mask         = EXYNOS_USBPHY_ENABLE,
-       .hostphy_en_mask        = EXYNOS_USBPHY_ENABLE,
-       .rate_to_clksel         = samsung_usbphy_rate_to_clksel_64xx,
-       .set_isolation          = samsung_usbphy_set_isolation_4210,
-       .phy_enable             = samsung_usb2phy_enable,
-       .phy_disable            = samsung_usb2phy_disable,
-};
-
-static const struct samsung_usbphy_drvdata usb2phy_exynos4x12 = {
-       .cpu_type               = TYPE_EXYNOS4X12,
-       .devphy_en_mask         = EXYNOS_USBPHY_ENABLE,
-       .hostphy_en_mask        = EXYNOS_USBPHY_ENABLE,
-       .rate_to_clksel         = samsung_usbphy_rate_to_clksel_4x12,
-       .set_isolation          = samsung_usbphy_set_isolation_4210,
-       .phy_enable             = samsung_usb2phy_enable,
-       .phy_disable            = samsung_usb2phy_disable,
-};
-
-static struct samsung_usbphy_drvdata usb2phy_exynos5 = {
-       .cpu_type               = TYPE_EXYNOS5250,
-       .hostphy_en_mask        = EXYNOS_USBPHY_ENABLE,
-       .hostphy_reg_offset     = EXYNOS_USBHOST_PHY_CTRL_OFFSET,
-       .rate_to_clksel         = samsung_usbphy_rate_to_clksel_4x12,
-       .set_isolation          = samsung_usbphy_set_isolation_4210,
-       .phy_enable             = samsung_exynos5_usb2phy_enable,
-       .phy_disable            = samsung_exynos5_usb2phy_disable,
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id samsung_usbphy_dt_match[] = {
-       {
-               .compatible = "samsung,s3c64xx-usb2phy",
-               .data = &usb2phy_s3c64xx,
-       }, {
-               .compatible = "samsung,exynos4210-usb2phy",
-               .data = &usb2phy_exynos4,
-       }, {
-               .compatible = "samsung,exynos4x12-usb2phy",
-               .data = &usb2phy_exynos4x12,
-       }, {
-               .compatible = "samsung,exynos5250-usb2phy",
-               .data = &usb2phy_exynos5
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
-#endif
-
-static struct platform_device_id samsung_usbphy_driver_ids[] = {
-       {
-               .name           = "s3c64xx-usb2phy",
-               .driver_data    = (unsigned long)&usb2phy_s3c64xx,
-       }, {
-               .name           = "exynos4210-usb2phy",
-               .driver_data    = (unsigned long)&usb2phy_exynos4,
-       }, {
-               .name           = "exynos4x12-usb2phy",
-               .driver_data    = (unsigned long)&usb2phy_exynos4x12,
-       }, {
-               .name           = "exynos5250-usb2phy",
-               .driver_data    = (unsigned long)&usb2phy_exynos5,
-       },
-       {},
-};
-
-MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids);
-
-static struct platform_driver samsung_usb2phy_driver = {
-       .probe          = samsung_usb2phy_probe,
-       .remove         = samsung_usb2phy_remove,
-       .id_table       = samsung_usbphy_driver_ids,
-       .driver         = {
-               .name   = "samsung-usb2phy",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(samsung_usbphy_dt_match),
-       },
-};
-
-module_platform_driver(samsung_usb2phy_driver);
-
-MODULE_DESCRIPTION("Samsung USB 2.0 phy controller");
-MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:samsung-usb2phy");
diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c
deleted file mode 100644 (file)
index cc08192..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/* linux/drivers/usb/phy/phy-samsung-usb3.c
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- *              http://www.samsung.com
- *
- * Author: Vivek Gautam <gautam.vivek@samsung.com>
- *
- * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/usb/samsung_usb_phy.h>
-#include <linux/platform_data/samsung-usbphy.h>
-
-#include "phy-samsung-usb.h"
-
-/*
- * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
- */
-static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy)
-{
-       u32 reg;
-       u32 refclk;
-
-       refclk = sphy->ref_clk_freq;
-
-       reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
-               PHYCLKRST_FSEL(refclk);
-
-       switch (refclk) {
-       case FSEL_CLKSEL_50M:
-               reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
-                       PHYCLKRST_SSC_REFCLKSEL(0x00));
-               break;
-       case FSEL_CLKSEL_20M:
-               reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
-                       PHYCLKRST_SSC_REFCLKSEL(0x00));
-               break;
-       case FSEL_CLKSEL_19200K:
-               reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
-                       PHYCLKRST_SSC_REFCLKSEL(0x88));
-               break;
-       case FSEL_CLKSEL_24M:
-       default:
-               reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
-                       PHYCLKRST_SSC_REFCLKSEL(0x88));
-               break;
-       }
-
-       return reg;
-}
-
-static void samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy)
-{
-       void __iomem *regs = sphy->regs;
-       u32 phyparam0;
-       u32 phyparam1;
-       u32 linksystem;
-       u32 phybatchg;
-       u32 phytest;
-       u32 phyclkrst;
-
-       /* Reset USB 3.0 PHY */
-       writel(0x0, regs + EXYNOS5_DRD_PHYREG0);
-
-       phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0);
-       /* Select PHY CLK source */
-       phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
-       /* Set Loss-of-Signal Detector sensitivity */
-       phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
-       phyparam0 |= PHYPARAM0_REF_LOSLEVEL;
-       writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0);
-
-       writel(0x0, regs + EXYNOS5_DRD_PHYRESUME);
-
-       /*
-        * Setting the Frame length Adj value[6:1] to default 0x20
-        * See xHCI 1.0 spec, 5.2.4
-        */
-       linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
-                       LINKSYSTEM_FLADJ(0x20);
-       writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM);
-
-       phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1);
-       /* Set Tx De-Emphasis level */
-       phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
-       phyparam1 |= PHYPARAM1_PCS_TXDEEMPH;
-       writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1);
-
-       phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG);
-       phybatchg |= PHYBATCHG_UTMI_CLKSEL;
-       writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG);
-
-       /* PHYTEST POWERDOWN Control */
-       phytest = readl(regs + EXYNOS5_DRD_PHYTEST);
-       phytest &= ~(PHYTEST_POWERDOWN_SSP |
-                       PHYTEST_POWERDOWN_HSP);
-       writel(phytest, regs + EXYNOS5_DRD_PHYTEST);
-
-       /* UTMI Power Control */
-       writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI);
-
-       phyclkrst = samsung_usb3phy_set_refclk(sphy);
-
-       phyclkrst |= PHYCLKRST_PORTRESET |
-                       /* Digital power supply in normal operating mode */
-                       PHYCLKRST_RETENABLEN |
-                       /* Enable ref clock for SS function */
-                       PHYCLKRST_REF_SSP_EN |
-                       /* Enable spread spectrum */
-                       PHYCLKRST_SSC_EN |
-                       /* Power down HS Bias and PLL blocks in suspend mode */
-                       PHYCLKRST_COMMONONN;
-
-       writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
-
-       udelay(10);
-
-       phyclkrst &= ~(PHYCLKRST_PORTRESET);
-       writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
-}
-
-static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy)
-{
-       u32 phyutmi;
-       u32 phyclkrst;
-       u32 phytest;
-       void __iomem *regs = sphy->regs;
-
-       phyutmi = PHYUTMI_OTGDISABLE |
-                       PHYUTMI_FORCESUSPEND |
-                       PHYUTMI_FORCESLEEP;
-       writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI);
-
-       /* Resetting the PHYCLKRST enable bits to reduce leakage current */
-       phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST);
-       phyclkrst &= ~(PHYCLKRST_REF_SSP_EN |
-                       PHYCLKRST_SSC_EN |
-                       PHYCLKRST_COMMONONN);
-       writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
-
-       /* Control PHYTEST to remove leakage current */
-       phytest = readl(regs + EXYNOS5_DRD_PHYTEST);
-       phytest |= (PHYTEST_POWERDOWN_SSP |
-                       PHYTEST_POWERDOWN_HSP);
-       writel(phytest, regs + EXYNOS5_DRD_PHYTEST);
-}
-
-static int samsung_usb3phy_init(struct usb_phy *phy)
-{
-       struct samsung_usbphy *sphy;
-       unsigned long flags;
-       int ret = 0;
-
-       sphy = phy_to_sphy(phy);
-
-       /* Enable the phy clock */
-       ret = clk_prepare_enable(sphy->clk);
-       if (ret) {
-               dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
-               return ret;
-       }
-
-       spin_lock_irqsave(&sphy->lock, flags);
-
-       /* setting default phy-type for USB 3.0 */
-       samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
-
-       /* Disable phy isolation */
-       if (sphy->drv_data->set_isolation)
-               sphy->drv_data->set_isolation(sphy, false);
-
-       /* Initialize usb phy registers */
-       sphy->drv_data->phy_enable(sphy);
-
-       spin_unlock_irqrestore(&sphy->lock, flags);
-
-       /* Disable the phy clock */
-       clk_disable_unprepare(sphy->clk);
-
-       return ret;
-}
-
-/*
- * The function passed to the usb driver for phy shutdown
- */
-static void samsung_usb3phy_shutdown(struct usb_phy *phy)
-{
-       struct samsung_usbphy *sphy;
-       unsigned long flags;
-
-       sphy = phy_to_sphy(phy);
-
-       if (clk_prepare_enable(sphy->clk)) {
-               dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__);
-               return;
-       }
-
-       spin_lock_irqsave(&sphy->lock, flags);
-
-       /* setting default phy-type for USB 3.0 */
-       samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
-
-       /* De-initialize usb phy registers */
-       sphy->drv_data->phy_disable(sphy);
-
-       /* Enable phy isolation */
-       if (sphy->drv_data->set_isolation)
-               sphy->drv_data->set_isolation(sphy, true);
-
-       spin_unlock_irqrestore(&sphy->lock, flags);
-
-       clk_disable_unprepare(sphy->clk);
-}
-
-static int samsung_usb3phy_probe(struct platform_device *pdev)
-{
-       struct samsung_usbphy *sphy;
-       struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev);
-       struct device *dev = &pdev->dev;
-       struct resource *phy_mem;
-       void __iomem    *phy_base;
-       struct clk *clk;
-       int ret;
-
-       phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       phy_base = devm_ioremap_resource(dev, phy_mem);
-       if (IS_ERR(phy_base))
-               return PTR_ERR(phy_base);
-
-       sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
-       if (!sphy)
-               return -ENOMEM;
-
-       clk = devm_clk_get(dev, "usbdrd30");
-       if (IS_ERR(clk)) {
-               dev_err(dev, "Failed to get device clock\n");
-               return PTR_ERR(clk);
-       }
-
-       sphy->dev = dev;
-
-       if (dev->of_node) {
-               ret = samsung_usbphy_parse_dt(sphy);
-               if (ret < 0)
-                       return ret;
-       } else {
-               if (!pdata) {
-                       dev_err(dev, "no platform data specified\n");
-                       return -EINVAL;
-               }
-       }
-
-       sphy->plat              = pdata;
-       sphy->regs              = phy_base;
-       sphy->clk               = clk;
-       sphy->phy.dev           = sphy->dev;
-       sphy->phy.label         = "samsung-usb3phy";
-       sphy->phy.type          = USB_PHY_TYPE_USB3;
-       sphy->phy.init          = samsung_usb3phy_init;
-       sphy->phy.shutdown      = samsung_usb3phy_shutdown;
-       sphy->drv_data          = samsung_usbphy_get_driver_data(pdev);
-
-       sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
-       if (sphy->ref_clk_freq < 0)
-               return -EINVAL;
-
-       spin_lock_init(&sphy->lock);
-
-       platform_set_drvdata(pdev, sphy);
-
-       return usb_add_phy_dev(&sphy->phy);
-}
-
-static int samsung_usb3phy_remove(struct platform_device *pdev)
-{
-       struct samsung_usbphy *sphy = platform_get_drvdata(pdev);
-
-       usb_remove_phy(&sphy->phy);
-
-       if (sphy->pmuregs)
-               iounmap(sphy->pmuregs);
-       if (sphy->sysreg)
-               iounmap(sphy->sysreg);
-
-       return 0;
-}
-
-static struct samsung_usbphy_drvdata usb3phy_exynos5 = {
-       .cpu_type               = TYPE_EXYNOS5250,
-       .devphy_en_mask         = EXYNOS_USBPHY_ENABLE,
-       .rate_to_clksel         = samsung_usbphy_rate_to_clksel_4x12,
-       .set_isolation          = samsung_usbphy_set_isolation_4210,
-       .phy_enable             = samsung_exynos5_usb3phy_enable,
-       .phy_disable            = samsung_exynos5_usb3phy_disable,
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id samsung_usbphy_dt_match[] = {
-       {
-               .compatible = "samsung,exynos5250-usb3phy",
-               .data = &usb3phy_exynos5
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
-#endif
-
-static struct platform_device_id samsung_usbphy_driver_ids[] = {
-       {
-               .name           = "exynos5250-usb3phy",
-               .driver_data    = (unsigned long)&usb3phy_exynos5,
-       },
-       {},
-};
-
-MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids);
-
-static struct platform_driver samsung_usb3phy_driver = {
-       .probe          = samsung_usb3phy_probe,
-       .remove         = samsung_usb3phy_remove,
-       .id_table       = samsung_usbphy_driver_ids,
-       .driver         = {
-               .name   = "samsung-usb3phy",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(samsung_usbphy_dt_match),
-       },
-};
-
-module_platform_driver(samsung_usb3phy_driver);
-
-MODULE_DESCRIPTION("Samsung USB 3.0 phy controller");
-MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:samsung-usb3phy");
index 04778cf80d60cf0095a6311a0f19896554fb67ce..44ea082e40dc070ab6386c12ae6ec53ed5b71957 100644 (file)
@@ -104,7 +104,6 @@ struct twl6030_usb {
        int                     irq2;
        enum omap_musb_vbus_id_status linkstat;
        u8                      asleep;
-       bool                    irq_enabled;
        bool                    vbus_enable;
        const char              *regulator;
 };
@@ -373,7 +372,6 @@ static int twl6030_usb_probe(struct platform_device *pdev)
 
        INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
 
-       twl->irq_enabled = true;
        status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
                        IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
                        "twl6030_usb", twl);
index 1b9bf8d83235b50d1efc0f971a8443ac2783d159..b3b6813ab270c046f9cf70ab99aa11821e2a7b0b 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
@@ -438,6 +440,43 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
 /*
  *             platform functions
  */
+static const struct of_device_id usbhs_of_match[] = {
+       {
+               .compatible = "renesas,usbhs-r8a7790",
+               .data = (void *)USBHS_TYPE_R8A7790,
+       },
+       {
+               .compatible = "renesas,usbhs-r8a7791",
+               .data = (void *)USBHS_TYPE_R8A7791,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, usbhs_of_match);
+
+static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
+{
+       struct renesas_usbhs_platform_info *info;
+       struct renesas_usbhs_driver_param *dparam;
+       const struct of_device_id *of_id = of_match_device(usbhs_of_match, dev);
+       u32 tmp;
+       int gpio;
+
+       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return NULL;
+
+       dparam = &info->driver_param;
+       dparam->type = of_id ? (u32)of_id->data : 0;
+       if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp))
+               dparam->buswait_bwait = tmp;
+       gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0,
+                                      NULL);
+       if (gpio > 0)
+               dparam->enable_gpio = gpio;
+
+       return info;
+}
+
 static int usbhs_probe(struct platform_device *pdev)
 {
        struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev);
@@ -446,6 +485,10 @@ static int usbhs_probe(struct platform_device *pdev)
        struct resource *res, *irq_res;
        int ret;
 
+       /* check device node */
+       if (pdev->dev.of_node)
+               info = pdev->dev.platform_data = usbhs_parse_dt(&pdev->dev);
+
        /* check platform information */
        if (!info) {
                dev_err(&pdev->dev, "no platform information\n");
@@ -689,6 +732,7 @@ static struct platform_driver renesas_usbhs_driver = {
        .driver         = {
                .name   = "renesas_usbhs",
                .pm     = &usbhsc_pm_ops,
+               .of_match_table = of_match_ptr(usbhs_of_match),
        },
        .probe          = usbhs_probe,
        .remove         = usbhs_remove,
diff --git a/include/linux/platform_data/samsung-usbphy.h b/include/linux/platform_data/samsung-usbphy.h
deleted file mode 100644 (file)
index 1bd24cb..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2012 Samsung Electronics Co.Ltd
- *             http://www.samsung.com/
- * Author: Praveen Paneri <p.paneri@samsung.com>
- *
- * Defines platform data for samsung usb phy driver.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __SAMSUNG_USBPHY_PLATFORM_H
-#define __SAMSUNG_USBPHY_PLATFORM_H
-
-/**
- * samsung_usbphy_data - Platform data for USB PHY driver.
- * @pmu_isolation: Function to control usb phy isolation in PMU.
- */
-struct samsung_usbphy_data {
-       void (*pmu_isolation)(int on);
-};
-
-extern void samsung_usbphy_set_pdata(struct samsung_usbphy_data *pd);
-
-#endif /* __SAMSUNG_USBPHY_PLATFORM_H */
index c3a61853cd1306f7a34cdc9b26a9e08b39c49ce2..c540557b564be30b9fe2ef5218316bb426747c3e 100644 (file)
@@ -345,12 +345,13 @@ static inline int usb_ep_queue(struct usb_ep *ep,
  * @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
+ * 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.
+ * 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
+ * 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).
  */
index 0154b2859fd7dd868a65dcb30bc6455594e23b65..6d2a16b834bf19f3a75abce80388e2c10e02bf2a 100644 (file)
@@ -32,6 +32,16 @@ struct usb_endpoint_descriptor_no_audio {
        __u8  bInterval;
 } __attribute__((packed));
 
+struct usb_functionfs_descs_head_v2 {
+       __le32 magic;
+       __le32 length;
+       __le32 flags;
+       /*
+        * __le32 fs_count, hs_count, fs_count; must be included manually in
+        * the structure taking flags into consideration.
+        */
+} __attribute__((packed));
+
 /* Legacy format, deprecated as of 3.14. */
 struct usb_functionfs_descs_head {
        __le32 magic;
@@ -92,7 +102,7 @@ struct usb_ext_prop_desc {
  * structure.  Any flags that are not recognised cause the whole block to be
  * rejected with -ENOSYS.
  *
- * Legacy descriptors format:
+ * Legacy descriptors format (deprecated as of 3.14):
  *
  * | off | name      | type         | description                          |
  * |-----+-----------+--------------+--------------------------------------|
index a87e99f37c52e4239d4467c524929dd010162740..88d5e71be0449ff7afdb6bdefbea98045fefd612 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * ffs-test.c.c -- user mode filesystem api for usb composite function
+ * ffs-test.c -- user mode filesystem api for usb composite function
  *
  * Copyright (C) 2010 Samsung Electronics
  *                    Author: Michal Nazarewicz <mina86@mina86.com>
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 #include <pthread.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -106,7 +107,9 @@ static void _msg(unsigned level, const char *fmt, ...)
 /******************** Descriptors and Strings *******************************/
 
 static const struct {
-       struct usb_functionfs_descs_head header;
+       struct usb_functionfs_descs_head_v2 header;
+       __le32 fs_count;
+       __le32 hs_count;
        struct {
                struct usb_interface_descriptor intf;
                struct usb_endpoint_descriptor_no_audio sink;
@@ -114,11 +117,12 @@ static const struct {
        } __attribute__((packed)) fs_descs, hs_descs;
 } __attribute__((packed)) descriptors = {
        .header = {
-               .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+               .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+               .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
+                                    FUNCTIONFS_HAS_HS_DESC),
                .length = cpu_to_le32(sizeof descriptors),
-               .fs_count = cpu_to_le32(3),
-               .hs_count = cpu_to_le32(3),
        },
+       .fs_count = cpu_to_le32(3),
        .fs_descs = {
                .intf = {
                        .bLength = sizeof descriptors.fs_descs.intf,
@@ -142,6 +146,7 @@ static const struct {
                        /* .wMaxPacketSize = autoconfiguration (kernel) */
                },
        },
+       .hs_count = cpu_to_le32(3),
        .hs_descs = {
                .intf = {
                        .bLength = sizeof descriptors.fs_descs.intf,
@@ -168,6 +173,89 @@ static const struct {
        },
 };
 
+static size_t descs_to_legacy(void **legacy, const void *descriptors_v2)
+{
+       const unsigned char *descs_end, *descs_start;
+       __u32 length, fs_count = 0, hs_count = 0, count;
+
+       /* Read v2 header */
+       {
+               const struct {
+                       const struct usb_functionfs_descs_head_v2 header;
+                       const __le32 counts[];
+               } __attribute__((packed)) *const in = descriptors_v2;
+               const __le32 *counts = in->counts;
+               __u32 flags;
+
+               if (le32_to_cpu(in->header.magic) !=
+                   FUNCTIONFS_DESCRIPTORS_MAGIC_V2)
+                       return 0;
+               length = le32_to_cpu(in->header.length);
+               if (length <= sizeof in->header)
+                       return 0;
+               length -= sizeof in->header;
+               flags = le32_to_cpu(in->header.flags);
+               if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                             FUNCTIONFS_HAS_SS_DESC))
+                       return 0;
+
+#define GET_NEXT_COUNT_IF_FLAG(ret, flg) do {          \
+                       if (!(flags & (flg)))           \
+                               break;                  \
+                       if (length < 4)                 \
+                               return 0;               \
+                       ret = le32_to_cpu(*counts);     \
+                       length -= 4;                    \
+                       ++counts;                       \
+               } while (0)
+
+               GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC);
+               GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC);
+               GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC);
+
+               count = fs_count + hs_count;
+               if (!count)
+                       return 0;
+               descs_start = (const void *)counts;
+
+#undef GET_NEXT_COUNT_IF_FLAG
+       }
+
+       /*
+        * Find the end of FS and HS USB descriptors.  SS descriptors
+        * are ignored since legacy format does not support them.
+        */
+       descs_end = descs_start;
+       do {
+               if (length < *descs_end)
+                       return 0;
+               length -= *descs_end;
+               descs_end += *descs_end;
+       } while (--count);
+
+       /* Allocate legacy descriptors and copy the data. */
+       {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+               struct {
+                       struct usb_functionfs_descs_head header;
+                       __u8 descriptors[];
+               } __attribute__((packed)) *out;
+#pragma GCC diagnostic pop
+
+               length = sizeof out->header + (descs_end - descs_start);
+               out = malloc(length);
+               out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+               out->header.length = cpu_to_le32(length);
+               out->header.fs_count = cpu_to_le32(fs_count);
+               out->header.hs_count = cpu_to_le32(hs_count);
+               memcpy(out->descriptors, descs_start, descs_end - descs_start);
+               *legacy = out;
+       }
+
+       return length;
+}
+
 
 #define STR_INTERFACE_ "Source/Sink"
 
@@ -487,12 +575,29 @@ ep0_consume(struct thread *ignore, const void *buf, size_t nbytes)
        return nbytes;
 }
 
-static void ep0_init(struct thread *t)
+static void ep0_init(struct thread *t, bool legacy_descriptors)
 {
+       void *legacy;
        ssize_t ret;
+       size_t len;
+
+       if (legacy_descriptors) {
+               info("%s: writing descriptors\n", t->filename);
+               goto legacy;
+       }
 
-       info("%s: writing descriptors\n", t->filename);
+       info("%s: writing descriptors (in v2 format)\n", t->filename);
        ret = write(t->fd, &descriptors, sizeof descriptors);
+
+       if (ret < 0 && errno == EINVAL) {
+               warn("%s: new format rejected, trying legacy\n", t->filename);
+legacy:
+               len = descs_to_legacy(&legacy, &descriptors);
+               if (len) {
+                       ret = write(t->fd, legacy, len);
+                       free(legacy);
+               }
+       }
        die_on(ret < 0, "%s: write: descriptors", t->filename);
 
        info("%s: writing strings\n", t->filename);
@@ -503,14 +608,15 @@ static void ep0_init(struct thread *t)
 
 /******************** Main **************************************************/
 
-int main(void)
+int main(int argc, char **argv)
 {
+       bool legacy_descriptors;
        unsigned i;
 
-       /* XXX TODO: Argument parsing missing */
+       legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l");
 
        init_thread(threads);
-       ep0_init(threads);
+       ep0_init(threads, legacy_descriptors);
 
        for (i = 1; i < sizeof threads / sizeof *threads; ++i)
                init_thread(threads + i);
This page took 0.127778 seconds and 5 git commands to generate.