summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac112
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac212
-rw-r--r--Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt39
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3-st.txt68
-rw-r--r--Documentation/devicetree/bindings/usb/mxs-phy.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,dwc3.txt66
-rw-r--r--Documentation/devicetree/bindings/usb/renesas_usbhs.txt24
-rw-r--r--Documentation/devicetree/bindings/usb/udc-xilinx.txt18
-rw-r--r--MAINTAINERS3
-rw-r--r--drivers/usb/dwc3/Kconfig17
-rw-r--r--drivers/usb/dwc3/Makefile7
-rw-r--r--drivers/usb/dwc3/core.c9
-rw-r--r--drivers/usb/dwc3/core.h4
-rw-r--r--drivers/usb/dwc3/debug.c32
-rw-r--r--drivers/usb/dwc3/debug.h200
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c4
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c4
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c4
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c131
-rw-r--r--drivers/usb/dwc3/dwc3-st.c367
-rw-r--r--drivers/usb/dwc3/ep0.c65
-rw-r--r--drivers/usb/dwc3/gadget.c138
-rw-r--r--drivers/usb/dwc3/gadget.h56
-rw-r--r--drivers/usb/dwc3/io.h30
-rw-r--r--drivers/usb/dwc3/trace.c19
-rw-r--r--drivers/usb/dwc3/trace.h220
-rw-r--r--drivers/usb/gadget/Kconfig9
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/composite.c1
-rw-r--r--drivers/usb/gadget/configfs.c1
-rw-r--r--drivers/usb/gadget/function/Makefile10
-rw-r--r--drivers/usb/gadget/function/f_acm.c49
-rw-r--r--drivers/usb/gadget/function/f_fs.c52
-rw-r--r--drivers/usb/gadget/function/f_loopback.c3
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c27
-rw-r--r--drivers/usb/gadget/function/f_obex.c28
-rw-r--r--drivers/usb/gadget/function/f_serial.c19
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c511
-rw-r--r--drivers/usb/gadget/function/f_uac1.c337
-rw-r--r--drivers/usb/gadget/function/f_uac2.c520
-rw-r--r--drivers/usb/gadget/function/f_uvc.c274
-rw-r--r--drivers/usb/gadget/function/f_uvc.h13
-rw-r--r--drivers/usb/gadget/function/g_zero.h13
-rw-r--r--drivers/usb/gadget/function/u_fs.h2
-rw-r--r--drivers/usb/gadget/function/u_serial.c30
-rw-r--r--drivers/usb/gadget/function/u_uac1.c38
-rw-r--r--drivers/usb/gadget/function/u_uac1.h31
-rw-r--r--drivers/usb/gadget/function/u_uac2.h42
-rw-r--r--drivers/usb/gadget/function/u_uvc.h39
-rw-r--r--drivers/usb/gadget/function/uvc.h4
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c46
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h33
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c327
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.h22
-rw-r--r--drivers/usb/gadget/function/uvc_video.c48
-rw-r--r--drivers/usb/gadget/function/uvc_video.h24
-rw-r--r--drivers/usb/gadget/legacy/Kconfig3
-rw-r--r--drivers/usb/gadget/legacy/Makefile6
-rw-r--r--drivers/usb/gadget/legacy/audio.c149
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c1
-rw-r--r--drivers/usb/gadget/legacy/inode.c1
-rw-r--r--drivers/usb/gadget/legacy/webcam.c75
-rw-r--r--drivers/usb/gadget/legacy/zero.c21
-rw-r--r--drivers/usb/gadget/udc/Kconfig17
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c81
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h7
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c10
-rw-r--r--drivers/usb/gadget/udc/udc-core.c31
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c2180
-rw-r--r--drivers/usb/misc/usbtest.c113
-rw-r--r--drivers/usb/musb/musb_cppi41.c4
-rw-r--r--drivers/usb/musb/musb_dsps.c2
-rw-r--r--drivers/usb/phy/Kconfig16
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/phy-msm-usb.c15
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c6
-rw-r--r--drivers/usb/phy/phy-samsung-usb.c241
-rw-r--r--drivers/usb/phy/phy-samsung-usb.h349
-rw-r--r--drivers/usb/phy/phy-samsung-usb2.c541
-rw-r--r--drivers/usb/phy/phy-samsung-usb3.c350
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c2
-rw-r--r--drivers/usb/renesas_usbhs/common.c44
-rw-r--r--include/linux/platform_data/samsung-usbphy.h27
-rw-r--r--include/linux/usb/gadget.h18
-rw-r--r--include/uapi/linux/usb/functionfs.h19
-rw-r--r--tools/usb/ffs-test.c126
87 files changed, 5998 insertions, 2565 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
new file mode 100644
index 000000000000..8ba9a123316e
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -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
index 000000000000..2bfdd4efa9bd
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -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/phy/qcom-dwc3-usb-phy.txt b/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
new file mode 100644
index 000000000000..86f2dbe07ed4
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
@@ -0,0 +1,39 @@
+Qualcomm DWC3 HS AND SS PHY CONTROLLER
+--------------------------------------
+
+DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer
+controllers. Each DWC3 PHY controller should have its own node.
+
+Required properties:
+- compatible: should contain one of the following:
+ - "qcom,dwc3-hs-usb-phy" for High Speed Synopsis PHY controller
+ - "qcom,dwc3-ss-usb-phy" for Super Speed Synopsis PHY controller
+- reg: offset and length of the DWC3 PHY controller register set
+- #phy-cells: must be zero
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+ clock-names.
+- clock-names: Should contain "ref" for the PHY reference clock
+
+Optional clocks:
+ "xo" External reference clock
+
+Example:
+ phy@100f8800 {
+ compatible = "qcom,dwc3-hs-usb-phy";
+ reg = <0x100f8800 0x30>;
+ clocks = <&gcc USB30_0_UTMI_CLK>;
+ clock-names = "ref";
+ #phy-cells = <0>;
+
+ status = "ok";
+ };
+
+ phy@100f8830 {
+ compatible = "qcom,dwc3-ss-usb-phy";
+ reg = <0x100f8830 0x30>;
+ clocks = <&gcc USB30_0_MASTER_CLK>;
+ clock-names = "ref";
+ #phy-cells = <0>;
+
+ status = "ok";
+ };
diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt
new file mode 100644
index 000000000000..f9d70252bbb2
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/dwc3-st.txt
@@ -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>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/usb/mxs-phy.txt b/Documentation/devicetree/bindings/usb/mxs-phy.txt
index 96681c93b86d..379b84a567cc 100644
--- a/Documentation/devicetree/bindings/usb/mxs-phy.txt
+++ b/Documentation/devicetree/bindings/usb/mxs-phy.txt
@@ -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,imx6sx-usbphy" for imx6sx
"fsl,imx23-usbphy" is still a fallback for other strings
- reg: Should contain registers location and length
diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
new file mode 100644
index 000000000000..ca164e71dd50
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
@@ -0,0 +1,66 @@
+Qualcomm SuperSpeed DWC3 USB SoC controller
+
+Required properties:
+- compatible: should contain "qcom,dwc3"
+- clocks: A list of phandle + clock-specifier pairs for the
+ clocks listed in clock-names
+- clock-names: Should contain the following:
+ "core" Master/Core clock, have to be >= 125 MHz for SS
+ operation and >= 60MHz for HS operation
+
+Optional clocks:
+ "iface" System bus AXI clock. Not present on all platforms
+ "sleep" Sleep clock, used when USB3 core goes into low
+ power mode (U3).
+
+Required child node:
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+Phy documentation is provided in the following places:
+Documentation/devicetree/bindings/phy/qcom,dwc3-usb-phy.txt
+
+Example device nodes:
+
+ hs_phy: phy@100f8800 {
+ compatible = "qcom,dwc3-hs-usb-phy";
+ reg = <0x100f8800 0x30>;
+ clocks = <&gcc USB30_0_UTMI_CLK>;
+ clock-names = "ref";
+ #phy-cells = <0>;
+
+ status = "ok";
+ };
+
+ ss_phy: phy@100f8830 {
+ compatible = "qcom,dwc3-ss-usb-phy";
+ reg = <0x100f8830 0x30>;
+ clocks = <&gcc USB30_0_MASTER_CLK>;
+ clock-names = "ref";
+ #phy-cells = <0>;
+
+ status = "ok";
+ };
+
+ usb3_0: usb30@0 {
+ compatible = "qcom,dwc3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gcc USB30_0_MASTER_CLK>;
+ clock-names = "core";
+
+ ranges;
+
+ status = "ok";
+
+ dwc3@10000000 {
+ compatible = "snps,dwc3";
+ reg = <0x10000000 0xcd00>;
+ interrupts = <0 205 0x4>;
+ phys = <&hs_phy>, <&ss_phy>;
+ phy-names = "usb2-phy", "usb3-phy";
+ tx-fifo-resize;
+ dr_mode = "host";
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
new file mode 100644
index 000000000000..b08c903f8668
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
@@ -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>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/udc-xilinx.txt b/Documentation/devicetree/bindings/usb/udc-xilinx.txt
new file mode 100644
index 000000000000..47b4e397a08d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/udc-xilinx.txt
@@ -0,0 +1,18 @@
+Xilinx USB2 device controller
+
+Required properties:
+- compatible : Should be "xlnx,usb2-device-4.00.a"
+- reg : Physical base address and size of the USB2
+ device registers map.
+- interrupts : Should contain single irq line of USB2 device
+ controller
+- xlnx,has-builtin-dma : if DMA is included
+
+Example:
+ axi-usb2-device@42e00000 {
+ compatible = "xlnx,usb2-device-4.00.a";
+ interrupts = <0x0 0x39 0x1>;
+ reg = <0x42e00000 0x10000>;
+ xlnx,has-builtin-dma;
+ };
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 809ecd680d88..3ca017a3ddea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
@@ -9683,7 +9684,7 @@ USB WEBCAM GADGET
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: linux-usb@vger.kernel.org
S: Maintained
-F: drivers/usb/gadget/function/*uvc*.c
+F: drivers/usb/gadget/function/*uvc*
F: drivers/usb/gadget/legacy/webcam.c
USB WIRELESS RNDIS DRIVER (rndis_wlan)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 785510a0a0c3..f4e5cc60db0b 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -80,6 +80,23 @@ 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.
+
+config USB_DWC3_QCOM
+ tristate "Qualcomm Platforms"
+ depends on ARCH_QCOM || COMPILE_TEST
+ default USB_DWC3
+ help
+ Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
+ say 'Y' or 'M' if you have one such device.
+
comment "Debugging features"
config USB_DWC3_DEBUG
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 10ac3e72482e..bb34fbcfeab3 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -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,5 @@ 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_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9069984fe5cf..b0f4d52b7f04 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -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;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 48fb264065db..66f62563bcf9 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -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
index 000000000000..0be6885bc370
--- /dev/null
+++ b/drivers/usb/dwc3/debug.c
@@ -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);
+}
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index fceb39dc4bba..07fbc2d94fd4 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -16,8 +16,206 @@
* 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 */
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index f9fb8adb785b..3951a65fea04 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -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.
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index fc0de3753648..2f537d588225 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -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);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index a60bab7dfa0a..436fb08c40b8 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -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-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
new file mode 100644
index 000000000000..611f8e7e8299
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct dwc3_qcom {
+ struct device *dev;
+
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *sleep_clk;
+};
+
+static int dwc3_qcom_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct dwc3_qcom *qdwc;
+ int ret;
+
+ qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
+ if (!qdwc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, qdwc);
+
+ qdwc->dev = &pdev->dev;
+
+ qdwc->core_clk = devm_clk_get(qdwc->dev, "core");
+ if (IS_ERR(qdwc->core_clk)) {
+ dev_err(qdwc->dev, "failed to get core clock\n");
+ return PTR_ERR(qdwc->core_clk);
+ }
+
+ qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface");
+ if (IS_ERR(qdwc->iface_clk)) {
+ dev_dbg(qdwc->dev, "failed to get optional iface clock\n");
+ qdwc->iface_clk = NULL;
+ }
+
+ qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep");
+ if (IS_ERR(qdwc->sleep_clk)) {
+ dev_dbg(qdwc->dev, "failed to get optional sleep clock\n");
+ qdwc->sleep_clk = NULL;
+ }
+
+ ret = clk_prepare_enable(qdwc->core_clk);
+ if (ret) {
+ dev_err(qdwc->dev, "failed to enable core clock\n");
+ goto err_core;
+ }
+
+ ret = clk_prepare_enable(qdwc->iface_clk);
+ if (ret) {
+ dev_err(qdwc->dev, "failed to enable optional iface clock\n");
+ goto err_iface;
+ }
+
+ ret = clk_prepare_enable(qdwc->sleep_clk);
+ if (ret) {
+ dev_err(qdwc->dev, "failed to enable optional sleep clock\n");
+ goto err_sleep;
+ }
+
+ ret = of_platform_populate(node, NULL, NULL, qdwc->dev);
+ if (ret) {
+ dev_err(qdwc->dev, "failed to register core - %d\n", ret);
+ goto err_clks;
+ }
+
+ return 0;
+
+err_clks:
+ clk_disable_unprepare(qdwc->sleep_clk);
+err_sleep:
+ clk_disable_unprepare(qdwc->iface_clk);
+err_iface:
+ clk_disable_unprepare(qdwc->core_clk);
+err_core:
+ return ret;
+}
+
+static int dwc3_qcom_remove(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qdwc = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(&pdev->dev);
+
+ clk_disable_unprepare(qdwc->sleep_clk);
+ clk_disable_unprepare(qdwc->iface_clk);
+ clk_disable_unprepare(qdwc->core_clk);
+
+ return 0;
+}
+
+static const struct of_device_id of_dwc3_match[] = {
+ { .compatible = "qcom,dwc3" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_match);
+
+static struct platform_driver dwc3_qcom_driver = {
+ .probe = dwc3_qcom_probe,
+ .remove = dwc3_qcom_remove,
+ .driver = {
+ .name = "qcom-dwc3",
+ .owner = THIS_MODULE,
+ .of_match_table = of_dwc3_match,
+ },
+};
+
+module_platform_driver(dwc3_qcom_driver);
+
+MODULE_ALIAS("platform:qcom-dwc3");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
new file mode 100644
index 000000000000..c7602b5362ad
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -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");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 21a352079bc2..b35938777dde 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -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));
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 490a6ca00733..0fcc0a35ae05 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -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);
}
/*
@@ -1235,6 +1147,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);
ret = __dwc3_gadget_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1254,6 +1167,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) {
@@ -1744,11 +1659,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;
@@ -1847,6 +1759,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
@@ -2021,9 +1935,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;
@@ -2210,8 +2121,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);
@@ -2230,8 +2139,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
@@ -2316,8 +2223,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;
@@ -2415,8 +2320,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.
@@ -2521,10 +2424,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;
}
@@ -2601,6 +2500,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 */
@@ -2754,7 +2655,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;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index a0ee75b68a80..178ad8982206 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -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 */
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index d94441c14d8c..6a79c8e66bbc 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -20,27 +20,51 @@
#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
index 000000000000..6cd166412ad0
--- /dev/null
+++ b/drivers/usb/dwc3/trace.c
@@ -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
index 000000000000..78aff1da089a
--- /dev/null
+++ b/drivers/usb/dwc3/trace.h
@@ -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>
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 5c822afb6d70..c4880fc0d86e 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -181,6 +181,15 @@ config USB_F_MASS_STORAGE
config USB_F_FS
tristate
+config USB_F_UAC1
+ tristate
+
+config USB_F_UAC2
+ tristate
+
+config USB_F_UVC
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9add915d41f7..598a67d6ba05 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -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
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 6935a822ce2b..e07eddbb3f8c 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2073,6 +2073,7 @@ static const struct usb_gadget_driver composite_driver_template = {
.unbind = composite_unbind,
.setup = composite_setup,
+ .reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 811c2c7cc269..34034333f7f6 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1450,6 +1450,7 @@ static const struct usb_gadget_driver configfs_driver_template = {
.unbind = configfs_composite_unbind,
.setup = composite_setup,
+ .reset = composite_disconnect,
.disconnect = composite_disconnect,
.max_speed = USB_SPEED_SUPER,
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 83ae1065149d..90701aa5a826 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -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,9 @@ 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
+usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
+obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index ab1065afbbd0..6da4685490ef 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -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:
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 0dc3552d1360..4ad11e03cf54 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1032,6 +1032,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
case FUNCTIONFS_ENDPOINT_REVMAP:
ret = epfile->ep->num;
break;
+ case FUNCTIONFS_ENDPOINT_DESC:
+ {
+ int desc_idx;
+ struct usb_endpoint_descriptor *desc;
+
+ switch (epfile->ffs->gadget->speed) {
+ case USB_SPEED_SUPER:
+ desc_idx = 2;
+ break;
+ case USB_SPEED_HIGH:
+ desc_idx = 1;
+ break;
+ default:
+ desc_idx = 0;
+ }
+ desc = epfile->ep->descs[desc_idx];
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = copy_to_user((void *)value, desc, sizeof(*desc));
+ if (ret)
+ ret = -EFAULT;
+ return ret;
+ }
default:
ret = -ENOTTY;
}
@@ -1534,7 +1557,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
epfile->ffs = ffs;
mutex_init(&epfile->mutex);
init_waitqueue_head(&epfile->wait);
- sprintf(epfiles->name, "ep%u", i);
+ if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
+ else
+ sprintf(epfiles->name, "ep%u", i);
if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
&ffs_epfile_operations,
&epfile->dentry))) {
@@ -2083,10 +2109,12 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
break;
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
flags = get_unaligned_le32(data + 8);
+ ffs->user_flags = flags;
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
FUNCTIONFS_HAS_HS_DESC |
FUNCTIONFS_HAS_SS_DESC |
- FUNCTIONFS_HAS_MS_OS_DESC)) {
+ FUNCTIONFS_HAS_MS_OS_DESC |
+ FUNCTIONFS_VIRTUAL_ADDR)) {
ret = -ENOSYS;
goto error;
}
@@ -2346,7 +2374,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
break;
default:
- BUG();
+ WARN(1, "%d: unknown event, this should not happen\n", type);
+ return;
}
{
@@ -2393,7 +2422,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_endpoint_descriptor *ds = (void *)desc;
struct ffs_function *func = priv;
struct ffs_ep *ffs_ep;
- unsigned ep_desc_id, idx;
+ unsigned ep_desc_id;
+ int idx;
static const char *speed_names[] = { "full", "high", "super" };
if (type != FFS_DESCRIPTOR)
@@ -2441,7 +2471,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
} else {
struct usb_request *req;
struct usb_ep *ep;
+ u8 bEndpointAddress;
+ /*
+ * We back up bEndpointAddress because autoconfig overwrites
+ * it with physical endpoint address.
+ */
+ bEndpointAddress = ds->bEndpointAddress;
pr_vdebug("autoconfig\n");
ep = usb_ep_autoconfig(func->gadget, ds);
if (unlikely(!ep))
@@ -2456,6 +2492,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
ffs_ep->req = req;
func->eps_revmap[ds->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+ /*
+ * If we use virtual address mapping, we restore
+ * original bEndpointAddress value.
+ */
+ if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ ds->bEndpointAddress = bEndpointAddress;
}
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
@@ -2900,6 +2942,8 @@ static int ffs_func_setup(struct usb_function *f,
ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
if (unlikely(ret < 0))
return ret;
+ if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
+ ret = func->ffs->eps_addrmap[ret];
break;
default:
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 4557cd03f0b1..bf04389137e6 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -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);
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b96393908860..811929cd4c9e 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -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);
-
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index aebae1853bce..5f40080c92cc 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -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;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 9ecbcbf36a45..2e02dfabc7ae 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -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:
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index d3cd52db78fe..80be25b32cd7 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -23,6 +23,15 @@
#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;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(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);
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 2b4c82d84bfc..f7b203293205 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -11,24 +11,12 @@
#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");
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 3ed89ecc8d6d..a5a27a504d67 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -20,35 +20,7 @@
#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)
{
@@ -170,6 +149,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
}
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)
{
uint num = 0;
@@ -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");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index e2a1f50bd93c..e126439e4b65 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -27,24 +28,12 @@
#include <media/v4l2-event.h>
#include "uvc.h"
+#include "uvc_v4l2.h"
+#include "uvc_video.h"
+#include "u_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_maxburst;
-module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
-
/* --------------------------------------------------------------------------
* Function descriptors
*/
@@ -75,7 +64,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
-static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
+static struct usb_interface_assoc_descriptor uvc_iad = {
.bLength = sizeof(uvc_iad),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0,
@@ -86,7 +75,7 @@ static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
.iFunction = 0,
};
-static struct usb_interface_descriptor uvc_control_intf __initdata = {
+static struct usb_interface_descriptor uvc_control_intf = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_CONTROL,
@@ -98,7 +87,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = {
.iInterface = 0,
};
-static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_control_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -107,7 +96,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
.bInterval = 8,
};
-static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
+static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
.bLength = sizeof(uvc_ss_control_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The following 3 values can be tweaked if necessary. */
@@ -116,14 +105,14 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
.wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
-static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
+static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubType = UVC_EP_INTERRUPT,
.wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
-static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
+static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
@@ -135,7 +124,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
.iInterface = 0,
};
-static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
+static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
@@ -147,7 +136,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
.iInterface = 0,
};
-static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -158,7 +147,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
*/
};
-static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -169,7 +158,7 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
*/
};
-static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
+static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -181,7 +170,7 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
*/
};
-static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
+static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
.bLength = sizeof(uvc_ss_streaming_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
@@ -208,6 +197,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
NULL,
};
+void uvc_set_trace_param(unsigned int trace)
+{
+ uvc_gadget_trace_param = trace;
+}
+EXPORT_SYMBOL(uvc_set_trace_param);
+
/* --------------------------------------------------------------------------
* Control requests
*/
@@ -251,6 +246,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 +409,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;
@@ -434,7 +437,7 @@ uvc_register_video(struct uvc_device *uvc)
} \
} while (0)
-static struct usb_descriptor_header ** __init
+static struct usb_descriptor_header **
uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
{
struct uvc_input_header_descriptor *uvc_streaming_header;
@@ -554,45 +557,26 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
return hdr;
}
-static void
-uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_composite_dev *cdev = c->cdev;
- struct uvc_device *uvc = to_uvc(f);
-
- INFO(cdev, "uvc_function_unbind\n");
-
- video_unregister_device(uvc->vdev);
- v4l2_device_unregister(&uvc->v4l2_dev);
- uvc->control_ep->driver_data = NULL;
- uvc->video.ep->driver_data = NULL;
-
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
- usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
- kfree(uvc->control_buf);
-
- usb_free_all_descriptors(f);
-
- kfree(uvc);
-}
-
-static int __init
+static int
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
+ struct usb_string *us;
unsigned int max_packet_mult;
unsigned int max_packet_size;
struct usb_ep *ep;
+ struct f_uvc_opts *opts;
int ret = -EINVAL;
INFO(cdev, "uvc_function_bind\n");
+ opts = to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
*/
- streaming_interval = clamp(streaming_interval, 1U, 16U);
- streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
- streaming_maxburst = min(streaming_maxburst, 15U);
+ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
+ opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
+ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
* module parameters.
@@ -600,30 +584,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
* NOTE: We assume that the user knows what they are doing and won't
* give parameters that their UDC doesn't support.
*/
- if (streaming_maxpacket <= 1024) {
+ if (opts->streaming_maxpacket <= 1024) {
max_packet_mult = 1;
- max_packet_size = streaming_maxpacket;
- } else if (streaming_maxpacket <= 2048) {
+ max_packet_size = opts->streaming_maxpacket;
+ } else if (opts->streaming_maxpacket <= 2048) {
max_packet_mult = 2;
- max_packet_size = streaming_maxpacket / 2;
+ max_packet_size = opts->streaming_maxpacket / 2;
} else {
max_packet_mult = 3;
- max_packet_size = streaming_maxpacket / 3;
+ max_packet_size = opts->streaming_maxpacket / 3;
}
- uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
- uvc_fs_streaming_ep.bInterval = streaming_interval;
+ uvc_fs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
+ uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
- uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
- uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
- uvc_hs_streaming_ep.bInterval = streaming_interval;
+ uvc_hs_streaming_ep.wMaxPacketSize =
+ cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
+ uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
- uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
- uvc_ss_streaming_ep.bInterval = streaming_interval;
+ uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
+ uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
- uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
- max_packet_size * max_packet_mult * streaming_maxburst;
+ cpu_to_le16(max_packet_size * max_packet_mult *
+ opts->streaming_maxburst);
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
@@ -653,6 +639,18 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+ us = usb_gstrings_attach(cdev, uvc_function_strings,
+ ARRAY_SIZE(uvc_en_us_strings));
+ if (IS_ERR(us)) {
+ ret = PTR_ERR(us);
+ goto error;
+ }
+ uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
+ uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
+ ret = us[UVC_STRING_STREAMING_IDX].id;
+ uvc_streaming_intf_alt0.iInterface = ret;
+ uvc_streaming_intf_alt1.iInterface = ret;
+
/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
@@ -697,7 +695,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Initialise video. */
- ret = uvc_video_init(&uvc->video);
+ ret = uvcg_video_init(&uvc->video);
if (ret < 0)
goto error;
@@ -720,10 +718,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;
@@ -733,104 +730,81 @@ error:
* USB gadget function
*/
-/**
- * uvc_bind_config - add a UVC function to a configuration
- * @c: the configuration to support the UVC instance
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @uvc_setup(). Caller is also responsible for
- * calling @uvc_cleanup() before module unload.
- */
-int __init
-uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *fs_control,
- const struct uvc_descriptor_header * const *ss_control,
- const struct uvc_descriptor_header * const *fs_streaming,
- const struct uvc_descriptor_header * const *hs_streaming,
- const struct uvc_descriptor_header * const *ss_streaming)
+static void uvc_free_inst(struct usb_function_instance *f)
{
- struct uvc_device *uvc;
- int ret = 0;
+ struct f_uvc_opts *opts = to_f_uvc_opts(f);
- /* TODO Check if the USB device controller supports the required
- * features.
- */
- if (!gadget_is_dualspeed(c->cdev->gadget))
- return -EINVAL;
+ kfree(opts);
+}
- uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
- if (uvc == NULL)
- return -ENOMEM;
+static struct usb_function_instance *uvc_alloc_inst(void)
+{
+ struct f_uvc_opts *opts;
- uvc->state = UVC_STATE_DISCONNECTED;
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ opts->func_inst.free_func_inst = uvc_free_inst;
- /* Validate the descriptors. */
- if (fs_control == NULL || fs_control[0] == NULL ||
- fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
- goto error;
+ return &opts->func_inst;
+}
- if (ss_control == NULL || ss_control[0] == NULL ||
- ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
- goto error;
+static void uvc_free(struct usb_function *f)
+{
+ struct uvc_device *uvc = to_uvc(f);
- if (fs_streaming == NULL || fs_streaming[0] == NULL ||
- fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+ kfree(uvc);
+}
- if (hs_streaming == NULL || hs_streaming[0] == NULL ||
- hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct uvc_device *uvc = to_uvc(f);
- if (ss_streaming == NULL || ss_streaming[0] == NULL ||
- ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
- goto error;
+ INFO(cdev, "%s\n", __func__);
- uvc->desc.fs_control = fs_control;
- uvc->desc.ss_control = ss_control;
- uvc->desc.fs_streaming = fs_streaming;
- uvc->desc.hs_streaming = hs_streaming;
- uvc->desc.ss_streaming = ss_streaming;
+ video_unregister_device(uvc->vdev);
+ v4l2_device_unregister(&uvc->v4l2_dev);
+ uvc->control_ep->driver_data = NULL;
+ uvc->video.ep->driver_data = NULL;
- /* String descriptors are global, we only need to allocate string IDs
- * for the first UVC function. UVC functions beyond the first (if any)
- * will reuse the same IDs.
- */
- if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) {
- ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings);
- if (ret)
- goto error;
- uvc_iad.iFunction =
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
- uvc_control_intf.iInterface =
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
- ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
- uvc_streaming_intf_alt0.iInterface = ret;
- uvc_streaming_intf_alt1.iInterface = ret;
- }
+ usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+ kfree(uvc->control_buf);
+
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
+{
+ struct uvc_device *uvc;
+ struct f_uvc_opts *opts;
+
+ uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
+ if (uvc == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ uvc->state = UVC_STATE_DISCONNECTED;
+ opts = to_f_uvc_opts(fi);
+
+ uvc->desc.fs_control = opts->fs_control;
+ uvc->desc.ss_control = opts->ss_control;
+ uvc->desc.fs_streaming = opts->fs_streaming;
+ uvc->desc.hs_streaming = opts->hs_streaming;
+ uvc->desc.ss_streaming = opts->ss_streaming;
/* Register the function. */
uvc->func.name = "uvc";
- uvc->func.strings = uvc_function_strings;
uvc->func.bind = uvc_function_bind;
- uvc->func.unbind = uvc_function_unbind;
+ uvc->func.unbind = uvc_unbind;
uvc->func.get_alt = uvc_function_get_alt;
uvc->func.set_alt = uvc_function_set_alt;
uvc->func.disable = uvc_function_disable;
uvc->func.setup = uvc_function_setup;
+ uvc->func.free_func = uvc_free;
- ret = usb_add_function(c, &uvc->func);
- if (ret)
- kfree(uvc);
-
- return ret;
-
-error:
- kfree(uvc);
- return ret;
+ return &uvc->func;
}
-module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
-
+DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index ec52752f7326..d0a73bdcbba1 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -16,12 +16,13 @@
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
-int uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *fs_control,
- 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);
+#include "uvc.h"
+
+void uvc_function_setup_continue(struct uvc_device *uvc);
+
+void uvc_function_connect(struct uvc_device *uvc);
+
+void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* _F_UVC_H_ */
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f180904f8a..2ce28b9d97cc 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -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 */
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index d48897e8ffeb..cd128e31f808 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -224,6 +224,8 @@ struct ffs_data {
void *ms_os_descs_ext_prop_name_avail;
void *ms_os_descs_ext_prop_data_avail;
+ unsigned user_flags;
+
u8 eps_addrmap[15];
unsigned short strings_count;
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index ad0aca812002..491082aaf103 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -55,11 +55,8 @@
* 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;
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 7a55fea43430..a44a07f30281 100644
--- a/drivers/usb/gadget/function/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
@@ -23,22 +24,6 @@
* 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;
}
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 18c2e729faf6..f8b17fe82efe 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -23,6 +23,14 @@
#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
index 000000000000..78dd37279bd4
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -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
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
new file mode 100644
index 000000000000..2a8dfdff0332
--- /dev/null
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -0,0 +1,39 @@
+/*
+ * u_uvc.h
+ *
+ * Utility definitions for the uvc function
+ *
+ * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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_UVC_H
+#define U_UVC_H
+
+#include <linux/usb/composite.h>
+
+#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
+
+struct f_uvc_opts {
+ struct usb_function_instance func_inst;
+ unsigned int uvc_gadget_trace_param;
+ unsigned int streaming_interval;
+ unsigned int streaming_maxpacket;
+ unsigned int streaming_maxburst;
+ const struct uvc_descriptor_header * const *fs_control;
+ const struct uvc_descriptor_header * const *ss_control;
+ const struct uvc_descriptor_header * const *fs_streaming;
+ const struct uvc_descriptor_header * const *hs_streaming;
+ const struct uvc_descriptor_header * const *ss_streaming;
+};
+
+void uvc_set_trace_param(unsigned int trace);
+
+#endif /* U_UVC_H */
+
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 7a9111de8054..f67695cb28f8 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -53,6 +53,7 @@ struct uvc_event
#ifdef __KERNEL__
#include <linux/usb.h> /* For usb_endpoint_* */
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/videodev2.h>
#include <linux/version.h>
@@ -96,9 +97,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
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 1c29bc954db9..8ea8b3b227b4 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -28,7 +28,7 @@
/* ------------------------------------------------------------------------
* Video buffers queue management.
*
- * Video queues is initialized by uvc_queue_init(). The function performs
+ * Video queues is initialized by uvcg_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
* Video buffers are managed by videobuf2. The driver uses a mutex to protect
@@ -126,13 +126,12 @@ static struct vb2_ops uvc_queue_qops = {
.wait_finish = uvc_wait_finish,
};
-static int uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type)
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
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;
@@ -154,7 +153,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue,
/*
* Free the video buffers.
*/
-static void uvc_free_buffers(struct uvc_video_queue *queue)
+void uvcg_free_buffers(struct uvc_video_queue *queue)
{
mutex_lock(&queue->mutex);
vb2_queue_release(&queue->queue);
@@ -164,8 +163,8 @@ static void uvc_free_buffers(struct uvc_video_queue *queue)
/*
* Allocate the video buffers.
*/
-static int uvc_alloc_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
{
int ret;
@@ -176,8 +175,7 @@ static int uvc_alloc_buffers(struct uvc_video_queue *queue,
return ret ? ret : rb->count;
}
-static int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf)
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
int ret;
@@ -188,8 +186,7 @@ static int uvc_query_buffer(struct uvc_video_queue *queue,
return ret;
}
-static int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf)
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
unsigned long flags;
int ret;
@@ -213,8 +210,8 @@ done:
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
* available.
*/
-static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *buf, int nonblocking)
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
+ int nonblocking)
{
int ret;
@@ -231,8 +228,8 @@ static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
* This function implements video queue polling and is intended to be used by
* the device poll handler.
*/
-static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
- struct file *file, poll_table *wait)
+unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait)
{
unsigned int ret;
@@ -243,8 +240,7 @@ static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
return ret;
}
-static int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma)
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
{
int ret;
@@ -261,8 +257,8 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue,
*
* NO-MMU arch need this function to make mmap() work correctly.
*/
-static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff)
{
unsigned long ret;
@@ -285,7 +281,7 @@ static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
* This function acquires the irq spinlock and can be called from interrupt
* context.
*/
-static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
{
struct uvc_buffer *buf;
unsigned long flags;
@@ -324,9 +320,9 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
* the main queue.
*
* This function can't be called from interrupt context. Use
- * uvc_queue_cancel() instead.
+ * uvcg_queue_cancel() instead.
*/
-static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
{
unsigned long flags;
int ret = 0;
@@ -363,8 +359,8 @@ done:
}
/* called with &queue_irqlock held.. */
-static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
- struct uvc_buffer *buf)
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
@@ -392,7 +388,7 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
return nextbuf;
}
-static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
{
struct uvc_buffer *buf = NULL;
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index 8e76ce982f1e..03919c724961 100644
--- a/drivers/usb/gadget/function/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -57,6 +57,39 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
return vb2_is_streaming(&queue->queue);
}
+int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
+
+void uvcg_free_buffers(struct uvc_video_queue *queue);
+
+int uvcg_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+
+int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
+
+int uvcg_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *buf, int nonblocking);
+
+unsigned int uvcg_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait);
+
+int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma);
+
+#ifndef CONFIG_MMU
+unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff);
+#endif /* CONFIG_MMU */
+
+void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+
+int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
+
+struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+
+struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
+
#endif /* __KERNEL__ */
#endif /* _UVC_QUEUE_H_ */
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ad48e81155e2..b52f2681ec21 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -23,8 +23,10 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include "f_uvc.h"
#include "uvc.h"
#include "uvc_queue.h"
+#include "uvc_video.h"
/* --------------------------------------------------------------------------
* Requests handling
@@ -48,7 +50,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
}
/* --------------------------------------------------------------------------
- * V4L2
+ * V4L2 ioctls
*/
struct uvc_format
@@ -63,8 +65,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 +101,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 +142,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 uvcg_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 uvcg_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 = uvcg_queue_buffer(&video->queue, b);
+ if (ret < 0)
+ return ret;
- return uvc_v4l2_get_format(video, fmt);
- }
+ return uvcg_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 uvcg_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 = uvcg_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 uvcg_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;
+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;
- }
+ uvcg_video_enable(video, 0);
+ uvcg_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
@@ -327,7 +328,7 @@ uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_mmap(&uvc->video.queue, vma);
+ return uvcg_queue_mmap(&uvc->video.queue, vma);
}
static unsigned int
@@ -336,7 +337,7 @@ uvc_v4l2_poll(struct file *file, poll_table *wait)
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_poll(&uvc->video.queue, file, wait);
+ return uvcg_queue_poll(&uvc->video.queue, file, wait);
}
#ifndef CONFIG_MMU
@@ -347,19 +348,19 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
+ return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
}
#endif
-static struct v4l2_file_operations uvc_v4l2_fops = {
+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
- .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+ .get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};
diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h
new file mode 100644
index 000000000000..2683b92fda65
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_v4l2.h
@@ -0,0 +1,22 @@
+/*
+ * uvc_v4l2.h -- USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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 __UVC_V4L2_H__
+#define __UVC_V4L2_H__
+
+extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops;
+extern struct v4l2_file_operations uvc_v4l2_fops;
+
+#endif /* __UVC_V4L2_H__ */
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index a5eb9a3fbb7a..c3e1f27dbbef 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/video.h>
#include <media/v4l2-dev.h>
@@ -85,7 +86,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
- uvc_queue_next_buffer(&video->queue, buf);
+ uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
video->payload_size = 0;
@@ -118,7 +119,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
- uvc_queue_next_buffer(&video->queue, buf);
+ uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
}
}
@@ -171,19 +172,19 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
break;
case -ESHUTDOWN: /* disconnect from host. */
- printk(KERN_INFO "VS request cancelled.\n");
- uvc_queue_cancel(queue, 1);
+ printk(KERN_DEBUG "VS request cancelled.\n");
+ uvcg_queue_cancel(queue, 1);
goto requeue;
default:
printk(KERN_INFO "VS request completed with status %d.\n",
req->status);
- uvc_queue_cancel(queue, 0);
+ uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvc_queue_head(&video->queue);
+ buf = uvcg_queue_head(&video->queue);
if (buf == NULL) {
spin_unlock_irqrestore(&video->queue.irqlock, flags);
goto requeue;
@@ -195,7 +196,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
usb_ep_set_halt(ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
- uvc_queue_cancel(queue, 0);
+ uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
@@ -274,13 +275,12 @@ error:
*/
/*
- * uvc_video_pump - Pump video data into the USB requests
+ * uvcg_video_pump - Pump video data into the USB requests
*
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
-static int
-uvc_video_pump(struct uvc_video *video)
+int uvcg_video_pump(struct uvc_video *video)
{
struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
@@ -288,7 +288,7 @@ uvc_video_pump(struct uvc_video *video)
unsigned long flags;
int ret;
- /* FIXME TODO Race between uvc_video_pump and requests completion
+ /* FIXME TODO Race between uvcg_video_pump and requests completion
* handler ???
*/
@@ -309,10 +309,10 @@ uvc_video_pump(struct uvc_video *video)
/* Retrieve the first available video buffer and fill the
* request, protected by the video queue irqlock.
*/
- spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvc_queue_head(&video->queue);
+ spin_lock_irqsave(&queue->irqlock, flags);
+ buf = uvcg_queue_head(queue);
if (buf == NULL) {
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
break;
}
@@ -323,11 +323,11 @@ uvc_video_pump(struct uvc_video *video)
if (ret < 0) {
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
usb_ep_set_halt(video->ep);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
- uvc_queue_cancel(queue, 0);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ uvcg_queue_cancel(queue, 0);
break;
}
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
@@ -339,8 +339,7 @@ uvc_video_pump(struct uvc_video *video)
/*
* Enable or disable the video stream.
*/
-static int
-uvc_video_enable(struct uvc_video *video, int enable)
+int uvcg_video_enable(struct uvc_video *video, int enable)
{
unsigned int i;
int ret;
@@ -356,11 +355,11 @@ uvc_video_enable(struct uvc_video *video, int enable)
usb_ep_dequeue(video->ep, video->req[i]);
uvc_video_free_requests(video);
- uvc_queue_enable(&video->queue, 0);
+ uvcg_queue_enable(&video->queue, 0);
return 0;
}
- if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+ if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
return ret;
if ((ret = uvc_video_alloc_requests(video)) < 0)
@@ -372,14 +371,13 @@ uvc_video_enable(struct uvc_video *video, int enable)
} else
video->encode = uvc_video_encode_isoc;
- return uvc_video_pump(video);
+ return uvcg_video_pump(video);
}
/*
* Initialize the UVC video stream.
*/
-static int
-uvc_video_init(struct uvc_video *video)
+int uvcg_video_init(struct uvc_video *video)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
@@ -391,7 +389,7 @@ uvc_video_init(struct uvc_video *video)
video->imagesize = 320 * 240 * 2;
/* Initialize the video buffers queue. */
- uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
return 0;
}
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
new file mode 100644
index 000000000000..ef00f06fa00b
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -0,0 +1,24 @@
+/*
+ * uvc_video.h -- USB Video Class Gadget driver
+ *
+ * Copyright (C) 2009-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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 __UVC_VIDEO_H__
+#define __UVC_VIDEO_H__
+
+int uvcg_video_pump(struct uvc_video *video);
+
+int uvcg_video_enable(struct uvc_video *video, int enable);
+
+int uvcg_video_init(struct uvc_video *video);
+
+#endif /* __UVC_VIDEO_H__ */
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index aa376f006333..24392d269709 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -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,
@@ -466,6 +468,7 @@ config USB_G_WEBCAM
depends on VIDEO_DEV
select USB_LIBCOMPOSITE
select VIDEOBUF2_VMALLOC
+ select USB_F_UVC
help
The Webcam Gadget acts as a composite USB Audio and Video Class
device. It provides a userspace API to process UVC control requests
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
index edba2d1ee0f3..7f485f25705e 100644
--- a/drivers/usb/gadget/legacy/Makefile
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -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
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 6eb695e5e43a..f46a3956e43d 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -21,6 +21,66 @@
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;
}
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 225e385a6160..1b075132f8f1 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -410,6 +410,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = {
.bind = dbgp_bind,
.unbind = dbgp_unbind,
.setup = dbgp_setup,
+ .reset = dbgp_disconnect,
.disconnect = dbgp_disconnect,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index e96077b8bf79..edefec2cc584 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1775,6 +1775,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
.bind = gadgetfs_bind,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
+ .reset = gadgetfs_disconnect,
.disconnect = gadgetfs_disconnect,
.suspend = gadgetfs_suspend,
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index a11d8e420bfe..04a3da20f742 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -12,23 +12,31 @@
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <linux/usb/video.h>
-#include "f_uvc.h"
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "uvc_queue.c"
-#include "uvc_video.c"
-#include "uvc_v4l2.c"
-#include "f_uvc.c"
+#include "u_uvc.h"
USB_GADGET_COMPOSITE_OPTIONS();
+
+/*-------------------------------------------------------------------------*/
+
+/* module parameters specific to the Video streaming endpoint */
+static unsigned int streaming_interval = 1;
+module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_interval, "1 - 16");
+
+static unsigned int streaming_maxpacket = 1024;
+module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
+
+static unsigned int streaming_maxburst;
+module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
+
+static unsigned int trace;
+module_param(trace, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -63,6 +71,9 @@ static struct usb_gadget_strings *webcam_device_strings[] = {
NULL,
};
+static struct usb_function_instance *fi_uvc;
+static struct usb_function *f_uvc;
+
static struct usb_device_descriptor webcam_device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
@@ -326,9 +337,17 @@ 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);
+ int status = 0;
+
+ f_uvc = usb_get_function(fi_uvc);
+ if (IS_ERR(f_uvc))
+ return PTR_ERR(f_uvc);
+
+ status = usb_add_function(c, f_uvc);
+ if (status < 0)
+ usb_put_function(f_uvc);
+
+ return status;
}
static struct usb_configuration webcam_config_driver = {
@@ -342,14 +361,36 @@ static struct usb_configuration webcam_config_driver = {
static int /* __init_or_exit */
webcam_unbind(struct usb_composite_dev *cdev)
{
+ if (!IS_ERR_OR_NULL(f_uvc))
+ usb_put_function(f_uvc);
+ if (!IS_ERR_OR_NULL(fi_uvc))
+ usb_put_function_instance(fi_uvc);
return 0;
}
static int __init
webcam_bind(struct usb_composite_dev *cdev)
{
+ struct f_uvc_opts *uvc_opts;
int ret;
+ fi_uvc = usb_get_function_instance("uvc");
+ if (IS_ERR(fi_uvc))
+ return PTR_ERR(fi_uvc);
+
+ uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
+
+ uvc_opts->streaming_interval = streaming_interval;
+ uvc_opts->streaming_maxpacket = streaming_maxpacket;
+ uvc_opts->streaming_maxburst = streaming_maxburst;
+ uvc_set_trace_param(trace);
+
+ uvc_opts->fs_control = uvc_fs_control_cls;
+ uvc_opts->ss_control = uvc_ss_control_cls;
+ uvc_opts->fs_streaming = uvc_fs_streaming_cls;
+ uvc_opts->hs_streaming = uvc_hs_streaming_cls;
+ uvc_opts->ss_streaming = uvc_ss_streaming_cls;
+
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
@@ -373,7 +414,7 @@ webcam_bind(struct usb_composite_dev *cdev)
return 0;
error:
- webcam_unbind(cdev);
+ usb_put_function_instance(fi_uvc);
return ret;
}
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index c3d496828b74..ebf09f439f3a 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -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);
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 34ebaa68504c..3ea287b0e448 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -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.
@@ -354,6 +354,21 @@ config USB_EG20T
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
This driver can be used with Intel's Quark X1000 SOC platform
+
+config USB_GADGET_XILINX
+ tristate "Xilinx USB Driver"
+ depends on OF || COMPILE_TEST
+ help
+ USB peripheral controller driver for Xilinx USB2 device.
+ Xilinx USB2 device is a soft IP which supports both full
+ and high speed USB 2.0 data transfers. It has seven configurable
+ endpoints(bulk or interrupt or isochronous), as well as
+ endpoint zero(for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "udc-xilinx" and force all
+ gadget drivers to also be dynamically linked.
+
#
# LAST -- dummy/emulated controller
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 4096122bb283..a7f4491593f1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
+obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 08df5c4f46ce..ecd10b574bfd 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -318,8 +318,26 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
gr_free_dma_desc_chain(dev, req);
- if (ep->is_in) /* For OUT, actual gets updated bit by bit */
+ if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */
req->req.actual = req->req.length;
+ } else if (req->oddlen && req->req.actual > req->evenlen) {
+ /*
+ * Copy to user buffer in this case where length was not evenly
+ * divisible by ep->ep.maxpacket and the last descriptor was
+ * actually used.
+ */
+ char *buftail = ((char *)req->req.buf + req->evenlen);
+
+ memcpy(buftail, ep->tailbuf, req->oddlen);
+
+ if (req->req.actual > req->req.length) {
+ /* We got more data than was requested */
+ dev_dbg(ep->dev->dev, "Overflow for ep %s\n",
+ ep->ep.name);
+ gr_dbgprint_request("OVFL", ep, req);
+ req->req.status = -EOVERFLOW;
+ }
+ }
if (!status) {
if (ep->is_in)
@@ -379,6 +397,15 @@ static void gr_start_dma(struct gr_ep *ep)
/* A descriptor should already have been allocated */
BUG_ON(!req->curr_desc);
+ /*
+ * The DMA controller can not handle smaller OUT buffers than
+ * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly
+ * long packet are received. Therefore an internal bounce buffer gets
+ * used when such a request gets enabled.
+ */
+ if (!ep->is_in && req->oddlen)
+ req->last_desc->data = ep->tailbuf_paddr;
+
wmb(); /* Make sure all is settled before handing it over to DMA */
/* Set the descriptor pointer in the hardware */
@@ -480,11 +507,11 @@ static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req,
dma_addr_t start = req->req.dma + bytes_used;
u16 size = min(bytes_left, ep->bytes_per_buffer);
- /* Should not happen however - gr_queue stops such lengths */
- if (size < ep->bytes_per_buffer)
- dev_warn(ep->dev->dev,
- "Buffer overrun risk: %u < %u bytes/buffer\n",
- size, ep->bytes_per_buffer);
+ if (size < ep->bytes_per_buffer) {
+ /* Prepare using bounce buffer */
+ req->evenlen = req->req.length - bytes_left;
+ req->oddlen = size;
+ }
ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
if (ret)
@@ -584,18 +611,6 @@ static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags)
return -EINVAL;
}
- /*
- * The DMA controller can not handle smaller OUT buffers than
- * maxpacket. It could lead to buffer overruns if unexpectedly long
- * packet are received.
- */
- if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
- dev_err(dev->dev,
- "OUT request length %d is not multiple of maxpacket\n",
- req->req.length);
- return -EMSGSIZE;
- }
-
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
dev_err(dev->dev, "-ESHUTDOWN");
return -ESHUTDOWN;
@@ -1286,8 +1301,8 @@ static int gr_handle_out_ep(struct gr_ep *ep)
if (ctrl & GR_DESC_OUT_CTRL_SE)
req->setup = 1;
- if (len < ep->ep.maxpacket || req->req.actual == req->req.length) {
- /* Short packet or the expected size - we are done */
+ if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) {
+ /* Short packet or >= expected size - we are done */
if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) {
/*
@@ -2015,6 +2030,11 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
}
list_add_tail(&ep->ep_list, &dev->ep_list);
+ ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit,
+ &ep->tailbuf_paddr, GFP_ATOMIC);
+ if (!ep->tailbuf)
+ return -ENOMEM;
+
return 0;
}
@@ -2067,9 +2087,24 @@ static int gr_udc_init(struct gr_udc *dev)
return 0;
}
+static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
+{
+ struct gr_ep *ep;
+
+ if (is_in)
+ ep = &dev->epi[num];
+ else
+ ep = &dev->epo[num];
+
+ if (ep->tailbuf)
+ dma_free_coherent(dev->dev, ep->ep.maxpacket_limit,
+ ep->tailbuf, ep->tailbuf_paddr);
+}
+
static int gr_remove(struct platform_device *pdev)
{
struct gr_udc *dev = platform_get_drvdata(pdev);
+ int i;
if (dev->added)
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
@@ -2084,6 +2119,11 @@ static int gr_remove(struct platform_device *pdev)
gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
+ for (i = 0; i < dev->nepo; i++)
+ gr_ep_remove(dev, i, 0);
+ for (i = 0; i < dev->nepi; i++)
+ gr_ep_remove(dev, i, 1);
+
return 0;
}
static int gr_request_irq(struct gr_udc *dev, int irq)
@@ -2131,7 +2171,6 @@ static int gr_probe(struct platform_device *pdev)
dev->gadget.name = driver_name;
dev->gadget.max_speed = USB_SPEED_HIGH;
dev->gadget.ops = &gr_ops;
- dev->gadget.quirk_ep_out_aligned_size = true;
spin_lock_init(&dev->lock);
dev->regs = regs;
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 8388897d9ec3..4297c4e8021f 100644
--- a/drivers/usb/gadget/udc/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -156,6 +156,10 @@ struct gr_ep {
struct list_head queue;
struct list_head ep_list;
+
+ /* Bounce buffer for end of "odd" sized OUT requests */
+ void *tailbuf;
+ dma_addr_t tailbuf_paddr;
};
struct gr_request {
@@ -167,6 +171,9 @@ struct gr_request {
struct gr_dma_desc *curr_desc; /* Current descriptor */
struct gr_dma_desc *last_desc; /* Last in the chain */
+ u16 evenlen; /* Size of even length head (if oddlen != 0) */
+ u16 oddlen; /* Size of odd length tail if buffer length is "odd" */
+
u8 setup; /* Setup packet */
};
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index de2a8713b428..de9c400b9944 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -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;
}
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index b0d98172bc07..ad1ceac15468 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -109,8 +109,20 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
static void usb_gadget_state_work(struct work_struct *work)
{
struct usb_gadget *gadget = work_to_gadget(work);
+ struct usb_udc *udc = NULL;
+
+ mutex_lock(&udc_lock);
+ list_for_each_entry(udc, &udc_list, list)
+ if (udc->gadget == gadget)
+ goto found;
+ mutex_unlock(&udc_lock);
- sysfs_notify(&gadget->dev.kobj, NULL, "state");
+ return;
+
+found:
+ mutex_unlock(&udc_lock);
+
+ sysfs_notify(&udc->dev.kobj, NULL, "state");
}
void usb_gadget_set_state(struct usb_gadget *gadget,
@@ -124,6 +136,23 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
/**
+ * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
+ * @gadget: The gadget which bus reset occurs
+ * @driver: The gadget driver we want to notify
+ *
+ * If the udc driver has bus reset handler, it needs to call this when the bus
+ * reset occurs, it notifies the gadget driver that the bus reset occurs as
+ * well as updates gadget state.
+ */
+void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ driver->reset(gadget);
+ usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
+}
+EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
+
+/**
* usb_gadget_udc_start - tells usb device controller to start up
* @gadget: The gadget we want to get started
* @driver: The driver we want to bind to @gadget
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
new file mode 100644
index 000000000000..ed27e1687a4e
--- /dev/null
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -0,0 +1,2180 @@
+/*
+ * Xilinx USB peripheral controller driver
+ *
+ * Copyright (C) 2004 by Thomas Rathbone
+ * Copyright (C) 2005 by HP Labs
+ * Copyright (C) 2005 by David Brownell
+ * Copyright (C) 2010 - 2014 Xilinx, Inc.
+ *
+ * Some parts of this driver code is based on the driver for at91-series
+ * USB peripheral controller (at91_udc.c).
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any
+ * later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/prefetch.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Register offsets for the USB device.*/
+#define XUSB_EP0_CONFIG_OFFSET 0x0000 /* EP0 Config Reg Offset */
+#define XUSB_SETUP_PKT_ADDR_OFFSET 0x0080 /* Setup Packet Address */
+#define XUSB_ADDRESS_OFFSET 0x0100 /* Address Register */
+#define XUSB_CONTROL_OFFSET 0x0104 /* Control Register */
+#define XUSB_STATUS_OFFSET 0x0108 /* Status Register */
+#define XUSB_FRAMENUM_OFFSET 0x010C /* Frame Number Register */
+#define XUSB_IER_OFFSET 0x0110 /* Interrupt Enable Register */
+#define XUSB_BUFFREADY_OFFSET 0x0114 /* Buffer Ready Register */
+#define XUSB_TESTMODE_OFFSET 0x0118 /* Test Mode Register */
+#define XUSB_DMA_RESET_OFFSET 0x0200 /* DMA Soft Reset Register */
+#define XUSB_DMA_CONTROL_OFFSET 0x0204 /* DMA Control Register */
+#define XUSB_DMA_DSAR_ADDR_OFFSET 0x0208 /* DMA source Address Reg */
+#define XUSB_DMA_DDAR_ADDR_OFFSET 0x020C /* DMA destination Addr Reg */
+#define XUSB_DMA_LENGTH_OFFSET 0x0210 /* DMA Length Register */
+#define XUSB_DMA_STATUS_OFFSET 0x0214 /* DMA Status Register */
+
+/* Endpoint Configuration Space offsets */
+#define XUSB_EP_CFGSTATUS_OFFSET 0x00 /* Endpoint Config Status */
+#define XUSB_EP_BUF0COUNT_OFFSET 0x08 /* Buffer 0 Count */
+#define XUSB_EP_BUF1COUNT_OFFSET 0x0C /* Buffer 1 Count */
+
+#define XUSB_CONTROL_USB_READY_MASK 0x80000000 /* USB ready Mask */
+#define XUSB_CONTROL_USB_RMTWAKE_MASK 0x40000000 /* Remote wake up mask */
+
+/* Interrupt register related masks.*/
+#define XUSB_STATUS_GLOBAL_INTR_MASK 0x80000000 /* Global Intr Enable */
+#define XUSB_STATUS_DMADONE_MASK 0x04000000 /* DMA done Mask */
+#define XUSB_STATUS_DMAERR_MASK 0x02000000 /* DMA Error Mask */
+#define XUSB_STATUS_DMABUSY_MASK 0x80000000 /* DMA Error Mask */
+#define XUSB_STATUS_RESUME_MASK 0x01000000 /* USB Resume Mask */
+#define XUSB_STATUS_RESET_MASK 0x00800000 /* USB Reset Mask */
+#define XUSB_STATUS_SUSPEND_MASK 0x00400000 /* USB Suspend Mask */
+#define XUSB_STATUS_DISCONNECT_MASK 0x00200000 /* USB Disconnect Mask */
+#define XUSB_STATUS_FIFO_BUFF_RDY_MASK 0x00100000 /* FIFO Buff Ready Mask */
+#define XUSB_STATUS_FIFO_BUFF_FREE_MASK 0x00080000 /* FIFO Buff Free Mask */
+#define XUSB_STATUS_SETUP_PACKET_MASK 0x00040000 /* Setup packet received */
+#define XUSB_STATUS_EP1_BUFF2_COMP_MASK 0x00000200 /* EP 1 Buff 2 Processed */
+#define XUSB_STATUS_EP1_BUFF1_COMP_MASK 0x00000002 /* EP 1 Buff 1 Processed */
+#define XUSB_STATUS_EP0_BUFF2_COMP_MASK 0x00000100 /* EP 0 Buff 2 Processed */
+#define XUSB_STATUS_EP0_BUFF1_COMP_MASK 0x00000001 /* EP 0 Buff 1 Processed */
+#define XUSB_STATUS_HIGH_SPEED_MASK 0x00010000 /* USB Speed Mask */
+/* Suspend,Reset,Suspend and Disconnect Mask */
+#define XUSB_STATUS_INTR_EVENT_MASK 0x01E00000
+/* Buffers completion Mask */
+#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK 0x0000FEFF
+/* Mask for buffer 0 and buffer 1 completion for all Endpoints */
+#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK 0x00000101
+#define XUSB_STATUS_EP_BUFF2_SHIFT 8 /* EP buffer offset */
+
+/* Endpoint Configuration Status Register */
+#define XUSB_EP_CFG_VALID_MASK 0x80000000 /* Endpoint Valid bit */
+#define XUSB_EP_CFG_STALL_MASK 0x40000000 /* Endpoint Stall bit */
+#define XUSB_EP_CFG_DATA_TOGGLE_MASK 0x08000000 /* Endpoint Data toggle */
+
+/* USB device specific global configuration constants.*/
+#define XUSB_MAX_ENDPOINTS 8 /* Maximum End Points */
+#define XUSB_EP_NUMBER_ZERO 0 /* End point Zero */
+/* DPRAM is the source address for DMA transfer */
+#define XUSB_DMA_READ_FROM_DPRAM 0x80000000
+#define XUSB_DMA_DMASR_BUSY 0x80000000 /* DMA busy */
+#define XUSB_DMA_DMASR_ERROR 0x40000000 /* DMA Error */
+/*
+ * When this bit is set, the DMA buffer ready bit is set by hardware upon
+ * DMA transfer completion.
+ */
+#define XUSB_DMA_BRR_CTRL 0x40000000 /* DMA bufready ctrl bit */
+/* Phase States */
+#define SETUP_PHASE 0x0000 /* Setup Phase */
+#define DATA_PHASE 0x0001 /* Data Phase */
+#define STATUS_PHASE 0x0002 /* Status Phase */
+
+#define EP0_MAX_PACKET 64 /* Endpoint 0 maximum packet length */
+#define STATUSBUFF_SIZE 2 /* Buffer size for GET_STATUS command */
+#define EPNAME_SIZE 4 /* Buffer size for endpoint name */
+
+/* container_of helper macros */
+#define to_udc(g) container_of((g), struct xusb_udc, gadget)
+#define to_xusb_ep(ep) container_of((ep), struct xusb_ep, ep_usb)
+#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req)
+
+/**
+ * struct xusb_req - Xilinx USB device request structure
+ * @usb_req: Linux usb request structure
+ * @queue: usb device request queue
+ * @ep: pointer to xusb_endpoint structure
+ */
+struct xusb_req {
+ struct usb_request usb_req;
+ struct list_head queue;
+ struct xusb_ep *ep;
+};
+
+/**
+ * struct xusb_ep - USB end point structure.
+ * @ep_usb: usb endpoint instance
+ * @queue: endpoint message queue
+ * @udc: xilinx usb peripheral driver instance pointer
+ * @desc: pointer to the usb endpoint descriptor
+ * @rambase: the endpoint buffer address
+ * @offset: the endpoint register offset value
+ * @name: name of the endpoint
+ * @epnumber: endpoint number
+ * @maxpacket: maximum packet size the endpoint can store
+ * @buffer0count: the size of the packet recieved in the first buffer
+ * @buffer1count: the size of the packet received in the second buffer
+ * @curbufnum: current buffer of endpoint that will be processed next
+ * @buffer0ready: the busy state of first buffer
+ * @buffer1ready: the busy state of second buffer
+ * @is_in: endpoint direction (IN or OUT)
+ * @is_iso: endpoint type(isochronous or non isochronous)
+ */
+struct xusb_ep {
+ struct usb_ep ep_usb;
+ struct list_head queue;
+ struct xusb_udc *udc;
+ const struct usb_endpoint_descriptor *desc;
+ u32 rambase;
+ u32 offset;
+ char name[4];
+ u16 epnumber;
+ u16 maxpacket;
+ u16 buffer0count;
+ u16 buffer1count;
+ u8 curbufnum;
+ bool buffer0ready;
+ bool buffer1ready;
+ bool is_in;
+ bool is_iso;
+};
+
+/**
+ * struct xusb_udc - USB peripheral driver structure
+ * @gadget: USB gadget driver instance
+ * @ep: an array of endpoint structures
+ * @driver: pointer to the usb gadget driver instance
+ * @setup: usb_ctrlrequest structure for control requests
+ * @req: pointer to dummy request for get status command
+ * @dev: pointer to device structure in gadget
+ * @usb_state: device in suspended state or not
+ * @remote_wkp: remote wakeup enabled by host
+ * @setupseqtx: tx status
+ * @setupseqrx: rx status
+ * @addr: the usb device base address
+ * @lock: instance of spinlock
+ * @dma_enabled: flag indicating whether the dma is included in the system
+ * @read_fn: function pointer to read device registers
+ * @write_fn: function pointer to write to device registers
+ */
+struct xusb_udc {
+ struct usb_gadget gadget;
+ struct xusb_ep ep[8];
+ struct usb_gadget_driver *driver;
+ struct usb_ctrlrequest setup;
+ struct xusb_req *req;
+ struct device *dev;
+ u32 usb_state;
+ u32 remote_wkp;
+ u32 setupseqtx;
+ u32 setupseqrx;
+ void __iomem *addr;
+ spinlock_t lock;
+ bool dma_enabled;
+
+ unsigned int (*read_fn)(void __iomem *);
+ void (*write_fn)(void __iomem *, u32, u32);
+};
+
+/* Endpoint buffer start addresses in the core */
+static u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500,
+ 0x1600 };
+
+static const char driver_name[] = "xilinx-udc";
+static const char ep0name[] = "ep0";
+
+/* Control endpoint configuration.*/
+static const struct usb_endpoint_descriptor config_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(EP0_MAX_PACKET),
+};
+
+/**
+ * xudc_write32 - little endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32(void __iomem *addr, u32 offset, u32 val)
+{
+ iowrite32(val, addr + offset);
+}
+
+/**
+ * xudc_read32 - little endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32(void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+/**
+ * xudc_write32_be - big endian write to device registers
+ * @addr: base addr of device registers
+ * @offset: register offset
+ * @val: data to be written
+ */
+static void xudc_write32_be(void __iomem *addr, u32 offset, u32 val)
+{
+ iowrite32be(val, addr + offset);
+}
+
+/**
+ * xudc_read32_be - big endian read from device registers
+ * @addr: addr of device register
+ * Return: value at addr
+ */
+static unsigned int xudc_read32_be(void __iomem *addr)
+{
+ return ioread32be(addr);
+}
+
+/**
+ * xudc_wrstatus - Sets up the usb device status stages.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_wrstatus(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+ u32 epcfgreg;
+
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset)|
+ XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+ udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+}
+
+/**
+ * xudc_epconfig - Configures the given endpoint.
+ * @ep: pointer to the usb device endpoint structure.
+ * @udc: pointer to the usb peripheral controller structure.
+ *
+ * This function configures a specific endpoint with the given configuration
+ * data.
+ */
+static void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc)
+{
+ u32 epcfgreg;
+
+ /*
+ * Configure the end point direction, type, Max Packet Size and the
+ * EP buffer location.
+ */
+ epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) |
+ (ep->ep_usb.maxpacket << 15) | (ep->rambase));
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+
+ /* Set the Buffer count and the Buffer ready bits.*/
+ udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET,
+ ep->buffer0count);
+ udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET,
+ ep->buffer1count);
+ if (ep->buffer0ready)
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ if (ep->buffer1ready)
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+}
+
+/**
+ * xudc_start_dma - Starts DMA transfer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @src: DMA source address.
+ * @dst: DMA destination address.
+ * @length: number of bytes to transfer.
+ *
+ * Return: 0 on success, error code on failure
+ *
+ * This function starts DMA transfer by writing to DMA source,
+ * destination and lenth registers.
+ */
+static int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src,
+ dma_addr_t dst, u32 length)
+{
+ struct xusb_udc *udc = ep->udc;
+ int rc = 0;
+ u32 timeout = 500;
+ u32 reg;
+
+ /*
+ * Set the addresses in the DMA source and
+ * destination registers and then set the length
+ * into the DMA length register.
+ */
+ udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src);
+ udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst);
+ udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length);
+
+ /*
+ * Wait till DMA transaction is complete and
+ * check whether the DMA transaction was
+ * successful.
+ */
+ do {
+ reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET);
+ if (!(reg & XUSB_DMA_DMASR_BUSY))
+ break;
+
+ /*
+ * We can't sleep here, because it's also called from
+ * interrupt context.
+ */
+ timeout--;
+ if (!timeout) {
+ dev_err(udc->dev, "DMA timeout\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ } while (1);
+
+ if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) &
+ XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){
+ dev_err(udc->dev, "DMA Error\n");
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/**
+ * xudc_dma_send - Sends IN data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to data to be sent.
+ * @length: number of bytes to send.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ * code on failure.
+ *
+ * This function sends data using DMA.
+ */
+static int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *buffer, u32 length)
+{
+ u32 *eprambase;
+ dma_addr_t src;
+ dma_addr_t dst;
+ struct xusb_udc *udc = ep->udc;
+
+ src = req->usb_req.dma + req->usb_req.actual;
+ if (req->usb_req.length)
+ dma_sync_single_for_device(udc->dev, src,
+ length, DMA_TO_DEVICE);
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ dst = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF0COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | (1 << ep->epnumber));
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase +
+ ep->ep_usb.maxpacket);
+ dst = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF1COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of ping pong buffers are ready currently .*/
+ return -EAGAIN;
+ }
+
+ return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_dma_receive - Receives OUT data using DMA.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @buffer: pointer to storage buffer of received data.
+ * @length: number of bytes to receive.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free and error
+ * code on failure.
+ *
+ * This function receives data using DMA.
+ */
+static int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *buffer, u32 length)
+{
+ u32 *eprambase;
+ dma_addr_t src;
+ dma_addr_t dst;
+ struct xusb_udc *udc = ep->udc;
+
+ dst = req->usb_req.dma + req->usb_req.actual;
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data */
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ src = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+ (1 << ep->epnumber));
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data */
+ eprambase = (u32 __force *)(udc->addr +
+ ep->rambase + ep->ep_usb.maxpacket);
+ src = virt_to_phys(eprambase);
+ udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET,
+ XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM |
+ (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of the ping-pong buffers are ready currently */
+ return -EAGAIN;
+ }
+
+ return xudc_start_dma(ep, src, dst, length);
+}
+
+/**
+ * xudc_eptxrx - Transmits or receives data to or from an endpoint.
+ * @ep: pointer to the usb endpoint configuration structure.
+ * @req: pointer to the usb request structure.
+ * @bufferptr: pointer to buffer containing the data to be sent.
+ * @bufferlen: The number of data bytes to be sent.
+ *
+ * Return: 0 on success, -EAGAIN if no buffer is free.
+ *
+ * This function copies the transmit/receive data to/from the end point buffer
+ * and enables the buffer for transmission/reception.
+ */
+static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req,
+ u8 *bufferptr, u32 bufferlen)
+{
+ u32 *eprambase;
+ u32 bytestosend;
+ int rc = 0;
+ struct xusb_udc *udc = ep->udc;
+
+ bytestosend = bufferlen;
+ if (udc->dma_enabled) {
+ if (ep->is_in)
+ rc = xudc_dma_send(ep, req, bufferptr, bufferlen);
+ else
+ rc = xudc_dma_receive(ep, req, bufferptr, bufferlen);
+ return rc;
+ }
+ /* Put the transmit buffer into the correct ping-pong buffer.*/
+ if (!ep->curbufnum && !ep->buffer0ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase);
+ if (ep->is_in) {
+ memcpy(eprambase, bufferptr, bytestosend);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF0COUNT_OFFSET, bufferlen);
+ } else {
+ memcpy(bufferptr, eprambase, bytestosend);
+ }
+ /*
+ * Enable the buffer for transmission.
+ */
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ ep->buffer0ready = 1;
+ ep->curbufnum = 1;
+ } else if (ep->curbufnum && !ep->buffer1ready) {
+ /* Get the Buffer address and copy the transmit data.*/
+ eprambase = (u32 __force *)(udc->addr + ep->rambase +
+ ep->ep_usb.maxpacket);
+ if (ep->is_in) {
+ memcpy(eprambase, bufferptr, bytestosend);
+ udc->write_fn(udc->addr, ep->offset +
+ XUSB_EP_BUF1COUNT_OFFSET, bufferlen);
+ } else {
+ memcpy(bufferptr, eprambase, bytestosend);
+ }
+ /*
+ * Enable the buffer for transmission.
+ */
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT));
+ ep->buffer1ready = 1;
+ ep->curbufnum = 0;
+ } else {
+ /* None of the ping-pong buffers are ready currently */
+ return -EAGAIN;
+ }
+ return rc;
+}
+
+/**
+ * xudc_done - Exeutes the endpoint data transfer completion tasks.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ * @status: Status of the data transfer.
+ *
+ * Deletes the message from the queue and updates data transfer completion
+ * status.
+ */
+static void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status)
+{
+ struct xusb_udc *udc = ep->udc;
+
+ list_del_init(&req->queue);
+
+ if (req->usb_req.status == -EINPROGRESS)
+ req->usb_req.status = status;
+ else
+ status = req->usb_req.status;
+
+ if (status && status != -ESHUTDOWN)
+ dev_dbg(udc->dev, "%s done %p, status %d\n",
+ ep->ep_usb.name, req, status);
+ /* unmap request if DMA is present*/
+ if (udc->dma_enabled && ep->epnumber && req->usb_req.length)
+ usb_gadget_unmap_request(&udc->gadget, &req->usb_req,
+ ep->is_in);
+
+ if (req->usb_req.complete) {
+ spin_unlock(&udc->lock);
+ req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+ spin_lock(&udc->lock);
+ }
+}
+
+/**
+ * xudc_read_fifo - Reads the data from the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Pulls OUT packet data from the endpoint buffer.
+ */
+static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+ u8 *buf;
+ u32 is_short, count, bufferspace;
+ u8 bufoffset;
+ u8 two_pkts = 0;
+ int ret;
+ int retval = -EAGAIN;
+ struct xusb_udc *udc = ep->udc;
+
+ if (ep->buffer0ready && ep->buffer1ready) {
+ dev_dbg(udc->dev, "Packet NOT ready!\n");
+ return retval;
+ }
+top:
+ if (ep->curbufnum)
+ bufoffset = XUSB_EP_BUF1COUNT_OFFSET;
+ else
+ bufoffset = XUSB_EP_BUF0COUNT_OFFSET;
+
+ count = udc->read_fn(udc->addr + ep->offset + bufoffset);
+
+ if (!ep->buffer0ready && !ep->buffer1ready)
+ two_pkts = 1;
+
+ buf = req->usb_req.buf + req->usb_req.actual;
+ prefetchw(buf);
+ bufferspace = req->usb_req.length - req->usb_req.actual;
+ is_short = count < ep->ep_usb.maxpacket;
+
+ if (unlikely(!bufferspace)) {
+ /*
+ * This happens when the driver's buffer
+ * is smaller than what the host sent.
+ * discard the extra data.
+ */
+ if (req->usb_req.status != -EOVERFLOW)
+ dev_dbg(udc->dev, "%s overflow %d\n",
+ ep->ep_usb.name, count);
+ req->usb_req.status = -EOVERFLOW;
+ xudc_done(ep, req, -EOVERFLOW);
+ return 0;
+ }
+
+ ret = xudc_eptxrx(ep, req, buf, count);
+ switch (ret) {
+ case 0:
+ req->usb_req.actual += min(count, bufferspace);
+ dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n",
+ ep->ep_usb.name, count, is_short ? "/S" : "", req,
+ req->usb_req.actual, req->usb_req.length);
+ bufferspace -= count;
+ /* Completion */
+ if ((req->usb_req.actual == req->usb_req.length) || is_short) {
+ if (udc->dma_enabled && req->usb_req.length)
+ dma_sync_single_for_cpu(udc->dev,
+ req->usb_req.dma,
+ req->usb_req.actual,
+ DMA_FROM_DEVICE);
+ xudc_done(ep, req, 0);
+ return 0;
+ }
+ if (two_pkts) {
+ two_pkts = 0;
+ goto top;
+ }
+ break;
+ case -EAGAIN:
+ dev_dbg(udc->dev, "receive busy\n");
+ break;
+ case -EINVAL:
+ case -ETIMEDOUT:
+ /* DMA error, dequeue the request */
+ xudc_done(ep, req, -ECONNRESET);
+ retval = 0;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * xudc_write_fifo - Writes data into the given endpoint buffer.
+ * @ep: pointer to the usb device endpoint structure.
+ * @req: pointer to the usb request structure.
+ *
+ * Return: 0 if request is completed and -EAGAIN if not completed.
+ *
+ * Loads endpoint buffer for an IN packet.
+ */
+static int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req)
+{
+ u32 max;
+ u32 length;
+ int ret;
+ int retval = -EAGAIN;
+ struct xusb_udc *udc = ep->udc;
+ int is_last, is_short = 0;
+ u8 *buf;
+
+ max = le16_to_cpu(ep->desc->wMaxPacketSize);
+ buf = req->usb_req.buf + req->usb_req.actual;
+ prefetch(buf);
+ length = req->usb_req.length - req->usb_req.actual;
+ length = min(length, max);
+
+ ret = xudc_eptxrx(ep, req, buf, length);
+ switch (ret) {
+ case 0:
+ req->usb_req.actual += length;
+ if (unlikely(length != max)) {
+ is_last = is_short = 1;
+ } else {
+ if (likely(req->usb_req.length !=
+ req->usb_req.actual) || req->usb_req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ }
+ dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n",
+ __func__, ep->ep_usb.name, length, is_last ? "/L" : "",
+ is_short ? "/S" : "",
+ req->usb_req.length - req->usb_req.actual, req);
+ /* completion */
+ if (is_last) {
+ xudc_done(ep, req, 0);
+ retval = 0;
+ }
+ break;
+ case -EAGAIN:
+ dev_dbg(udc->dev, "Send busy\n");
+ break;
+ case -EINVAL:
+ case -ETIMEDOUT:
+ /* DMA error, dequeue the request */
+ xudc_done(ep, req, -ECONNRESET);
+ retval = 0;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * xudc_nuke - Cleans up the data transfer message list.
+ * @ep: pointer to the usb device endpoint structure.
+ * @status: Status of the data transfer.
+ */
+static void xudc_nuke(struct xusb_ep *ep, int status)
+{
+ struct xusb_req *req;
+
+ while (!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct xusb_req, queue);
+ xudc_done(ep, req, status);
+ }
+}
+
+/**
+ * xudc_ep_set_halt - Stalls/unstalls the given endpoint.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @value: value to indicate stall/unstall.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc;
+ unsigned long flags;
+ u32 epcfgreg;
+
+ if (!_ep || (!ep->desc && ep->epnumber)) {
+ pr_debug("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+ udc = ep->udc;
+
+ if (ep->is_in && (!list_empty(&ep->queue)) && value) {
+ dev_dbg(udc->dev, "requests pending can't halt\n");
+ return -EAGAIN;
+ }
+
+ if (ep->buffer0ready || ep->buffer1ready) {
+ dev_dbg(udc->dev, "HW buffers busy can't halt\n");
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (value) {
+ /* Stall the device.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ } else {
+ /* Unstall the device.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ if (ep->epnumber) {
+ /* Reset the toggle bit.*/
+ epcfgreg = udc->read_fn(ep->udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ }
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @ep: pointer to the xusb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep_enable(struct xusb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct xusb_udc *udc = ep->udc;
+ u32 tmp;
+ u32 epcfg;
+ u32 ier;
+ u16 maxpacket;
+
+ ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0);
+ /* Bit 3...0:endpoint number */
+ ep->epnumber = (desc->bEndpointAddress & 0x0f);
+ ep->desc = desc;
+ ep->ep_usb.desc = desc;
+ tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ switch (tmp) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ dev_dbg(udc->dev, "only one control endpoint\n");
+ /* NON- ISO */
+ ep->is_iso = 0;
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_INT:
+ /* NON- ISO */
+ ep->is_iso = 0;
+ if (maxpacket > 64) {
+ dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ }
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ /* NON- ISO */
+ ep->is_iso = 0;
+ if (!(is_power_of_2(maxpacket) && maxpacket >= 8 &&
+ maxpacket <= 512)) {
+ dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket);
+ return -EINVAL;
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO */
+ ep->is_iso = 1;
+ break;
+ }
+
+ ep->buffer0ready = 0;
+ ep->buffer1ready = 0;
+ ep->curbufnum = 0;
+ ep->rambase = rambase[ep->epnumber];
+ xudc_epconfig(ep, udc);
+
+ dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n",
+ ep->epnumber, maxpacket);
+
+ /* Enable the End point.*/
+ epcfg = udc->read_fn(udc->addr + ep->offset);
+ epcfg |= XUSB_EP_CFG_VALID_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfg);
+ if (ep->epnumber)
+ ep->rambase <<= 2;
+
+ /* Enable buffer completion interrupts for endpoint */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber);
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ /* for OUT endpoint set buffers ready to receive */
+ if (ep->epnumber && !ep->is_in) {
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ 1 << ep->epnumber);
+ ep->buffer0ready = 1;
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET,
+ (1 << (ep->epnumber +
+ XUSB_STATUS_EP_BUFF2_SHIFT)));
+ ep->buffer1ready = 1;
+ }
+
+ return 0;
+}
+
+/**
+ * xudc_ep_enable - Enables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct xusb_ep *ep;
+ struct xusb_udc *udc;
+ unsigned long flags;
+ int ret;
+
+ if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("%s: bad ep or descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = to_xusb_ep(_ep);
+ udc = ep->udc;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ret = __xudc_ep_enable(ep, desc);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * xudc_ep_disable - Disables the given endpoint.
+ * @_ep: pointer to the usb endpoint structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_disable(struct usb_ep *_ep)
+{
+ struct xusb_ep *ep;
+ unsigned long flags;
+ u32 epcfg;
+ struct xusb_udc *udc;
+
+ if (!_ep) {
+ pr_debug("%s: invalid ep\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = to_xusb_ep(_ep);
+ udc = ep->udc;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ xudc_nuke(ep, -ESHUTDOWN);
+
+ /* Restore the endpoint's pristine config */
+ ep->desc = NULL;
+ ep->ep_usb.desc = NULL;
+
+ dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
+ /* Disable the endpoint.*/
+ epcfg = udc->read_fn(udc->addr + ep->offset);
+ epcfg &= ~XUSB_EP_CFG_VALID_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfg);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_alloc_request - Initializes the request queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: pointer to request structure on success and a NULL on failure.
+ */
+static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc;
+ struct xusb_req *req;
+
+ udc = ep->udc;
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req) {
+ dev_err(udc->dev, "%s:not enough memory", __func__);
+ return NULL;
+ }
+
+ req->ep = ep;
+ INIT_LIST_HEAD(&req->queue);
+ return &req->usb_req;
+}
+
+/**
+ * xudc_free_request - Releases the request from queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ */
+static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+
+ kfree(req);
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @ep0: pointer to the xusb endpoint 0 structure.
+ * @req: pointer to the xusb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req)
+{
+ struct xusb_udc *udc = ep0->udc;
+ u32 length;
+ u8 *corebuf;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+ return -EINVAL;
+ }
+ if (!list_empty(&ep0->queue)) {
+ dev_dbg(udc->dev, "%s:ep0 busy\n", __func__);
+ return -EBUSY;
+ }
+
+ req->usb_req.status = -EINPROGRESS;
+ req->usb_req.actual = 0;
+
+ list_add_tail(&req->queue, &ep0->queue);
+
+ if (udc->setup.bRequestType & USB_DIR_IN) {
+ prefetch(req->usb_req.buf);
+ length = req->usb_req.length;
+ corebuf = (void __force *) ((ep0->rambase << 2) +
+ udc->addr);
+ length = req->usb_req.actual = min_t(u32, length,
+ EP0_MAX_PACKET);
+ memcpy(corebuf, req->usb_req.buf, length);
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ } else {
+ if (udc->setup.wLength) {
+ /* Enable EP0 buffer to receive data */
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ } else {
+ xudc_wrstatus(udc);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * xudc_ep0_queue - Adds the request to endpoint 0 queue.
+ * @_ep: pointer to the usb endpoint 0 structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_ep *ep0 = to_xusb_ep(_ep);
+ struct xusb_udc *udc = ep0->udc;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ret = __xudc_ep0_queue(ep0, req);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * xudc_ep_queue - Adds the request to endpoint queue.
+ * @_ep: pointer to the usb endpoint structure.
+ * @_req: pointer to the usb request structure.
+ * @gfp_flags: Flags related to the request call.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_udc *udc = ep->udc;
+ int ret;
+ unsigned long flags;
+
+ if (!ep->desc) {
+ dev_dbg(udc->dev, "%s:queing request to disabled %s\n",
+ __func__, ep->name);
+ return -ESHUTDOWN;
+ }
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ dev_dbg(udc->dev, "%s, bogus device state\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ if (udc->dma_enabled) {
+ ret = usb_gadget_map_request(&udc->gadget, &req->usb_req,
+ ep->is_in);
+ if (ret) {
+ dev_dbg(udc->dev, "gadget_map failed ep%d\n",
+ ep->epnumber);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EAGAIN;
+ }
+ }
+
+ if (list_empty(&ep->queue)) {
+ if (ep->is_in) {
+ dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n");
+ if (!xudc_write_fifo(ep, req))
+ req = NULL;
+ } else {
+ dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n");
+ if (!xudc_read_fifo(ep, req))
+ req = NULL;
+ }
+ }
+
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * xudc_ep_dequeue - Removes the request from the queue.
+ * @_ep: pointer to the usb device endpoint structure.
+ * @_req: pointer to the usb request structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct xusb_ep *ep = to_xusb_ep(_ep);
+ struct xusb_req *req = to_xusb_req(_req);
+ struct xusb_udc *udc = ep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ /* Make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->usb_req == _req)
+ break;
+ }
+ if (&req->usb_req != _req) {
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return -EINVAL;
+ }
+ xudc_done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * xudc_ep0_enable - Enables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ * @desc: pointer to usb endpoint descriptor.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 enable should not be called by gadget layer.
+ */
+static int xudc_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+/**
+ * xudc_ep0_disable - Disables the given endpoint.
+ * @ep: pointer to the usb endpoint structure.
+ *
+ * Return: error always.
+ *
+ * endpoint 0 disable should not be called by gadget layer.
+ */
+static int xudc_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+static const struct usb_ep_ops xusb_ep0_ops = {
+ .enable = xudc_ep0_enable,
+ .disable = xudc_ep0_disable,
+ .alloc_request = xudc_ep_alloc_request,
+ .free_request = xudc_free_request,
+ .queue = xudc_ep0_queue,
+ .dequeue = xudc_ep_dequeue,
+ .set_halt = xudc_ep_set_halt,
+};
+
+static const struct usb_ep_ops xusb_ep_ops = {
+ .enable = xudc_ep_enable,
+ .disable = xudc_ep_disable,
+ .alloc_request = xudc_ep_alloc_request,
+ .free_request = xudc_free_request,
+ .queue = xudc_ep_queue,
+ .dequeue = xudc_ep_dequeue,
+ .set_halt = xudc_ep_set_halt,
+};
+
+/**
+ * xudc_get_frame - Reads the current usb frame number.
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: current frame number for success and error value on failure.
+ */
+static int xudc_get_frame(struct usb_gadget *gadget)
+{
+ struct xusb_udc *udc;
+ int frame;
+
+ if (!gadget)
+ return -ENODEV;
+
+ udc = to_udc(gadget);
+ frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET);
+ return frame;
+}
+
+/**
+ * xudc_wakeup - Send remote wakeup signal to host
+ * @gadget: pointer to the usb gadget structure.
+ *
+ * Return: 0 on success and error on failure
+ */
+static int xudc_wakeup(struct usb_gadget *gadget)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ u32 crtlreg;
+ int status = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* Remote wake up not enabled by host */
+ if (!udc->remote_wkp)
+ goto done;
+
+ crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+ crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK;
+ /* set remote wake up bit */
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+ /*
+ * wait for a while and reset remote wake up bit since this bit
+ * is not cleared by HW after sending remote wakeup to host.
+ */
+ mdelay(2);
+
+ crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK;
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+ status = 0;
+done:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/**
+ * xudc_pullup - start/stop USB traffic
+ * @gadget: pointer to the usb gadget structure.
+ * @is_on: flag to start or stop
+ *
+ * Return: 0 always
+ *
+ * This function starts/stops SIE engine of IP based on is_on.
+ */
+static int xudc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ unsigned long flags;
+ u32 crtlreg;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET);
+ if (is_on)
+ crtlreg |= XUSB_CONTROL_USB_READY_MASK;
+ else
+ crtlreg &= ~XUSB_CONTROL_USB_READY_MASK;
+
+ udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * xudc_eps_init - initialize endpoints.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_eps_init(struct xusb_udc *udc)
+{
+ u32 ep_number;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+ for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) {
+ struct xusb_ep *ep = &udc->ep[ep_number];
+
+ if (ep_number) {
+ list_add_tail(&ep->ep_usb.ep_list,
+ &udc->gadget.ep_list);
+ usb_ep_set_maxpacket_limit(&ep->ep_usb,
+ (unsigned short) ~0);
+ snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number);
+ ep->ep_usb.name = ep->name;
+ ep->ep_usb.ops = &xusb_ep_ops;
+ } else {
+ ep->ep_usb.name = ep0name;
+ usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET);
+ ep->ep_usb.ops = &xusb_ep0_ops;
+ }
+
+ ep->udc = udc;
+ ep->epnumber = ep_number;
+ ep->desc = NULL;
+ /*
+ * The configuration register address offset between
+ * each endpoint is 0x10.
+ */
+ ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10);
+ ep->is_in = 0;
+ ep->is_iso = 0;
+ ep->maxpacket = 0;
+ xudc_epconfig(ep, udc);
+
+ /* Initialize one queue per endpoint */
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+/**
+ * xudc_stop_activity - Stops any further activity on the device.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_stop_activity(struct xusb_udc *udc)
+{
+ int i;
+ struct xusb_ep *ep;
+
+ for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ xudc_nuke(ep, -ESHUTDOWN);
+ }
+}
+
+/**
+ * xudc_start - Starts the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to gadget driver structure
+ *
+ * Return: zero on success and error on failure
+ */
+static int xudc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+ const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (udc->driver) {
+ dev_err(udc->dev, "%s is already bound to %s\n",
+ udc->gadget.name, udc->driver->driver.name);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* hook up the driver */
+ udc->driver = driver;
+ udc->gadget.speed = driver->max_speed;
+
+ /* Enable the control endpoint. */
+ ret = __xudc_ep_enable(ep0, desc);
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+err:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return ret;
+}
+
+/**
+ * xudc_stop - stops the device.
+ * @gadget: pointer to the usb gadget structure
+ * @driver: pointer to usb gadget driver structure
+ *
+ * Return: zero always
+ */
+static int xudc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct xusb_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->driver = NULL;
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+
+ xudc_stop_activity(udc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops xusb_udc_ops = {
+ .get_frame = xudc_get_frame,
+ .wakeup = xudc_wakeup,
+ .pullup = xudc_pullup,
+ .udc_start = xudc_start,
+ .udc_stop = xudc_stop,
+};
+
+/**
+ * xudc_clear_stall_all_ep - clears stall of every endpoint.
+ * @udc: pointer to the udc structure.
+ */
+static void xudc_clear_stall_all_ep(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep;
+ u32 epcfgreg;
+ int i;
+
+ for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ if (ep->epnumber) {
+ /* Reset the toggle bit.*/
+ epcfgreg = udc->read_fn(udc->addr + ep->offset);
+ epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep->offset, epcfgreg);
+ }
+ }
+}
+
+/**
+ * xudc_startup_handler - The usb device controller interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus: The mask value containing the interrupt sources.
+ *
+ * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts.
+ */
+static void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+ u32 intrreg;
+
+ if (intrstatus & XUSB_STATUS_RESET_MASK) {
+
+ dev_dbg(udc->dev, "Reset\n");
+
+ if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK)
+ udc->gadget.speed = USB_SPEED_HIGH;
+ else
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ xudc_stop_activity(udc);
+ xudc_clear_stall_all_ep(udc);
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+ /* Set device address and remote wakeup to 0 */
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+ udc->remote_wkp = 0;
+
+ /* Enable the suspend, resume and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+ }
+ if (intrstatus & XUSB_STATUS_SUSPEND_MASK) {
+
+ dev_dbg(udc->dev, "Suspend\n");
+
+ /* Enable the reset, resume and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ if (udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+ if (intrstatus & XUSB_STATUS_RESUME_MASK) {
+ bool condition = (udc->usb_state != USB_STATE_SUSPENDED);
+
+ dev_WARN_ONCE(udc->dev, condition,
+ "Resume IRQ while not suspended\n");
+
+ dev_dbg(udc->dev, "Resume\n");
+
+ /* Enable the reset, suspend and disconnect */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK |
+ XUSB_STATUS_DISCONNECT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ udc->usb_state = 0;
+
+ if (udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+ if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) {
+
+ dev_dbg(udc->dev, "Disconnect\n");
+
+ /* Enable the reset, resume and suspend */
+ intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK |
+ XUSB_STATUS_SUSPEND_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg);
+
+ if (udc->driver && udc->driver->disconnect) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+}
+
+/**
+ * xudc_ep0_stall - Stall endpoint zero.
+ * @udc: pointer to the udc structure.
+ *
+ * This function stalls endpoint zero.
+ */
+static void xudc_ep0_stall(struct xusb_udc *udc)
+{
+ u32 epcfgreg;
+ struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO];
+
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+}
+
+/**
+ * xudc_setaddress - executes SET_ADDRESS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB SET_ADDRESS command
+ */
+static void xudc_setaddress(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ int ret;
+
+ req->usb_req.length = 0;
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+
+ dev_err(udc->dev, "Can't respond to SET ADDRESS request\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_getstatus - executes GET_STATUS command
+ * @udc: pointer to the udc structure.
+ *
+ * This function executes USB GET_STATUS command
+ */
+static void xudc_getstatus(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ struct xusb_ep *target_ep;
+ u16 status = 0;
+ u32 epcfgreg;
+ int epnum;
+ u32 halt;
+ int ret;
+
+ switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ /* Get device status */
+ status = 1 << USB_DEVICE_SELF_POWERED;
+ if (udc->remote_wkp)
+ status |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+ case USB_RECIP_INTERFACE:
+ break;
+ case USB_RECIP_ENDPOINT:
+ epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ target_ep = &udc->ep[epnum];
+ epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+ halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
+ if (udc->setup.wIndex & USB_DIR_IN) {
+ if (!target_ep->is_in)
+ goto stall;
+ } else {
+ if (target_ep->is_in)
+ goto stall;
+ }
+ if (halt)
+ status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ goto stall;
+ }
+
+ req->usb_req.length = 2;
+ *(u16 *)req->usb_req.buf = cpu_to_le16(status);
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+stall:
+ dev_err(udc->dev, "Can't respond to getstatus request\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_set_clear_feature - Executes the set feature and clear feature commands.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Processes the SET_FEATURE and CLEAR_FEATURE commands.
+ */
+static void xudc_set_clear_feature(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req = udc->req;
+ struct xusb_ep *target_ep;
+ u8 endpoint;
+ u8 outinbit;
+ u32 epcfgreg;
+ int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0);
+ int ret;
+
+ switch (udc->setup.bRequestType) {
+ case USB_RECIP_DEVICE:
+ switch (udc->setup.wValue) {
+ case USB_DEVICE_TEST_MODE:
+ /*
+ * The Test Mode will be executed
+ * after the status phase.
+ */
+ break;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ if (flag)
+ udc->remote_wkp = 1;
+ else
+ udc->remote_wkp = 0;
+ break;
+ default:
+ xudc_ep0_stall(udc);
+ break;
+ }
+ break;
+ case USB_RECIP_ENDPOINT:
+ if (!udc->setup.wValue) {
+ endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ target_ep = &udc->ep[endpoint];
+ outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
+ outinbit = outinbit >> 7;
+
+ /* Make sure direction matches.*/
+ if (outinbit != target_ep->is_in) {
+ xudc_ep0_stall(udc);
+ return;
+ }
+ epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
+ if (!endpoint) {
+ /* Clear the stall.*/
+ epcfgreg &= ~XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr,
+ target_ep->offset, epcfgreg);
+ } else {
+ if (flag) {
+ epcfgreg |= XUSB_EP_CFG_STALL_MASK;
+ udc->write_fn(udc->addr,
+ target_ep->offset,
+ epcfgreg);
+ } else {
+ /* Unstall the endpoint.*/
+ epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK |
+ XUSB_EP_CFG_DATA_TOGGLE_MASK);
+ udc->write_fn(udc->addr,
+ target_ep->offset,
+ epcfgreg);
+ }
+ }
+ }
+ break;
+ default:
+ xudc_ep0_stall(udc);
+ return;
+ }
+
+ req->usb_req.length = 0;
+ ret = __xudc_ep0_queue(ep0, req);
+ if (ret == 0)
+ return;
+
+ dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+ xudc_ep0_stall(udc);
+}
+
+/**
+ * xudc_handle_setup - Processes the setup packet.
+ * @udc: pointer to the usb device controller structure.
+ *
+ * Process setup packet and delegate to gadget layer.
+ */
+static void xudc_handle_setup(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct usb_ctrlrequest setup;
+ u32 *ep0rambase;
+
+ /* Load up the chapter 9 command buffer.*/
+ ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET);
+ memcpy(&setup, ep0rambase, 8);
+
+ udc->setup = setup;
+ udc->setup.wValue = cpu_to_le16(setup.wValue);
+ udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+ udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+ /* Clear previous requests */
+ xudc_nuke(ep0, -ECONNRESET);
+
+ if (udc->setup.bRequestType & USB_DIR_IN) {
+ /* Execute the get command.*/
+ udc->setupseqrx = STATUS_PHASE;
+ udc->setupseqtx = DATA_PHASE;
+ } else {
+ /* Execute the put command.*/
+ udc->setupseqrx = DATA_PHASE;
+ udc->setupseqtx = STATUS_PHASE;
+ }
+
+ switch (udc->setup.bRequest) {
+ case USB_REQ_GET_STATUS:
+ /* Data+Status phase form udc */
+ if ((udc->setup.bRequestType &
+ (USB_DIR_IN | USB_TYPE_MASK)) !=
+ (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ xudc_getstatus(udc);
+ return;
+ case USB_REQ_SET_ADDRESS:
+ /* Status phase from udc */
+ if (udc->setup.bRequestType != (USB_DIR_OUT |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+ xudc_setaddress(udc);
+ return;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* Requests with no data phase, status phase from udc */
+ if ((udc->setup.bRequestType & USB_TYPE_MASK)
+ != USB_TYPE_STANDARD)
+ break;
+ xudc_set_clear_feature(udc);
+ return;
+ default:
+ break;
+ }
+
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget, &setup) < 0)
+ xudc_ep0_stall(udc);
+ spin_lock(&udc->lock);
+}
+
+/**
+ * xudc_ep0_out - Processes the endpoint 0 OUT token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_out(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req;
+ u8 *ep0rambase;
+ unsigned int bytes_to_rx;
+ void *buffer;
+
+ req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+
+ switch (udc->setupseqrx) {
+ case STATUS_PHASE:
+ /*
+ * This resets both state machines for the next
+ * Setup packet.
+ */
+ udc->setupseqrx = SETUP_PHASE;
+ udc->setupseqtx = SETUP_PHASE;
+ req->usb_req.actual = req->usb_req.length;
+ xudc_done(ep0, req, 0);
+ break;
+ case DATA_PHASE:
+ bytes_to_rx = udc->read_fn(udc->addr +
+ XUSB_EP_BUF0COUNT_OFFSET);
+ /* Copy the data to be received from the DPRAM. */
+ ep0rambase = (u8 __force *) (udc->addr +
+ (ep0->rambase << 2));
+ buffer = req->usb_req.buf + req->usb_req.actual;
+ req->usb_req.actual = req->usb_req.actual + bytes_to_rx;
+ memcpy(buffer, ep0rambase, bytes_to_rx);
+
+ if (req->usb_req.length == req->usb_req.actual) {
+ /* Data transfer completed get ready for Status stage */
+ xudc_wrstatus(udc);
+ } else {
+ /* Enable EP0 buffer to receive data */
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * xudc_ep0_in - Processes the endpoint 0 IN token.
+ * @udc: pointer to the usb device controller structure.
+ */
+static void xudc_ep0_in(struct xusb_udc *udc)
+{
+ struct xusb_ep *ep0 = &udc->ep[0];
+ struct xusb_req *req;
+ unsigned int bytes_to_tx;
+ void *buffer;
+ u32 epcfgreg;
+ u16 count = 0;
+ u16 length;
+ u8 *ep0rambase;
+ u8 test_mode = udc->setup.wIndex >> 8;
+
+ req = list_first_entry(&ep0->queue, struct xusb_req, queue);
+ bytes_to_tx = req->usb_req.length - req->usb_req.actual;
+
+ switch (udc->setupseqtx) {
+ case STATUS_PHASE:
+ switch (udc->setup.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* Set the address of the device.*/
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET,
+ udc->setup.wValue);
+ break;
+ case USB_REQ_SET_FEATURE:
+ if (udc->setup.bRequestType ==
+ USB_RECIP_DEVICE) {
+ if (udc->setup.wValue ==
+ USB_DEVICE_TEST_MODE)
+ udc->write_fn(udc->addr,
+ XUSB_TESTMODE_OFFSET,
+ test_mode);
+ }
+ break;
+ }
+ req->usb_req.actual = req->usb_req.length;
+ xudc_done(ep0, req, 0);
+ break;
+ case DATA_PHASE:
+ if (!bytes_to_tx) {
+ /*
+ * We're done with data transfer, next
+ * will be zero length OUT with data toggle of
+ * 1. Setup data_toggle.
+ */
+ epcfgreg = udc->read_fn(udc->addr + ep0->offset);
+ epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK;
+ udc->write_fn(udc->addr, ep0->offset, epcfgreg);
+ udc->setupseqtx = STATUS_PHASE;
+ } else {
+ length = count = min_t(u32, bytes_to_tx,
+ EP0_MAX_PACKET);
+ /* Copy the data to be transmitted into the DPRAM. */
+ ep0rambase = (u8 __force *) (udc->addr +
+ (ep0->rambase << 2));
+ buffer = req->usb_req.buf + req->usb_req.actual;
+ req->usb_req.actual = req->usb_req.actual + length;
+ memcpy(ep0rambase, buffer, length);
+ }
+ udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count);
+ udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @intrstatus: It's the mask value for the interrupt sources on endpoint 0.
+ *
+ * Processes the commands received during enumeration phase.
+ */
+static void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus)
+{
+
+ if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) {
+ xudc_handle_setup(udc);
+ } else {
+ if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK)
+ xudc_ep0_out(udc);
+ else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK)
+ xudc_ep0_in(udc);
+ }
+}
+
+/**
+ * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler.
+ * @udc: pointer to the udc structure.
+ * @epnum: End point number for which the interrupt is to be processed
+ * @intrstatus: mask value for interrupt sources of endpoints other
+ * than endpoint 0.
+ *
+ * Processes the buffer completion interrupts.
+ */
+static void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum,
+ u32 intrstatus)
+{
+
+ struct xusb_req *req;
+ struct xusb_ep *ep;
+
+ ep = &udc->ep[epnum];
+ /* Process the End point interrupts.*/
+ if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum))
+ ep->buffer0ready = 0;
+ if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum))
+ ep->buffer1ready = 0;
+
+ if (list_empty(&ep->queue))
+ return;
+
+ req = list_first_entry(&ep->queue, struct xusb_req, queue);
+
+ if (ep->is_in)
+ xudc_write_fifo(ep, req);
+ else
+ xudc_read_fifo(ep, req);
+}
+
+/**
+ * xudc_irq - The main interrupt handler.
+ * @irq: The interrupt number.
+ * @_udc: pointer to the usb device controller structure.
+ *
+ * Return: IRQ_HANDLED after the interrupt is handled.
+ */
+static irqreturn_t xudc_irq(int irq, void *_udc)
+{
+ struct xusb_udc *udc = _udc;
+ u32 intrstatus;
+ u32 ier;
+ u8 index;
+ u32 bufintr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * Event interrupts are level sensitive hence first disable
+ * IER, read ISR and figure out active interrupts.
+ */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier &= ~XUSB_STATUS_INTR_EVENT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ /* Read the Interrupt Status Register.*/
+ intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET);
+
+ /* Call the handler for the event interrupt.*/
+ if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) {
+ /*
+ * Check if there is any action to be done for :
+ * - USB Reset received {XUSB_STATUS_RESET_MASK}
+ * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK}
+ * - USB Resume received {XUSB_STATUS_RESUME_MASK}
+ * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK}
+ */
+ xudc_startup_handler(udc, intrstatus);
+ }
+
+ /* Check the buffer completion interrupts */
+ if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) {
+ /* Enable Reset, Suspend, Resume and Disconnect */
+ ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET);
+ ier |= XUSB_STATUS_INTR_EVENT_MASK;
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK)
+ xudc_ctrl_ep_handler(udc, intrstatus);
+
+ for (index = 1; index < 8; index++) {
+ bufintr = ((intrstatus &
+ (XUSB_STATUS_EP1_BUFF1_COMP_MASK <<
+ (index - 1))) || (intrstatus &
+ (XUSB_STATUS_EP1_BUFF2_COMP_MASK <<
+ (index - 1))));
+ if (bufintr) {
+ xudc_nonctrl_ep_handler(udc, index,
+ intrstatus);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/**
+ * xudc_probe - The device probe function for driver initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 for success and error value on failure
+ */
+static int xudc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct xusb_udc *udc;
+ struct xusb_ep *ep0;
+ int irq;
+ int ret;
+ u32 ier;
+ u8 *buff;
+
+ udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ /* Create a dummy request for GET_STATUS, SET_ADDRESS */
+ udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req),
+ GFP_KERNEL);
+ if (!udc->req)
+ return -ENOMEM;
+
+ buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ udc->req->usb_req.buf = buff;
+
+ /* Map the registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ udc->addr = devm_ioremap_resource(&pdev->dev, res);
+ if (!udc->addr)
+ return -ENOMEM;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "unable to get irq\n");
+ return irq;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0,
+ dev_name(&pdev->dev), udc);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "unable to request irq %d", irq);
+ goto fail;
+ }
+
+ udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma");
+
+ /* Setup gadget structure */
+ udc->gadget.ops = &xusb_udc_ops;
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb;
+ udc->gadget.name = driver_name;
+
+ spin_lock_init(&udc->lock);
+
+ /* Check for IP endianness */
+ udc->write_fn = xudc_write32_be;
+ udc->read_fn = xudc_read32_be;
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J);
+ if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET))
+ != TEST_J) {
+ udc->write_fn = xudc_write32;
+ udc->read_fn = xudc_read32;
+ }
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0);
+
+ xudc_eps_init(udc);
+
+ ep0 = &udc->ep[0];
+
+ /* Set device address to 0.*/
+ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
+
+ ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
+ if (ret)
+ goto fail;
+
+ udc->dev = &udc->gadget.dev;
+
+ /* Enable the interrupts.*/
+ ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK |
+ XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK |
+ XUSB_STATUS_SETUP_PACKET_MASK |
+ XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK;
+
+ udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier);
+
+ platform_set_drvdata(pdev, udc);
+
+ dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to 0x%08X %s\n",
+ driver_name, (u32)res->start, (u32 __force)udc->addr,
+ udc->dma_enabled ? "with DMA" : "without DMA");
+
+ return 0;
+fail:
+ dev_err(&pdev->dev, "probe failed, %d\n", ret);
+ return ret;
+}
+
+/**
+ * xudc_remove - Releases the resources allocated during the initialization.
+ * @pdev: pointer to the platform device structure.
+ *
+ * Return: 0 always
+ */
+static int xudc_remove(struct platform_device *pdev)
+{
+ struct xusb_udc *udc = platform_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id usb_of_match[] = {
+ { .compatible = "xlnx,usb2-device-4.00.a", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, usb_of_match);
+
+static struct platform_driver xudc_driver = {
+ .driver = {
+ .name = driver_name,
+ .of_match_table = usb_of_match,
+ },
+ .probe = xudc_probe,
+ .remove = xudc_remove,
+};
+
+module_platform_driver(xudc_driver);
+
+MODULE_DESCRIPTION("Xilinx udc driver");
+MODULE_AUTHOR("Xilinx, Inc");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 829f446064ea..90e6644dc886 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -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,
};
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 3ee133f675ab..acdfb3e68a90 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -212,7 +212,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);
@@ -290,7 +290,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);
}
}
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index c791ba5da91a..154bcf1b5dfa 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -870,6 +870,7 @@ static int dsps_suspend(struct device *dev)
struct musb *musb = platform_get_drvdata(glue->musb);
void __iomem *mbase = musb->ctrl_base;
+ del_timer_sync(&glue->timer);
glue->context.control = dsps_readl(mbase, wrp->control);
glue->context.epintr = dsps_readl(mbase, wrp->epintr_set);
glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set);
@@ -895,6 +896,7 @@ static int dsps_resume(struct device *dev)
dsps_writel(mbase, wrp->mode, glue->context.mode);
dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
+ setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
return 0;
}
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index e253fa05be68..0cd1f44f0ee8 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -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
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 24a91332d4ad..75f2bba58c84 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -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
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index afc09087ec36..7bb48af9e027 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -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");
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 00972eca04e7..0e0c41587a08 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -125,6 +125,11 @@ 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 mxs_phy_data imx6sx_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
@@ -135,6 +140,7 @@ 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
index ac025ca08425..000000000000
--- a/drivers/usb/phy/phy-samsung-usb.c
+++ /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
index 80eedd45a20a..000000000000
--- a/drivers/usb/phy/phy-samsung-usb.h
+++ /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
index b3ba86627b72..000000000000
--- a/drivers/usb/phy/phy-samsung-usb2.c
+++ /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
index cc0819248acf..000000000000
--- a/drivers/usb/phy/phy-samsung-usb3.c
+++ /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");
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index 04778cf80d60..44ea082e40dc 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -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);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 1b9bf8d83235..b3b6813ab270 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -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
index 1bd24cba982b..000000000000
--- a/include/linux/platform_data/samsung-usbphy.h
+++ /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 */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index c3a61853cd13..d18811433324 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -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).
*/
@@ -816,6 +817,8 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
* Called in a context that permits sleeping.
* @suspend: Invoked on USB suspend. May be called in_interrupt.
* @resume: Invoked on USB resume. May be called in_interrupt.
+ * @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
+ * and should be called in_interrupt.
* @driver: Driver model state for this driver.
*
* Devices are disabled till a gadget driver successfully bind()s, which
@@ -873,6 +876,7 @@ struct usb_gadget_driver {
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
+ void (*reset)(struct usb_gadget *);
/* FIXME support safe rmmod */
struct device_driver driver;
@@ -1013,6 +1017,12 @@ extern void usb_gadget_set_state(struct usb_gadget *gadget,
/*-------------------------------------------------------------------------*/
+/* utility to tell udc core that the bus reset occurs */
+extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver);
+
+/*-------------------------------------------------------------------------*/
+
/* utility wrapping a simple endpoint selection policy */
extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 0154b2859fd7..295ba299e7bd 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -19,6 +19,7 @@ enum functionfs_flags {
FUNCTIONFS_HAS_HS_DESC = 2,
FUNCTIONFS_HAS_SS_DESC = 4,
FUNCTIONFS_HAS_MS_OS_DESC = 8,
+ FUNCTIONFS_VIRTUAL_ADDR = 16,
};
/* Descriptor of an non-audio endpoint */
@@ -32,6 +33,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 +103,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 |
* |-----+-----------+--------------+--------------------------------------|
@@ -265,6 +276,12 @@ struct usb_functionfs_event {
*/
#define FUNCTIONFS_ENDPOINT_REVMAP _IO('g', 129)
+/*
+ * Returns endpoint descriptor. If function is not active returns -ENODEV.
+ */
+#define FUNCTIONFS_ENDPOINT_DESC _IOR('g', 130, \
+ struct usb_endpoint_descriptor)
+
#endif /* _UAPI__LINUX_FUNCTIONFS_H__ */
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
index a87e99f37c52..88d5e71be044 100644
--- a/tools/usb/ffs-test.c
+++ b/tools/usb/ffs-test.c
@@ -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);