diff options
68 files changed, 3055 insertions, 2337 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/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 cef181a9d8bd..fe3eed89e4c3 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,imx23-usbphy" is still a fallback for other strings - reg: Should contain registers location and length - interrupts: Should contain phy interrupt diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt new file mode 100644 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/MAINTAINERS b/MAINTAINERS index 5e7866a486b0..5d66c037be18 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> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 785510a0a0c3..5238251ec653 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -80,6 +80,15 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_ST + tristate "STMicroelectronics Platforms" + depends on ARCH_STI && OF + default USB_DWC3 + help + STMicroelectronics SoCs with one DesignWare Core USB3 IP + inside (i.e. STiH407). + Say 'Y' or 'M' if you have one such device. + comment "Debugging features" config USB_DWC3_DEBUG diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 10ac3e72482e..3e88c77483c1 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,4 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b769c1faaf03..f2050e844b4b 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 9dcfbe7cd5f5..3ef5e4fc315b 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-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, ¶ms); 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 349cacc577d8..f2dbaca5df2c 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); } /* @@ -1233,6 +1145,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", request, ep->name, request->length); + trace_dwc3_ep_queue(req); spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1253,6 +1166,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, unsigned long flags; int ret = 0; + trace_dwc3_ep_dequeue(req); + spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(r, &dep->request_list, list) { @@ -1743,11 +1658,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, u8 epnum = (i << 1) | (!!direction); dep = kzalloc(sizeof(*dep), GFP_KERNEL); - if (!dep) { - dev_err(dwc->dev, "can't allocate endpoint %d\n", - epnum); + if (!dep) return -ENOMEM; - } dep->dwc = dwc; dep->number = epnum; @@ -1846,6 +1758,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int s_pkt = 0; unsigned int trb_status; + trace_dwc3_complete_trb(dep, trb); + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) /* * We continue despite the error. There is not much we @@ -2020,9 +1934,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (!(dep->flags & DWC3_EP_ENABLED)) return; - dev_vdbg(dwc->dev, "%s: %s\n", dep->name, - dwc3_ep_event_string(event->endpoint_event)); - if (epnum == 0 || epnum == 1) { dwc3_ep0_interrupt(dwc, event); return; @@ -2215,8 +2126,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -2235,8 +2144,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a @@ -2321,8 +2228,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) u32 reg; u8 speed; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; @@ -2420,8 +2325,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) { - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * TODO take core out of low power mode when that's * implemented. @@ -2526,10 +2429,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, break; } - dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n", - dwc3_gadget_link_string(dwc->link_state), - dwc->link_state, dwc3_gadget_link_string(next), next); - dwc->link_state = next; } @@ -2606,6 +2505,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, static void dwc3_process_event_entry(struct dwc3 *dwc, const union dwc3_event *event) { + trace_dwc3_event(event->raw); + /* Endpoint IRQ, handle it and return early */ if (event->type.is_devspec == 0) { /* depevt */ @@ -2759,7 +2660,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { - dev_err(dwc->dev, "failed to allocate setup buffer\n"); ret = -ENOMEM; goto err2; } 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..68302aa604be 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -181,6 +181,12 @@ config USB_F_MASS_STORAGE config USB_F_FS tristate +config USB_F_UAC1 + tristate + +config USB_F_UAC2 + tristate + choice tristate "USB Gadget Drivers" default USB_ETH 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..4514e73d9e70 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1955,8 +1955,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); } if (cdev->req) { - kfree(cdev->req->buf); usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + kfree(cdev->req->buf); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } cdev->next_string_id = 0; diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 83ae1065149d..a49fdf7b59e1 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,7 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +usb_f_uac1-y := f_uac1.o u_uac1.o +obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o +usb_f_uac2-y := f_uac2.o +obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o 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_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..7c091a328228 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; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 4096) { + ret = -EINVAL; + goto end; + } + + opts->int_interval = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_interval = + __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR, + f_ss_opts_int_interval_show, + f_ss_opts_int_interval_store); + +static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxpacket); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + if (num > 1024) { + ret = -EINVAL; + goto end; + } + + opts->int_maxpacket = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxpacket = + __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxpacket_show, + f_ss_opts_int_maxpacket_store); + +static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_mult); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 2) { + ret = -EINVAL; + goto end; + } + + opts->int_mult = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_mult = + __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR, + f_ss_opts_int_mult_show, + f_ss_opts_int_mult_store); + +static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->int_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxburst = + __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxburst_show, + f_ss_opts_int_maxburst_store); + static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_pattern.attr, &f_ss_opts_isoc_interval.attr, @@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_isoc_mult.attr, &f_ss_opts_isoc_maxburst.attr, &f_ss_opts_bulk_buflen.attr, + &f_ss_opts_int_interval.attr, + &f_ss_opts_int_maxpacket.attr, + &f_ss_opts_int_mult.attr, + &f_ss_opts_int_maxburst.attr, NULL, }; @@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; + ss_opts->int_interval = GZERO_INT_INTERVAL; + ss_opts->int_maxpacket = GZERO_INT_MAXPACKET; config_group_init_type_name(&ss_opts->func_inst.group, "", &ss_func_type); 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..187c3a04cf70 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -29,21 +29,9 @@ #include "uvc.h" unsigned int uvc_gadget_trace_param; - -/*-------------------------------------------------------------------------*/ - -/* module parameters specific to the Video streaming endpoint */ -static unsigned int streaming_interval = 1; -module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_interval, "1 - 16"); - -static unsigned int streaming_maxpacket = 1024; -module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); - +static unsigned int streaming_interval; +static unsigned int streaming_maxpacket; static unsigned int streaming_maxburst; -module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); /* -------------------------------------------------------------------------- * Function descriptors @@ -251,6 +239,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) return -EINVAL; + /* Tell the complete callback to generate an event for the next request + * that will be enqueued by UVCIOC_SEND_RESPONSE. + */ + uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN); + uvc->event_length = le16_to_cpu(ctrl->wLength); + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_SETUP; memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); @@ -408,7 +402,9 @@ uvc_register_video(struct uvc_device *uvc) video->v4l2_dev = &uvc->v4l2_dev; video->fops = &uvc_v4l2_fops; + video->ioctl_ops = &uvc_v4l2_ioctl_ops; video->release = video_device_release; + video->vfl_dir = VFL_DIR_TX; strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); uvc->vdev = video; @@ -720,10 +716,9 @@ error: if (uvc->video.ep) uvc->video.ep->driver_data = NULL; - if (uvc->control_req) { + if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); - kfree(uvc->control_buf); - } + kfree(uvc->control_buf); usb_free_all_descriptors(f); return ret; @@ -749,7 +744,9 @@ uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *ss_control, const struct uvc_descriptor_header * const *fs_streaming, const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming) + const struct uvc_descriptor_header * const *ss_streaming, + unsigned int stream_interv, unsigned int stream_maxpkt, + unsigned int stream_maxburst, unsigned int trace) { struct uvc_device *uvc; int ret = 0; @@ -787,6 +784,10 @@ uvc_bind_config(struct usb_configuration *c, ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; + streaming_interval = stream_interv; + streaming_maxpacket = stream_maxpkt; + streaming_maxburst = stream_maxburst; + uvc_gadget_trace_param = trace; uvc->desc.fs_control = fs_control; uvc->desc.ss_control = ss_control; uvc->desc.fs_streaming = fs_streaming; @@ -831,6 +832,4 @@ error: return ret; } -module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(trace, "Trace level bitmask"); diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index ec52752f7326..74b9602ef2d8 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -21,7 +21,11 @@ int uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *hs_control, const struct uvc_descriptor_header * const *fs_streaming, const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming); + const struct uvc_descriptor_header * const *ss_streaming, + unsigned int streaming_interval_webcam, + unsigned int streaming_maxpacket_webcam, + unsigned int streaming_maxburst_webcam, + unsigned int uvc_gadget_trace_webcam); #endif /* _F_UVC_H_ */ 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_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/uvc.h b/drivers/usb/gadget/function/uvc.h index 7a9111de8054..0a283b1237d5 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -96,9 +96,6 @@ extern unsigned int uvc_gadget_trace_param; * Driver specific constants */ -#define DRIVER_VERSION "0.1.0" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) - #define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1c29bc954db9..8590f9ff0849 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -132,7 +132,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue, int ret; queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ad48e81155e2..14c3a3734b95 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -48,7 +48,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) } /* -------------------------------------------------------------------------- - * V4L2 + * V4L2 ioctls */ struct uvc_format @@ -63,8 +63,29 @@ static struct uvc_format uvc_formats[] = { }; static int -uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + + strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); + strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), + sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + return 0; +} + +static int +uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + fmt->fmt.pix.pixelformat = video->fcc; fmt->fmt.pix.width = video->width; fmt->fmt.pix.height = video->height; @@ -78,8 +99,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; struct uvc_format *format; unsigned int imagesize; unsigned int bpl; @@ -116,209 +140,184 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_open(struct file *file) +uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle; - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (handle == NULL) - return -ENOMEM; - - v4l2_fh_init(&handle->vfh, vdev); - v4l2_fh_add(&handle->vfh); + struct uvc_video *video = &uvc->video; - handle->device = &uvc->video; - file->private_data = &handle->vfh; + if (b->type != video->queue.queue.type) + return -EINVAL; - uvc_function_connect(uvc); - return 0; + return uvc_alloc_buffers(&video->queue, b); } static int -uvc_v4l2_release(struct file *file) +uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct uvc_video *video = handle->device; - - uvc_function_disconnect(uvc); - - uvc_video_enable(video, 0); - uvc_free_buffers(&video->queue); - - file->private_data = NULL; - v4l2_fh_del(&handle->vfh); - v4l2_fh_exit(&handle->vfh); - kfree(handle); + struct uvc_video *video = &uvc->video; - return 0; + return uvc_query_buffer(&video->queue, b); } -static long -uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int +uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct usb_composite_dev *cdev = uvc->func.config->cdev; struct uvc_video *video = &uvc->video; - int ret = 0; - - switch (cmd) { - /* Query capabilities */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof *cap); - strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); - strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), - sizeof cap->bus_info); - cap->version = DRIVER_VERSION_NUMBER; - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - break; - } - - /* Get & Set format */ - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt = arg; + int ret; - if (fmt->type != video->queue.queue.type) - return -EINVAL; + ret = uvc_queue_buffer(&video->queue, b); + if (ret < 0) + return ret; - return uvc_v4l2_get_format(video, fmt); - } + return uvc_video_pump(video); +} - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; +static int +uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - if (fmt->type != video->queue.queue.type) - return -EINVAL; + return uvc_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); +} - return uvc_v4l2_set_format(video, fmt); - } +static int +uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + int ret; - /* Buffers & streaming */ - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *rb = arg; + if (type != video->queue.queue.type) + return -EINVAL; - if (rb->type != video->queue.queue.type) - return -EINVAL; + /* Enable UVC video. */ + ret = uvc_video_enable(video, 1); + if (ret < 0) + return ret; - ret = uvc_alloc_buffers(&video->queue, rb); - if (ret < 0) - return ret; + /* + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ + uvc_function_setup_continue(uvc); + uvc->state = UVC_STATE_STREAMING; - ret = 0; - break; - } - - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; + return 0; +} - return uvc_query_buffer(&video->queue, buf); - } +static int +uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - case VIDIOC_QBUF: - if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0) - return ret; + if (type != video->queue.queue.type) + return -EINVAL; - return uvc_video_pump(video); + return uvc_video_enable(video, 0); +} - case VIDIOC_DQBUF: - return uvc_dequeue_buffer(&video->queue, arg, - file->f_flags & O_NONBLOCK); +static int +uvc_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) + return -EINVAL; - case VIDIOC_STREAMON: - { - int *type = arg; + return v4l2_event_subscribe(fh, sub, 2, NULL); +} - if (*type != video->queue.queue.type) - return -EINVAL; +static int +uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} - /* Enable UVC video. */ - ret = uvc_video_enable(video, 1); - if (ret < 0) - return ret; +static long +uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); - /* - * Complete the alternate setting selection setup phase now that - * userspace is ready to provide video frames. - */ - uvc_function_setup_continue(uvc); - uvc->state = UVC_STATE_STREAMING; + switch (cmd) { + case UVCIOC_SEND_RESPONSE: + return uvc_send_response(uvc, arg); - return 0; + default: + return -ENOIOCTLCMD; } +} - case VIDIOC_STREAMOFF: - { - int *type = arg; - - if (*type != video->queue.queue.type) - return -EINVAL; +static const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { + .vidioc_querycap = uvc_v4l2_querycap, + .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, + .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .vidioc_reqbufs = uvc_v4l2_reqbufs, + .vidioc_querybuf = uvc_v4l2_querybuf, + .vidioc_qbuf = uvc_v4l2_qbuf, + .vidioc_dqbuf = uvc_v4l2_dqbuf, + .vidioc_streamon = uvc_v4l2_streamon, + .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_subscribe_event = uvc_v4l2_subscribe_event, + .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, + .vidioc_default = uvc_v4l2_ioctl_default, +}; - return uvc_video_enable(video, 0); - } +/* -------------------------------------------------------------------------- + * V4L2 + */ - /* Events */ - case VIDIOC_DQEVENT: - { - struct v4l2_event *event = arg; - - ret = v4l2_event_dequeue(&handle->vfh, event, - file->f_flags & O_NONBLOCK); - if (ret == 0 && event->type == UVC_EVENT_SETUP) { - struct uvc_event *uvc_event = (void *)&event->u.data; - - /* Tell the complete callback to generate an event for - * the next request that will be enqueued by - * uvc_event_write. - */ - uvc->event_setup_out = - !(uvc_event->req.bRequestType & USB_DIR_IN); - uvc->event_length = uvc_event->req.wLength; - } +static int +uvc_v4l2_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle; - return ret; - } + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; - case VIDIOC_SUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; + v4l2_fh_init(&handle->vfh, vdev); + v4l2_fh_add(&handle->vfh); - if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) - return -EINVAL; + handle->device = &uvc->video; + file->private_data = &handle->vfh; - return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); - } + uvc_function_connect(uvc); + return 0; +} - case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_event_unsubscribe(&handle->vfh, arg); +static int +uvc_v4l2_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); + struct uvc_video *video = handle->device; - case UVCIOC_SEND_RESPONSE: - ret = uvc_send_response(uvc, arg); - break; + uvc_function_disconnect(uvc); - default: - return -ENOIOCTLCMD; - } + uvc_video_enable(video, 0); + uvc_free_buffers(&video->queue); - return ret; -} + file->private_data = NULL; + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); -static long -uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); + return 0; } static int @@ -355,7 +354,7 @@ static struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, - .ioctl = uvc_v4l2_ioctl, + .ioctl = video_ioctl2, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, #ifndef CONFIG_MMU diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index a5eb9a3fbb7a..1ff478a961a6 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -171,7 +171,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) break; case -ESHUTDOWN: /* disconnect from host. */ - printk(KERN_INFO "VS request cancelled.\n"); + printk(KERN_DEBUG "VS request cancelled.\n"); uvc_queue_cancel(queue, 1); goto requeue; diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index aa376f006333..bbd4b8545b9d 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, 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/webcam.c b/drivers/usb/gadget/legacy/webcam.c index a11d8e420bfe..f826622d1dc2 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -29,6 +29,25 @@ #include "f_uvc.c" USB_GADGET_COMPOSITE_OPTIONS(); + +/*-------------------------------------------------------------------------*/ + +/* module parameters specific to the Video streaming endpoint */ +static unsigned int streaming_interval = 1; +module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +static unsigned int streaming_maxpacket = 1024; +module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); + +static unsigned int streaming_maxburst; +module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + +static unsigned int trace; +module_param(trace, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); /* -------------------------------------------------------------------------- * Device descriptor */ @@ -326,9 +345,11 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { static int __init webcam_config_bind(struct usb_configuration *c) { - return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, - uvc_fs_streaming_cls, uvc_hs_streaming_cls, - uvc_ss_streaming_cls); + return uvc_bind_config(c, uvc_fs_control_cls, + uvc_ss_control_cls, uvc_fs_streaming_cls, + uvc_hs_streaming_cls, uvc_ss_streaming_cls, + streaming_interval, streaming_maxpacket, + streaming_maxburst, trace); } static struct usb_configuration webcam_config_driver = { 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..0c172809b53b 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. 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/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(¶m->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 47ae6455d073..ecdd6328aafb 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -200,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) if (!list_empty(&controller->early_tx_list)) { ret = HRTIMER_RESTART; hrtimer_forward_now(&controller->early_tx, - ktime_set(0, 50 * NSEC_PER_USEC)); + ktime_set(0, 20 * NSEC_PER_USEC)); } spin_unlock_irqrestore(&musb->lock, flags); @@ -278,7 +278,7 @@ static void cppi41_dma_callback(void *private_data) hrtimer_start_range_ns(&controller->early_tx, ktime_set(0, usecs * NSEC_PER_USEC), - 40 * NSEC_PER_USEC, + 20 * NSEC_PER_USEC, HRTIMER_MODE_REL); } } 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 c42bdf0c4a1f..8c2f23b75d6d 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -125,10 +125,16 @@ static const struct mxs_phy_data imx6sl_phy_data = { MXS_PHY_NEED_IP_FIX, }; +static const struct mxs_phy_data vf610_phy_data = { + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, +}; + static const struct of_device_id mxs_phy_dt_ids[] = { { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, + { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c deleted file mode 100644 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..c540557b564b 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). */ diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 0154b2859fd7..6d2a16b834bf 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -32,6 +32,16 @@ struct usb_endpoint_descriptor_no_audio { __u8 bInterval; } __attribute__((packed)); +struct usb_functionfs_descs_head_v2 { + __le32 magic; + __le32 length; + __le32 flags; + /* + * __le32 fs_count, hs_count, fs_count; must be included manually in + * the structure taking flags into consideration. + */ +} __attribute__((packed)); + /* Legacy format, deprecated as of 3.14. */ struct usb_functionfs_descs_head { __le32 magic; @@ -92,7 +102,7 @@ struct usb_ext_prop_desc { * structure. Any flags that are not recognised cause the whole block to be * rejected with -ENOSYS. * - * Legacy descriptors format: + * Legacy descriptors format (deprecated as of 3.14): * * | off | name | type | description | * |-----+-----------+--------------+--------------------------------------| 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); |